GLdc/tests/README.md
2026-06-22 09:05:44 +01:00

5.7 KiB
Raw Blame History

GLdc test suite

The tests are plain C++ headers under tests/. Every class deriving from test::TestCase (or GLTestCase) is auto-discovered (see tools/test_generator.py) and each void test_*() method becomes a test case. They build into the gldc_tests binary.

The same tests run on two backends:

  • desktop / software backend — fast iteration in CI;
  • Dreamcast / kospvr backend — the real target, run under an emulator.

Suites that drive the GL API derive from GLTestCase (tools/gl_test.h), which initialises the GPU exactly once and resets GL state before each test (see "Running once on the Dreamcast" below). Pure non-GL suites (e.g. the standalone allocator tests) derive from test::TestCase directly.

Desktop

# configure + build (32-bit desktop build)
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Debug ..
make gldc_tests

# run everything (headless)
SDL_VIDEODRIVER=dummy ./tests/gldc_tests

# run a single suite (prefix match on "Suite::test")
SDL_VIDEODRIVER=dummy ./tests/gldc_tests TextureFormatTests

Dreamcast

Build with the KallistiOS toolchain inside the kazade/dreamcast-sdk container, then run the resulting .elf under the nitrocast emulator:

# build (from the repo root; KOS_BASE is set inside the container)
podman run --rm -v "$PWD":"$PWD":Z -w "$PWD" localhost/kazade/dreamcast-sdk \
    /bin/sh -c "source /etc/bash.bashrc; \
        mkdir -p dcbuild && cd dcbuild && \
        cmake -DCMAKE_TOOLCHAIN_FILE=../toolchains/Dreamcast.cmake \
              -DCMAKE_BUILD_TYPE=Release -DBUILD_SAMPLES=OFF .. && \
        make gldc_tests"

# run on the emulator. -u enables dcload host-file syscalls and -C mounts the
# given directory as the dcload filesystem (KOS sees it as /pc), which is where
# the golden references are read from. The build copies goldens/ next to the
# .elf, so run from there:
cd dcbuild/tests
nitrocast -b -e gldc_tests.elf -u -C . -r 32

The allocator unit tests are desktop-only (they assert exact host heap addresses); they are automatically excluded from the Dreamcast build.

Running once on the Dreamcast

InitGPU()/ShutdownGPU() map to pvr_init()/pvr_shutdown(), which must not be torn down and brought back up between tests. GLTestCase therefore opens the PVR (and the display) lazily on the first test, registers an atexit() handler to shut it down once at program exit, and only resets GL state between tests. New GL-driving suites should derive from GLTestCase; if they need extra setup they should override set_up() and call GLTestCase::set_up() first.

What's covered

File Focus
test_glcolor.h glColor* / glColorPointer state
test_glteximage2d.h basic internal-format selection
test_pvr_vertex_submission.h TA poly-list structure & headers
test_vertex_formats.h glVertexPointer types/sizes/strides, immediate mode, glDrawElements
test_texcoord_formats.h glTexCoordPointer type scaling, immediate glTexCoord
test_texture_formats.h byte-exact texture conversion (RGB565 / ARGB4444 / ARGB1555 / RGBA8 / RED / ALPHA / paletted), glTexSubImage2D, errors
test_golden_rendering.h end-to-end rendered-output comparison

The format/submission tests work by inspecting the internal state the driver produces — the converted texture bytes (TextureObject::data) and the submitted vertices in OP_LIST / PT_LIST / TR_LIST — so they are exact and deterministic, and they pin down the per-type readers and pixel conversions without needing a framebuffer.

Golden-image tests

tools/golden.h contains a small, self-contained, deterministic CPU rasteriser. It consumes the same TA poly-lists the real backend submits and reproduces the backend's triangle-strip walk and perspective divide, then fills triangles with Gouraud vertex colour (optionally modulated by a decoded texture). Output is compared against committed PPM references in tests/goldens/.

Because the rasteriser lives in the test harness (not the library) it doesn't move when the library changes, so a diff in the rendered output reliably points at a regression in GLdc's transform / colour / clipping / submission pipeline. The poly-lists are produced by the real (SH4-compiled) library, so the same references are validated against actual Dreamcast output too — on the Dreamcast the textured cases even sample the decoded texture straight out of PVR VRAM. Comparison is tolerant (per-channel + mismatch-fraction thresholds) so sub-LSB rounding never causes flakiness.

The references live in tests/goldens/. On the desktop they are read straight from the source tree; on the Dreamcast they are read through the dcload mount at /pc/goldens (the build copies them next to the .elf, so -C . from the build directory exposes them).

Adding or updating a golden

  1. Write a test that draws something and calls golden::rasterize_all_lists(img) then assert_true(golden::check(img, "my_scene")).

  2. Generate (or refresh) the reference image (desktop):

    GLDC_UPDATE_GOLDENS=1 SDL_VIDEODRIVER=dummy ./tests/gldc_tests GoldenRenderingTests
    
  3. Eyeball the new tests/goldens/my_scene.ppm before committing it (any image viewer / convert ... png works) and commit it alongside the test.

On a mismatch the harness writes my_scene.actual.ppm and my_scene.diff.ppm (differing pixels highlighted red) next to the golden for inspection.

Note: the video mode is 640×480 and the viewport y-flip is relative to that height, so golden scenes place the viewport at (0, 480 - H) to capture a W×H image at the top-left of the framebuffer.