1
votes

We are working with interlaced HD video. The metal color attachment I use for rendering has the dimensions of one field (1920*540 RGBA). When I try to copy two rendered fields into the same MTLBuffer that has the size of 1920*1080*4 = 8294400 bytes it works only if the destination offset is zero.

let commandBuffer = commandQueue.makeCommandBuffer()
let blitEncoder = commandBuffer.makeBlitCommandEncoder()
blitEncoder.copy(from: attachmentTexture,
                 sourceSlice: 0,
                 sourceLevel: 0,
                 sourceOrigin: MTLOriginMake(0, 0, 0),
                 sourceSize: MTLSizeMake(attachmentTexture.width, attachmentTexture.height, 1),
                 to: destinationBuffer,
                 destinationOffset: 1920*4,
                 destinationBytesPerRow: 1920*4*2,
                 destinationBytesPerImage: destinationBuffer.length)
blitEncoder.endEncoding()
commandBuffer.commit()

For the first field where the destination offset is zero the function works well. The destination buffer is filled for every second row.

But when I want to write the second field with the same code into the same MTLBuffer object only with the destinationOffset set to 1920*4 like you see in the code above (to start with the second row in the buffer) then I get an assertion like this:

-[MTLDebugBlitCommandEncoder validateCopyFromTexture:sourceSlice:sourceLevel:sourceOrigin:sourceSize:toBuffer:destinationOffset:destinationBytesPerRow:destinationBytesPerImage:options:]:677: failed assertion `totalBytesUsed(8302080) must be <= destinationBuffer length.'

The totalBytesUsed are exactly the destination buffer length in bytes plus the offset. So every offset I use in this function will result in this assertion error.

Can someone explain me how to use this function correctly because the other way around like creating two MTLTexture objects (odd and even fields) for an incoming video frame works well with similar parameters.

1

1 Answers

1
votes

I ran into something like this recently, too.

You're passing destinationBuffer.length to the destinationBytesPerImage: parameter. As you've noticed, Metal is adding together the offset and the bytes-per-image value and comparing that against the length of the to: destination buffer (destinationBuffer). It's noticing that offset plus bytes-per-image won't fit into the buffer and refusing to accept that.

You may be able to simply pass 0 for destinationBytesPerImage:, since you're not working with a 3D or 2D array texture. If that doesn't work, pass destinationBuffer.length - 1920*4.