UI rewrite 3rd attempt
This commit is contained in:
parent
c9fd1b1585
commit
1f5697326c
48 changed files with 2155 additions and 6190 deletions
182
README.md
182
README.md
|
|
@ -1,111 +1,105 @@
|
|||
# Crafter.Graphics
|
||||
|
||||
Catcrafts' Vulkan-based graphics + UI library. C++20 modules, ray-traced 3D, compute-shader UI, fully bindless via `VK_EXT_descriptor_heap`.
|
||||
Vulkan-based graphics library built around C++20 modules and the bindless
|
||||
`VK_EXT_descriptor_heap` extension. Provides window management, ray
|
||||
tracing, and a compute-shader-driven UI on a single, opinionated stack.
|
||||
|
||||
This is **V2** of the library — a from-scratch rewrite that replaces the
|
||||
old `RenderingElement2D`-style UI (verbose, no batching, per-element
|
||||
descriptor surgery) with a declarative widget tree rendered through a
|
||||
single compute dispatch.
|
||||
## What's in here
|
||||
|
||||
## Capabilities
|
||||
- **Window** — Wayland and Win32 backends, swapchain ring, frame pacing,
|
||||
input events. Pick a backend at build time via the target triple.
|
||||
- **Device** — single-Vulkan-instance bring-up. The library targets
|
||||
`VK_EXT_descriptor_heap` exclusively; pipelines are created with
|
||||
`VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT` so there are no
|
||||
descriptor-set layouts and push constants travel via
|
||||
`vkCmdPushDataEXT`.
|
||||
- **DescriptorHeapVulkan** — bindless slot allocator. `AllocateImageSlots`
|
||||
/ `AllocateBufferSlots` / `AllocateSamplerSlots`, with byte-offset
|
||||
helpers for direct descriptor writes.
|
||||
- **VulkanBuffer\<T, Mapped\>** — typed buffer with optional host mapping
|
||||
and a `FlushDevice` that issues the right host-write barrier.
|
||||
- **ImageVulkan\<Pixel\>** — image + staging buffer, mip-chain support,
|
||||
one-shot uploads via a command buffer.
|
||||
- **PipelineRTVulkan / ShaderBindingTableVulkan / RTPass** — ray-tracing
|
||||
pipeline, SBT, and a `RenderPass` that dispatches it.
|
||||
- **ComputeShader** — the Tier 1 wrapper used by the UI system. Loads a
|
||||
`.spv`, builds a heap-bound compute pipeline, dispatches with
|
||||
`vkCmdPushDataEXT`. Use it directly for any custom compute.
|
||||
- **UI** — three-tier UI system; see below.
|
||||
- **FontAtlas** — single-channel SDF atlas (1024×1024, 32pt base,
|
||||
shelf-packed, lazy `Ensure` per codepoint, dirty-flush via `Update`).
|
||||
- **Mesh / RenderingElement3D / Animation** — BLAS/TLAS construction
|
||||
and 3D scene plumbing for the ray-tracing path.
|
||||
|
||||
- **3D rendering** through `VK_KHR_ray_tracing_pipeline`. `RTPass` is the
|
||||
reusable wrapper; `Mesh` builds BLAS, `RenderingElement3D` builds TLAS.
|
||||
- **2D / UI rendering** through one compute shader per frame. Widgets
|
||||
emit `UIItem`s into a per-frame mapped SSBO; the shader scans it and
|
||||
composites onto the swapchain image. SDF glyph atlas means one
|
||||
texture covers all sizes / DPI scales.
|
||||
- **Bindless descriptor model** via `VK_EXT_descriptor_heap` — one
|
||||
resource heap + one sampler heap, bound once per frame. RT and UI
|
||||
passes share the same heap.
|
||||
- **`Window::passes`** — render passes are pluggable (`RenderPass*`
|
||||
vector). Add `RTPass`, `UIScene`, your own pass, in any order. Window
|
||||
inserts storage→storage barriers between consecutive passes.
|
||||
- **Cross-platform window backend** — Wayland (with fractional scale +
|
||||
XKB keyboard) or Win32, picked at compile time from the target triple.
|
||||
## UI system (three tiers)
|
||||
|
||||
## Quick start
|
||||
The UI is *deliberately* layered to balance no-boilerplate against
|
||||
no-lock-in:
|
||||
|
||||
- **Tier 1 — `ComputeShader`.** Load any `.spv`, dispatch with push
|
||||
constants, library inserts inter-dispatch barriers. The escape hatch:
|
||||
if the standard shaders don't fit, write your own compute and
|
||||
dispatch it next to them.
|
||||
- **Tier 2 — `UIRenderer` + standard shaders.** Four shipped compute
|
||||
shaders (`drawQuads`, `drawCircles`, `drawImages`, `drawText`), POD
|
||||
item structs (`QuadItem`, `CircleItem`, `ImageItem`, `GlyphItem`), a
|
||||
shared GLSL contract in [shaders/ui-shared.glsl](shaders/ui-shared.glsl),
|
||||
and helpers (`RegisterBuffer`, `RegisterImage`, `RegisterSampler`,
|
||||
`FillHeader`, `Dispatch*`, `ShapeText`). You build your own per-shader
|
||||
SSBOs (manual batching) and call one `Dispatch*` per shader type per
|
||||
frame. Item array order = draw order.
|
||||
- **Tier 3 — stateless presentation functions.** `DrawButton`,
|
||||
`DrawCheckbox`, `DrawSlider`, `DrawProgressBar`. Each is a small
|
||||
function that *appends* items to your buffers — they don't dispatch.
|
||||
Colors come in as small inline `*Colors` aggregates, no library
|
||||
`Theme` type. **The source is the customization API**: if a
|
||||
component doesn't fit, copy its body and edit it. No virtual hooks,
|
||||
no extension points.
|
||||
|
||||
What's *not* in the UI: widget tree, layout engine (just a `Rect::SubRect`
|
||||
carving helper), theming, hit-testing, focus management. State for
|
||||
interactive components (hover, drag, focus) lives in user-owned POD
|
||||
structs, not the library.
|
||||
|
||||
### UI dispatch model
|
||||
|
||||
Standard shaders dispatch one workgroup per 8×8 *screen tile* — each
|
||||
thread iterates every item in the SSBO in array order, accumulating
|
||||
into a local `dst`, and stores once. Total cost is `O(W·H·N)`; works
|
||||
well up to a few hundred items at 1080p. Splitting one buffer into
|
||||
multiple dispatches doesn't help — the same total work plus barrier
|
||||
overhead. If you need to render thousands of UI items, you want a
|
||||
different shader (tile binning, per-item-list resolve), not more
|
||||
dispatches.
|
||||
|
||||
## Build
|
||||
|
||||
The repository is built with `crafter-build` (a project-config based
|
||||
build system; the project description lives in `project.cpp`):
|
||||
|
||||
```bash
|
||||
# Build the library
|
||||
cd Crafter.Graphics2
|
||||
crafter-build
|
||||
|
||||
# Build + run an example
|
||||
cd examples/VulkanUI
|
||||
crafter-build -r
|
||||
crafter-build # build the library
|
||||
crafter-build -r # build and run (in an example directory)
|
||||
```
|
||||
|
||||
Build dependencies (cloned automatically): `Vulkan-Headers`,
|
||||
`Vulkan-Utility-Libraries`, `Crafter.Event`, `Crafter.Math`,
|
||||
`Crafter.Asset`. System dependencies: `clang++` with C++20 modules and
|
||||
`libstd` PCM, `libvulkan`, `libwayland-client` + `xkbcommon`
|
||||
(or `kernel32/user32/gdi32` on Windows).
|
||||
|
||||
## Module layout
|
||||
|
||||
The library is one C++20 module, `Crafter.Graphics`, with partitions
|
||||
grouped by concern:
|
||||
|
||||
| Partition family | Purpose |
|
||||
|------------------------|------------------------------------------------------------|
|
||||
| `:Window`, `:Device` | Window + Vulkan instance/device |
|
||||
| `:RenderPass`, `:RTPass` | Pluggable pass interface + ray-tracing helper |
|
||||
| `:DescriptorHeapVulkan`, `:VulkanBuffer`, `:ImageVulkan`, `:SamplerVulkan` | Bindless heap + GPU buffers / images / samplers |
|
||||
| `:PipelineRTVulkan`, `:ShaderVulkan`, `:ShaderBindingTableVulkan` | RT pipeline plumbing |
|
||||
| `:Mesh`, `:RenderingElement3D` | BLAS / TLAS for ray tracing |
|
||||
| `:Font` | TTF loading + UTF-8 metrics |
|
||||
| `:UI*` | Widget tree, layout, hit-testing, theme, draw list, atlas, renderer, scene |
|
||||
|
||||
The umbrella `import Crafter.Graphics;` re-exports everything.
|
||||
|
||||
## UI architecture (one paragraph)
|
||||
|
||||
Widgets are value types with a fluent builder API. Composite containers
|
||||
(`VStack`, `HStack`, `Stack`, `Overlay`, `TabView`, `ScrollView`) take
|
||||
children as `&&` parameter packs and own them inside a `UIScene` arena.
|
||||
Layout is two-pass measure/arrange (WPF / Avalonia / Flutter style)
|
||||
with `Length::Px` / `Pct` / `Auto` / `Frac` units and DPI scaling
|
||||
threaded through. Each frame, `UIScene` walks the tree, emits a flat
|
||||
`UIItem` array into a mapped SSBO, and the compute shader scans it
|
||||
front-to-back compositing rectangles, rounded rectangles, SDF glyphs,
|
||||
and bindless images. Mouse clicks bubble through `OnMouseClick`; focus
|
||||
+ `OnTextInput` / `OnKeyDown` route to the focused widget. Themes are
|
||||
flat structs (`ButtonStyle`, `InputFieldStyle`) applied per-instance via
|
||||
`.style(theme.primary)`.
|
||||
The build picks the window backend automatically: Wayland on Linux,
|
||||
Win32 on Windows / mingw. Cross-compile via the standard `--target=...`
|
||||
flag.
|
||||
|
||||
## Examples
|
||||
|
||||
- [`examples/VulkanTriangle`](examples/VulkanTriangle/) — minimal
|
||||
ray-traced triangle. The reference for `RTPass` + descriptor heap
|
||||
setup with no UI.
|
||||
- [`examples/VulkanUI`](examples/VulkanUI/) — the full Phase 2/3
|
||||
surface: stacks, themed buttons, progress bar, tab view, focusable
|
||||
input fields with caret blink + key repeat.
|
||||
- [`examples/VulkanAnimated`](examples/VulkanAnimated/) — `Observable<T>`
|
||||
+ per-frame ticks driving live HUD bars and labels with no manual
|
||||
invalidation.
|
||||
See [examples/](examples/). Quick map:
|
||||
|
||||
## V1 known limitations
|
||||
|
||||
- Single-font, LTR-only text. No bold / italic / kerning beyond stb's
|
||||
default. No multi-line wrap or BiDi.
|
||||
- No tile-binning in the UI compute shader; the naive front-to-back
|
||||
per-pixel scan handles a few hundred items effortlessly. Past
|
||||
~5,000 items the data layout supports tile-binning without an API
|
||||
change.
|
||||
- No render-target-to-texture for world-space UI yet.
|
||||
- No animation primitives in the UI module — drive `Observable<T>`s
|
||||
yourself from `onUpdate`.
|
||||
|
||||
## Status
|
||||
|
||||
Phase 1, 2, and 3 V1 of the rewrite are complete:
|
||||
ray-traced 3D path migrated to `RenderPass`, full UI widget set
|
||||
rendering through compute, focus + keyboard input + Wayland key repeat
|
||||
working end-to-end. Verified on NVIDIA GeForce RTX 4090 with
|
||||
`VK_EXT_descriptor_heap` and `VK_LAYER_KHRONOS_validation` clean.
|
||||
- [HelloWindow](examples/HelloWindow/) — minimal window, no rendering.
|
||||
- [VulkanTriangle](examples/VulkanTriangle/) — ray-traced triangle, the
|
||||
smallest test of the bindless + ray-tracing path.
|
||||
- [HelloUI](examples/HelloUI/) — UI smoke test using all three tiers
|
||||
(background quad, slider, progress bar, button with text label,
|
||||
cursor-tracking circle).
|
||||
- [CustomShader](examples/CustomShader/) — Tier 1 demo: a user-authored
|
||||
compute shader inverting RGB under a list of item-circles, dispatched
|
||||
alongside the standard `drawQuads`. The "could attempt #2 do this?" test.
|
||||
|
||||
## License
|
||||
|
||||
LGPL v3 — see [LICENSE](LICENSE).
|
||||
LGPL 3.0. See per-file headers and `LICENSE`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue