Reputation: 333
I am trying to implement two distinct CAMetalLayers
and use one MTLRenderCommandEncoder
to render the same scene to both layers (Metal for OS X).
For this purpose, I've tried creating one MTLRenderPassDescriptor
and attaching the two layers' textures to its color attachments. My render method looks like the following:
- (void)render {
dispatch_semaphore_wait(_inflight_semaphore, DISPATCH_TIME_FOREVER);
id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
__block dispatch_semaphore_t block_sema = _inflight_semaphore;
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
dispatch_semaphore_signal(block_sema);
}];
MTLRenderPassDescriptor *renderPass = [MTLRenderPassDescriptor renderPassDescriptor];
for (int i = 0; i < [_metalLayers count]; i++) {
_metalDrawables[i] = [_metalLayers[i] nextDrawable];
renderPass.colorAttachments[i].texture = _metalDrawables[[_metalDrawables count] - 1].texture;
renderPass.colorAttachments[i].clearColor = MTLClearColorMake(0.5, 0.5, (float)i / (float)[_metalLayers count], 1);
renderPass.colorAttachments[i].storeAction = MTLStoreActionStore;
renderPass.colorAttachments[i].loadAction = MTLLoadActionClear;
}
id<MTLRenderCommandEncoder> commandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPass];
[commandEncoder setRenderPipelineState:_pipeline];
[commandEncoder setVertexBuffer:_positionBuffer offset:0 atIndex:0 ];
[commandEncoder setVertexBuffer:_colorBuffer offset:0 atIndex:1 ];
[commandEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3 instanceCount:1];
[commandEncoder endEncoding];
for (int i = 0; i < [_metalDrawables count]; i++) {
[commandBuffer presentDrawable:_metalDrawables[i]];
}
[commandBuffer commit];
}
However, the scene gets rendered to just one of the layers, which turns out to be the one associated with the first color attachment's texture. The other layer is cleared with the specified clear color, but nothing is drawn.
Has the approach given any chance of succeeding or is using the render pass descriptor's color attachments entirely pointless when trying to render the same scene to multiple screens (i.e. CAMetalLayers
)? If so, is there any other conceivable approach to achieve this result?
Upvotes: 5
Views: 4538
Reputation: 345
To write to multiple render targets, you would need to explicitly write out to that render target in your fragment shader. @lock has already pointed this out.
struct MyFragmentOutput {
// color attachment 0
float4 clr_f [[ color(0) ]];
// color attachment 1
int4 clr_i [[ color(1) ]];
// color attachment 2
uint4 clr_ui [[ color(2) ]];
};
fragment MyFragmentOutput
my_frag_shader( ... )
{
MyFragmentOutput f;
....
f.clr_f = ...;
f.clr_i = ...;
...
return f;
}
However, this is an overkill since you don't really need the GPU to render the scene twice. So the answer above by @Kacper is more accurate for your case. However, to add to his answer, I would recommend using BlitEncoder that can copy data between two textures on the GPU, which I assume should be much faster than the CPU.
Upvotes: 3
Reputation:
As far as I read about this problem you can try to render just to one MTLTexture (not drawable layer) and then try to use MTLTexture methods getBytes and replaceRegion to copy texture data into two drawable layers.
Currently I am working on rendering to ordinary texture but I encounter some artifacts and currently it not working for me, maybe you will find way to solve that.
Upvotes: 2