Platform and GPU Support - limztudio/NWBLot GitHub Wiki
Platform and GPU Support
NWBLot builds native 64-bit targets for Windows x64, Windows ARM64, and Linux x64. The engine does not support 32-bit targets. Build architecture and renderer capability are separate concerns: a native ARM64 executable still selects rendering paths from the Vulkan capabilities exposed by the active adapter.
Windows hosts require Windows 10 version 1709 or newer, covering the launcher's required native architecture query and the frame runtime's current Per-Monitor v2 DPI APIs. For compiler, SDK, and preset setup, see Build and Verification. This page is the canonical description of renderer platform support, hard GPU requirements, and optional paths.
Graphics provider selection is a compile-time boundary; this page describes the currently implemented Vulkan provider. NWB_GRAPHICS_BACKEND defaults to Vulkan. Selecting Metal is rejected until its provider is implemented and does not enable additional platform support. See Architecture for the neutral contracts and selected includes.
Vulkan Baseline
The Vulkan loader must expose API variant 0 and Vulkan 1.3 or newer. The renderer then admits a physical device by its extensions, features, entry points, queue topology, and, for a windowed device, surface capabilities. A Vulkan 1.3 version string alone is not sufficient.
Always-required extensions are:
- instance:
VK_KHR_get_physical_device_properties2 - device:
VK_KHR_maintenance1 - device:
VK_EXT_descriptor_buffer
A windowed device additionally requires the platform surface extensions and VK_KHR_swapchain. A headless device does not require presentation support.
Windowed runtime selection rejects CPU/software Vulkan adapters. Headless validation may use a CPU adapter when it satisfies the requested headless contract.
The descriptorBuffer feature is mandatory. Descriptor buffers are the renderer's only descriptor transport; there is no classic descriptor-set fallback. The bindless path also requires buffer device address, descriptor indexing, runtime descriptor arrays, non-uniform sampled-image and storage-buffer indexing, and scalar block layout. See Bindless Descriptor Heap.
Other required adapter features include:
- dynamic rendering and synchronization2, supplied by Vulkan 1.3 core or their KHR extension paths
- timeline semaphores, shader float16, shader int16 and int64, storage-buffer 16-bit access, shader draw parameters, and subgroup extended types
- sampler anisotropy, image-cube arrays, extended image gather, depth clamp, non-solid fill, geometry shaders, and tessellation shaders
- fragment and vertex pipeline stores and atomics, dual-source and independent blending, and storage-image reads and writes without a format
- full 32-bit draw indices, multi-draw indirect, and indirect first-instance support
Windowed presentation images require only color-attachment usage. The engine does not request transfer-destination or sampled-image usage for swapchain images. When readback is enabled before device creation and the surface advertises transfer-source usage, that optional usage is added; otherwise framebuffer capture reports unavailable and normal presentation continues.
Required and Optional Paths
| Capability | Status | Behavior when unavailable |
|---|---|---|
| Descriptor buffers | Required | Device creation fails; there is no alternate descriptor transport. |
| Dynamic rendering and synchronization2 | Required | Device creation fails if neither the core nor extension route supplies the feature. |
| Native mesh shaders | Optional | Mesh expansion runs in compute before ordinary rasterization. |
| Acceleration structures and RayQuery | Optional | Shadows, caustics, and surfel GI use GPU-built software triangle BVHs. Loader applications can force this route on an RT-capable adapter with --disable-hardware-ray-tracing (Core::HardwareRayTracingPolicy::Disabled, before instance/device creation, immutable afterwards): the backend omits optional RT feature extensions, rejects conflicting required RT extensions, creates the logical device without RayQuery, ray-tracing pipelines, acceleration structures, or an AS descriptor layout, and reports Vulkan: hardware ray tracing policy=disabled; RayQuery=0 RayTracingPipeline=0 RayTracingAccelStruct=0 AccelStructDescriptors=0 AccelStructLayout=0. The disabled-policy matrix is ctest -L software_raytracing (see tests/smoke/software_raytracing.md); it checks Loader: hardware ray tracing disabled before device creation, actual software shadow dispatch, and feature-specific software-route evidence. This establishes selected startup, traversal-route, and rendered-output coverage; it does not establish image identity with hardware RT or hardware-path frame rates. |
| ASTC or BC texture compression | Optional | Texture upload falls back to an uncompressed sampled format. |
| Dedicated Compute or Transfer queue | Optional | Compatible task-graph work routes to Graphics. |
| HDR10 presentation | Optional | The swapchain remains SDR. |
| Swapchain transfer-source usage | Optional | Presentation continues, but framebuffer readback reports unavailable. |
Other Vulkan features, including fragment shading rate and advanced ray-tracing extensions, are enabled only when their extension, feature structure, and required entry points are usable. They do not change the minimum renderer contract.
Windows ARM64 Mesh Policy
Windows ARM64 is a native build target and does not rely on x64 process emulation.
Native mesh shaders are disabled by default on Windows ARM64 even when the driver advertises VK_EXT_mesh_shader. Extension advertisement alone does not qualify correct mesh-output behavior on a new adapter. The portable routes are:
eligible engine mesh program -> cached object-space vertices and indices -> indexed VS + PS
other mesh programs/draws -> derived CS -> expanded vertex buffer -> VS + PS
Material authors still provide the renderer's mesh and ps stages. The cooker derives the internal compute stage from the mesh shader; projects do not author a separate compatibility material.
A qualified adapter may opt in before graphics instance creation:
if(!graphics.setNativeMeshShadersEnabled(true))
return false;
if(!graphics.createInstance(instanceParameters))
return false;
The choice is immutable after instance creation. Effective native support still requires the extension, the meshShader feature, and the native draw entry point. Task-shader support is opportunistic rather than required. A CPU Vulkan device admitted for headless operation also keeps native mesh shaders disabled and selects an applicable portable route.
Once a mesh-capable configuration selects the native path, a mesh-pipeline failure is an error; the renderer does not change material paths silently in the middle of a frame. See Mesh Model and Skinning and Renderer Feature Paths.
Texture Format Portability
Cooked textures retain a UASTC payload. At load time, the renderer selects a sampled texture format supported by the current device; it does not bake one desktop or mobile GPU format into the asset.
The LDR order is:
UASTC LDR -> ASTC 4x4 -> BC7 -> RGBA8
The opaque HDR order is:
UASTC HDR -> ASTC HDR 4x4 -> BC6H -> RGBA16_FLOAT
HDR textures with constant alpha or a companion UASTC alpha stream decode and merge into RGBA16_FLOAT, because one sampled-image descriptor owns the complete RGBA result. Each candidate format must support sampled-image creation and the upload path. If compressed image creation fails after selection, the loader retries the remaining formats in the same order.
ASTC is tried first on every adapter, followed by BC and the uncompressed format shown above. Architecture or vendor name never selects the upload format; reported format support does. See Texture Conversion.
Tile-Based GPU Behavior
The renderer uses vendor-neutral Vulkan dynamic rendering. It does not require Qualcomm tile extensions, a vendor-specific tile-shading path, or memoryless attachments.
G-buffer color and depth initialization is fused into the first raster segment as attachment CLEAR load operations. If a barrier divides raster work, the continuation resumes with LOAD. The attachments use STORE because later graph tasks sample their contents. This avoids a separate transfer clear that would materialize attachment data solely for initialization and is friendly to tile-based renderers without changing the cross-vendor contract.
This is a concrete tile-locality optimization, not a claim that every target is transient or that all passes remain on chip. Device memory behavior remains the Vulkan implementation's responsibility.
Portable compute code also avoids assuming that a 64-thread workgroup is one hardware subgroup. The active surfel reduction uses groupshared memory and barriers; its wave-only variant is disabled.
Ray Effects and Software BVHs
Hardware ray tracing is optional. The graphics module requests available ray-tracing extensions, but path selection remains capability-driven.
The renderer selects the hardware shadow backend only when acceleration structures and inline RayQuery are usable. That path builds runtime BLAS/TLAS data and traces opaque visibility with RayQuery compute shaders. Ray-tracing pipeline support is separate and is not required for the inline shadow or surfel trace.
When acceleration structures plus RayQuery are unavailable, the renderer builds or refits per-mesh GPU triangle BVHs and a scene BVH from live geometry, then traverses them in compute shaders. These structures are runtime-built and device-local; they are not cooked assets. The software route provides shadow visibility, refractive caustic photons, and surfel GI without hardware ray tracing. BVH traversal initializes miss outputs (nwbBvhPickNearChild zeroes descendNode/farChildIndex on miss) for Slang definite assignment, and no-RT light-space capture reuses the persistent indexed-raster route with a shared interpolation helper. The BVH inner-node traversal is shared through nwbBvhPickNearChild (bvh_common.slangi).
Transparent colored shadows use staged hardware ray queries (gather/evaluate/overflow-continuation) when acceleration structures plus RayQuery are usable, and software BVH traversal otherwise. On a hardware frame, the staged path evaluates transparent RGB transmittance; opaque-only hardware scenes skip that transparent tail.
Hardware and software caustic producers share the same optical model and accumulator/resolve targets. A missing optional caustic pipeline is non-fatal and leaves the cleared black irradiance target as an additive no-op for that frame. See Renderer Feature Paths and CSG and Transparency.
Temporal Surfel GI
Diffuse global illumination uses a persistent surfel field. It can trace against the hardware TLAS through inline RayQuery or against the software scene BVH. Both backends write the same surfel records and resolve into the same screen-space irradiance textures.
Spatial hash gathering and history seeding compare each resident's exact cached signed cell against the visited cell. A shared bucket alone is insufficient: hash collisions must not duplicate contributions or introduce lighting discontinuities as a receiver crosses a cell boundary. Hash build refreshes ownership from the full FP32 position each frame; spawn initializes the same cell before publishing its bucket head. Frame-start snapshots retain cells and links together with their matching heads.
The live and snapshot core records retain a compiled 96-byte stride: FP32 position and normal, three FP32 float4 SH records, full-width links and frame counters, and signed int3 cell ownership. A shared sampleState encodes dead records as zero and live sample counts 0 through 65535 as count plus one. The current radius is the fixed 0.9 world units supplied by frame constants, so it needs no per-record copy; the cell size is 0.6. Reusing the existing record space removes repeated resident position-to-cell quantization without adding pool or snapshot storage. This layout preserves precision and does not by itself establish a frame-rate improvement.
Surfel GI is progressive:
- the spawn pass considers one candidate per 16 x 16 pixel screen tile
- a new field traces every selected surfel on its bootstrap frame
- steady state updates one quarter of the surfels per frame
- a surfel starts with up to 64 rays and uses 12 fresh rays after eight accumulated samples (4 uniform explorer + up to 8 guided; each mature guide retrained once per four surfel visits)
- the running mean retains at most 64 samples of history weight so lighting can respond to change
- a surfel unseen for 60 frames becomes eligible for recycling
An early capture can therefore look sparse or dim while a stable scene seeds and converges. Time improves the result only while a valid surfel backend and its resources are active. If GI preparation is unavailable, deferred lighting uses hemi-ambient for that frame; waiting does not turn a disabled path on.
No fixed frame count guarantees a complete image. For settled CSG/GI visual qualification, the temporal smoke fixture first reaches graphics frame 360, then renders for at least 30 steady-clock seconds before selecting and logging its actual first capture frame. Its 32 captures use the requested spacing relative to that frame. Startup preparation, compilation, and asset loading are outside the timed interval. This is a visual acquisition protocol rather than a renderer convergence contract or a requirement for every test. Motion, visibility changes, surfel recycling, and stochastic sampling continue to affect the field. Persistent surfel storage survives a window resize, although size-dependent resolve targets are recreated. See Renderer Frame Flow.
CSG changes also require matched no-CSG performance qualification on the selected route. Feature-specific CPU snapshots, bindings, diagnostics, and GPU arrays must remain conditional on admitted CSG work; ordinary shader variants exclude unused CSG storage and constants. Compare CPU/frame time and GPU pass timings under the same resolution, scene, binary/assets, timing publication, and capture conditions after settling. A visual pass or equivalent ordinary shader output alone does not establish unchanged frame rate, and measurements on one adapter do not qualify other drivers.
Framebuffer Capture
Swapchain readback is opt-in before device creation. When the surface exposes transfer-source usage, a smoke-test presentation contributor can wait for a requested number of accepted graphics frames, copy the application-owned backbuffer through the GPU task graph, wait for the exact submission to complete, and write a 24-bit BMP atomically.
The capture helper accepts a single-sample 2D SDR RGBA8 or BGRA8 UNORM/SRGB swapchain. If transfer-source usage is unavailable, capture skips cleanly and normal presentation continues. This is an unattended renderer-verification facility, not a general desktop or window capture API.