floooh / sokol

minimal cross-platform standalone C headers
https://floooh.github.io/sokol-html5
zlib License
6.94k stars 488 forks source link

Using Sokol_gfx with multiple CAMetalLayers #885

Closed danielchasehooper closed 7 months ago

danielchasehooper commented 1 year ago

Sokol_gfx seems to be optimized for situations where you have a single metal destination. Our app can have multiple document windows open, each with their own metal layer displaying document content. Is there a way to support this use case?

floooh commented 1 year ago

This isn't supported unfortunately. I had started to explore this idea in this abandondend multiwindow branch: https://github.com/floooh/sokol/tree/sapp-multiwindow, but was running into a lot of problems driving multiple MTKView in sokol_app.h in the same frame loop, the whole thing simply became too brittle before I gave up.

The sokol_gfx.h implementation would basically use the incomplete sg_context stuff (which currently only works for multiple GL contexts) and associate a context with each CAMetalLayer swapchain. But I also don't like this whole context idea anymore, it just doesn't quite fit into a "post-GL-API".

If I would ever revisit that idea, I would try to use regular pass objects instead which render into offscreen render targets, and then "somehow" blit those offscreen render targets into the various CAMetalLayer surfaces.

But this is definitely not at the top of the priority queue at the moment.

floooh commented 1 year ago

PS: if you're adventurous, you could try running multiple default-passes in one frame (like in this multiwindow-glfw sample: https://github.com/floooh/sokol-samples/blob/4e65abf8266911b124df931b674b65a6ec8fc4dd/glfw/multiwindow-glfw.c#L174-L199, but in the renderpass_descriptor_cb and drawable_cb callbacks which are passed into sokol-gfx the sg_setup() function, return a different MTLRenderPassDescriptor and MTLDrawable object for each default pass, so that each default pass renders into a different MTLDrawable obtained from a CAMetalLayer object.

For this to work you need to abandon sokol_app.h completely and only use sokol_gfx.h with your own window system glue.

danielchasehooper commented 1 year ago

We're not using sokol_app.h, so the drawable_cb approach may work for us. Thank you.

I was kind of curious why renderpass_descriptor_cb exists though. Is it just for use with MTKView? Is there ever a situation that its implementation would be anything other than:

const void* get_render_pass_descriptor(void) {
MTLRenderPassDescriptor *pass = [MTLRenderPassDescriptor renderPassDescriptor];
pass.colorAttachments[0].texture = currentDrawable.texture;
return pass;
}

(please ignore the fact that pass isn't retained anywhere, and that multiple calls to this function would create additional passes :) )

floooh commented 1 year ago

Yes, mainly because MTKView has a currentRenderPassDescriptor convenience method.

In sokol-gfx I'm just expecting such a pre-configured render-pass-descriptor for the default pass because it also references all the resources needed for handling things like MSAA antialiasing in the default pass (the alternative would be to pass the currentDrawable along with a matching depth-stencil texture and optionally multi-sampled texture to sokol-gfx, and sokol-gfx would then setup its own render pass descriptor (like it happens for offscreen passes).

Without MTKView, you can just create the RenderPassDescriptor similar to your code snippet, but you also need to take care of the depth-stencil texture and MSAA texture and configure the RenderPassDescriptor as described here under 'Discussion': https://developer.apple.com/documentation/metalkit/mtkview/1536024-currentrenderpassdescriptor?language=objc

floooh commented 7 months ago

...this should now also be fixed with https://github.com/floooh/sokol/pull/985.