我正试图从RAW内存中制作第一个CVPixelBuffer
然后从CVPixelBuffer
中选择MTLTexture
,但是运行以下代码后,我总是出现错误
CVMetalTextureCacheCreateTextureFromImage失败,状态:-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支持时会发生错误。但是,您不能从Bytes中创建支持IOSurface的CVPixelBuffer。因此,尽管设置了kCVPixelBufferMetalCompatibilityKey
,但CVPixelBufferCreateWithBytes
(及其平面对应物(不会用IOSurface支持缓冲区。
绕过这个(以及可能的第三个(的2种方法
- 创建一个空的CVpixelBuffer并使用memcpy。您需要在每个循环中创建一个新的pixelbuffer,因此建议使用PixelBufferPool
CVPixelBufferPoolRef pixelPool; // initialised prior
void *srcBaseAddress; // initialised prior
CVPixelBufferRef currentFrame;
CVPixelBufferPoolCreatePixelBuffer(nil, pixelPool, ¤tFrame);
CVPixelBufferLockBaseAddress(currentFrame,0);
void *cvBaseAddress=CVPixelBufferGetBaseAddress(currentFrame);
size_t size= CVPixelBufferGetDataSize(currentFrame);
memcpy(cvBaseAddress,srcBaseAddress,size);
- 完全跳过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的可绘制纹理,但在渲染到中间纹理时运气不佳(并不是说我很努力地让它工作(希望其中一个能为你工作。