Welcome to the third module of our course. In the previous module, we explored how the C++ standard library's data structures are implemented and how their memory layouts impact performance. This hardware-aware perspective is crucial for system programming. Now, we will shift our focus from data processing to data visualization, beginning the journey of putting pixels on the screen.
Today's lesson is the first step in building the graphical foundation for your whiteboard application. Our objective is to create and manage a hardware-accelerated window using the SDL2 library. SDL2 (Simple DirectMedia Layer) is a powerful, low-level, cross-platform library that gives us direct access to graphics hardware, input devices, and audio. Its C-based API is a perfect real-world scenario to apply the C++ RAII principles we covered in Module 1 for robust resource management.
The Anatomy of an SDL Application
Before writing any code, it's essential to understand the lifecycle of a typical real-time graphics application. This structure will form the backbone of your whiteboard program:
- Initialization: Start up the necessary SDL subsystems (e.g., video, events).
- Resource Creation: Create a window to display graphics and a rendering context to draw with.
- Application Loop: Enter a loop that runs every frame. Inside the loop, you will:
- Process user input and system events (like closing the window).
- Update the application's state.
- Render the scene.
- Cleanup: Once the loop terminates, destroy the window and rendering context, and shut down SDL to release all resources.
This is a classic C-style pattern. We will first understand the core C functions and then see how to wrap them in C++ classes for safety and elegance.
A Step-by-Step Guide to Creating a Window
The following video provides an excellent walkthrough of creating a basic Window class that encapsulates the SDL setup. It clearly demonstrates the initialization, creation, and event handling steps.
Making a Window - C++/SDL 2 Tutorials Ep. 2
This tutorial by ArchieG, "Making a Window," will guide us through the fundamental SDL functions.
First, watch the segment on initialization and window creation from this section. Pay attention to: SDL_Init() with the SDL_INIT_VIDEO flag. The parameters of SDL_CreateWindow(), especially the use of SDL_WINDOWPOS_CENTERED. The pattern of checking the return values for errors. Next, watch the segment on implementing the application loop and handling the quit event from this part. Focus on: The structure of the while loop that keeps the application running. The use of SDL_PollEvent() to process the event queue. How to detect the SDL_QUIT event to gracefully exit the application.
Let's break down the key functions you just saw.
int SDL_Init(Uint32 flags): This function initializes SDL. You pass it flags to specify which subsystems you need.SDL_INIT_VIDEOis essential for any windowing and rendering.SDL_Window* SDL_CreateWindow(const char* title, int x, int y, int w, int h, Uint32 flags): This function creates the window itself. It returns a pointer to anSDL_Windowobject, which is an opaque handle to the window resource. If it fails, it returnsNULL.int SDL_PollEvent(SDL_Event* event): This function checks the event queue for pending events. If an event exists, it populates theSDL_Eventstruct you provide and returns 1. Otherwise, it returns 0. You typically call this in a loop to process all events each frame.
Enabling Hardware Acceleration
A window is just a blank slate. To draw on it using the GPU, we need to create a rendering context. For your project, our goal is to use Skia, which can leverage several graphics backends. We will use OpenGL, as it is the most ubiquitously supported.
To do this, we must explicitly tell SDL we want to create a window that is compatible with OpenGL. This is done by setting specific attributes before creating the window and then creating an OpenGL context after the window is created.
The video below briefly explains the role of window flags, including those for graphics APIs.
[Ep. 9] Creating a Window in SDL | Introduction to SDL2
In this clip from a tutorial by Mike Shah, he discusses the flags parameter of SDL_CreateWindow.
Watch from this point. Notice the mention of flags like SDL_WINDOW_OPENGL and SDL_WINDOW_VULKAN. This is how you signal your intent to use a specific graphics API with the window.
The process for creating an OpenGL-accelerated window is:
- Set OpenGL Attributes: Before creating the window, use
SDL_GL_SetAttribute()to specify the OpenGL version you want (e.g., version 3.3 Core Profile). - Create the Window: Call
SDL_CreateWindow(), making sure to include theSDL_WINDOW_OPENGLflag. - Create the OpenGL Context: After the window is successfully created, call
SDL_GL_CreateContext()with the window handle. This function returns anSDL_GLContexthandle, which represents the GPU's state machine for your window. - Swap Buffers: In your main loop, after you've finished drawing for a frame, you call
SDL_GL_SwapWindow(). This presents your rendered image to the screen. This is the core of "double buffering," a technique to prevent screen tearing.
Here is an image showing a minimal, complete program that uses SDL. While it uses the SDL_Renderer API instead of a direct OpenGL context, the overall structure of initialization, window creation, looping, and cleanup is identical.

