Crafter.Graphics/examples/README.md

120 lines
5.7 KiB
Markdown
Raw Normal View History

2026-05-02 21:08:20 +02:00
# Examples
Each example is a self-contained `crafter-build` project that depends on
the parent `Crafter.Graphics` via `LocalProject`. To build and run any
of them:
```bash
cd examples/<name>
crafter-build -r
```
## Index
### [HelloWindow](HelloWindow/)
Minimum viable program: open a window, run the event loop. No Vulkan
rendering. Useful as a smoke test for `Device::Initialize` + `Window` +
the platform backend.
### [VulkanTriangle](VulkanTriangle/)
Ray-traced single triangle through `vkCmdTraceRaysKHR`. Shows the full
ray-tracing setup: `DescriptorHeapVulkan` with image and buffer slots,
`PipelineRTVulkan` from raygen / miss / closesthit SPIR-V, BLAS via
`Mesh::Build`, TLAS via `RenderingElement3D::BuildTLAS`, direct
`vkWriteResourceDescriptorsEXT` for swapchain views, `RTPass` on
`window.passes`. Smallest test of the bindless ray-tracing path.
### [HelloUI](HelloUI/)
Compute-shader UI demo using all three UI tiers:
- **Tier 3** components: `DrawButton`, `DrawSlider`, `DrawProgressBar`,
composed via `Rect::SubRect` for resize-safe layout.
- **Tier 2** standard shaders: `DispatchQuads` for the background and
components, `DispatchCircles` for a cursor-tracking dot,
`DispatchText` for the button label (with the FontAtlas wired up to
`UIRenderer`).
- **Tier 1** is available too — any custom `ComputeShader` registered
on the same heap can be dispatched alongside the standard ones.
Hit-testing and animation are user code (see the `EventListener`
subscriptions on `window.onMouseMove` / `onMouseLeftClick`); the
library does not track widgets or focus.
Drop a TTF in this directory as `font.ttf` before running (the example
loads it via `Font("font.ttf")`).
2026-05-12 00:24:48 +02:00
### [InputSystem](InputSystem/)
Guided tour of `Crafter::Input`: name actions ("Jump", "Move", "Fire",
"Look", "Zoom"), bind them to keys / mouse / gamepad (with composite
bindings for WASD-as-Vector2 and analog sticks), and consume them as
events. Demonstrates:
- The compile-time `Key(CrafterKeys::Space)` helper that folds to a
per-platform raw scancode — bindings stay cross-platform-readable
in source while runtime data stores raw codes only.
- All four action types (Button, Axis1D, Vector2) with multiple
bindings per action (any-of semantics).
- `Map::StartRebind` — press R, then press any input to remap "Jump"
at runtime. Captured input is filtered out for that frame.
- `BindingToString` / `BindingFromString` round-trip — print the
default bindings as the on-disk format.
- Gamepad hot-plug events: plug a controller in mid-run and the
bindings start firing immediately.
Console-driven (no UI rendering needed); focus the window and watch
stdout.
2026-05-02 21:08:20 +02:00
### [CustomShader](CustomShader/)
Tier 1 demo: a user-authored compute shader (`inverse-circle.comp.glsl`)
running alongside the shipped `drawQuads`. The custom shader inverts RGB
under each item-circle — exactly the kind of effect attempt #2's closed
shader couldn't express. Shows:
- Defining your own item POD struct in C++ + matching `std430` struct
in GLSL.
- `#include "../../shaders/ui-shared.glsl"` for the bindless heap
declarations + `UIDispatchHeader` push-constant contract.
- `ComputeShader::Load` for the `.spv`, `UIRenderer::RegisterBuffer`
for your SSBO, `FillHeader` to populate the standard prefix, and
`UIRenderer::Dispatch` to launch — the same pattern the standard
shaders use under the hood.
- The inter-dispatch SHADER_WRITE → SHADER_READ|WRITE barrier is
inserted automatically, so the custom shader sees the colored stripes
drawn by the prior `DispatchQuads` and reads/writes the swapchain
image safely.
### [RayQueryPick](RayQueryPick/)
Regression test for the WebGPU software ray-query shim. Builds a
512-instance TLAS and shoots one ray through a `rayQuery=true`
`PlainComputeShader`, reading the committed hit back to the host and
checking it against the analytically-known answer. Guards against the
hardcoded-leaf-start TLAS-traversal bug (issue #25) that made every
rayQuery pick miss for realistic instance counts. WebGPU/DOM only.
### [HDRBloom](HDRBloom/)
Cross-backend-shaped HDR bloom on the WebGPU backend (issue #27). The RT
pipeline's RESOLVE writes linear radiance into a user `rgba16float`
texture (`StorageImage2D`, `PipelineRTWebGPU::Init(..., RGBA16Float)`),
two `PlainComputeShader` passes threshold + blur it through float storage
targets (`UICustomBindingKind::StorageTexture`), and a UI custom shader
composites scene + bloom with a Reinhard tonemap onto the canvas.
Exercises the three primitives an HDR post-process chain needs: float
textures, write-only storage-texture bindings, and pre-tonemap radiance
out of the wavefront. The threshold/blur passes run from `onBeforeUpdate`
so each gets its own queue submit — the storage-write → sampled-read
barrier WebGPU only provides between submits (or between passes), never
within a single compute pass. WebGPU/DOM only; the same chain is wireable
on Vulkan today via an offscreen HDR heap image + a composite `RenderPass`
(the present path records passes generically and barriers between them).
### [RTMultiShadow](RTMultiShadow/)
Multi-light shadowing through the wavefront RT pipeline (issue #30). Five
pillars on a checkered ground, four colored point lights; the closest-hit
emits one shadow ray **per light** from the same invocation, so up to four
rays per pixel resolve in a single SHADE pass. Exercises both halves of
the >1-ray-per-pixel-per-bounce widening: the atomic `rtAccumulate`
(per-channel f32 CAS — concurrent same-pixel adds don't race) and
`RTPass::raysPerPixel` (scales the ray/hit/payload buffers so the
per-light emits aren't capacity-dropped). Any regression shows up as
flickering dark noise or a missing shadow color in the overlap regions.
WebGPU/DOM only.