如何设置从MTLBuffer到2D MTLTexture的字节对齐?



我有一个浮点值数组,它代表一个 2D 图像(从 CCD 考虑(,我最终想要渲染成一个MTLView。这是在macOS上,但我希望能够在某个时候将其应用于iOS。我最初使用数据创建一个MTLBuffer

NSData *floatData = ...;
id<MTLBuffer> metalBuffer = [device newBufferWithBytes:floatData.bytes
length:floatData.length
options:MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeManaged];

从这里,我通过几个计算管道运行缓冲区。接下来,我想创建一个 RGBMTLTexture对象,以传递给几个CIFilter/MPS 筛选器,然后显示。创建一个使用已创建的缓冲区作为后盾的纹理以避免制作另一个副本似乎是有意义的。 (我已经成功地使用了像素格式为MTLPixelFormatR32Float的纹理。

// create texture with scaled buffer - this is a wrapper, i.e. it shares memory with the buffer
MTLTextureDescriptor *desc;
desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR32Float
width:width
height:height
mipmapped:NO];
desc.usage = MTLResourceUsageRead;
desc.storageMode = scaledBuffer.storageMode; // must match buffer
id<MTLTexture> scaledTexture = [scaledBuffer newTextureWithDescriptor:desc
offset:0 
bytesPerRow:imageWidth * sizeof(float)];

图像尺寸为 242x242。当我运行这个时,我得到:

validateNewTexture:89: failed assertion `BytesPerRow of a buffer-backed
texture with pixelFormat(MTLPixelFormatR32Float) must be aligned to 256 bytes,
found bytesPerRow(968)'

我知道我需要使用:

NSUInteger alignmentBytes = [self.device minimumLinearTextureAlignmentForPixelFormat:MTLPixelFormatR32Float];

如何定义缓冲区以使字节正确对齐?

更一般地说,这是此类数据的合适方法吗?这是我有效地将浮点数据转换为具有颜色的阶段。澄清一下,这是我的下一步:

// render into RGB texture
MPSImageConversion *imageConversion = [[MPSImageConversion alloc] initWithDevice:self.device
  srcAlpha:MPSAlphaTypeAlphaIsOne
 destAlpha:MPSAlphaTypeAlphaIsOne
backgroundColor:nil
conversionInfo:NULL];
[imageConversion encodeToCommandBuffer:commandBuffer
sourceImage:scaledTexture
destinationImage:intermediateRGBTexture];

其中intermediateRGBTexture是使用MTLPixelFormatRGBA16Float定义的 2D 纹理,以利用 EDR。

如果纹理与缓冲区共享相同的后备内存对您很重要,并且您希望纹理反映实际图像尺寸,则需要确保缓冲区中的数据从一开始就正确对齐。

您需要确保缓冲区有空间容纳所有对齐的数据,然后一次复制一行,而不是一次复制所有源数据。

NSUInteger rowAlignment = [self.device minimumLinearTextureAlignmentForPixelFormat:MTLPixelFormatR32Float];
NSUInteger sourceBytesPerRow = imageWidth * sizeof(float);
NSUInteger bytesPerRow = AlignUp(sourceBytesPerRow, rowAlignment);
id<MTLBuffer> metalBuffer = [self.device newBufferWithLength:bytesPerRow * imageHeight
options:MTLResourceCPUCacheModeDefaultCache];
const uint8_t *sourceData = floatData.bytes;
uint8_t *bufferData = metalBuffer.contents;
for (int i = 0; i < imageHeight; ++i) {
memcpy(bufferData + (i * bytesPerRow), sourceData + (i * sourceBytesPerRow), sourceBytesPerRow);
}

其中AlignUp是您选择的对齐函数或宏。像这样:

static inline NSUInteger AlignUp(NSUInteger n, NSInteger alignment) {
return ((n + alignment - 1) / alignment) * alignment;
}

由您决定增加的复杂性是否值得保存副本,但这是实现所需内容的一种方法。

最新更新