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

Author Topic: On SFML states interference with OpenGL native rendering  (Read 3453 times)

0 Members and 1 Guest are viewing this topic.

Suslik

  • Newbie
  • *
  • Posts: 33
    • View Profile
On SFML states interference with OpenGL native rendering
« on: February 01, 2016, 09:20:14 am »
First of all, I've been using SFML for quite some time now both for my work and for educational purposes (my students really get a jumpstart into C++ graphics with it) and I'm really happy with most of its design decisions.

However, there's a few design decisions I don't like. One of them I wanted to discuss today regards native OpenGL rendering with SFML.

As far as I see, currently SFML supports native OpenGL rendering in a way when user theoretically can inject any kind of OpenGL code between native SFML calls and it should work as long as the user knows what they're doing. SFML even provides some basic API call to help doing so like sf::Texture::bind() or sf::Shader::bind(). However, in practice it turns out that in order to mix native OpenGL with SFML, user must have deeper OpenGL understanding than functionality he's actually going to use because of hidden states that SFML sets.

Here's an example of very basic OpenGL code injected into SFML:
sf::RenderWindow window(...);
while(window.isOpen())
{
  while(window.pullEvent(...)){...}
  window.setActive(1); //just to be sure
  float vertices[] = {0, 0, 0,   100, 0, 0,  0, 100, 0};
  int indices[] = {0, 1, 2};
  glEnableClientState (GL_VERTEX_ARRAY);
  glVertexPointer(3, GL_FLOAT, sizeof(float) * 3, vertices);
  glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, indices);

  window.display();
}
 
The code is very straightforward and it if you run it, it works great. Right? Wrong. Thing is, it works well *most of the time* because SFML has internal buffer bindings for its own rendering, for example here:
Code: [Select]
//RenderTarget.h, line 356
void RenderTarget::resetGLStates()
{
  //...
  glCheck(glEnableClientState(GL_VERTEX_ARRAY));
  glCheck(glEnableClientState(GL_COLOR_ARRAY));
  glCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); 
  //...
}
And when the user calls glDrawElements() or any similar function, colors are actually read from internal SFML buffers and of course that's undefined behaviour. It may even not crash most of the time, but when it does, crash occurs in nvogl.dll(windows) and that's pretty hard to track especially for those learning OpenGL.

Similar problems arise when using native OpenGL texturing. Since most OpenGL users are used to normalized texture coordinates, it's very confusing to track down glMatrixMode(GL_TEXTURE) that SFML uses.

Yes I understand that it's easy to say that the user must not make any assumptions on any states left by SFML. But due to vast number of non-obvious states that the user may forget to set to default values, debugging such errors may prove to be very frustrating, especially when the same code works perfectly fine in native code without SFML. I really think SFML should provide some functionality similar to resetGlStates(), but instead of resetting states for SFML usage, reset them to default OpenGL ones(matrices, client buffers, etc).
« Last Edit: February 01, 2016, 09:24:40 am by Suslik »

Mario

  • SFML Team
  • Hero Member
  • *****
  • Posts: 879
    • View Profile
Re: On SFML states interference with OpenGL native rendering
« Reply #1 on: February 01, 2016, 07:12:39 pm »
I'm understanding your problem, but shouldn't you never assume some client states (or any other values) to be set by default? Of course, this makes things quite a bit more complicated for learners, but at the same time I think this teaches good practices, even if that "preset code" is provided as some premade utility call. Even some books do it like that and just go "we'll have to set some defaults, don't worry, we'll explain them later".

Besides that, I don't know any elegant way right now to solve this issue without additional overhead. resetGlStates() could go back to the very first/initial settings (and setting an optional parameter to true) by saving these before, but I'm not 100% sure on that.

binary1248

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1405
  • I am awesome.
    • View Profile
    • The server that really shouldn't be running
Re: On SFML states interference with OpenGL native rendering
« Reply #2 on: February 10, 2016, 10:13:27 pm »
This is exactly what pushGLStates() and popGLStates() is for.

When you push, it tells SFML to "save whatever state is current" onto the stack. This could be your own state, or the default state of a context when it is first created. Because SFML doesn't (and is not interested in) saving every single bit of state there is, it only takes care of whatever state it actually manipulates itself. Thus, when you push, you make sure that SFML can't alter anything you currently have active, whether it is something you explicitly set, or something that was already set when the context was created.

Because of this, it doesn't make sense to provide a function that can reset the state back to the default as it was when the context was created. In order to do this, SFML would have to track what the default value was and explicitly set it. These values are even allowed to change between OpenGL versions if Khronos decided to be really liberal with themselves.

No robust OpenGL code should assume that the context is in a specific state in order to function properly. Strictly speaking, SFML doesn't either, since resetGLStates() should always be called if you are unsure whether your OpenGL operations might have an effect on SFML. When it comes to state that was introduced after OpenGL 1.2 (such as buffer object binding), SFML "doesn't assume it exists", so you will need to take care of its housekeeping yourself. An exception to this is the current shader program binding, which SFML also does take care of.

This code should take care of almost all scenarios:
while(window.isOpen())
{
  window.clear();

  // Your OpenGL stuff here

  window.pushGLStates();

  // Your SFML drawing here

  window.popGLStates();

  // Your other OpenGL stuff here

  window.display();
}
If this doesn't cover some of your use cases, you will have to explain them in a bit more detail, or provide some more examples.
SFGUI # SFNUL # GLS # Wyrm <- Why do I waste my time on such a useless project? Because I am awesome (first meaning).