Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sokol_gfx: Support for multiple contexts/windows #44

Closed
pplux opened this issue Apr 17, 2018 · 16 comments
Closed

sokol_gfx: Support for multiple contexts/windows #44

pplux opened this issue Apr 17, 2018 · 16 comments
Assignees

Comments

@pplux
Copy link
Contributor

pplux commented Apr 17, 2018

As far as I can see by the implementation, sokol_gfx can not support multiple windows/contexts. Maybe it is just a matter of storing the _sg_backend object and restoring it with a reset_state_cache call between contexts.

@floooh
Copy link
Owner

floooh commented Apr 17, 2018

Argh, yeah, I knew that would come up sooner or later ;) For the GL and D3D11 backends this isn't a big issue, all the currently global state can be gathered in a context struct behind a single global pointer, and some sort of context concept (with a 'currently active context').

I need to come up with an idea for the Metal backend though. In C, ARC doesn't allow to store Obj-C id's in structs, that's the reason why there are so many globals in the Metal backend currently.

@floooh floooh self-assigned this Apr 17, 2018
@pplux
Copy link
Contributor Author

pplux commented Apr 18, 2018

I have no experience with Obj-C, but I thought there were ways to hold resources from C, with the
ownership qualification (http://clang.llvm.org/docs/AutomaticReferenceCounting.html#ownership-qualification) but really, no idea.

Personally, I'm a huge fan of no-global-variables, I'd rather have an API where a context is created and every single call uses that context as first parameter, with a complete re-entrant API (even better if that means that each context can be handled by a different thread).

In the meantime maybe the "makeCurrent" function, or whatever solution you finally decide to settle with, might be a platform dependent solution. One of the beautiful things of sokol_gfx is the fact that it doesn't completely hides the platform under some abstraction, shaders for instance depends on the platform, and there are slightly different ways of declaring vertices, etc... I would find perfectly fine having the makeCurrent function working only on some backends.

If the makCurrent is allowed to work only on some platforms I can help with the OpenGL backend, which is the one I normally use if you want to.

@floooh
Copy link
Owner

floooh commented Apr 18, 2018

__unsafe_unretained works in structs with Obj-C ARC, but I still need to keep a strong reference somewhere so that the object won't be released. Bit of a chicken/egg problem... I'll also need to find out what the equivalent of a GL context is for other APIs (in D3D11 a device is associated with a swap chain, but I don't know if it's possible to have several devices in the same process, likewise, in Metal, the docs say one MTLDevice represents one GPU, so what is a GL context would be done through a MTLRenderPassDescriptor and Drawable (which is already provided from the outside, so Sokol wouldn't actually need any extra context switching features for Metal...)

BUUUUT: ...maybe the GL Sokol backend also doesn't need anything extra... The GL context is managed outside Sokol anyway, and you would switch to another GL context with glfwMakeContextCurrent() outside of a begin_default_pass/end_pass/commit. The only thing you'd need to do is calling sg_reset_state_cache() after sg_commit() (it probably even makes sense to do this automatically in sg_commit().

Did you try if this works?

// render to first window...
glfwMakeContextCurrent(w0);
sg_begin_default_pass(...);
...
sg_end_pass();
sg_commit();
sg_reset_state_cache();

// render to another window...
glfwMakeContextCurrent(w1);
sg_begin_default_pass(...);
...
sg_end_pass();
sg_commit();
sg_reset_state_cache();
...

From sokol's point of view, two normal frames would be rendered, but outside sokol you're switching to another GL context between the first and second frame...

If this works I'd prefer this solution (but maybe move the reset state cache into sg_commit).

@pplux
Copy link
Contributor Author

pplux commented Apr 18, 2018

Indeed that should work on GLFW since GLFW context by default share OpenGL resources, thanks! I will definitely try that out. I haven't tested this, since I was checking if sokol would be able to work with multiple windows before actually working on it.

@floooh
Copy link
Owner

floooh commented Apr 18, 2018

You could also create unshared resources by switching to the right GL context right before calling the sg_create_xxx() functions, you just need to make sure to use the right resources with the right context later...

I guess I'll try to write a little GLFW multiwindow demo later today to see if everything works as expected :)

@floooh
Copy link
Owner

floooh commented Apr 18, 2018

Hmm already found a problem with this approach... the global VAO object that's created because of OSX Core Profile requirements only exists on one context... I'll try find a stateless solution, but the naive solution (just check if a vertex array is bound, if not create and bind one) clashes with the idea that applications can do their own GL rendering...

@floooh
Copy link
Owner

floooh commented Apr 18, 2018

...I guess the best solution is indeed to have 2 new functions:

glfwMakeContextCurrent(w);
sg_context ctx = sg_init_context();
...
sg_activate_context(ctx);
...

...this would only store data that's associated with a GL context, and inside sg_activate_context() I could also do the sg_reset_state_cache()...

@pplux
Copy link
Contributor Author

pplux commented Apr 18, 2018

That looks fine for me, as said I'd rather reentrant api without global variables, buuuut that just my personal preference. It would be nice if sg_activate_context internally restores the state (reset_state_cache)

@floooh
Copy link
Owner

floooh commented Apr 18, 2018

Ok, I've started implementing context support in a branch and also wrote a small GLFW sample. I stumbled over another more important problem: I need to track in which context a resource has been created, and the resource needs to be destroyed while the same context is active (when the resource is destroyed manually).

For automatic resource destruction I had to add a method sg_discard_context(ctx), similar to sg_shutdown() this destroys any left-over resources, but only for the context that's being discarded.

So all in all it looks like this now:

  • there's a new handle type sg_context
  • ...and 3 new functions:
/* called once after a new GL context is initialized */
sg_context sg_setup_context();
/* called after switching GL contexts */
void sg_activate_context(sg_context ctx);
/* called before discarding GL context, also destroys all context resources */
void sg_discard_context();

Here's a sample program with GLFW: https://github.com/floooh/sokol-samples/blob/contexts/glfw/multiwindow-glfw.c

screen shot 2018-04-18 at 21 04 43

@pplux
Copy link
Contributor Author

pplux commented Apr 19, 2018

Wonderful!! thanks for being so quickly!

@floooh
Copy link
Owner

floooh commented Apr 20, 2018

This stuff is now in the master branch. I'm closing this issue. If anything pops up related to this it's better to open a new ticket.

@floooh floooh closed this as completed Apr 20, 2018
@timeinpixels
Copy link

Quick question - will this work with two separate threads?
One OpenGL context per thread. The 2nd thread will only call sg_update_image.
I tried it but I am having some issues...

@floooh
Copy link
Owner

floooh commented Jul 31, 2022

Nope, the sokol header APIs all expect to be called from the same thread they have been initialized on.

@larpon
Copy link
Contributor

larpon commented Jul 25, 2023

Question: can we initialize a sokol_gl context for each new sokol_gfx context somehow? (I've tried with various ways over various versions of gfx/gl and I've only had little success in getting sokol_gl rendering going in multiple windows).

@floooh
Copy link
Owner

floooh commented Jul 25, 2023

It might work by running sgl_make_context() after it's associated glfwMakeContextCurrent() and sg_activate_context(), to make sure that any resource objects created by sokol-gl end up in the right GL- and sokol-gfx context but that's never been tested. sokol-gl contexts were actually intended for rendering into different render passes, not different GL contexts, but maybe it still works.

The entire idea of sokol-gfx contexts is more or less just a hack to support rendering into different GL contexts, but working with multiple GL contexts is so fragile that it doesn't make much sense to attempt fixing any issues.

@larpon
Copy link
Contributor

larpon commented Jul 25, 2023

Thanks for the answer. I'll see if I can get it to work with this order - I was fighting some assert errors and think I also tried using sgl_make_context after those calls already (although with some offscreen stuff also, which might also have complicated things). My code base has grown a bit out of control since, though so I'll try it again with a more minimal setup to test with instead 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants