Tutorial 131: The SDL_GPU Renderer: Build, Verify and Query

CNA Tutorials  ·  CNA snapshot 3b43bd86

ℹ

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, 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 3b43bd86: 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 3b43bd8687ed46523d873f9bec4b32bd8481dcb5 (27 October 2026), a development snapshot 948 commits after the 0.1.0 release. The product version string is still 0.1.0, and a plain git clone of CNA gives you main, the 0.1.0 release, which predates the changes described here (among them SDL_GPU becoming the Windows, macOS and iOS default). Clone -b next.

What SDL_GPU is

SDL_GPU is CNA's renderer for SDL3's own GPU API (SDL_gpu.h). CNA builds pipelines and command buffers through SDL, and SDL picks its own Vulkan, Direct3D 12 or Metal GPU driver underneath. That makes it a translation layer in CNA’s own category scheme (GraphicsRendererCategory::TranslationLayer), it is declared Supported in CNA’s maturity table (a declaration, not a measurement), and it is CNA’s modern native path: the default renderer on Windows, macOS and iOS. At this snapshot it is validated on Linux (Vulkan) and, cross-compiled, under Wine (Direct3D 12) only; no native Windows run, no re-run on Apple hardware and no iOS build of it exist yet.

  • Draws in 2D and 3D. It reports ThreeD. SpriteBatch, the stock effects, the PBR effects, skinned meshes, render targets, cube and 3D textures, instancing and multiple render targets are implemented.
  • Modern graphics features. Float and half-float render targets, shadow sampling, image-based lighting and base-instance drawing are claimed by the renderer — see Modern features. Occlusion queries are not. CNA has no compute-shader, storage-buffer, indirect-draw or GPU-timer API on any renderer.
  • SDL3 is not optional. The identity is an SDL3 API, so it needs the SDL3 platform (the default CNA_PLATFORM). SDL_GPU is one of the two renderer identities that link SDL3 directly, together with FNA3D.
  • No sibling repository beyond sharp-runtime. It does not use easy-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: SDL3, SDL_mixer, draco, googletest, avatar catalog

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 lineWhat it tells you
CNA: Using SDL_GPU graphics rendererThe 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 falseWhether 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, 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
    {
        // new PresentationParameters() is XNA's 0x0 full-screen default, so state the size
        PresentationParameters parameters = PresentationParameters::CreateWindowedEXT(64, 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 17 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, 17> 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},
        }};
        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 usual route; an opt-in installed C++ SDK also exists); 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 (33 features, 8 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:

HostSDL_gpu driver (per CNA's own build comments)How the SPIR-V stock shaders reach itCNA_SDL_GPU_SHADERCROSS default
Linux, AndroidVulkanConsumed directly; no translation, no extra dependency.OFF
WindowsDirect3D 12Translated at run time to the driver's native format by SDL_shadercross.ON
AppleMetalTranslated 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, and CI runs it on lavapipe in the platform-contract and glTF stride jobs. The Direct3D 12 side is exercised by the cross-compiled SDL_GPU tests run under Wine with vkd3d-proton (scripts/run-wine-vkd3d-headless.sh); the native-Windows MSVC workflow has an SDL_GPU Direct3D 12 smoke leg, but it is manual-dispatch only and CNA records it as not run yet. On macOS a gating CI leg runs an SDL_GPU smoke set on SDL’s Metal driver, and had not recorded a run at this snapshot; CNA’s early-October Mac mini M4 campaign, before this snapshot, recorded SDL’s Metal driver passing CNA’s full test tree (10,962 tests with named skips only; 90 of 91 cna-samples, LensFlare refused because SDL’s GPU API has no occlusion queries; SDL’s Metal driver ignores the sampler LOD bias) and was not repeated. Treat Windows and Apple as “built to work”, not qualified.

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_shared or shaderc and, for a native (non-cross) build, falls back to the usual system library directories. Found → CustomEffects is yes. Not found → it is no.
  • Windows, Apple and Emscripten builds: never available. The raw SPIR-V a ShaderEffect produces 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 the vertex and fragment stages, in every build that has a device (on Windows and Apple it goes through SDL_shadercross like the stock shaders); 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 9 of CNA’s 10 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.

CapabilitySDL_GPUNotes
ThreeDyesStock 3D draws are implemented.
DepthStencilBuffer, StencilBufferyes (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.
MultiSampleAntiAliasingprobedTrue if any 2x, 4x or 8x mode survives the driver's sample-count query for your colour and depth formats.
MultipleRenderTargetsyes under HiDefThe 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, AdditiveBlendingyesAll sixteen XNA vertex-stream bindings are supported; wireframe is native line fill.
OcclusionQuerynoThe vendored SDL_gpu exposes no occlusion-query or query-pool commands. Creating a query throws.
CustomEffectsonly with libshadercSee above.
CompiledEffectsonly with CNA_SDL_GPU_COMPILED_EFFECTS=ONOff by default.
FloatRenderTargets, HalfFloatRenderTargetsprobedDerived from whether the driver accepts RGBA32F and RGBA16F as colour target plus sampler.
HalfFloatTextureLinearFilteringprobedReported exactly where an RGBA16F render target exists.
⚠

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 and the two float render-target capabilities. GraphicsDevice::SupportsCapability() derives the float capabilities from format probes, so it correctly says yes where the driver can create those targets. 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:

UseSupportedRefused
Texture2DColor, Bgr565, Bgra5551, Bgra4444, Dxt1/Dxt3/Dxt5 (native BC when the driver has it, CPU decode otherwise), NormalizedByte2/NormalizedByte4, Alpha8; and Rgba1010102, Rg32, Rgba64 and the single/vector/half-float formats in their exact native storage — each only where the device can sample that storageThe wide and float formats on a device that cannot sample them; never substituted by another format
TextureCubeColor, Dxt1/Dxt3/Dxt5Everything else
Texture3DColor, Single, Vector4 — each only where the device can sample a 3D texture in that formatEverything else
RenderTarget2DColor, Rgba64, Single, Vector2, Vector4, HalfSingle, HalfVector2, HalfVector4, HdrBlendable — each only if the driver accepts it as colour target plus samplerPacked 16-bit, DXT, NormalizedByte*, Rgba1010102, Rg32, Alpha8

For HDR you can both render into a half-float or float target and, where the device can sample the format, 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

At this snapshot the renderer implements these features beyond the XNA surface:

  • Base-instance drawing (DrawInstancedPrimitivesBaseInstanceEXT): SDL_GPU is the only renderer that implements it. Its stock, instanced-matrix and compiled-effect routes expand the per-instance streams on the host, starting from the first instance.
  • Reported limits. The capability profile carries the uniform-buffer size and offset alignment, the sampled textures per shader stage and four colour attachments.
  • Shadow sampling and image-based lighting. The renderer claims both, so the stock effects’ shadow and environment receivers are honoured here once you supply the shadow map and the environment textures; CNA does not generate either (Tutorial 59, Tutorial 114).
  • What it does not have or does differently: occlusion queries; and an arbitrary BlendState.MultiSampleMask: on a multisampled target a mask that keeps every sample or none is applied (none drops the draw) and any other is refused with NotSupportedException, because the pinned SDL reserves the field (RendererFeature::MultiSampleMask answers Restricted). A cube face in any slot of a multiple-render-target set works, and SpriteSortMode::Immediate draws each sprite with the texture as it is at its Draw call: an upload to a texture that a queued draw samples submits the queued work first. The renderer’s own limitation text says CNAEXT ShaderEffect instancing is not implemented, although its draw path does route instanced custom-effect draws.

All of these are reachable from game code: shadow and environment reception go through the stock and PBR effects, and base-instance drawing through the CNAEXT draw call. Ask the capability profile (BaseInstanceDrawing, ShadowSampling, ImageBasedLighting) before you rely on one.

When it does not start

SymptomCause and fix
Configure stops: CNA_ENABLE_SDL=OFF, but this configuration genuinely requires SDL: …, CNA_GRAPHICS_RENDERER=SDL_GPUCNA_ENABLE_SDL=OFF refuses the two SDL3-direct renderers by name. Leave CNA_ENABLE_SDL at AUTO or ON; a build without SDL is windowless (CNA_PLATFORM=HEADLESS or TERMINAL).
Configure stops on an unknown or misspelled nameCMake matches the 10 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 failedOnly in builds with CNA_SDL_GPU_SHADERCROSS=ON; the message carries SDL's own error text, which names what failed.
CustomEffects is no on LinuxNo 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 itThe device's XNA profile is Reach, the default. Ask for GraphicsProfile::HiDef, as the probe does with setGraphicsProfileProperty().
Creating an occlusion query throwsBy design: this renderer has no occlusion queries. Check OcclusionQuery first.
The whole test set fails on Validation Error or VUID- outputSDL_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_GPU is one of three renderer families (with EasyGL and WEBGPU) that register the shared cross-renderer fixtures, as SdlGpu_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 and a headless device, many of them under the SdlGpu label.
  • No XNA pixel oracle. SDL_GPU is not compared with the real-XNA reference scenes at all; only DIRECTX9 is 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).
  • CI reaches it in smoke and contract jobs only. The Linux platform-contract and glTF stride jobs build and run SDL_GPU on lavapipe; the macOS smoke leg is gating but had not recorded a run at this snapshot; the iOS legs are non-gating; the Windows Direct3D 12 leg is manual. No job runs the whole SDL_GPU test set, and nothing above is a recorded pass of it on a server.

Where to go next