Felipe Tavares' Avatar

HLSL Pixel Shaders With D3d9 SDL_Renderer

April 18, '21

Image of a colored Mandelbrot fractal rendered with HLSL and SDL. Orange colors.

Sometimes when working with SDL you may want to have custom effects that run fullscreen for every pixel. In some cases you can just do that on the CPU side, but that quickly becomes a CPU bottleneck as the resolution increases.

One solution to avoid that is to use shaders. The most common way to add shaders to SDL is to use OpenGL, which is multiplatform. Because of that you can write shaders once and then just run those same shaders in all the platforms that you are compiling for. However, in a few cases, you may want to have a custom shaders that works only for DirectX when your code is running on Windows. Some reasons you may want that are:

The Idea

Image of a colored Mandelbrot fractal rendered with HLSL and SDL. Blue colors.

The main idea is to define the same D3D_RenderData that is used by SDL’s Direct3d9 driver. This way we can then access everything that’s inside it. In our case, the piece of information we want to acces is the D3D device.

typedef struct
{
  // ...
} D3D_RenderData;

// ... very code, many line ...

IDirect3DDevice9* device = reinterpret_cast<D3D_RenderData*>(renderer->driverdata)->device;

Then we can simply call the normal D3D functions using our device to set the shader:

assert(D3D_OK == IDirect3DDevice9_CreatePixelShader(device, g_ps21_main, &shader));


// ...

assert(D3D_OK == IDirect3DDevice9_SetPixelShader(device, shader));

Compiling HLSL Shaders

To compile HLSL shaders we use fxc.exe, which is part of the DirectX SDK (it can be downloaded from Microsoft).

For example, if we have a fractal.hlsl that we want to compile to a binary and put that binary in a C array in a header file, we can do that by this command:

fxc /O3 /T ps_2_a fractal.hlsl /Fh fractal.h

Notice we have to set the shader profile (with /T) to ps_2_a (it could also be ps_2_0 or ps_2_b, but the first is the least limited). This is because SDL’s driver for Direct3D9 does not setup a vertex shader, so we need to either use the version 2 or go to 3 and also make a vertex shader, but then we would need to worry about what exactly we are passing to that vertex shader and in the case of passing textures we would probably need to drop SDL_Renderer altogether and almosts write a new custom driver for SDL. At that point it is probably better to drop SDL and just go with full Windows + DirectX.

Example Project on GitHub

I’ve created a full example project on GitHub, which compiles and produces the beautiful Mandelbrot fractal you saw in the screenshots.

Example Project: SDL_Renderer + HLSL