OSX MetalKit CVMetalTextureCacheCreateTextureFromImage 失败,状态:-6660

OSX MetalKit CVMetalTextureCacheCreateTextureFromImage failed, status: -6660

我首先尝试从 RAW 内存中制作 CVPixelBuffer 然后 MTLTexture 来自 CVPixelBuffer,但是 在 运行 以下代码之后我总是遇到错误

CVMetalTextureCacheCreateTextureFromImage failed, status: -6660 0x0

这个错误是从哪里来的?

    id<MTLTexture> makeTextureWithBytes(id<MTLDevice> mtl_device, 
                                        int width, 
                                        int height, 
                                        void *baseAddress, int bytesPerRow)
    {

    CVMetalTextureCacheRef textureCache = NULL;

    CVReturn status = CVMetalTextureCacheCreate(kCFAllocatorDefault, nullptr, mtl_device, nullptr, &textureCache);
    if(status != kCVReturnSuccess || textureCache == NULL)
    {
        return nullptr;
    }

    NSDictionary* cvBufferProperties = @{
        (__bridge NSString*)kCVPixelBufferOpenGLCompatibilityKey : @YES,
        (__bridge NSString*)kCVPixelBufferMetalCompatibilityKey : @YES,
    };

    CVPixelBufferRef pxbuffer = NULL;

    status = CVPixelBufferCreateWithBytes(kCFAllocatorDefault,
                                          width,
                                          height,
                                          kCVPixelFormatType_32BGRA,
                                          baseAddress,
                                          bytesPerRow,
                                          releaseCallback,
                                          NULL/*releaseRefCon*/,
                                          (__bridge CFDictionaryRef)cvBufferProperties,
                                          &pxbuffer);

    if(status != kCVReturnSuccess || pxbuffer == NULL)
    {
        return nullptr;
    }

    CVMetalTextureRef cvTexture = NULL;   

    status = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                       textureCache,
                                                       pxbuffer,
                                                       nullptr,
                                                       MTLPixelFormatBGRA8Unorm,
                                                       1920,
                                                       1080,
                                                       0,
                                                       &cvTexture);

    if(status != kCVReturnSuccess || cvTexture == NULL)
    {
        std::cout << "CVMetalTextureCacheCreateTextureFromImage failed, status: " << status << " " << cvTexture << std::endl;
        return nullptr;
    }
    id<MTLTexture> metalTexture = CVMetalTextureGetTexture(cvTexture);

    CFRelease(cvTexture);

    return metalTexture;
}

当 CVPixelBuffer 不受 IOSurface 支持时会发生错误。但是,您不能从字节创建 IOSurface 支持的 CVPixelBuffer。因此,尽管设置了 kCVPixelBufferMetalCompatibilityKeyCVPixelBufferCreateWithBytes(及其平面对应物)不会使用 IOSurface 支持缓冲区。

2 种解决方法(可能还有第 3 种)

  1. 创建一个空的 CVpixelBuffer 并使用 memcpy。您需要在每个循环中创建一个新的像素缓冲区,因此建议使用 PixelBufferPool。
CVPixelBufferPoolRef pixelPool; // initialised prior
void *srcBaseAddress;  // initialised prior

CVPixelBufferRef currentFrame; 
CVPixelBufferPoolCreatePixelBuffer(nil, pixelPool, &currentFrame);

CVPixelBufferLockBaseAddress(currentFrame,0);
void *cvBaseAddress=CVPixelBufferGetBaseAddress(currentFrame);
size_t size= CVPixelBufferGetDataSize(currentFrame);
memcpy(cvBaseAddress,srcBaseAddress,size);
  1. 完全跳过CVPixelBuffer,直接写入MTLTexture内存;只要金属支持你的像素格式。但是,由于您可能正在渲染到 RGB 显示器,因此您可以编写自己的金属内核以转换为 RGB。记得先用metalTextureDescriptor制作贴图。
id<MTLDevice> metalDevice; // You know how to get this
unsigned int width; // from your source image data
unsigned int height;// from your source image data
unsigned int rowBytes; // from your source image data

MTLTextureDescriptor *mtd = [MTLTextureDescriptor 
texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRG422 
        width:width
        height:height
        mipmapped:NO];

id<MTLTexture> mtlTexture = [metalDevice newTextureWithDescriptor:mtd];


[mtlTexture replaceRegion:MTLRegionMake2D(0,0,width,height) 
        mipmapLevel:0
        withBytes:srcBaseAddress
        bytesPerRow:rowBytes];

第三种方法可能是将 CVPixelBuffer 转换为 CIImage,并使用 Metal 支持的 CIContext。像这样;

id<MTLDevice> metalDevice;
CIContext* ciContext = [CIContext contextWithMTLDevice:metalDevice
        options:[NSDictionary dictionaryWithObjectsAndKeys:@(NO),kCIContextUseSoftwareRenderer,nil]
    ];
CIImage *inputImage = [[CIImage alloc] initWithCVPixelBuffer:currentFrame];

[ciContext render:inputImage 
    toMTLTexture:metalTexture 
    commandBuffer:metalCommandBuffer
    bounds:viewRect
    colorSpace:colorSpace];

我成功地使用它直接渲染到 CAMetalLayer 的可绘制纹理,但渲染到中间纹理时运气不佳(并不是说我非常努力地让它工作)希望其中一个对你有用。