【问题标题】:How To Set Up Byte Alignment From a MTLBuffer to a 2D MTLTexture?如何设置从 MTLBuffer 到 2D MTLTexture 的字节对齐?
【发布时间】:2020-10-08 11:54:47
【问题描述】:

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

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

从这里开始,我通过几个计算管道运行缓冲区。接下来,我想创建一个 RGB MTLTexture 对象以传递给几个 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。

【问题讨论】:

    标签: cocoa metal metalkit mtlbuffer


    【解决方案1】:

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

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

    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;
    }
    

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

    【讨论】:

    • 感谢您的详细回复(以及其他非常有用的 SO 答案和教程!)。我实现了这个,但我认为memcpy 命令中有两个错误(我得到了EXC_BAD_ACCESS)。由于数据数组被声明为float*bytesPerRowsourceBytesPerRow 应除以 4(指针运算考虑到浮点大小)。而且我认为最后一个参数应该是sourceBytesPerRow?当我运行它时,我在转换图像时得到source 0x… may have no more than 4 feature channels for MPSUnaryImageKernels;也许这是另一个问题。
    • 确实,为草率的指针数学道歉。我已更新答案以使用基于字节的算术并将最后一个参数固定为memcpy
    猜你喜欢
    • 1970-01-01
    • 2018-10-23
    • 1970-01-01
    • 2018-11-29
    • 1970-01-01
    • 2017-11-05
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    相关资源
    最近更新 更多