我需要在Metal中实现离屏渲染,并复制到系统内存中。而不在屏幕上绘图。
这段代码工作得很好,但我不确定这是否是一段正确的代码,我需要调用commandBuffer.enqueue吗?
// rendering to offscreen texture
auto commandQueue = [device newCommandQueue];
auto commandBuffer = [commandQueue commandBuffer];
//[commandBuffer enqueue]; // Do I need this command?
id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:mtlDescriptor];
// perform encoding
[renderEncoder endEncoding];
[commandBuffer commit];
auto commandBuffer = [commandQueue commandBuffer];
id<MTLBlitCommandEncoder> blitEncoder = [commandBuffer blitCommandEncoder];
// Copying offscreen texture to a new managed texture
[blitEncoder copyFromTexture:drawable.texture sourceSlice:0 sourceLevel:level sourceOrigin:region.origin sourceSize:region.size toTexture:_mtlTexture destinationSlice:0 destinationLevel:level destinationOrigin:{xOffset, yOffset, 0}];
[blitEncoder endEncoding];
[commandBuffer commit];
[commandBuffer WaitUntilCompleted]; // I add waiting to get a fully completed texture for copying.
// Final stage - we copy a texture to our buffer in system memory
getBytes_bytesPerRow_fromRegion_mipmapLevel()
我需要调用commandBuffer.enqueue吗? 如果我删除commandBuffer.WaitUntilCompleted,我只能得到半帧的内容. 似乎getBytes_bytesPerRow_fromRegion_mipmapLevel并没有检查渲染是否完成。
或者我应该创建 "managed "而不是 "private "的离屏纹理,然后直接复制到我的缓冲区。
// creating offscreen texture "managed"
// rendering to offscreen texture
auto commandQueue = [device newCommandQueue];
auto commandBuffer = [commandQueue commandBuffer];
//[commandBuffer enqueue]; // Do I need this command?
id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:mtlDescriptor];
// perform encoding
[renderEncoder endEncoding];
[commandBuffer commit];
[commandBuffer waitUntilCompleted];
// Copying "managed" offscreen texture to my buffer
getBytes_bytesPerRow_fromRegion_mipmapLevel()
1) 你不需要调用 enqueue
上的命令缓冲区。这用于在多线程方案中明确指定命令缓冲区的顺序的情况,在这里无关紧要。你的命令缓冲区在被提交后会被隐式地enqueued。
2)你确实需要等待命令缓冲区完成后再将其内容复制到系统内存中。通常情况下,GPU和CPU能够异步运行,而不需要互相等待,这是必不可少的,但在你的用例中,你想要的恰恰相反,等待就是让它们保持锁步的方式。
3)如果你不需要渲染后的图像副本作为纹理在GPU上做进一步的工作,你应该可以完全省略完全的blit,前提是你渲染到的纹理是在托管存储模式下。你可以调用 synchronizeResource:
来代替 blit 编码器,这将使渲染工作的结果在系统内存中的纹理副本中可见,然后你可以直接从那里复制。
如果由于某些原因,渲染目标不能在托管存储中(我注意到你使用的是可绘制文件--这些实际上是可绘制文件吗?MTLDrawable
视图或图层提供的,如果是的话,为什么?),事实上,你需要将其复制到一个托管的纹理或共享管理的缓冲区,以便在CPU端复制位。