Repository navigation
Renderer Feature Paths
NWBLot selects renderer paths from enabled Vulkan features, loaded entry points, surface formats, and queue topology. Optional features may choose an implemented alternate path; required features fail device creation instead of changing the renderer contract.
See Platform and GPU Support for the supported CPU targets, exact Vulkan baseline, Windows ARM64 policy, texture-format order, tile-GPU behavior, GI convergence, and framebuffer-capture limits.
VK_EXT_descriptor_buffer and its descriptorBuffer feature are required. The global bindless heap is the only renderer resource transport, with resource and sampler tables shared by raster, CSG, AVBOIT, shadow, caustic, surfel, and UI paths. Hardware acceleration-structure bindings use their descriptor-buffer layout as well.
Descriptor indexing remains required for non-uniform shader heap indexing. There is no classic descriptor-set or bounded per-pass descriptor fallback. See Bindless Descriptor Heap.
Renderer subsystems declare work and resource uses through the shared GpuTaskGraph. The compiler assigns compatible physical Graphics, Compute, and Transfer queues, forms packets, and derives barriers and timeline waits.
A dedicated Compute or Transfer queue is optional. When one is unavailable, compatible work routes to Graphics. Multiple queues create overlap only where declared dependencies and resource ownership permit it; renderer feature selection does not fabricate an alias queue to preserve a fixed lane layout.
Frame-lagged asynchronous lighting is an explicit opt-in. It becomes active only after a compatible topology and an accepted history snapshot are available. Lighting then consumes the previous accepted shadow, caustic, and surfel textures while the current graph produces the next snapshot. Missing, rejected, or recreated history returns the renderer to the current-frame path until a new snapshot is accepted.
See GPU Task Graph and Renderer Frame Flow.
ECS renderer materials expose authored mesh and ps stages. The renderer selects a path before material drawing:
native: MeshShader + PS
expanded: derived CS -> expanded vertex buffer -> VS + PS
indexed: cached object-space vertices and indices -> indexed VS + PS
The native path requires VK_EXT_mesh_shader, the enabled meshShader feature, and the native draw entry point. Task shaders are optional. If those requirements are not effective, the renderer uses either the cooker-derived compute stage, which expands the same mesh payload into the renderer-owned emulation vertex buffer bridged to rasterization by the internal vertex shader, or the persistent indexed raster route (RenderPath::VertexIndexed): a single decoder writes cached 48-byte object-space vertices plus a trailing u32 index region, and steady frames issue indexed draws with no decode or view-dependent triangle-index work while raster applies the current transforms with hardware clipping and culling. Mixed indexed/custom draw sets keep the ordinary raster callback where the shared-output replacement cannot represent them. Draws without a compatible object-vertex stage or required object-geometry sharing use compute emulation.
Windows ARM64 disables native mesh shaders by default even if the driver advertises the extension. A qualified adapter can opt in with GraphicsRuntime::setNativeMeshShadersEnabled(true) before GraphicsRuntime::createInstance. The setting cannot change after instance creation. CPU Vulkan devices admitted for headless operation also keep native mesh shaders disabled; windowed runtime selection rejects CPU adapters.
Mesh buffers and their heap entries are created with the mesh resource. Material preparation and drawing consume those persistent bindings; they do not allocate an alternate descriptor path during the frame.
Shadow backend selection uses acceleration structures plus RayQuery. Ray-tracing pipeline support is queried separately and is not required for inline RayQuery shadows.
When acceleration structures and RayQuery are usable, the renderer builds runtime BLAS/TLAS data and traces opaque visibility in a compute shader. Opaque instances use the first accepted opaque hit. Transparent candidates are not committed by this pass, allowing an opaque blocker behind transparent geometry to remain visible to the query.
When soft-shadow resources are ready, the hardware path traces half-resolution cone-jittered opaque visibility and feeds the merged opaque/transparent temporal-then-spatial resolve (one combined temporal filter shared by both channels, shadow_reproject_merge_combined_cs). If that chain is unavailable for a frame, the path writes direct full-resolution opaque visibility.
Without the hardware requirements, the renderer builds or refits per-mesh triangle BVHs and a per-frame scene BVH on the GPU. Compute traversal writes the same full-resolution shadow visibility consumed by deferred lighting. Live skinned geometry feeds the runtime BVH path; acceleration structures are not cooked into assets.
The software path can also feed the shared half-resolution soft-shadow resolve. If optional soft resources are unavailable, the full-resolution hard visibility remains valid. A frame with no ready shadow producer clears visibility to all-lit white so deferred lighting never samples stale data.
On the no-RT path the renderer can additionally resolve depth from light space instead of tracing the software BVH. RendererSystem::setSoftwareShadowSettings selects the backend (SoftwareShadowBackend::Automatic by default, SoftwareTrace to force BVH tracing, LightSpace to request the depth path; defaults are 512 directional / 256 point resolution with a 256 MiB budget, valid range 32..2048). BuildLightSpacePlan admits only eligible directional and point lights (spots are skipped), at most one view per directional light and six per point light, and drops lights that exceed the memory budget or the device storage-buffer range instead of failing the frame. The plan renders caster depth into a D32 array (one layer per view); capture reuses the persistent indexed-raster route (RenderPath::VertexIndexed) with a shared interpolation helper (light_space_interpolation.slangi), then a capture pass and a shade pass resolve visibility. The transparent soft trace consumes the ready snapshot (RecordLightSpaceResolve) in place of its BVH traversal. Light-space quality is explicit: SoftwareShadowCoverage selects Reference or FittedVolume, SoftwareShadowBlockerSearch selects nine-tap ReferenceGrid9, five-tap compact-cross CompactCross5, or single center-sampled Center1 penumbra estimation, and SoftwareShadowCaptureCadence selects EveryFrame, one-frame ReuseOneFrame, or two-frame ReuseTwoFrames accepted-capture reuse. ReuseOneFrame/ReuseTwoFrames retain depth, counts, shaded events, and GPU-fitted views for one/two subsequent accepted frames while still shading current receivers and declaring current BVH/material fallback inputs; only compatible map generations reuse, and changed geometry revisions, material bytes, light data, ordering, layout, resources, or invalid history force a refresh (LightSpaceCaptureHistory, ticket-owned pending/accepted captures). The snapshot is carried as a LightSpaceShadowSnapshot on the shadow graph plan and task payloads, and it stays empty on hardware frames, so the depth path never runs alongside RayQuery. Changing the settings resets the snapshot, prepared resources, temporal seeding, and transparent-sampling history.
Transparent occluders use staged hardware ray queries when acceleration structures and RayQuery are usable, and software BVH traversal otherwise. The material surface hook supplies optical values, and traversal accumulates RGB transmittance through the volume.
On a hardware frame, a gather pass collects per-pixel transparent crossings into a crossings buffer, an evaluate pass computes RGB transmittance, and an indirect-argument overflow continuation pass drains pixels whose crossing list overflowed the per-pixel batch. Larger crossing batches keep continuation work small. When the colored half-resolution soft path is ready, the transparent RGB signal filters into HalfA while the opaque wavelet output is retained in HalfB, and one combined full-resolution upsample resolves both with a shared float4 guided fit. Fusion requires exactly one wavelet pass per channel and all prepared pipelines; if transparent work fails, the retained opaque buffer resolves through the original scalar upsample. Fully lit tiles skip wavelet filtering work. The combined-resolve preparation is shared by both backends: software frames prepare the same combined upsample/wavelet pipelines as hardware frames (prepareSoftCombinedResolvePipelines runs in both preflight branches), so the software soft-transparent fold uses the same HalfA/HalfB fusion rather than a software-only path. If the staged path cannot run, a full-resolution software traversal multiplies colored transparent transmittance onto the hardware opaque mask. Opaque-only hardware scenes skip the transparent tail.
Bounded CSG shadows run on both hardware and software paths: receiver crossings are captured in light-space maps, built-in cutter intervals are subtracted analytically before opaque-depth/transparent-transmission resolve, and CSG maps compose with hardware visibility without a software triangle BVH. Camera work is bounded to projected cutter regions, interval sort payloads stay compact, and empty hardware transparent work is skipped; unsupported or overflowing cases take conservative fallback paths.
A material marked refractive is eligible as a caustic caster. Its surface hook supplies refraction and transmission parameters; an eligible directional or spot light supplies the photon source.
The renderer has two photon producers:
- a hardware ray-tracing pipeline over the scene TLAS when its pipeline resources are usable
- a compute producer over the software scene and mesh BVHs when the renderer is on the software ray backend
Both use the same Fresnel and Beer-Lambert optical helpers, target refractive instance bounds, and splat RGB flux into the same integer accumulator. The common resolve writes screen-space caustic irradiance that deferred lighting adds before tone mapping. If no producer is ready, the cleared black irradiance target is an additive no-op.
Caustic resolve owns separate prepare, wavelet, and upsample pipelines (caustic_resolve_state.h): preparation keeps the dynamic program while wavelet and upsample use constant stage variants. Upsample threads do not reserve the wavelet shared-memory tile. Dilations 8 and 16 additionally use a direct-texture wavelet variant without the small-dilation shared tile. The chain uses five dilations and seven dispatches with the shared 56-byte push layout and filtering equations.
Photon emission alternates complementary checkerboard halves of the full grid across frames. The shared accumulator and resolve preserve energy while reducing the current-frame photon count. CausticQualitySettings::photonGridDivisor (1, 2, or 4) scales the photon-grid side before the temporal-phase split, preserving energy normalization while changing noise, detail, and convergence. Emission-target preparation filters opaque (non-refractive) instances before mesh resource resolution, so opaque geometry never reaches photon dispatch. See CSG and Transparency.
Transparent materials with a finite surface IOR above 1.0 participate in camera-visible refraction (nwbRefractionIorEligible in impl/assets/graphics/refraction/ior.slangi). A depth-tested capture retains one primary refractive instance per pixel and removes it from AVBOIT accumulation; the resolve owns its optical transmission through either the scene TLAS with bounded ray queries or a screen-space depth march. Hardware tracing and the whole feature are project toggles that default to enabled and degrade without changing capability reporting. See CSG and Transparency.
Diffuse GI uses one persistent surfel field with two trace backends:
- inline RayQuery over the hardware TLAS
- compute traversal over the software scene BVH
The field stores world position, normal, directional irradiance, age, and hash links. Per-frame graph work ages and recycles stale surfels, rebuilds the world hash, spawns candidates from G-buffer samples, generates indirect trace arguments from the live count, traces selected surfels, resolves irradiance at the selected gather resolution (SurfelGiQualitySettings::resolveResolution: half by default, selectable quarter), and surface-aware upsamples it to the full-resolution texture read by deferred lighting.
The trace is progressive. Bootstrap updates every selected surfel; steady state updates one quarter per frame. History is a bounded running mean, and stale surfels are recycled. A stable scene therefore improves over time, but no fixed number of frames guarantees a complete image. If the selected backend or its resources are unavailable, alpha-zero irradiance falls back to hemi-ambient for that frame; waiting alone does not enable a failed path.
The active reduction uses groupshared memory rather than assuming one 64-lane subgroup. See Platform and GPU Support for the current temporal constants.
Shadow receiver quality is explicit (ShadowQualitySettings::receiverResolution: half by default, selectable quarter) and shared by the hardware, software-BVH, and light-space resolve paths. TransparentShadowSampling selects three-sample ReferenceThree or single-sample TemporalOne transparent filtering; TemporalOne requires an accepted transparent temporal history and falls back to full sampling without one.
Reflections combine hierarchical screen-space tracing with a bounded hardware ray queue. ReflectionTraceMode selects Disabled, ScreenSpace, Hardware, or the default Hybrid route per frame; Hybrid continues unresolved or uncertain screen samples through hardware. A min/max visible-depth pyramid prunes screen traversal, temporal accumulation and separate spatial filtering reuse accepted radiance, and reflected paths that enter authored optical volumes transport bounded secondary transmission.
Material surface hooks supply RGB specular F0 and perceptual roughness through a dedicated G-buffer target, so reflection quality stays material-driven. Reflection radiance for opaque surfaces and primary glass is published by one graph domain and consumed by composite and the refraction resolve. Hardware preparation failure dispatches a prepared fallback pipeline while retaining the declared resource set. See Reflections.
Texture format is selected per device from the cooked UASTC payload:
LDR: ASTC 4x4 -> BC7 -> RGBA8
opaque HDR: ASTC HDR 4x4 -> BC6H -> RGBA16_FLOAT
HDR alpha: RGBA16_FLOAT
Every candidate must support texture creation and shader sampling. Compressed image creation retries the remaining candidates on failure. ASTC and BC are optional capabilities, not platform requirements. See Texture Conversion and Platform and GPU Support.
HDR10 is a project opt-in. A compatible surface selects the 10-bit Rec.2020/PQ swapchain; otherwise presentation remains SDR. See HDR10 Presentation.
Swapchain readback is also opt-in and requires transfer-source surface usage. Unsupported surfaces continue presenting normally and report readback unavailable. The current one-shot capture consumer belongs to smoke verification and accepts only a single-sample SDR RGBA8/BGRA8 swapchain. See Platform and GPU Support.
Capability selection happens before work is recorded. Optional subfeatures such as soft-shadow temporal accumulation, transparent soft resolve, and caustic production may fail closed to their documented simpler result for that frame. Required descriptor transport, required Vulkan features, or a selected native mesh pipeline do not silently switch contracts after recording begins.
- Architecture
- Engine Concepts
- Asset Flow
- Frame Life Cycle
- Runtime Boot
- Project API
- Runtime and ECS
- CPU Task Scheduler
- Asset Pipeline
- Texture Conversion
- FBX to NWB Conversion
- Asset Types
- Cooked Volumes
- Filesystem
- Shader and Material System
- Mesh Model and Skinning