Blackframe is a headless spectral rendering engine written in strict C++26 on the host and CUDA C++20 on the device. It currently renders deterministic four-wavelength scenes through a readable scalar reference, an Embree CPU wavefront backend, and a CUDA wavefront backend.
The engine is built as a numerical and architectural foundation rather than a general-purpose production renderer. Backends and optional capabilities are selected explicitly: an unavailable or unsupported path fails before rendering instead of silently choosing a substitute.
- Transport uses
float; reference calculations and optional compensated accumulation usedouble. Conversions, ABI sizes, offsets, and host/device alignments are tested. - Vectors, points, and normals are distinct types. The math layer includes matrices, quaternions, affine and projection transforms, robust inverses, local frames, intervals, bounds, and AABB intersection.
- Rays carry a bounded parameter interval, time, visibility mask, and current medium identifier. Primary pinhole rays support deterministic center sampling or indexed subpixel jitter.
- Scalar paths propagate full ray differentials across ideal specular events. CPU and CUDA wavefront paths carry explicit ray cones, advance their world-space footprint at hits, and apply the same reflection, Snell, diffuse, and GGX spread rules without changing the traversal ABI.
- Analytic spheres, planes, and disks coexist with watertight triangles. Surface interactions keep geometric and shading normals separate and expose UVs, derivatives, stable IDs, and time.
- Immutable
FrameScenesnapshots own stable object, geometry, material, texture, light, and instance identifiers. Nested transforms are resolved when the snapshot closes. - Backend-free
SceneDescriptionvalues have a strict versioned native JSON codec with canonical output, inline mesh/image snapshots, hard budgets, and explicit schema errors. - Scene assets resolve only below an explicitly supplied absolute scene directory. Portable dependency manifests retain canonical relative paths, byte sizes, and raw-content SHA-256 hashes; no current-directory, environment, parent-directory, or extension search is performed.
- One backend-neutral scene compiler validates an explicit versioned capability set, preserves the
active render selections and stable light IDs, lowers to an immutable
FrameScene, and emits a deterministic semantic SHA-256 without pointer or backend state. - The headless runner loads the strict native JSON, compiles it once, preflights the explicitly
requested
scalar_ref,cpu_embree, orgpu_cudapath, and writes EXR, PNG, and a versioned run metrics report. It never retries with another backend. - Strict OBJ and ASCII PLY import validates positions, normals, UVs, and triangle indices. Compacted meshes retain contiguous position, normal, UV, and index storage without duplicating aligned vertices unnecessarily.
- Scene updates distinguish full rebuilds from transform-only refits. Incompatible refits fail instead of rebuilding implicitly.
- Each path carries four wavelengths in the 360–830 nm visible range together with their sampling PDFs. Constant, black, and tabulated spectra feed CIE 1931 XYZ and signed scene-linear RGB.
SampleStreamindexes every value by pixel, sample, dimension, and seed. Independent hashing, PCG32, stratified and Latin-hypercube sampling, high-dimensional Sobol with Owen scrambling, and common geometric mappings are deterministic and use no global RNG.ClosureSetstores at most eight closures inline, without allocating at a hit. Explicit probabilities form mixtures whose values and PDFs include component selection exactly once.- Active closures include Lambert, energy-preserving Oren–Nayar, ideal mirror and specular transmission, GGX conductor and dielectric, and rotated anisotropic GGX.
- Fresnel dielectric and wavelength-dependent complex conductor Fresnel use the full equations. Delta and continuous events retain distinct probability measures.
- Scene transport supports spectral surface emission, a constant environment, punctual lights, and emissive meshes. Next-event estimation and balance or power MIS avoid double counting.
- Diffuse, glossy, specular, transmission, and volume depth budgets are tracked separately. Russian roulette is configurable and compensated.
| Backend | Role | Acceleration and execution |
|---|---|---|
| Scalar reference | Independent readable oracle | Analytic traversal and compensated reference film |
| CPU wavefront | Production CPU path | Embree 4, bounded queues, stable compaction, one or many workers |
| CUDA wavefront | Production GPU path | CUDA BLAS/TLAS, device queues, kernels, compaction, streams, and events |
All three paths share the same wavelength, PDF, event, closure, light, sampler, and path-state conventions. The CUDA backend traverses its own deterministic BVH; it never uses Embree or hidden CPU execution. Synchronous and asynchronous CUDA modes are selected when the workspace is created, and mode mismatches are errors.
The CPU scheduler records queue capacity, occupancy, overflow, dispatches, lanes, and stage time. CUDA adds bounded SoA queues, stable prefix-scan compaction, memory and transfer telemetry, optional NVTX ranges, and reusable RAII workspaces with explicit out-of-memory reporting.
- Film storage keeps weighted sums, weights, and sample counts per pixel. It supports float accumulation, compensated double reference accumulation, crops, tiles, and deterministic merges.
- Output includes scene-linear 32-bit RGB OpenEXR and an optional PNG preview with fixed exposure and display transform.
- OpenImageIO loads immutable interleaved-float host images and caches them by canonical path and mandatory color-space tag. PNM/PFM, PNG, and EXR are covered by tests.
- Data and scene-linear values stay unchanged. Tagged sRGB channels use a fixed IEC decode while alpha and extra channels remain untouched; tags are never guessed.
- Host sampling supports nearest, bilinear, and Catmull–Rom bicubic reconstruction with per-tap repeat, clamp, mirrored-repeat, or black-border addressing.
- Mip pyramids are generated from the converted snapshot down to 1x1 with exact-area box averages, including odd extents. Trilinear sampling blends bilinear results at an explicit finite LOD.
- EWA filtering consumes an explicit UV footprint and bounds anisotropy and texel visits. Host evaluation supports float transport and double reference precision; CUDA surface maps use the same bounded float contract.
- Spectral materials can bind data-tagged tangent-space normal maps and filtered bump maps by
texture ID. Normal-map Y orientation is explicit, bump gradients preserve the current shading
normal, and geometric normals remain unchanged for visibility and ray offsets.
scalar_refuses ray differentials; CPU and CUDA wavefront paths use propagated ray cones. - Host UDIM descriptors resolve one
<UDIM>token with standard ten-column numbering and return the addressed image plus tile-local UVs. Missing tiles fail explicitly instead of selecting a neighbor, tile 1001, or a diagnostic color. - Constant float, color, and spectrum textures are evaluated by scalar, CPU, and CUDA backends. Invalid tags, modes, coordinates, channels, budgets, or LODs fail explicitly.
GoogleTest and CTest cover algebra, transforms, geometry, sampling, PDFs, white-furnace energy, scene lifetime, acceleration parity, queue behavior, image I/O, and deterministic replay. Google Benchmark records performance and quality reports without putting fragile timing limits in unit tests.
The same-sample scalar/CPU and scalar/CUDA transport matrices currently require:
| Metric | Threshold |
|---|---|
| Linear MSE | at most 1e-10 |
| Linear RMSE | at most 1e-5 |
| Maximum absolute error | at most 1e-4 |
| Display PSNR | at least 80 dB, or positive infinity |
The tracked CornellDiffuse controls include 64x64 at 4096 spp and 256x256 at 1024 spp scene-linear references. The canonical 256-spp render has a neutral enclosure, red and green walls, a ceiling emitter, and exactly two white Lambertian spheres. Validation also reports relative MSE, luminance SMAPE, mean bias, heatmaps, time, rays, samples, queue statistics, and memory where applicable.
- Transport is vacuum-only. Unsupported media are rejected.
- Native rendering currently requires the configured global path-depth limit to be non-binding relative to the diffuse, glossy, specular, and volume category budgets. Other valid depth configurations are rejected explicitly until the total limit is carried by every backend.
- Image textures currently bind only to normal and bump material slots; reflectance and emission image parameters are not connected yet. Blackframe does not invent an implicit RGB-to-spectrum conversion.
- Ray cones are circular footprints. Their ideal reflection/transmission bound assumes a locally
constant closure frame; interpolated-normal variation is not bounded. Continuous lobes have
unbounded support, so diffuse and GGX use documented finite spread policies. Full anisotropic
derivatives remain a
scalar_refvalidation capability rather than per-lane wavefront state. - A latitude-longitude environment-map light contract exists, but scene transport currently resolves only the constant environment; maps are not connected to misses, NEE, or MIS.
- Geometry updates cover full rebuilds and frame-to-frame transform refits, not deformation or transform motion blur.
- The public MIS entry points start complete primary paths;
PathStatedoes not carry a prior vertex's directional PDFs. - A consumable CMake install/export package is not available yet.
- CMake 3.30 or newer and Ninja.
- A compiler with strict C++26 host support, including pack indexing. Supplied CPU presets use Clang.
- On Windows, compatible Visual Studio C++ build tools and a Windows SDK. CUDA presets must run from a Visual Studio developer environment.
- CUDA Toolkit 13.3.33 for CUDA presets. The supplied presets target architecture 86 and CUDA tests
use the matching
compute-sanitizer. - Internet access for the first configuration unless all pinned dependency archives are already present below the chosen build directory. OpenImageIO dependency setup also requires Git.
OpenImageIO, OpenEXR, Imath, Embree, stb, GoogleTest, and Google Benchmark are fetched at immutable
revisions with verified hashes as required by the selected preset. Dependency sources, generated
files, tests, reports, and render artifacts remain below build/; no manual third-party checkout is
required. Configuration fails when an enabled capability or toolchain is unavailable.
Every configuration writes blackframe-dependencies.json and its SHA-256 file in the selected
build directory. Important switches include BUILD_TESTING, BLACKFRAME_BUILD_BENCHMARKS,
BLACKFRAME_ENABLE_EMBREE, BLACKFRAME_ENABLE_OPENIMAGEIO, BLACKFRAME_ENABLE_STB, and
BLACKFRAME_ENABLE_CUDA.
All paths are explicit and absolute, and the commit identifier is mandatory run metadata:
render --scene C:\scenes\scene.json --backend cpu_embree `--exr C:\renders\scene.exr --png C:\renders\scene.png `--metrics C:\renders\scene.metrics.json --commit <commit-id>Windows debug:
cmake --preset windows-cpu-debug
cmake --build --preset windows-cpu-debug
ctest --preset windows-cpu-debugLinux debug:
cmake --preset linux-cpu-debug
cmake --build --preset linux-cpu-debug
ctest --preset linux-cpu-debugThe corresponding *-cpu-release presets enable benchmarks. Sanitizer presets are also declared
in CMakePresets.json.
From a Visual Studio developer shell on Windows:
cmake --preset windows-cuda-debug
cmake --build --preset windows-cuda-debug
ctest --preset windows-cuda-debugConfigure and build the CPU Release preset before running the CPU validation commands:
cmake --preset windows-cpu-release
cmake --build --preset windows-cpu-releaseThe canonical Cornell image is produced by the validation benchmark, not by a hidden scene loader:
ctest --test-dir build/windows-cpu-release `-R '^Blackframe\.Benchmarks\.CornellPowerMisConvergence\.Json$'`--output-on-failureRun the complete scalar-to-CPU wavefront parity inventory with:
ctest --test-dir build/windows-cpu-release `-R '^Blackframe\.EmbreeScalarWavefrontParity\.Json$'`--output-on-failureThe CUDA stable-compaction benchmark and its JSON contract use:
cmake --preset windows-cuda-release
cmake --build --preset windows-cuda-release
ctest --test-dir build/windows-cuda-release `-R '^Blackframe\.Benchmarks\.CudaWavefrontCompaction\.Json$'`--output-on-failureNsight Systems validation is opt-in and requires an absolute executable path:
cmake --preset windows-cuda-debug `-DBLACKFRAME_ENABLE_NSIGHT_VALIDATION=ON `-DBLACKFRAME_NSYS_EXECUTABLE='C:\absolute\path\to\nsys.exe'
ctest --test-dir build/windows-cuda-debug -C Debug `-R '^BlackframeCudaNsightValidation$'--output-on-failureAfter a Windows CPU debug build:
.\build\windows-cpu-debug\Blackframe\Applications\Headless\render.exe--capabilities
.\build\windows-cpu-debug\Blackframe\Applications\Headless\render.exe--help
.\build\windows-cpu-debug\Blackframe\Applications\Headless\render.exe serve
.\build\windows-cpu-debug\Blackframe\Applications\Headless\render.exe request pingRun serve and request in separate terminals. XPU plugins are accepted only by serve and must
be supplied through an explicit absolute path.