Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: One context per RenderTexture?  (Read 7598 times)

0 Members and 1 Guest are viewing this topic.

eigenbom

  • Full Member
  • ***
  • Posts: 228
    • View Profile
One context per RenderTexture?
« on: September 04, 2015, 01:08:31 am »
I've been doing some research into the sfml source and have found that RenderTextureImpl* and Window creates extra contexts. Due to the performance hit discussed over here http://en.sfml-dev.org/forums/index.php?topic=18585.0 I wonder if it would be better to use a single-context strategy in RenderTexture?

To be more specific: why does RenderTextureImplFBO create a new context, when it could just create an fbo on the shared context and bind it on setActive?

binary1248

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1405
  • I am awesome.
    • View Profile
    • The server that really shouldn't be running
Re: One context per RenderTexture?
« Reply #1 on: September 04, 2015, 02:00:37 am »
FBOs aren't shareable between contexts. You need to bind it in the same context that you created it in. This would mean that you would need to activate the shared context every time you want to bind and draw to the FBO. This would work, if it weren't for the fact that SFML likes to leave contexts activated forever until another gets activated (implicitly deactivating the previous one). And keeping the shared context activated might look like it works on paper, but at least on Windows, if the shared context is currently active on any thread, any newly created contexts that want to share resources with it (and therefore all other contexts as well) will not be able to, since wglShareLists() will fail half the time for whatever mysterious reason.

And then there's the issue of using the contexts as a container for the current render state. I don't know if this was part of the original design Laurent came up with, but keeping every target in their own context "simplifies" state management. RenderTarget does a crap load of caching in order to reduce "unnecessary" state changes. This only works if it is guaranteed that nothing else can touch the state. It's already bad enough that people who want to use GL have to resort to pushGLStates(), popGLStates() and resetGLStates(). If we threw all FBOs and thus RenderTargets into a single context, we would have to do the same among them as well.

The reason why I said unnecessary in quotes is because this is what it might look like at first glance. Indeed many newcomers to GL learn to cut down on redundant state changes. However, that advice also comes with a caveat: The assumption is that those users are working in a single context environment.

We might think that we are being smart and saving state changes, but anybody who has an idea of how drivers and GPUs work internally know that whenever you switch contexts, you are just causing a bunch of state changes that we don't notice in our code. We are moving them from application CPU time into driver CPU time, which ends up providing us with no benefits whatsoever. This is noticeable because in addition to OpenGL incurring a fair amount of overhead, SFML causes the driver to eat up a bit more CPU time when compared to other single context applications.

Multi-context is just a convenience. It doesn't map directly to the hardware and therefore comes with overhead. It was designed before DMA became a thing and thus doesn't take it into account. The only time when multi-context benefits you is when you know exactly what the driver has to do and can force it to do it in a secondary thread, this includes DMA. No matter how hard you try, all these classic APIs like OpenGL and DX up to 11 are inherently single threaded. It's a good thing that people woke up and changed that in Mantle, DX 12 and Vulkan.

Is a single context SFML possible? Yes, but not easily. As soon as you have multiple windows, there needs to be a way to target them for drawing. On Windows and Unix, you have the concept of a device context and drawable respectively. On OSX the targeting is done through an NSOpenGLView in a similar way. When activating a window's context, SFML goes ahead and changes the target for drawing to point to a new window as well. It doesn't have to change the context and could just as well reuse the previously active context to draw to the new window, but Laurent decided to just give each window their own context. This was probably necessary because of the "there is always an OpenGL context active on every thread" mantra. In a single-context model, we have stricter requirements. The context becomes a shared resource, meaning that we need to acquire it when we need it and we need to release it when we are done using it. This is doable in sfml-graphics code, but the user would also need to respect the model when acquiring the context to perform their own GL operations. If they forget to release the context, SFML would no longer be able to use it and everything would break. From a user-friendliness point of view, single-context would be less user-friendly than the current implementation, but that would really only affect GL users who should already be in a different mindset.

I've already tried many times in the past to get rid of any additional contexts and make SFML truly single-context. That is why I know it takes a lot of work and re-writing. No changes to the external API would be necessary, however the behaviour of Context::setActive() would need to be tweaked a bit.
SFGUI # SFNUL # GLS # Wyrm <- Why do I waste my time on such a useless project? Because I am awesome (first meaning).

eigenbom

  • Full Member
  • ***
  • Posts: 228
    • View Profile
Re: One context per RenderTexture?
« Reply #2 on: September 04, 2015, 03:00:30 am »
Thanks for the great explanation. So a single-context rewrite would eliminate all these extra contexts, but then each sf::Context/RenderTarget would have to manually cache whatever state it needed?

In any case, it seems that using many RenderTextures as they are is out of the question. Unfortunately there doesn't seem to be a nice way to manually write my own single-context FBO code because I need a rendertarget to use SFML's drawing routines. I wonder if you have any suggestions there?

Mario

  • SFML Team
  • Hero Member
  • *****
  • Posts: 878
    • View Profile
Re: One context per RenderTexture?
« Reply #3 on: September 04, 2015, 09:36:16 am »
You could just implement your own class inherited from sf::RenderTarget?

eigenbom

  • Full Member
  • ***
  • Posts: 228
    • View Profile
Re: One context per RenderTexture?
« Reply #4 on: September 05, 2015, 01:21:18 am »
Yep, that's one solution, although RenderTexture is a friend of Texture, etc., for a reason. Not all the internals of these things are available to external code -- although you can use glGet to grab the important gl ids.

I had written a reply with a few alternatives but the forum went down just as I posted it, gah. Needless to say there are a few ways around this. But ultimately, a single-context or manual-context system would be the best future for SFML, based on a lot of discussions I've had with other devs about this issue. Up until now, this is the only serious design flaw I've seen with sfml, but at least it's hidden behind the api.

SFML should remain simple to use, but in order to be Fast the devteam may need to consider opening up more of the internals so users can more easily incorporate optimisations.



 

anything