RAII: The C++ Way to Manage SDL Resources
SDL is a C library, which means it uses a "create/destroy" pattern for resource management. You get a raw pointer from a creation function (like SDL_CreateWindow) and you are responsible for passing that pointer to a destruction function (SDL_DestroyWindow) when you are done.
As we discussed in Module 1, manual resource management is error-prone. Forgetting to call a destroy function leads to resource leaks. This is where the Resource Acquisition Is Initialization (RAII) idiom becomes invaluable. We can create C++ wrapper classes that acquire the SDL resource in their constructor and release it in their destructor.
The following article demonstrates this perfectly, creating Application and Window classes to manage SDL's lifecycle.
GUI Development with C++, SDL2, and Dear ImGui
This article by Martin Fieber provides a clean, class-based structure for an SDL application. We will focus on the Window class implementation.
Please read the section titled Creating the window. Pay close attention to: The Window class holds SDL_Window* and SDL_Renderer* as private members. The constructor calls SDL_CreateWindow and SDL_CreateRenderer, acquiring the resources. The destructor calls SDL_DestroyRenderer and SDL_DestroyWindow, guaranteeing cleanup. The use of std::unique_ptr in the Application class to manage the lifetime of the Window object.
This class-based approach is exactly how you should structure your application. It transforms the manual C-style API of SDL into a safe, modern C++ interface.
Tying It All Together
Let's see the full sequence in a practical example that prepares us for Skia integration. The SkiaSDLExample.cpp file, although complex, contains the exact setup we need.
skia-c-examples/SkiaSDLExample.cpp at main · HinTak/skia-c-examples
This file from a Skia example repository shows how to create an OpenGL-enabled SDL window and connect it to Skia. We will focus only on the SDL parts for now.
Skim through the main function in the source code. You don't need to understand every line, but locate these key parts: Attribute Setting: Look for the calls to SDL_GL_SetAttribute near the top of main. This is where the OpenGL context is configured. Loop and Swap: Find the main application loop, a while loop. At the end of the loop, notice the call to SDL_GL_SwapWindow(window). This is the buffer swap. Cleanup: At the very end of main, observe the cleanup sequence: SDL_GL_DeleteContext, SDL_DestroyWindow, and finally SDL_Quit. This mirrors the RAII cleanup we saw in the previous article.
This example reinforces the complete lifecycle: configure, create, loop (and swap), and destroy.
Conclusion
In this lesson, we have laid the literal groundwork for your application. You now understand the essential components and lifecycle of an SDL2 program and how to create a hardware-accelerated window ready for high-performance rendering.
Key Takeaways:
- An SDL application follows a standard lifecycle: Initialize -> Create Resources -> Loop -> Clean Up.
SDL_Init()starts the required subsystems, withSDL_INIT_VIDEObeing fundamental.SDL_CreateWindow()creates the window on screen. To enable hardware acceleration via a specific graphics API like OpenGL, you must pass theSDL_WINDOW_OPENGLflag.- An OpenGL context is created with
SDL_GL_CreateContext()after the window exists. This context is the gateway to GPU commands. - The application's main loop continuously processes events with
SDL_PollEvent()and presents the rendered frame withSDL_GL_SwapWindow(). - Since SDL2 is a C library, RAII is the correct C++ approach to manage resources like windows and contexts, ensuring they are always cleaned up via destructors.
In our next lesson, we will build directly on today's work. With our OpenGL-enabled window ready, we will initialize Skia, create a GrDirectContext to interface with the GPU, and finally map our native window to a Skia SkSurface. This will give us our first GPU-backed drawing canvas.