Test a blit shader implementation

This commit is contained in:
2026-08-08 23:14:29 +01:00
parent f5ff6c70ea
commit ebd1801883
16 changed files with 372 additions and 8532 deletions
+150
View File
@@ -0,0 +1,150 @@
# Plan — Fullscreen Texture Blit with Contain-Fit
## Goal
Render a texture fullscreen with aspect-ratio preservation. If the texture's
aspect ratio does not match the window's, draw it to fit within the window —
never stretched, squashed, or cropped. Tall/narrow textures pillarbox (bars on
left/right); wide textures letterbox (bars on top/bottom).
This replaces the Suzanne mesh demo in `main.cpp`, which was for testing the RHI.
## Fit semantics (contain — never crop)
```
scale = min(win_w / tex_w, win_h / tex_h)
content_w = tex_w * scale
content_h = tex_h * scale
rect = centered: left = (win_w - content_w)/2, top = (win_h - content_h)/2
```
| Texture vs window aspect | Limiting dim | Result |
|--------------------------|--------------|-------------------|
| equal | — | fills exactly |
| wider (e.g. 2048x512) | width | letterbox (T/B) |
| taller (e.g. 512x2048) | height | pillarbox (L/R) |
The fit rect is recomputed per frame from the selected texture's dimensions and
the current window size, so window resize works without extra handling.
## Changes
### 1. RHI — texture size accessor
Add a value struct and a by-value getter (matches `prRhiGetSurfaceCapabilities`
pattern):
```c
typedef struct PrRhiTextureSize {
u32 width;
u32 height;
} PrRhiTextureSize;
PrRhiTextureSize prRhiGetTextureSize(PrRhiTexture *texture);
```
Files: `pr_rhi_types.h`, `pr_rhi.h`, `vulkan/pr_rhi_vk.h`,
`vulkan/pr_rhi_vk.c`, `vulkan/pr_rhi_vk_aliases.h`.
### 2. Offline shader compilation (drop Slang runtime)
- New `justfile` `shaders` recipe:
`slangc -target spirv -profile spirv_1_4 -o build/shaders/blit.spv assets/blit.slang`
(entry points auto-detected from `[shader(...)]` attributes).
`build` depends on it.
- `main.cpp` loads `build/shaders/blit.spv` via wapp file I/O
(`wpFileOpen` / `wpFileGetLength` / `wpFileRead`) into an arena buffer, then
`prRhiCreateShader`. No `slang.h` includes, no runtime compilation.
- Drop `-lslang` and the `-I .../slang` include from the build.
### 3. `assets/blit.slang`
Push constant block (32 bytes — Slang pads structs to 16-byte alignment, so the
C++ struct carries explicit `pad[3]` to match):
```hlsl
struct BlitData {
float4 rect; // NDC fit rect: x0, y0, x1, y1
uint selected;
uint pad[3];
};
```
- **VS**: generates a 4-vertex triangle-strip quad from `SV_VertexID` (no vertex
buffer), maps UV 01 into the NDC rect.
- **FS**: `textures[NonUniformResourceIndex(selected)].Sample(uv)` — reuses the
existing bindless descriptor array.
### 4. `main.cpp` — clean texture viewer
Remove everything mesh-related: tinyobj loading, vertex/index buffers,
`ShaderData` storage buffers / device addresses, the mesh shader + pipeline,
mouse orbit, and the mesh draw. Drop `-ltinyobjloader -lglm` from the link.
Remove unused `assets/shader.slang`, `suzanne.obj`, `suzanne.mtl`.
New flow: RHI init → window/instance/pdev/surface/device/swapchain → load 7
textures → bindless descriptor set (variable count 7) → load `blit.spv`
blit pipeline layout (`VERTEX|FRAGMENT` 32-byte push range) → blit pipeline
(no vertex input, `TRIANGLE_STRIP`, swapchain color format, no depth,
`cull NONE`, dynamic viewport/scissor).
Render loop per frame:
- `compute_fit_rect()` from the selected texture's dims + window size
- bind blit pipeline + descriptor set, push `BlitData{ rect, selected }`,
`prRhiCmdDraw(cb, 4, 1, 0, 0)`
- `+/-` cycles the selected texture; resize recomputes fit automatically
### 5. Test textures (PIL + `build/bin/toktx`)
Four generated KTX files in `assets/`, loaded alongside the 3 Suzanne textures
(`texture_count = 7`, explicit path array):
| File | Size | Shows |
|-----------------|-----------|--------------------------|
| `test_square.ktx` | 1024x1024 | bars on both axes |
| `test_fill.ktx` | 1920x1080 | fills the 16:9 window |
| `test_wide.ktx` | 2048x512 | letterbox (T/B) |
| `test_tall.ktx` | 512x2048 | pillarbox (L/R) |
Each with distinct gradients + a border grid so any stretch/squash is visible.
Generated with `--genmipmap` for mip-aware sampling.
## Verification
`just build && just run` — each texture fits without crop/stretch, `+/-` cycles,
window resize keeps fit, no Slang runtime in the binary.
## Verification results (2026-08-08)
Fit behavior confirmed by capturing the window (X11 driver) and sampling pixels:
| Texture | Window | Result verified |
|---------------------|---------------|------------------------------------------------|
| square 1024x1024 | 16:9 wide | pillarbox — pure-black L/R bars, full height |
| test_wide 2048x512 | 16:9 wide | letterbox — pure-black T/B bars, full width |
| test_tall 512x2048 | 16:9 wide | pillarbox — pure-black L/R bars, full height |
| test_fill 1920x1080 | 16:9 wide | fills exactly — no bars anywhere |
| square (resized) | portrait | fit recomputed per frame — flips to letterbox |
Texture cycling (`+/-`, `SDLK_PLUS/KP_PLUS/EQUALS`, `SDLK_MINUS/KP_MINUS`) is a
straightforward `selected` bump in the key handler; it was reviewed but not
exercise-tested in the headless verification env (KWin/XWayland drops XTEST
synthesised keys). The other fit cases were verified by temporarily launching
each texture as the initial selection.
### Bugs found and fixed during verification
1. **`VK_SUBOPTIMAL_KHR` aborted the app.** `prRhiAcquireNextImageVk` /
`prRhiPresentVk` routed `SUBOPTIMAL` into `_checkVk``__builtin_trap()`.
Both now treat it like `OUT_OF_DATE` (return `PR_RHI_SWAPCHAIN_OUT_OF_DATE`).
Triggered immediately under X11/XWayland.
2. **Swapchain recreate ignored surface extent.** `prRhiRecreateSwapchainVk`
hard-coded the passed width/height; on surfaces whose `currentExtent` is
meaningful (X11) that mismatched the drawable and re-looped on SUBOPTIMAL.
Now queries `vkGetPhysicalDeviceSurfaceCapabilitiesKHR` and falls back to the
passed size only when `currentExtent == 0xFFFFFFFF`.
3. **App used logical window size for the swapchain.** Under HiDPI the drawable
differs from `SDL_GetWindowSize` (1920x1080 logical → 2400x1350 drawable at
1.25x scale), which is the root cause of #1 under XWayland. main.cpp now uses
`SDL_GetWindowSizeInPixels` for swapchain width/height, fit math, viewport
and scissor.