Tutorial 131: The SDL_GPU Renderer: Build, Verify and Query
What you’ll learn: how to configure and build the SDL_GPU renderer, verify it with CNA's own demos, tests and a probe program, and what it reports for shaders, formats, compute, float targets and occlusion queries.
Before you start — Tutorial 72: Choosing a Renderer for the group comparison and Tutorial 20: Building and Running Your Game for the configure/build basics. This tutorial is about one renderer, SDL_GPU, in CNA snapshot 009d40f5: what it is, how to build it, how to prove it is the renderer that started, and what it does and does not report.
Snapshot, not release. Everything below is checked against CNA branch next at commit 009d40f5dd085c4e674d3479675fac84b12b3e0a (24 September 2026). The product version string is still 0.1.0-alpha.1, and a plain git clone of CNA gives you that older develop branch, which does not have this renderer's modern-graphics work. Clone -b next.
What SDL_GPU is
SDL_GPU is CNA's renderer for SDL3's own GPU API (SDL_gpu.h). CNA does not talk to Vulkan, Direct3D 12 or Metal here; it builds pipelines and command buffers through SDL, and SDL picks the native driver. That makes it a translation layer in CNA's own category scheme (GraphicsBackendCategory::TranslationLayer), it is declared Supported in CNA's maturity table (a declaration, not a measurement), and it is a separate implementation from VULKAN — Tutorial 85 covers the direct Vulkan renderer.
- Draws in 2D and 3D. It reports
ThreeD, so it is not one of the seven 2D-only renderers. SpriteBatch, the stock effects, skinned meshes, render targets, cube and 3D textures, instancing and multiple render targets are implemented. - Modern surface. Compute shaders, storage buffers, indirect drawing, float and half-float render targets, shadow sampling and image-based lighting are claimed by the renderer — see Modern features. Occlusion queries are not.
- SDL3 is not optional. The identity is an SDL3 API, so it needs the
SDL3platform (the defaultCNA_PLATFORM).SDL_GPUis one of the four renderer identities that link SDL3 directly, together withSDL_RENDERER,FNA3DandFREEDIRECT. - No sibling repository beyond
sharp-runtime. It does not useeasy-gl; the SDL_gpu code is part of the vendored SDL3 library CNA already builds.
Prerequisites
cd /path/to/workspace
git clone -b next https://github.com/libcna/cna.git
git clone -b next https://github.com/libcna/sharp-runtime.git # must be its `next` branch too
cd cna
git submodule update --init # non-recursive is correct: vendored SDL3, SDL_image, SDL_mixer, draco, googletest
Beyond the compiler and CMake (3.20 or newer, C++23) you need what every SDL-based CNA build needs — the X11/Wayland, GL and audio development packages that give the vendored SDL3 real video drivers; Building lists them — plus, at run time, a GPU and driver that SDL_gpu can use. On Linux and Android CNA's own comments describe this as SDL's Vulkan driver; on Windows and Apple platforms it is SDL's Direct3D 12 and Metal drivers.
Optional: a libshaderc development package (for example libshaderc-dev on Debian and Ubuntu) if you want ShaderEffect to work; the CMake lookup names are shaderc_shared and shaderc. Custom shaders explains why it is not needed for anything else.
Configure and build
cmake -S . -B build -DCNA_GRAPHICS_RENDERER=SDL_GPU \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache
cmake --build build --target cna_demo_renderer_selection cna_demo_2d --parallel
The per-renderer switch form -DCNA_RENDERER_SDL_GPU=ON is equivalent; use exactly one of the two forms. The ccache launchers are optional and only a build-speed convenience. Do not build --target CNA: it is an interface library with no sources.
Watch the configure output. Three lines matter:
| Configure line | What it tells you |
|---|---|
CNA: Using SDL_GPU graphics renderer | The identity was accepted. Names are exact-case in CMake: sdl_gpu is refused as an unknown name. |
CNA: SDL_shadercross pinned at … with SPIRV-Cross … | CNA_SDL_GPU_SHADERCROSS is on, so CNA fetches and links SDL_shadercross. Default on Windows and Apple, off elsewhere. |
CNA: SDL_GPU ShaderEffect enabled with target libshaderc at … or … ShaderEffect disabled … CustomEffects will report false | Whether custom ShaderEffect shaders can run in this build. See Custom shaders. |
Verify that SDL_GPU really started
Configuring a renderer does not prove it started. CNA ships a renderer-agnostic reference program, cna_demo_renderer_selection, that reports what the build contains and what actually became active:
./build/cna_demo_renderer_selection
In a single-renderer build the first line lists exactly SDL_GPU, and after the device is created it prints Active renderer: SDL_GPU followed by Selection is latched, as expected. Then look at something moving:
./build/cna_demo_2d --smoke 120 # the 2D sprite demo, exits cleanly after 120 frames
cna_demo_2d is the cross-renderer visual target and works with every renderer, so it is the quickest "is it drawing?" check here. The 3D house demo, cna_house3d_demo, is deliberately not built for this renderer — CNA gates it to the GL family, VULKAN, WEBGPU and FNA3D — so use the renderer's own tests for 3D:
ctest --test-dir build -R '^SdlGpu_Smoke$' --output-on-failure
ctest --test-dir build -L SdlGpu --output-on-failure # the whole SDL_GPU set
The tests open real windows and force SDL's x11 video driver, so they need an X display with a GPU behind it. CNA_TEST_DISPLAY is empty by default and tests inherit the caller's DISPLAY, so on a desktop machine they appear on your screen. CNA's standard way to run them without that is tools/platform/run_gpu_tests_private.sh build -L SdlGpu --output-on-failure -j1, which builds a private headless compositor plus Xwayland; the script itself notes that plain Xvfb cannot present Vulkan on a real GPU (no DRI3). Build the test executables first (cmake --build build --parallel builds them all). The SDL_GPU tests treat any output containing Validation Error, Validation Warning or VUID- as a failure, and a Debug (non-NDEBUG) build asks SDL_gpu for its debug mode, which on Vulkan means the validation layer.
No window: a headless device
SDL_GPU can also run without a window or swapchain. Set PresentationParameters::HeadlessEXT and the renderer renders into an offscreen back buffer you read back — CNA's own SdlGpu_HeadlessGraphicsDevice CTest exercises exactly this path (Vulkan on Linux; the Windows cross-build runs the same executable for Direct3D 12 under Wine). A minimal program, syntax-checked against the snapshot's headers:
#include <cstdio>
#include <exception>
#include "Microsoft/Xna/Framework/Color.hpp"
#include "Microsoft/Xna/Framework/Rectangle.hpp"
#include "Microsoft/Xna/Framework/Graphics/GraphicsAdapter.hpp"
#include "Microsoft/Xna/Framework/Graphics/GraphicsDevice.hpp"
#include "Microsoft/Xna/Framework/Graphics/GraphicsProfile.hpp"
#include "Microsoft/Xna/Framework/Graphics/PresentationParameters.hpp"
using namespace Microsoft::Xna::Framework;
using namespace Microsoft::Xna::Framework::Graphics;
int main()
{
try
{
PresentationParameters parameters;
parameters.setBackBufferWidthProperty(64);
parameters.setBackBufferHeightProperty(64);
parameters.setHeadlessEXTProperty(true); // no window, no swapchain
GraphicsAdapter& adapter = GraphicsAdapter::getDefaultAdapterProperty();
GraphicsDevice device(adapter, GraphicsProfile::HiDef, parameters);
std::printf("renderer: %.*s\n",
static_cast<int>(device.GetGraphicsRendererName().size()),
device.GetGraphicsRendererName().data());
const Color clear(13, 71, 199, 255);
device.Clear(clear);
const Rectangle probe(11, 7, 1, 1);
Color pixel;
device.GetBackBufferData(&probe, &pixel, 0, 1);
std::printf("round trip %s\n", pixel == clear ? "exact" : "MISMATCH");
return pixel == clear ? 0 : 1;
}
catch (const std::exception& error)
{
std::printf("failed: %s\n", error.what());
return 2;
}
}
Without a display, try SDL_VIDEODRIVER=dummy; CNA's own SDL_GPU tests support the dummy and offscreen video drivers through the CNA_SDLGPU_TEST_VIDEO_DRIVER cache variable. GetGraphicsRendererName() reports the device's real renderer — in a single-renderer build it prints SDL_GPU. To force a particular native driver, SDL's own SDL_GPU_DRIVER environment variable is what CNA's tests use (vulkan on Linux, direct3d12 for the Windows probe); that is an SDL setting, not a CNA option.
Ask the device what it can do
The next program is the tutorial's real payload: a tiny game that asks the live device about all 19 GraphicsCapability members and prints CNA's generated capability report. It compiles unchanged against every renderer, which makes it the right first probe for any new machine.
#include <array>
#include <iostream>
#include <string_view>
#include "CNA/GraphicsCapability.hpp"
#include "Microsoft/Xna/Framework/Game.hpp"
#include "Microsoft/Xna/Framework/GameTime.hpp"
#include "Microsoft/Xna/Framework/GraphicsDeviceManager.hpp"
#include "Microsoft/Xna/Framework/Graphics/DepthFormat.hpp"
#include "Microsoft/Xna/Framework/Graphics/GraphicsDevice.hpp"
#include "Microsoft/Xna/Framework/Graphics/GraphicsProfile.hpp"
using namespace Microsoft::Xna::Framework;
using namespace Microsoft::Xna::Framework::Graphics;
class ProbeGame final : public Game
{
public:
ProbeGame() : graphics_(this)
{
// The XNA default is the Reach profile (one render target). Ask for HiDef, and for a
// depth format that owns a stencil plane, so the answers below describe the renderer
// rather than these two defaults.
graphics_.setGraphicsProfileProperty(GraphicsProfile::HiDef);
graphics_.setPreferredDepthStencilFormatProperty(DepthFormat::Depth24Stencil8);
}
protected:
void Draw(const GameTime&) override
{
GraphicsDevice& device = getGraphicsDeviceProperty();
std::cout << "renderer: " << device.GetGraphicsRendererName() << '\n';
struct Row { std::string_view name; CNA::GraphicsCapability capability; };
constexpr std::array<Row, 19> rows{{
{"ThreeD", CNA::GraphicsCapability::ThreeD},
{"DepthStencilBuffer", CNA::GraphicsCapability::DepthStencilBuffer},
{"MultiSampleAntiAliasing", CNA::GraphicsCapability::MultiSampleAntiAliasing},
{"MultipleRenderTargets", CNA::GraphicsCapability::MultipleRenderTargets},
{"AnisotropicFiltering", CNA::GraphicsCapability::AnisotropicFiltering},
{"WireFrame", CNA::GraphicsCapability::WireFrame},
{"OcclusionQuery", CNA::GraphicsCapability::OcclusionQuery},
{"CustomEffects", CNA::GraphicsCapability::CustomEffects},
{"Texture3D", CNA::GraphicsCapability::Texture3D},
{"MultiStreamVertexInput", CNA::GraphicsCapability::MultiStreamVertexInput},
{"Instancing", CNA::GraphicsCapability::Instancing},
{"StencilBuffer", CNA::GraphicsCapability::StencilBuffer},
{"AdditiveBlending", CNA::GraphicsCapability::AdditiveBlending},
{"CompiledEffects", CNA::GraphicsCapability::CompiledEffects},
{"FloatRenderTargets", CNA::GraphicsCapability::FloatRenderTargets},
{"HalfFloatRenderTargets", CNA::GraphicsCapability::HalfFloatRenderTargets},
{"HalfFloatTextureLinearFiltering",
CNA::GraphicsCapability::HalfFloatTextureLinearFiltering},
{"ComputeShaders", CNA::GraphicsCapability::ComputeShaders},
{"IndirectDraw", CNA::GraphicsCapability::IndirectDraw},
}};
for (const Row& row : rows)
{
std::cout << " " << row.name << ": "
<< (device.SupportsCapability(row.capability) ? "yes" : "no") << '\n';
}
std::cout << "\n" << device.GetRendererCapabilityReportEXT() << '\n';
Exit();
}
private:
GraphicsDeviceManager graphics_;
};
int main()
{
ProbeGame game;
game.Run();
return 0;
}
To build it, put it in a project next to the cna checkout. As in Tutorial 3, CNA is added as a subdirectory (the C++ framework has no install package); switching off CNA's own tests and examples keeps your configure small:
cmake_minimum_required(VERSION 3.20)
project(SdlGpuProbe LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CNA_GRAPHICS_RENDERER "SDL_GPU" CACHE STRING "CNA graphics renderer")
set(CNA_BUILD_TESTS OFF)
set(CNA_BUILD_EXAMPLES OFF)
add_subdirectory(../cna ${CMAKE_BINARY_DIR}/cna)
add_executable(sdlgpu_probe main.cpp)
target_link_libraries(sdlgpu_probe PRIVATE CNA)
cna_link_sharp_runtime(sdlgpu_probe PRIVATE) # CNA's own helper, as its examples use
Two details in the probe are worth copying. The capability answers come from GraphicsDevice::SupportsCapability(), the public answer, and the report string is GetRendererCapabilityReportEXT(), the human-readable rendering of the detailed RendererCapabilityProfile (32 features, 22 limits, per-format usage). Tutorial 133 queries that profile directly.
Drivers and shaders: SPIR-V in, native out
CNA authors its stock shaders once, as committed SPIR-V. The renderer declares SPIR-V as its shader dialect. What happens next depends on the driver SDL chose:
| Host | SDL_gpu driver (per CNA's own build comments) | How the SPIR-V stock shaders reach it | CNA_SDL_GPU_SHADERCROSS default |
|---|---|---|---|
| Linux, Android | Vulkan | Consumed directly; no translation, no extra dependency. | OFF |
| Windows | Direct3D 12 | Translated at run time to the driver's native format by SDL_shadercross. | ON |
| Apple | Metal | Translated to MSL by SDL_shadercross. | ON |
Where the option is OFF and no installed driver accepts SPIR-V, device creation fails with CNA SDL_GPU: no installed SDL_gpu driver accepts SPIR-V; rebuild with CNA_SDL_GPU_SHADERCROSS=ON for native D3D12/Metal stock shaders. You may also switch it on yourself on Linux; a Vulkan device still takes the SPIR-V directly, because the renderer uses the ShaderCross route only when the device does not accept SPIR-V (a test hook can force it). The dependency is SDL_shadercross pinned at 1ff05bec573988a98ef9e0260b4da44f512b8367 with SPIRV-Cross vulkan-sdk-1.4.350.0, both fetched by CMake and linked statically; it is new since alpha.1. It only translates SPIR-V; it does not compile source, which is why it is unrelated to libshaderc.
Evidence is uneven across those rows. The Linux Vulkan route is the one CNA's routine CTests run. The Direct3D 12 side is exercised by a cross-compiled probe run under Wine with vkd3d-proton (scripts/run-wine-vkd3d-headless.sh), not by a native-Windows lane, and no GitHub workflow selects SDL_GPU at all. Treat Windows and Apple as "built to work", not as CI-verified.
Custom shaders and compiled effects
Two different things are called shaders here, and SDL_GPU treats them independently.
Custom ShaderEffect (the CustomEffects capability)
ShaderEffect takes your own shader source at run time. On SDL_GPU that needs a target-native libshaderc to compile GLSL to SPIR-V, and the build only looks for it on targets where the resulting SPIR-V is something the driver accepts:
- Linux (and other non-Windows, non-Apple, non-Emscripten targets): CMake looks for
shaderc_sharedorshadercand, for a native (non-cross) build, falls back to the usual system library directories. Found →CustomEffectsisyes. Not found → it isno. - Windows, Apple and Emscripten builds: never available. The raw SPIR-V a
ShaderEffectproduces cannot be translated to native output there, so CNA leaves the capability off rather than claim it. This does not affect stock effects or compiled effects.
Without the compiler a custom effect is not silently ignored: CNA's own smoke test asserts that the renderer's effect factory returns an invalid effect whose compile error mentions the missing target-native path. Check SupportsCapability(GraphicsCapability::CustomEffects) first. The renderer also accepts caller-supplied SPIR-V for vertex, fragment and compute stages; it does not claim GLSL as an input language even where libshaderc exists, because that route compiles for Vulkan's rules. See Tutorial 52 for the ShaderEffect API.
Compiled XNA effects (.fxb)
Direct3D 9 Effect Framework bytecode runs through MojoShader's own SDL_GPU adapter, which emits SPIR-V. It is opt-in:
cmake -S . -B build -DCNA_GRAPHICS_RENDERER=SDL_GPU -DCNA_SDL_GPU_COMPILED_EFFECTS=ON
The option defaults to OFF because it pulls the pinned FNA3D checkout (for MojoShader) into a renderer that does not otherwise need it; with it on, CompiledEffects reports yes. In a default configure it reports no — that is true of 24 of CNA's 25 renderer identities.
What it reports
The answers below are what GraphicsDevice::SupportsCapability() gives for the default configuration on a typical Vulkan device. "Probed" means the answer comes from the live driver, so it can differ between two machines.
| Capability | SDL_GPU | Notes |
|---|---|---|
ThreeD | yes | Stock 3D draws are implemented. |
DepthStencilBuffer, StencilBuffer | yes (format-dependent) | Follow the created depth format. The manager's default depth format has no stencil plane; the probe requests DepthFormat::Depth24Stencil8 so stencil is available. |
MultiSampleAntiAliasing | probed | True if any 2x, 4x or 8x mode survives the driver's sample-count query for your colour and depth formats. |
MultipleRenderTargets | yes under HiDef | The renderer supports up to 4 colour attachments, but the public answer is ANDed with the XNA profile: the default Reach profile has one render target, so the probe asks for HiDef. |
AnisotropicFiltering, WireFrame, Texture3D, MultiStreamVertexInput, Instancing, AdditiveBlending | yes | All sixteen XNA vertex-stream bindings are supported; wireframe is native line fill. |
OcclusionQuery | no | The vendored SDL_gpu exposes no occlusion-query or query-pool commands. Creating a query throws. |
CustomEffects | only with libshaderc | See above. |
CompiledEffects | only with CNA_SDL_GPU_COMPILED_EFFECTS=ON | Off by default. |
FloatRenderTargets, HalfFloatRenderTargets | probed | Derived from whether the driver accepts RGBA32F and RGBA16F as colour target plus sampler. |
HalfFloatTextureLinearFiltering | probed | Reported exactly where an RGBA16F render target exists. |
ComputeShaders, IndirectDraw | yes | Once a device exists; SDL_gpu's compute surface is core, not optional. |
Renderer-level and device-level answers differ, and the device wins. If you hold the renderer object itself (GraphicsDevice::GetRenderer()), its own SupportsCapability() switch answers false for OcclusionQuery, the two float render-target capabilities, ComputeShaders and IndirectDraw. GraphicsDevice::SupportsCapability() derives compute and indirect draw from the renderer's dedicated queries and the float capabilities from format probes, so it correctly says yes. Always ask the device, as the probe program does. This split is a documented quirk inside CNA, not something your game should work around.
Texture and render-target formats
A renderer that has not classified a SurfaceFormat falls back to the framework rule: Color only, anything else throws. SDL_GPU classifies as follows:
| Use | Supported | Refused |
|---|---|---|
Texture2D | Color, Bgr565, Bgra5551, Bgra4444, Dxt1/Dxt3/Dxt5 (native BC when the driver has it, CPU decode otherwise), NormalizedByte2/NormalizedByte4 | Rgba1010102, Rg32, Rgba64, Alpha8 and the single/vector/half-float texture formats. CNA's comment says SDL_gpu has candidate storage for many but the sampling and filtering verification is not there yet. |
TextureCube | Color, Dxt1/Dxt3/Dxt5 | Everything else |
Texture3D | Color | Everything else |
RenderTarget2D | Color, Rgba64, Single, Vector2, Vector4, HalfSingle, HalfVector2, HalfVector4, HdrBlendable — each only if the driver accepts it as colour target plus sampler | Packed 16-bit, DXT, NormalizedByte*, Rgba1010102, Rg32, Alpha8 |
The distinction matters for HDR: you can render into a half-float or float target on SDL_GPU, but you cannot create a float Texture2D from CPU data. The 7 CNA-specific *EXT formats defer to the common Color-only gate. Ask the device before relying on a format: GetRendererSurfaceFormatSupportEXT(format) and SupportsSurfaceFormatAsRenderTargetEXT(format).
Modern features
This renderer went through a dedicated modern-graphics campaign; the merged result at this snapshot is:
- Compute. Compute pipelines, storage buffers and 2D storage textures exist, with SPIR-V compute intake. Limits reported to the capability profile include storage and uniform buffer sizes, a 256-byte offset alignment and compute work-group invocation limits.
- Indirect draw, including a non-zero first instance (base-instance drawing), because
first_instanceis part of SDL's own indirect command. - Shadow sampling and image-based lighting. The renderer claims both. CNA's own CNAEXT shadow-map and IBL tests run only where the renderer reports
ThreeD,CustomEffectsand the shadow query, so on a build withoutlibshadercthey are skipped, and the engine layer's passes degrade or throw through its capability check. - What it does not have: occlusion queries and GPU timers. CNA's limitation text also notes that CNAEXT
ShaderEffectinstancing is not implemented here.
Whether you can use these from game code depends on the layer: the XNA-level capability answers above are always available, while compute and indirect drawing are CNAEXT-level features (Tutorial 115).
When it does not start
| Symptom | Cause and fix |
|---|---|
Configure stops: CNA_ENABLE_SDL=OFF, but this configuration genuinely requires SDL: …, CNA_GRAPHICS_RENDERER=SDL_GPU | The SDL-free switch refuses the four SDL3-direct renderers by name. Leave CNA_ENABLE_SDL at AUTO or ON, or choose a native platform with an SDL-free renderer. |
Configure stops: … require a renderer without a direct SDL3 dependency … | You combined CNA_PLATFORM=SDL2 with CNA_AUDIO_PLATFORM=SDL2. SDL2 and SDL3 cannot share a process; keep the default SDL3 platform. |
| Configure stops on an unknown or misspelled name | CMake matches the 25 public names exactly and case-sensitively: it is SDL_GPU, with the underscore, upper case. |
Run time: CNA SDL_GPU: SDL_CreateGPUDevice failed: … | SDL could not open a GPU device: no Vulkan (or D3D12/Metal) driver, or none that accepts the shader formats CNA requested. The renderer never substitutes another one; select a fallback chain (Tutorial 126) if you want that. |
Run time: SDL_shadercross initialization failed | Only in builds with CNA_SDL_GPU_SHADERCROSS=ON; the message carries SDL's own error text, which names what failed. |
CustomEffects is no on Linux | No libshaderc was found at configure time; look for the ShaderEffect disabled line and install the development package, then reconfigure. |
MultipleRenderTargets is no, though the hardware can do it | The device's XNA profile is Reach, the default. Ask for GraphicsProfile::HiDef, as the probe does with setGraphicsProfileProperty(). |
| Creating an occlusion query throws | By design: this renderer has no occlusion queries. Check OcclusionQuery first. |
The whole test set fails on Validation Error or VUID- output | SDL_gpu's Vulkan validation layer reported something. That is the intended gate; the message names the offending call. |
What backs these answers
- 32 shared parity fixtures.
SDL_GPUis one of four renderers (withEasyGL,WEBGPUandOPENGL4) that register the shared cross-renderer fixtures, asSdlGpu_Parity_<fixture>CTests. Each fixture carries its own expected result; the oracle is the fixture's assertions, not real XNA output. - A renderer-specific set covering 2D, 3D, effects, skinning, cube and 3D textures, MRT, MSAA, render-target lifetime, swapchain recovery, storage-buffer lifetime and a headless device, many of them under the
SdlGpulabel. The three-thousand-cycle modern-resource soak is built but deliberately not registered with CTest. - No XNA pixel oracle.
SDL_GPUis not compared with the real-XNA reference scenes at all; onlyDIRECTX9is gated on the whole corpus (EasyGL has a CTest on two line scenes that fails on any difference, SOFTWARE registers the same two but fails only on a render failure, FNA3D gates only that every scene renders; see the XNA oracle). - No CI workflow. No GitHub workflow in the repository selects
SDL_GPU. Everything above is what the test registrations and code say, not a recorded pass on a server.
Where to go next
- Tutorial 132: The WebGPU Renderer: Native and Browser
- Tutorial 130: Run CNA on Desktop OpenGL 4 (OPENGL4) — the other renderer that received the same modern-graphics work as
SDL_GPU. - Tutorial 133: Read the Renderer Capability Profile — the 32 features, 22 limits and per-format usage behind the report you printed.
- Tutorial 101: Querying renderer capabilities
- Tutorial 126: Build and Select Several Renderers
- Renderers reference and Runtime Renderer Selection
Deep dives on this topic
Long-form pages that explain the exact semantics, invariants and evidence behind this subject.
- SDL_GPU shader intake, pipeline keys and draw order — Why CNA's SDL_GPU renderer uses precompiled SPIR-V in SDL_gpu's set convention, which GLSL ShaderEffect accepts, how pipelines are keyed, what state is dynamic, and how draw order and vsync are kept.
- SDL_GPU uploads, render-target lifetime and swapchain recovery — What SDL_gpu validation exposed in CNA's SDL_GPU renderer, when uploads may cycle, how render targets outlive their wrappers, what MRT writes, and how a failed swapchain acquisition keeps the frame.