#5 Lina Engine: Rendering Engine Part1 - Window Class
So here we are, at one of the most important systems in the whole engine. The Rendering Engine. As I mentioned in Initial Layout post, the rendering system would be responsible for all the rendering currently. We plan to use OpenGL initially, however we hope to change the system into a modular structure so that it can support multiple libraries to handle GPU communications.
So far, we had written a main loop, a time management system and a console to debug our systems. Now it's time to actually come up with something to see, a window. In order to draw our game's window, we need to communicate with the operating system & GPU. Luckily, SDL handles the operating system communications, in a cross-platform manner. So we basically request a window from the SDL, then it would run the related code for the active platform to get a window from whatever operating system we are running on. So I had written a window class. It would be instantiated by the rendering system, and it will be responsible for asking SDL to get a window and handle it's attributes on itself. Meanwhile, the rendering system would hold an instance of this window, alive as much as the rendering system is alive, throughout the program.
As can be seen as this poorly drawn diagram, this will be the call order between our systems. The important part to understand is though, the Window Class does not do any drawing or rendering. It is only responsible for displaying the active buffer at the end of the frame. Whatever we draw in the backstage, is completely between the Rendering Engine and the OpenGL. Take a look at the initialization of the Window object:
There is a really important part to see in here. We set up some GL attributes for our window, then request a window from the SDL, which in turn gives us the window it has taken from the operating system. However, up until now, we have no control over the window. We literally have no command over it, so we would not be able to draw properly on it and display the buffers. The reason for this is the ownership of whatever operating system we are on. The window is still owned by the OS, and we need it's permission on every single action we want to apply to the window, which probably it will not approve for the most of it. We need to override this ownership, and there comes into the play, OpenGL Context. As you can see, we create a gLContext. This is basically a cool way of OpenGL saying to the operating system; "Hey buddy, we have the control over the window that you pass, so be aware of it, we will handle it from here with the GPU". A GLContext is basically a singular object that holds all the buffer commands, it is the alive and breathing instance of the OpenGL in our main systems, and enables us to have control over what we draw and where we draw it. If you are looking for more information on the GLContext, take a look at this link here.
Now the operating system is out of the way, the only job that the window is supposed to do is to update the screen. For this, we basically call a single function of SDL in the Update method of our window class. (Which is updated after every rendered frame by the Rendering Engine). And it goes like this: SDL_GL_SwapWindow(m_Window); What this call does is, it swaps the window buffers. Take a look at the code above, and notice that we are setting a double buffer attribute for the OpenGL. This means that, memory for 2 windows will be allocated. The reason to do it like this is to prevent drawing artifacts from being displayed to the user. The logic is simple, there are 2 spaces in the memory to buffer. For the sake of simplicity, let's call these Window A and Window B. This is how things are done in the background by using swap windows:
We display the Window A in the frame F, while GPU draws all the buffers into Window B.
At the end of the frame F, we swap the windows. We start displaying the Window B, which just finished drawing.
At the beginning of the frame F2, we are currently displaying the Window B, while the drawing is done on the Window A.
Then it goes on forever, so we never show the window that we are currently drawing into, we always show what was previously drawn. This way, we can make sure that the user doesn't get to see any artifacts caused by frame-rate or hardware problems during the drawing.
Finally, we only have some basic control structures to delete the memory for the window if window exit event was received from the operating system through SDL. And actually, that's all about the window class, we do not have any more functionality yet, but we definitely will be implementing some in the future. Hope that helps, see you next time.