From 65c19ea84ac0507964662efc4bf391283a35b6f1 Mon Sep 17 00:00:00 2001 From: catbot Date: Tue, 16 Jun 2026 16:57:07 +0000 Subject: [PATCH 01/47] perf(ui): gate per-pixel clip compares behind a flags bit (#50) uiResolveScreenPixel ran four float compares against hdr.clipRectPx for every pixel of every UI dispatch, even though the clip rect is the full-surface default {0,0,1e9,1e9} in the overwhelmingly common case. Reserve the high bit of the header flags word (UI_FLAG_CLIP) and have FillHeader set it only when the clip rect is narrower than the surface (extracted into the unit-testable UIRenderer::ClipFlags). The shader gates the compares on the bit; the branch is push-constant-uniform so it never diverges. When the rect covers the surface the skipped compares could never reject an in-surface pixel, so output is pixel-identical. Mirrored on the WebGPU/WGSL path (dom-webgpu.js) for parity. Adds the UIClipFlag CPU test covering the gate decision. Co-Authored-By: Claude Opus 4.8 --- additional/dom-webgpu.js | 13 ++- .../Crafter.Graphics-UI-Shared.cpp | 18 ++- interfaces/Crafter.Graphics-UI.cppm | 18 +++ project.cpp | 28 +++++ shaders/ui-shared.glsl | 19 +++- tests/UIClipFlag/main.cpp | 105 ++++++++++++++++++ 6 files changed, 192 insertions(+), 9 deletions(-) create mode 100644 tests/UIClipFlag/main.cpp diff --git a/additional/dom-webgpu.js b/additional/dom-webgpu.js index c35456b..72ed20e 100644 --- a/additional/dom-webgpu.js +++ b/additional/dom-webgpu.js @@ -256,12 +256,17 @@ struct UIDispatchHeader { @group(1) @binding(0) var outTex : texture_storage_2d; @group(1) @binding(1) var prevTex : texture_2d; +// Renderer-reserved high flag bit (mirrors ui-shared.glsl::UI_FLAG_CLIP). +const UI_FLAG_CLIP : u32 = 0x80000000u; + fn uiResolvePixel(coord: vec2) -> bool { if (coord.x >= hdr.surfaceW || coord.y >= hdr.surfaceH) { return false; } - let fx = f32(coord.x); let fy = f32(coord.y); - if (fx < hdr.clipX || fy < hdr.clipY) { return false; } - if (fx >= hdr.clipX + hdr.clipW) { return false; } - if (fy >= hdr.clipY + hdr.clipH) { return false; } + if ((hdr.flags & UI_FLAG_CLIP) != 0u) { + let fx = f32(coord.x); let fy = f32(coord.y); + if (fx < hdr.clipX || fy < hdr.clipY) { return false; } + if (fx >= hdr.clipX + hdr.clipW) { return false; } + if (fy >= hdr.clipY + hdr.clipH) { return false; } + } return true; } diff --git a/implementations/Crafter.Graphics-UI-Shared.cpp b/implementations/Crafter.Graphics-UI-Shared.cpp index 6ac2c62..c3dee39 100644 --- a/implementations/Crafter.Graphics-UI-Shared.cpp +++ b/implementations/Crafter.Graphics-UI-Shared.cpp @@ -38,11 +38,27 @@ UIDispatchHeader UIRenderer::FillHeader(std::uint32_t itemBufferSlot, #else h.frameIdx = 0; #endif - h.flags = flags; + h.flags = ClipFlags(clipRectPx, window_->width, window_->height, flags); h._pad = 0; return h; } +std::uint32_t UIRenderer::ClipFlags(std::array clipRectPx, + std::uint32_t surfaceWidth, + std::uint32_t surfaceHeight, + std::uint32_t flags) noexcept { + // The reserved kUIFlagClip bit signals "clip rect is narrower than the + // surface". When the rect already covers the whole surface (the common + // {0,0,1e9,1e9} default), the four per-pixel clip compares in + // uiResolveScreenPixel can never reject an in-surface pixel, so we leave + // the bit clear and the shader skips them — pixel-identical output. + const bool clipCoversSurface = + clipRectPx[0] <= 0.0f && clipRectPx[1] <= 0.0f + && clipRectPx[0] + clipRectPx[2] >= static_cast(surfaceWidth) + && clipRectPx[1] + clipRectPx[3] >= static_cast(surfaceHeight); + return clipCoversSurface ? (flags & ~kUIFlagClip) : (flags | kUIFlagClip); +} + std::uint32_t UIRenderer::ShapeText(Font& font, float pxSize, float x, float baselineY, std::string_view utf8, diff --git a/interfaces/Crafter.Graphics-UI.cppm b/interfaces/Crafter.Graphics-UI.cppm index b5760f9..45f08e6 100644 --- a/interfaces/Crafter.Graphics-UI.cppm +++ b/interfaces/Crafter.Graphics-UI.cppm @@ -59,6 +59,12 @@ export namespace Crafter { }; static_assert(sizeof(UIDispatchHeader) == 48); + // Reserved `flags` bit (mirrors shaders/ui-shared.glsl::UI_FLAG_CLIP). + // FillHeader sets it when the clip rect is narrower than the surface so + // the standard shaders can skip the per-pixel clip compares otherwise. + // The remaining low bits stay free for user-defined feature flags. + inline constexpr std::uint32_t kUIFlagClip = 0x80000000u; + // ─── standard item PODs (match GLSL std430) ───────────────────────── struct QuadItem { float x, y, w, h; @@ -165,6 +171,18 @@ export namespace Crafter { std::array clipRectPx = {0.0f, 0.0f, 1e9f, 1e9f}, std::uint32_t flags = 0) const noexcept; + // Builds the header `flags` word: the caller's user flags with the + // reserved kUIFlagClip bit set iff `clipRectPx` does not already cover + // the whole surface. When the clip rect spans the surface, the four + // per-pixel clip compares in uiResolveScreenPixel can never reject an + // in-surface pixel, so the bit stays clear and the shaders skip them. + // Static + dimension-parameterised so the decision is unit-testable + // without a live Window; FillHeader feeds it the current window size. + static std::uint32_t ClipFlags(std::array clipRectPx, + std::uint32_t surfaceWidth, + std::uint32_t surfaceHeight, + std::uint32_t flags) noexcept; + void DispatchQuads(GraphicsCommandBuffer cmd, std::uint32_t bufferSlot, std::uint32_t itemCount, std::array clipRectPx = {0.0f, 0.0f, 1e9f, 1e9f}); void DispatchCircles(GraphicsCommandBuffer cmd, std::uint32_t bufferSlot, std::uint32_t itemCount, diff --git a/project.cpp b/project.cpp index d678456..c1ffbe2 100644 --- a/project.cpp +++ b/project.cpp @@ -413,6 +413,34 @@ extern "C" Configuration CrafterBuildProject(std::span a memImpls.emplace_back("tests/MemoryTypeFallback/main"); mc.GetInterfacesAndImplementations(ifaces, memImpls); cfg.tests.push_back(std::move(memTest)); + + // Issue #50: uiResolveScreenPixel now gates its per-pixel clip-rect + // compares on the reserved kUIFlagClip bit, which FillHeader sets only + // when the clip rect is narrower than the surface. The decision lives + // in UIRenderer::ClipFlags — pure CPU logic over the clip rect and the + // surface size — so this test drives it directly with synthetic + // dimensions, no Window or GPU device needed at runtime. + Test clipTest; + Configuration& clc = clipTest.config; + clc.path = cfg.path; + clc.name = "UIClipFlag"; + clc.outputName = "UIClipFlag"; + clc.type = ConfigurationType::Executable; + clc.target = cfg.target; + clc.march = cfg.march; + clc.mtune = cfg.mtune; + clc.debug = cfg.debug; + clc.sysroot = cfg.sysroot; + clc.dependencies = cfg.dependencies; + clc.externalDependencies = cfg.externalDependencies; + clc.compileFlags = cfg.compileFlags; + clc.linkFlags = cfg.linkFlags; + clc.defines = cfg.defines; + clc.cFiles = cfg.cFiles; + std::vector clipImpls(impls.begin(), impls.end()); + clipImpls.emplace_back("tests/UIClipFlag/main"); + clc.GetInterfacesAndImplementations(ifaces, clipImpls); + cfg.tests.push_back(std::move(clipTest)); } return cfg; diff --git a/shaders/ui-shared.glsl b/shaders/ui-shared.glsl index 52e29b3..8ec18b0 100644 --- a/shaders/ui-shared.glsl +++ b/shaders/ui-shared.glsl @@ -27,10 +27,19 @@ struct UIDispatchHeader { vec4 clipRectPx; // (xy, wh) — every standard shader honors this uint itemCount; uint frameIdx; - uint flags; // user-defined feature bits + uint flags; // feature bits — high bit reserved by the renderer uint _pad; // reserved — keep zeroed }; +// ─── header flag bits ─────────────────────────────────────────────────── +// `flags` is otherwise free for user-defined feature bits, but the renderer +// reserves the high bit: UI_FLAG_CLIP is set by UIRenderer::FillHeader when +// the clip rect does not already cover the whole surface. uiResolveScreenPixel +// gates its four clipRectPx compares on this bit so the overwhelmingly common +// full-surface case (clipRect == {0,0,1e9,1e9}) skips them entirely. The +// branch is push-constant-uniform across the dispatch, so it never diverges. +const uint UI_FLAG_CLIP = 0x80000000u; + // ─── standard item structs ────────────────────────────────────────────── // These match the C++ Crafter::QuadItem / CircleItem / ImageItem / GlyphItem // byte-for-byte under std430. @@ -121,9 +130,11 @@ QuadItem LoadQuadItem(uint heap, uint i) { bool uiResolveScreenPixel(UIDispatchHeader hdr, out ivec2 screenPx) { uvec2 px = gl_GlobalInvocationID.xy; if (px.x >= hdr.surfaceSize.x || px.y >= hdr.surfaceSize.y) return false; - if (float(px.x) < hdr.clipRectPx.x || float(px.y) < hdr.clipRectPx.y) return false; - if (float(px.x) >= hdr.clipRectPx.x + hdr.clipRectPx.z) return false; - if (float(px.y) >= hdr.clipRectPx.y + hdr.clipRectPx.w) return false; + if ((hdr.flags & UI_FLAG_CLIP) != 0u) { + if (float(px.x) < hdr.clipRectPx.x || float(px.y) < hdr.clipRectPx.y) return false; + if (float(px.x) >= hdr.clipRectPx.x + hdr.clipRectPx.z) return false; + if (float(px.y) >= hdr.clipRectPx.y + hdr.clipRectPx.w) return false; + } screenPx = ivec2(px); return true; } diff --git a/tests/UIClipFlag/main.cpp b/tests/UIClipFlag/main.cpp new file mode 100644 index 0000000..1357e1e --- /dev/null +++ b/tests/UIClipFlag/main.cpp @@ -0,0 +1,105 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Regression test for issue #50: uiResolveScreenPixel used to run four float +// compares against hdr.clipRectPx for every pixel of every dispatch, even +// though the clip rect is the full-surface default {0,0,1e9,1e9} in the +// overwhelmingly common case. The compares are now gated on the reserved +// kUIFlagClip bit, which UIRenderer::FillHeader sets (via ClipFlags) only when +// the clip rect is actually narrower than the surface. +// +// ClipFlags is the pure CPU decision behind that gate: given a clip rect, the +// surface size, and the caller's user flags, it returns the header flag word +// with kUIFlagClip set iff the rect does not cover the whole surface. This +// test drives it directly with synthetic dimensions — no Window or device. +// +// The contract it must hold for the shader gate to be correct: +// * full-surface (and over-covering) clips must NOT set the bit, so the +// shader skips the compares — and the skipped compares would never have +// rejected an in-surface pixel anyway, so output is pixel-identical; +// * any clip narrower than the surface on ANY edge MUST set the bit; +// * the caller's user flag bits are preserved either way; only the reserved +// high bit is touched. + +#include + +import Crafter.Graphics; +import std; +using namespace Crafter; + +namespace { + +int failures = 0; + +void Check(bool ok, std::string_view what) { + std::println("{} {}", ok ? "PASS" : "FAIL", what); + if (!ok) ++failures; +} + +constexpr std::array kFullDefault = {0.0f, 0.0f, 1e9f, 1e9f}; + +bool ClipBitSet(std::array rect, std::uint32_t w, std::uint32_t h, + std::uint32_t userFlags = 0) { + return (UIRenderer::ClipFlags(rect, w, h, userFlags) & kUIFlagClip) != 0u; +} + +} // namespace + +int main() { + constexpr std::uint32_t W = 1920, H = 1080; + + // --- full-surface clips leave the bit clear (the fast path) ----------- + Check(!ClipBitSet(kFullDefault, W, H), + "the {0,0,1e9,1e9} default does not set the clip bit"); + Check(!ClipBitSet({0.0f, 0.0f, float(W), float(H)}, W, H), + "a clip exactly matching the surface does not set the clip bit"); + Check(!ClipBitSet({-5.0f, -5.0f, float(W) + 10.0f, float(H) + 10.0f}, W, H), + "a clip that over-covers the surface does not set the clip bit"); + + // --- any narrower edge sets the bit ----------------------------------- + Check(ClipBitSet({0.0f, 0.0f, float(W) - 1.0f, float(H)}, W, H), + "a clip one pixel short on the right edge sets the clip bit"); + Check(ClipBitSet({0.0f, 0.0f, float(W), float(H) - 1.0f}, W, H), + "a clip one pixel short on the bottom edge sets the clip bit"); + Check(ClipBitSet({1.0f, 0.0f, float(W), float(H)}, W, H), + "a clip inset on the left edge sets the clip bit"); + Check(ClipBitSet({0.0f, 1.0f, float(W), float(H)}, W, H), + "a clip inset on the top edge sets the clip bit"); + Check(ClipBitSet({100.0f, 100.0f, 200.0f, 200.0f}, W, H), + "a small interior clip rect sets the clip bit"); + + // --- user flag bits survive the decision ------------------------------ + constexpr std::uint32_t kUser = 0x0000000Fu; // four low user bits + Check(UIRenderer::ClipFlags(kFullDefault, W, H, kUser) == kUser, + "full-surface clip preserves user flags and clears the reserved bit"); + Check(UIRenderer::ClipFlags({0.0f, 0.0f, 200.0f, 200.0f}, W, H, kUser) + == (kUser | kUIFlagClip), + "narrow clip preserves user flags and sets the reserved bit"); + // A caller that (wrongly) pre-set the reserved bit must not flip the + // decision for a full-surface clip — ClipFlags owns that bit. + Check(UIRenderer::ClipFlags(kFullDefault, W, H, kUIFlagClip | kUser) == kUser, + "full-surface clip clears a stale reserved bit the caller passed in"); + + if (failures != 0) { + std::println(std::cerr, "{} case(s) failed", failures); + return 1; + } + std::println(std::cout, "all UI clip-flag cases passed"); + return 0; +} From c1699868352e4a159b6053674ba159cd17394851 Mon Sep 17 00:00:00 2001 From: catbot Date: Tue, 16 Jun 2026 17:00:48 +0000 Subject: [PATCH 02/47] perf(font): cache per-codepoint advances in font units (#57) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Font::GetLineWidth called stbtt_GetCodepointHMetrics for every glyph on every invocation. InputField_HitTestCursor rescans the whole field once per character, making caret hit-testing O(n²) in HMetrics lookups. Memoise advances per Font via Font::AdvanceUnits: ASCII in a flat array, the rest in a map. Advances are cached in unscaled font *units* and rescaled per call — Glyph::advance in the FontAtlas is baked at kBaseSize and is wrong at any other size, so it cannot be reused. The per-glyph (int) truncation in GetLineWidth is preserved exactly. Adds the FontAdvanceCache CPU-only regression test covering size independence of the cached unit advance, the per-glyph-truncated rescale pipeline, cache-hit determinism, width scaling with size, and the non-ASCII map path. Co-Authored-By: Claude Opus 4.8 --- implementations/Crafter.Graphics-Font.cpp | 21 +++- interfaces/Crafter.Graphics-Font.cppm | 12 ++ project.cpp | 29 +++++ tests/FontAdvanceCache/font.ttf | Bin 0 -> 621572 bytes tests/FontAdvanceCache/main.cpp | 144 ++++++++++++++++++++++ 5 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 tests/FontAdvanceCache/font.ttf create mode 100644 tests/FontAdvanceCache/main.cpp diff --git a/implementations/Crafter.Graphics-Font.cpp b/implementations/Crafter.Graphics-Font.cpp index 18bbf89..ad3327b 100644 --- a/implementations/Crafter.Graphics-Font.cpp +++ b/implementations/Crafter.Graphics-Font.cpp @@ -56,6 +56,23 @@ Font::Font(const std::filesystem::path& fontFilePath) { this->ascent = ascent; this->descent = descent; this->lineGap = lineGap; + + asciiAdvance_.fill(-1); +} + +std::int32_t Font::AdvanceUnits(std::uint32_t cp) { + if (cp < asciiAdvance_.size()) { + if (asciiAdvance_[cp] < 0) { + int advance = 0, lsb = 0; + stbtt_GetCodepointHMetrics(&font, static_cast(cp), &advance, &lsb); + asciiAdvance_[cp] = advance; + } + return asciiAdvance_[cp]; + } + if (auto it = advanceUnits_.find(cp); it != advanceUnits_.end()) return it->second; + int advance = 0, lsb = 0; + stbtt_GetCodepointHMetrics(&font, static_cast(cp), &advance, &lsb); + return advanceUnits_.emplace(cp, advance).first->second; } std::uint32_t Font::GetLineWidth(const std::string_view text, float size) { @@ -65,9 +82,7 @@ std::uint32_t Font::GetLineWidth(const std::string_view text, float size) { while (i < text.size()) { std::uint32_t cp = DecodeUtf8(text, i); if (cp == 0) break; - int advance, lsb; - stbtt_GetCodepointHMetrics(&font, static_cast(cp), &advance, &lsb); - lineWidth += (int)(advance * scale); + lineWidth += (int)(AdvanceUnits(cp) * scale); } return lineWidth; } diff --git a/interfaces/Crafter.Graphics-Font.cppm b/interfaces/Crafter.Graphics-Font.cppm index 90faa8b..40e0a0b 100644 --- a/interfaces/Crafter.Graphics-Font.cppm +++ b/interfaces/Crafter.Graphics-Font.cppm @@ -63,5 +63,17 @@ namespace Crafter { float LineHeight(float size); float AscentPx(float size); float ScaleForSize(float size); + + // Horizontal advance for `cp` in unscaled font units, cached per Font. + // Stored in font units (not pixels) so it can be rescaled for any + // `size`; Glyph::advance in the FontAtlas is baked at kBaseSize and is + // wrong at other sizes, so it cannot be reused here. ASCII lands in a + // flat array (the common path for caret hit-testing, which rescans the + // whole field per character), everything else in the map. + std::int32_t AdvanceUnits(std::uint32_t cp); + + private: + std::array asciiAdvance_; // -1 = uncached + std::unordered_map advanceUnits_; }; } diff --git a/project.cpp b/project.cpp index d678456..2dc8e2f 100644 --- a/project.cpp +++ b/project.cpp @@ -413,6 +413,35 @@ extern "C" Configuration CrafterBuildProject(std::span a memImpls.emplace_back("tests/MemoryTypeFallback/main"); mc.GetInterfacesAndImplementations(ifaces, memImpls); cfg.tests.push_back(std::move(memTest)); + + // Issue #57: Font::GetLineWidth memoises per-codepoint advances in + // font units (Font::AdvanceUnits) and rescales per call, instead of + // calling stbtt_GetCodepointHMetrics for every glyph on every caret + // query. Pure CPU — Font only touches stb_truetype — so this drives + // the public API directly with no Vulkan device. The font file is + // copied next to the binary; the test also probes the project root. + Test advTest; + Configuration& vc = advTest.config; + vc.path = cfg.path; + vc.name = "FontAdvanceCache"; + vc.outputName = "FontAdvanceCache"; + vc.type = ConfigurationType::Executable; + vc.target = cfg.target; + vc.march = cfg.march; + vc.mtune = cfg.mtune; + vc.debug = cfg.debug; + vc.sysroot = cfg.sysroot; + vc.dependencies = cfg.dependencies; + vc.externalDependencies = cfg.externalDependencies; + vc.compileFlags = cfg.compileFlags; + vc.linkFlags = cfg.linkFlags; + vc.defines = cfg.defines; + vc.cFiles = cfg.cFiles; + vc.files = { fs::path("tests/FontAdvanceCache/font.ttf") }; + std::vector advImpls(impls.begin(), impls.end()); + advImpls.emplace_back("tests/FontAdvanceCache/main"); + vc.GetInterfacesAndImplementations(ifaces, advImpls); + cfg.tests.push_back(std::move(advTest)); } return cfg; diff --git a/tests/FontAdvanceCache/font.ttf b/tests/FontAdvanceCache/font.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f27f4ff59562d58480f1cb94194393484b8da9e9 GIT binary patch literal 621572 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

a=M8|yC%_H`?OKLz zR?lYgLv^z`WQia&DX@d5u-J`3l{u(op$2X2gZFEGL3i>$U$B$i{OoOY1B`;-E4m`N zSjk)y;$ksb1;)+6zV_Z~AQ!v)GcYnpGB7Z%2G6g9Zb$$*4ODJ|57mO~Z3j=Ov+zoQ zH(V;3GOmvLcLFqx&Quius()ktTQhSpU15-8h;!f;2Q_5bz?+pI2~L`UnUT2-w1gxP z6a-*n5xGPjA`3orhlvSRd@?Y=R75&(st5|Jswjd=P*o8~z6R}45Cks+0Zld=i$iZD zVXE*IQ4~-#_3`o#$V`ZG#UJghBo01HYs;Y|MGY>#loPjq6fg97Hc?oESFgItC?vHBqtS-|D z)z*kIPoLnO>Bm&#=jYoNQQybU=gZF@IxD?kh6W^@Kx;=qvSPNR% z9pVtq6sVkG{;!bf)ZdK)4n7Kulm9mUdjqOl{{R2~jVThGA6*@sI2f5gODUNo7+F~) z8QEA_e3-!lZQ!v3q}dsgDr7_EJhr-f+w@JqCp`xLbZ@c8lbt)B)7 zW5)mAnD&FmJX{++eKy@Bfn{x+rL#d z&iv8S)2*`g6f|QDQd-5CL>W6c1v$HCaC05^`?pn8jIoi|jxV&)Q(V+rL@YhB0CcY+ z=yEm&CeZkyD1*9#il~qvA1@C#D-$CFH)>-no5@dDR8$x=ekg2eE(lt|1sXh56=jEx zS~AB$Pb+!ODE)(x5j1ScC~POnnDP&DV99AuTZe!1!9$mfnxG>U85o#8g8Ob#3`(G} zH6=xHF;QV5$bu?X7Dfg^EG`fh0=vLm&{!Dc2y;<(LCByfySb=2XazqwshP4f$7P({ z7!nngd39-U+{VUM7A7{vNuc!B%jhq_nEmgB9HZ2~FZ_1={~8#*r2k!HR1~OF5RF8D+}mYN+$3LFN_SVjG#s&bP^df z`ku|;CnGH_FC(w5Dkvxbn$}i?9M}(T)PpV&5e4m?1FgJK78N%#XBB0NPhUMLDlTqk zd82^4Bcq42CQr0iWZDAGe;*lT|2<~hz-!BQ$Ip+;j?*Pk^p%Nb^WCVP=csogPr<{MNy;!x1gY^sG^{t0BFb=T*xUycS;}z-$3hK zKS%wW6r~OyaBBc9as@4Ct=Ci#km2Bx6SNM2mW68nzk%=2(PZ#;@Bq!dfR4;!VPs?i ztuY5JZ^qrX0JXTl^U|Q*rt{d~iO*mK~hx(wA(_(Sk+iTURqLEkOREa3v?11@-#1K zJsxSeB_M?8#Cx^Vo;w8+(!^&jIgh;u&%L7O=o6gk?};|nP}=k}OHH&T-mmy`vy z>$n&gnLvBLxEVAZ)R3Copymy0Japj=0|R_U5L7&Y2GzlBY(ZmD?t7qHBJMN#Gim$# z|GnxDYR?<|FJ0g3A}YGf>Yqz!bWBJ@M06;leEMYB$<1DE5#cRfUajFN z6%{EN#pR%rAQ%{!xWMBE3J$WM*)A50szOvySO8R2Kr0Gm&;j`D%HV1VQcp2TGoAXk z+JRB@uNh;4lC$y+M({m^4BQM1;ILz7kahso0-#YbMn-VbJN3il)Xd zqwfEGd5`JT-&TLnvBwNd|Cj!^VPpc=8S)I_4kAK~ER53ZOw7zIpb;TvA67;NPwx#1 z0TB-3py~y(wg&O`Y8Ux29wd@n^CWSfkz5+f^vqLes42P+#BD`??9vyZd`NR$yx z6uPfQ0=#w=yetrWx)o&S7Ie`Zcr+2TwiQ$eqDq602#}YRln@r=<6&n5U2FmBfeM3; zTM!3r{RB<#n~Q>)X=>`~%*u?+;DvRdf&Ua`Em{7|tqJdX1tleggrwy)H4IcF(w-+U z%2f$UNpVUF@GCG~6*o}-!x;b1;ok-gEde$s9wkq!U;mOA|1qj*>9MdmvvGnpxySt% zVDbX569b+2=RK{Cqs1?gMDD2XfpawD?vA?JIzvNGU1^nd(tyH0-MLa_|c5Etwc~C(I{2 zth^^IC@h-kRCJ_6AU6w-zi)avXr$|$jkUGKEBAjt+?{P5VRLTGM?vQRv)=*p6aNc< z&IM&sW6%fPoy}0ia2zHMJ`VwOvOMVQzbZx%usFki6{b3-vrN$SE{p*TC&A*0|5X@2 zfbZZ2btc#u0~jE8k!$={VblVP>o9<(hL50jJh=0` z8+%-Vify`pN%P0$W zQxpROqcZ4DagaD8J3|?R5&E6t|35P@FlsSRV^V{tVf6if8bi%91_nk8ggK1f|MwA8 zC5&4k<{;gz&dk8TD8t+Xa}Pt-|2$MR3@o4%*cjzmBpJ9E#2A!7 zEB77TSQ$Xuk9|NTdMg9yXeCxgW=<9kW+pafrYr_FHZ}0{nmW6mva*CY54W*w%*_rvYk`l6T^ZM=ab`hd!7y=4flO{AD{*TL@V;?r=@lSaL|S@I z6!R1rcO@<%BSWEDaVaSY2`MRY82#rfXguNne})(a28P3+d*Y#C?#EyP@gveb@lpQ+ z7!HBb2S^PAJ68wePH>#^GB7Y)LxdrN^Z%!)dKs7)+!+`c{xIKS5Mhu2t$Gn)WMtxJ z15MC^!UuG6Lj$yT1*(VCz@tm*EPkS*q7tGK!YXPaZ0u5?gPYjdmDM3rSW0Z{rl9%= zGn0{>G4TKTKd&I`R{p$#u3M4${}sF*R)9gU=(0shwN2>v`@`Iop}+q4rNmjaZ@=-U74sfZa!`wE*=phT_)kbU(!KK zfd2ny0NJw-9Itm7K#NPg7+lcZ3(DurptTy%vWo346AvhiLETHW z8LSu>7^}hiob(-Zpi!RAz|0JuI|i@p$JN4tT|@xtQ(L{bb?XJAqsJQesUCk{fY$ry zg4^_e8Q2&>3!0f2O#gplTF#`*pvs`ba1l})a>+8XuyHUluz+XEp&VB5DF{3P5e^!h zjBLy-ENn##91P5C9L(Sa4d9kyF*rFgBr!6Ar-P*-#%Y4pGqbTY5Y*scj883St}_vA zHfZJ+ZcSvQgSfgH1A~^PnvS{-gDQinijuIZiVA26g(|2w0qWO*Tiei~cKE(#@Um-D z6WF>DrkQ;9*0S0Hs-F5)omM$MnMMEl7$brV{q zI%Gwj!v&8k7DE=7f?5sWu9J)u=#FDvZVomE4Mq)6I{~@tgw}I{Oi!_kg2$9^SjEXJ z#hTj$&-Acz4Ys$|an_IzNsq}j(^oK4%;ypo@E4HMG>Y<2)m2jy6m(PdbTPE#W#LpX z(R7Z6xSeS~lM;gkL$pJLyd)C~tFD@y1PdFBp*j;gGm;0`7}%NE*fY`Rm!KX2op}xR zh_u7%e~@BK)2aH;3*X5C=z{I%pIDpXYqi1kwe0dx=Jo&o8I1pbWBkjc#Kgt|Y85kq_{;)KN({;jI-r|m zxfvOmG!+#jg#@`+IG7o=q?nn(hh#G|u&^?-fXu<01fOMQtSo4}HX?!v6b_94PyQER0IB82C`)r8$rerfkTR{da~p$R&)R`{Eh@gU_G$gB*S=$;QIO#L3PC zUS0=UQk&xoN>t!(D25zEJX9{yflpWnG~5fq0$iZe<{>wwLH4*p=6ICXMg#^%tcnN- zh*-ts;hCPEp6>Z~2a^Y=6%zXY8&fyaUj}uCvyd@&5lK!CW>zN9=@2T6?2M|64D8@F zbSIte|tVazUqgFeEZ_KoeMkXFlI6RWe{g@aS-HSWMmK*6BZH_;AG?gO_@S` zEx-UBsLKI$EWqam^MM-CObm>m+ZD5!{35rCtEzxjE()82`+lIVAgI?TDrzncTG+|X z5||huZzL=(pe3TIpQX?hVZ=B|w<$f8o5Pir%fvp&+QR2w6l0kT$XL)CF|eCN8NwX6 z*cq8Xdt;cGd?1dIVg}u%gfhaO1FB;{*Sk1?PnTw3WXeQR1YYX`I$T>&P)Jot0JLmK z7&6HN9k>PusIs6kuLolS!jY3EMR+hdr-guBXl&>4kCpK^12Y3?{zRQgiGdeWB_(}=g1p4UZ4C7t%oQXg6lA3(63yo!S&8q}ai%e3I3C#1|R0?#j?>kz@sf;$$0@(R)V&NgC}rY2q*`B7>r ze0n~5?hzvJ>@5M#`Mi3H+Iqaga!Ow6W|mSmMkbzWC0s%R0Rqz6Dk2IBA|e_VIxb#p z3i4*|DwYOFnIB#_NSkPg=!v`Wi73m7$;%20Yg%gi1u!r${lD{HfT4^rih-L!8q&Xk z>=|bAft3d}pe+%#kR8FGE2!(4{CIgJ#6^U7quy<2H#Bunni*%){TYv zc|}Bc`Gr*lBssZ6ctxcx^%*TejWt0*!M`t9n6;UiMP(UF{`Kh^gUWi5|KAwBnEo=z zF-(M{Uja}+%^T(P0&IG zNfx~A1)7V&i!US?SU@|vm_heSfrnP$^U0ChRaI40pfi=wL(tXDAx%|5a4v?f!VzWk za^+!__Oswq;^*R(=XVKJ))!V5X! zFfb}Ig)j&*C^0xYI6#|jnV5S8Sy{2|5L6OY;=$Y@2pY1+y+g1=MK;;gz|uCrPtMs* zOhCsVNmhIEG%VO}OC z7EMN0&`bvh3nL?g93wlUEF&8`n-2ps3v(+2E9h7ij!aHQCMIYJ1v+blF^PeZu@KZy z0U!FA&*lf(xu&6}q#z>&I>$|bAKbpvWz+>NO;$AmUu+2)(+3qj=EkCm;K?*GHa2zC zHDu0QOg8VjDj4m{f^!1(Vs)b;k_v5}{WG>_XLVuc@{VRS z*HSF2w)xFomg<&g%cvYECX(Rk8R+!o-`PY^zSa9Lz$Cz=44JP4E$86jW(Q5-Fp4uW zGf6PAFthkDFflSSGBG#6N)~WDf`^~;S^QK~ltqO>gUqTpcSwSKWn#vtcK@z-mZ7|U zkogow4!6>zm{_B1gV3g?&X%J~oy#N@oTMd!54!|7x%i8U`)ih@rsjh3necxB#sf@} z;Qf*24kjQMq3w@kX3hl_b&w%ZLG~fYRJ<7rYw}276?FF=R zDl*c6M^r{o5FA{f`<<`^7ice~Dk8|(*w~>PDG#{$LIY0&9C)3b@Ze)qhi|3y@{C0Z zNe>TbSpEx94Oih3v9uNkrA2=R2F5xjNd`{PsqGBRprV~69kf&fv;!R8sRGTZfD)Xs zvY?_U_=cw0K8*U0uU=)6bl71JGDqY855@*2B?fiSN-{}CW+o{%Cde)_&`~Rpqc5O+ zYe*YVRa->~H2b3pN+_^&Vxk7wSP$w>!Y8@K#O0V68#2P;Y?OVqjcr}bRqWLg{WZN+ zrIc;ub)vZ29>$)nt?<`32Q2L30Os|GzO? zFex(_G1xMAFf4EoHQ;ArV$kPf0v&w8$tW+u#Kj5PIuEKJ)!7*t`MLR+Ik^})(>WMH z#|ty_GJ&qI;bG+BVo79Ugyygg`l1En)Y@k#*7TE&UPO59#$5{ zwkEc^+M4R1W5vY9gavuH*jX8j7>xu#t1XShL`BWa%|SI3;=)SUO-E|#;GILJCLjgS z8EE*b6jRVqw4mz5$jsD~Q8ppjE;HK76ng(rQk(<$?j!RMA!%bfoq(91Nd>CD>T)`9 zz9BKDdhVf&Tq(Zh+AdK{%CU*Avdqj1&L)zetB}Itz}Fz@>M3dn1^F#zW0tQK6>$<3 z|M!sD$XZ)B(9@8G#ll!u%iGTYycZV~-jz(s42lf83>FN~J-X=6! zU<-jj;asQVROy*--(KvRtH3Ce<`xkL585BzL4iJ?-pXo77&9sR`#DxewobCMva&-7 z%vbUG`SI~NIT^4(1+QNRg{2RZGJ^(#F~b$e;3U5qBRhjCBOALIKNBM)B-I$%nUF-F zGk(epY}||-jBFg~;BaAr1tcRQb0T;Je>O9ym;)z!gi`P!ETB$2sA?_tD zVXa3jp^qHm3CWhW;qDP`ysT2uwhrMUO7S+B=^2!cXL&d$B`}Kpec_>{VPtG-5ahRz z12g@C*6J`ZfWlgYNg1@}iQyRR>8NXF(iVoNrUw4Kq0M)P{_gpzNiG;DMVN7povQzsCx#!Qx!aS2ik%i z=^&<}%)p?fp{%Q-%b>)dB&DjPtir`Bt*rcEReafsE9g&056G8{P)uU6zZCh%W;Ic$nn1~{AI1BLt`zPki#65 zhWVI5X;`1ZieWt@#Kn=)FbBI9BO8MzBP$zd+yW#6OUuYI@EBm^WMt=HWQT-2XpLSm zq`3qN4$!Hb;5mSNRzGP6T~Km`s6|WG92}5@imX1;LCDa++|FnG?)k0JbDK4kvxUp{pHj+23biI?dIgET{e11CQp%AIf0 zY>X`IjLZxy%%HtI&}-j7`@pk73!W8GWEtbZ5}+d`a$%R#f!E6k3JMAfNrKkPA>I8Z ztgH^Y_YJ;V8|nVH7g@V(!+m_hw{J(f0q);f(EV?*j!{vLpuQ#C&3+C7phNIPMTEJ* zd&QYR3#~xSU_k~(M(~J1F8CrO(1?M9IH;2n4;n)Uj|RYHBDaZ2LAJA-n}eNYEQoYV zow+!8XX0~LXWQ^_#5HxE8R}V8S%I#ZX3?OV>HZbNZ>viPGz?mpYs$dL01I1XhI9u3 zWhHqzSs7_zusgvgeSq944cXex23pC>%$x&CbD&GHplcEpQDhmwH-l!QsEKso7Lx*v z!9#YzLp=ZwS8%5ndfgtfN5J8W%QN6${)ELppbjuA(+>uA24e?K(6U+<(7^zZX#~(d zeQ*i^2Q@eJlz!0tdy!j&g+TYV2%Cd$Y68!9L_{#|QT(UO*rUkU%fQIM&%nU&iRlLe zH-jU@Fj0uV!RwpBp#lvLRB`YT*V&-cz`z&83xb!58^fm|M3vV@xVc5Rx-!K%L`8u{ zVHo~@V=7@%Vi01GWKaN|-h_RRd@;%%d1)yTVFm^{St$i+1#vN9NfA(g6MW;XAZU-g zsVQVoLEKmr(gIUcH%Hkb@4?lamD0xTV9(Z;>yqun^3BtWozaZl$HT{m<=+VwpD#{D zRaHe!RV~&Sm!}rNSmE=3nW;nB^p^tSl?5`tLzOYN({0L+6)cbgjkt4IY8wEsCA>x!pOkI&cw>bzzW*K$N>p~Tu>znZsrt% za$7!|pR|KEl1lJA9ycQg2Xq7)Huw!w9qAw;FUP>3rXr^;uMMtr1;tfVAUhtxYcfHb zK0wVHP@fWdT0H3N4rNne$YJrupi?_d)R=-HhsGyYR;Jj24~yTiBjP>isQ3ubq45(O znbJL#4Z6BJ`@l!VTRSEsFlEGm4vGgK84nsGVf`<_G?_`6L6gCZ;evyJ8Y3&ao-z{$ zE9inlX3*kXu7C&!9R|?RxGY5stPISItjw994fG6&Y@pMaz!fND;1o281X`Wuy#W+* zx*$D_EKCi=X#%xg7(shI;OZIJ*mA+Eju^m4U1M4V8ok%nVq`Ej)H2gHQ&mxtmy#3_ zSAJ|qRNolk3ds0%4(n?d{FlsveOK7JD@0N;h&kQ3FDsB zBC`xHLti;2#t1H94n-w479SRNS07FxPETG|sX)zO7ao0UIWG?;<=)QVIyWhPHGQ!q zjH--`%JTeDeB5$Ew%&}4jI+Gdb-m)_HnEHF=fFTkwNq|BhrV8PG^2_+$J zMg~S*Nii-KHU=$qCKfhOM-n8+genM~(Ub)Z<}fBQGchqT7K4%=3ux%O5HwE)Znc1p zMugN4jESJdxR`1p9k^9Yg-lf>RMkLruBslBDcZRP;$nzP3m_R)OcVQlmfEMMT;;*&{cAjm3?X zV{&CcxHuyd<6$p#-JZ6P26rZ=SKrwf`L8iEo5$Il0PRy^0Nr0Gz?1^ck6H{3+l1Me zI5@H8M+Qz#NC&#u7nIjviI9yAGe3gH$iT|M^G6t}5&2O|UJH^R1w~ZU!8?$}O<`kK zkg-TmnlUyv290WoDx0bsnL)=jL`B5@>|xZ0+_2uVur z?zZbT2{%&JHnx&e_jj>Nw9aJi?e&DXjp6@d#y?E;45Fa@f4p4mETH4EK?e~rGPQ!X zRD$nZW{d|F3c28OzeO2DrG$ipK$~EV)!CInXDSFAGlS0Z0iR;Pd|KAZG-zU6Y=y-X z&;_t8{KD)-acBHie`Bow_n$H2pQpT=DC}ZX(4LzA0!+V|BpHlBH^6Ah2{1D;b1^e9 zGBGj2&sP9HNh%7w@&bCM!dy`-JUm=X%*-RO8bz)a2sSG}7b$cb`e}-!sE_8F@P+ZbqGd*R`X?MG`F; zCH_8)P6MBp$;7|~ZYTU;P-M_!$Z-&52aTOFGAME|GP5vB@-Q&7vVbZ~P_rF$u4@ix zW&vC%6@$uWa8sZVG>w%HI@eVmG}zqAzydkK1ye<&1E;Ergo?H>c$c23xhdqtZcxVq zyj%pj$6i^E2~@AKLMA04Bc)v45qtMCF|tVbIV4wBCff%{urV?-MKCtFc?G#JIYxN7 zG9LBRI?$i#naRPY%d2G2)7jalr@*eD#QlKj{=?MNm>A*YZ0KDw97uky+a}EiZ5x3+ zsl~<4#0-vK2GDBt98gyp+<^uUXMm@!z-Kk)gL;En3@ofHt;jk!IB+PAbP!=+(A8E` zVNhgHR8a@_UbsPLvx$N)7X|NG!5R*r!V2C#Voco;;p-cL5e|$R@L-6x3lFzr4t{_Z z2;PjD$YB7PtK?vkWZ-3xaggKyEd+%eq70r^0G%HvC&i~qs%^2SDHj1|Xx zeRgpc2EEbvND1W%z;!`VD+FLH|XG^d{8e?OTwDi5g6B z;q2mEHPhMEC4rYkBET@z)hW{5-JJ{6TGLbHHRiweH(<^jkGXR_lngv$;{;N&3*!^A zK=}hyJ|r_PWBS3s%Mc71pO9x@U}a=wU~FJuW@2t*U}en#uZm(y0IiS$ud|Z@T|v+a zsRKZ2K#QN?(vU@Mpt(xWqBd2y2Lz1;jTyC}&Mb+regbk2(~rLapfRui$t?CvN(|Zz z{o6E@n3&jMEoLnSCU(#c!v@d>`Zi8R&`byqH!m}24hqy7U`gQO0Iem@W&z*7iL97` zn;W$85Uc|-XNaaD(m@e)?Tw;>yqvToXxfh-G#A1J+O*6HI`v$e4?6uPXpD#k_>v0~ zP){GUXCKnr1(8e(qocH85s{qh=Nz6FXcytm%PJXaP^ZQ!9m}K?oAK{8I3gG$>!ZT{ z-C&HYPl@~Yib=v#OEXGO2Na~C|C7P%g=HCV5E$6pY~sNtQ4V7gj{NfIKS|gY3=l zM!5e#@yx)$_=agGg9O72$jUk%P@yWq!^R9c?m^mthn10)5yp-52Cr_FVPIkc4~BtH z+GYgJ`GUKTETE&ur6I;hfl36Ic@5hhS!AOavv^Vo(bw z8$2z>B_b#)C<@wrF00LG%q}JdT7wB{RNK;0NbK>@m(4^&Jji-vv(%TTdY$S{LNl22Dtot>+RT|bjVps&8YGRI7C zYP@=tQBGG+&0r(we*6F5nD#O~Vo+w#W4P+TC(X#nA;`$W2%2aEH4q`ABkWupT>N-Y7iBWB^9~J_i+CA;y!hy)x3Q6uoQ%98(<66F`+$GC zjFzbLP0v=YtYE`(cYkZc*_K_lUWozP`etUDa(YZxLw(Ex)Z{E(^di#yjcknU1!rc z1bTr6M!gxYYNfkX_u3cxm9#TOr>IC;t0}tubI%A3NK1$e$^hjpP`xF<*v+KOAjVJ! zX-IH0Gcqy=voL{{h)X+gGc&Tnc+l}D8E}IQeArVlXt)oy<__E}1>L{^E+0VcKF}gP zOeNq2dEj${A#>(pjAG!n6ZCj|P+JZ>&IuWmWAx!u;^*X5;CBu?dlpn!$oMiTgO^k$ z1>AD|z|0wEXbu`TF<@X|Qefr)hp&T!t&kw7TfzuF|Av8qft7`UH4}6y9%yw8Xk9yV zJm{>gY-T^u;kJ+vXAor&6&BUt0G)+rYOW3&90a$e#YNc_MUjW5gI+cG+k%&Gmt`a! zWmLEp7 za`6nfltxhj?pvw|3M+y7mXJ=9D54$)Enh&KRAb6`(Aqv2vN9pYG%nduKt_O1L(JWY z(a*y210m_4>DY0w0zH zPBi&UexQ>f;k^h@&W1@uI`A_xfZ7=xYz#t-LZHQ`;QNN)`zat5Dqx;E?o&;NmpT}(e1 zWEnIVDjfK@8QB>@=e|oYvay2d2~Z8xfr%*x z6igbB_B10WBRje}Ha6Bs2FM{rtZadi4np#Bs>*U2@)|12f`Y;-id?+1+Twzc&I+i_ z3~FGCDuXwnKu>{UhV*KTO-k9|ET1rYO%W2DSF&f+G znV55PGYb4mH~V){-;mRdl|76}PDV^pT2N0NR^He!1u!Wwh%8BnH>GoZk2A!Q{{>jFH24h>p0b$1SzY$r`0326xjnQp272@^d<2Nsi1g}S9{x853&!hz2FKWin;2>q7ucfIf z2g(!dj10VNtfGv}EF$2ecpw3+2%77Itc^#R5nzU_X9VXqRq&)0L?HvHbHD&SqZd48 z1s=W1XYz}55Kvb$F#_%K)KFFzQRd*4))qDgk4S-9P~aQoKz(*~NSECdJXj3c{K*bF z7+h6NP29wk6&wnt%qEN_>87D_tUPYMzNOV=-u~sT{!EN4W(6??ycUcy4xTKGsytrN z|8B5**adnqGIP~W)6jI^%xBN{?_zO@QlM6#n<$SiXJBDE7o$jGq?w!DSvFR4zu@ph zQ02gF%YH&p|*+5_B;OKOZ~jtSLrN&j^&a1Q{3@azJZjK@CRm4RO*A;-F@5 zJm{FQY!sQuZ6czQ-~*w|L5D$!u(6wi57K8h7i9-6R$v!ZHf3iH3TI;E=a%PMoqqA< z*&|t-C3M1r{t7a_`d7@WBgD+F#=Z59p}qFMzaLoDj-Cfu&jdP{gDH(ki9w7(mLbwX zNLmVXu0ALxSr{2W7v4e(d|}WSE+hugT$>Bt@C)regB=cvC0+?}aajpjVKEgU&iSBF@hG^gAN80;Rc^&r2x)A;ED;7AE3FVkjYO~S5*zP z<`;UF6~ZN;sYP>9EJsv!kFctz|DJYN72H0NwT@r!Aum+CLupUw}!E3ABH_4!nQe>i;)pKPF`cHV#g3 zITpsi!1NfrZ(VmITZ)T=66g**CdO9q(aTKX;3j7RsNoal>zMwMzRiUS0hcX_Wlo6AbckAD4#@v<) zTdP(k3C5d&p8nyCXa50w%&@>}#$bbP|i-REpCj;nq({u)Q1{NlEmUM!8Kto{ia@^f*pu`4xye+AmC!_j9W~0zF(p!D&G6|2Jk0CQ$Fz6tp{ChmoB@o0*A| zT|rWanVCsmmxG;~1nLn%k`MTN4Kr4V?+!X;D#$0k08Z6N5kaE(*{} zNlk`wXc}Z>WM|-IWMK!VI7UWxW(WtG;G{qUP4OJ8%*>$d0othnSwsTfgC+;QI0rhq z1TJP!6ht}*F))BO{(vT>m4#JQl|VP@Fp7dkmO-5pQ0zih7K65SLq^Gkkydpx3EL(4 z=2N8IVD47$^k33SplBWSxI zEa$7hZ}en_x&$&22f5G_H1-eK7H4M8IMX}LR@yJn$H(1COo5wEO)9ChsmQ0m)@x!G z(=m;#vU1aevbey2cy?AdR`$k%;x^a%u=KsN^ck4`fBP@M#L6VeAj}{Oy2n$Rn+<-C zC%D>#jC_DM{!54n@N=*;2r~+U`bFZ94hpleu$Z_pXi|!qjSY5*CmUOmflYDhf%s~1 zPG)|g>bM)4QJE3~Vk)YVp5Y&?jC~^n8K3=IV`F4v%ozOlbWTQ*sJM)qEw?FTEtT?r z0fv`MKbX`QK!=gBF;s!7?f?H7Ky{NOXiX^t=;9uBhAM_L&@w}X@d1+}gC2t=!wd&8 zaYkkqF-9h41x5~bZALam9Z)?2ZR=<;FmW-mb1;Fo9)a8Yx}d2=#za;|Mn)Z91~xXx ziXiZn+}a4mY@Cb?Yz(QeEuXresz@E8lYx;@htV(6K^9cxn3?EX8dz$og9fcdK=*vH zfeIYx#oVA4D|jT!#0<0{1+>{6IqzV-d2*)Hz**S1$9q~1izM>rY;MU1q&zV|#2vX4@kk&RJ>k%0}=g9PPGSx|KkzPuhK06`0h zzyT=7z{U;^H>f&RR&`KQ4BYqvhnXnoDsyo$Q4v9YULH<11~o=C4#*|ups)fRw+G(! zYmC_NfE-4QE~Z9GlFZDBNs;A|t`Q!*ETSs<`jGGd1%!=cY#L+yzvT@nag5@BU$`rZ zsyHY@f&lD4F9rsNMNBpf%nXq8ycogjh%wLe@`_-x`FjMCHeDGQ82OlmnAF(qLF@du zwf<*=+DZSfGcYiUGyR2&>-`S^i=Sm+U{pX-uk+s@EdHEl_F5=fQW@F$pl)F|acTfXXFqMivG}#GR823?{yy zh9{#b=q`8xe$eEkpr9ZdhorWlu(7bRx-z>k>_W90$yXCBZ`&MW7i4AQ=df7GWcT;b z;>C!!lMv5|>! zgUSX|ZMZ%qxV)D&7I|dqAqc?LCZ{ZCsM*}1hCL>Q(q2ySFz+VOwO22<^gEQ}kb zfmh~(;ssQ&3NSN6^f0n925N)Ng!r9-3DiGh>SMBD;Aap9jh8d9FtBp6u%?67owIQ= zq;oJbGlR#uOh9ESWU-PdqaX6QEgbUNs>o<4UVsHNPK~+R|aMVF9rt2hhR5}GsrWzI5@H~ zvNAFHuro3-u(B|*f_9rS#DfNpO?){RK})C@7#U3&{Ujv>1wdEiO2|vfi-`z`3ySmc zfDX9f|GQ`1ZNd3`0lS|h?*yj9%!g&^q->=tl1g2s^9xV3bV#X;#BbV?x` zqjx$e?inF*|NlRO-hTm*ADGnGLHa=T@G(%lK>Wsx(C3wjt`FpQP}v67$F0Gz8I;UG z@x~<1WW&J0AOJqyorwuN$zlSkE5NgErVM_HDvC-h98%h#!%#qXq=|t}JOMG7#OFQH3BFsBfrXI)w5tS~Ksgyf z8~i~j)|Az{tkv9RLa!L^%R=BNH1t zXuS#}C_ES^G1)MH&%FiR(apld4895jw1(9LG%*T3Zpf4swD?a@P=Jj?MjJUBc`+!_pf7(ioNp!CcGPM_)yphK%b_spcTfo^Fv z0S}&=f){~uGjM~4fY>>tA@_EGrh!BiO__MY|GfzhXA}W7su)@S{bsUx_YRy_LH8I2 zGubc*Gl+vimXn={m5YUmft!^forjT|fr*iuDIK(G$ix>kjbh5^Cn6#uE&@JoA8Zq7 zRSPeNyf!0f?pP5te+<5nQxS9%8M~>XC{wT-$Z9vYa7K}Ex4Hk`FfRQU&B*YdfpMvQ zL_|cyy!d!0PSYje@brpB3NHpm231fp1p8mjK^Zh_!@>+cZ;BCAor8h^T&+M3 zrWR%95Z5*~H5XJjH5O$T6c<%>biC#0Xe7Y6o!`WS|DUIT5!g;u@L8{*c4avvf3|}x z`TzeL0|TQjIBv_qZt*>b%n;sqEGXGA;g{klNgVI+nZo<_9jMUkof<<3=E8F;5-R2&*v(N zc?_!mRT%z*+npeNY>WY*(eVHO|KDa{VDtjFGa&i`HzVm|VEn)Pe-guDaQjokL6w;i zvgj9dnh@xWdhnUjCZH-3-uUDY(`JXCOadCXU}Myg;FskCH9qHYxiGUDn=tAAy&WI+ z|3AoVMrCl=HG#v{YbA=^;4pcKf&WXj|xEF>xbs+NSIH%YJy zD>Ji;Dtkrn%JJTpn{wp*70*6Nns2{fTBTZ{{sY#3NUH6duo-2}9)kud?%Q~^ynGq4H@vT#Uf3k!l;Obh@2Tg7DakJppQ z1{4oqH!lE}^KBq=xwSy?#=ry?2ZbXmgCK)}gDwvnXdc^#0n|!k0S&OASp&Xm9(?T% z8{8^Z(Am4-d&)tUGDfn9%kdkq^KvtVLmj{+WM(PGBW!98?XQFUxB%=&uv?ha7(OH1 z2oq-nmHi+7&4BI=odIrvGJ?;hUC3m^zzb^q^Kf%EyLh>3}UN;6?& zVWz1;poOO!d^RvMG5J@nVOk9?*3v}`wYdH%Fh1ncHWL3gm&xXz0pkT&*{Q;0!yw3@ z;-JXG%mlj8mxY;$iOIy5kr8IEAcLTwm>?^MB&gs-x_(AY%~X&{CHvn!7A6+|Y(_

BcL7qG2iKj- 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ⅈ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�ExftZ5G<&LFl0T&26%DO+qbVH5@V)l5ghM!_A2 z4>vG4CtB!kFxSx&x6$2TK}hKaM(1P;-3^vH8<`lyR2B3T+(BjB1}0U{t}b_FcjZKd zh$LxHsjKX?$%2tlR76X6gO$z(2C)s|%1#?tR5!4wf|YILVQ|{O?W~>JrKhZ*VAExx zyTKY&nSvfzp~40>WhaZ)Qft6gIG^DZ3?F zNGosPa&`mNSX|CvTM`toS)vTGMR^08a{?$bSXC1@uwt0HfkiD5R9k@@!K|8)0xd}q zc17xLuz`hzf`Y;ZRyAl?DBD=*Zm@+*ZQxRNQczHEQ+D6LsI3f&MLU=r*fWrj-oOwN zp{HP@tO%}enIM7+Aa_BdVgsA%23A#1Q1*beT_BN&neq~pp|Pb5O7iwF+Y~l%IYXVO zkN|4npa&!@iXC*674$%%=(a)JIU+zhC^AC9CQ@&s0i%ev?gmE`*&yiv6j>(}S(sjA z^U!rT>uj*nMpgjR1hddZXM?qNmx6)~D2;Vl;8wbU%~@}Qfwt}jSFo2s$yf~%@`)RG zl${_c6_lKn6>Jprlr5kha?{z!#iHs2O8p=$s<1EuSw}cYyJJgWp!7v*D&D}P>IN#u zY?Lje6_L`5VkFd^?m8P8th8aF;-RyV!CG4p6lWlZLVW`d0#BU{45E;1zli~q!-e%W zm}%*5@WLVPt+SDVQABuyxt8t*ADs=BTDlv2L80iXte~gh)}@>X3oSn#P=emztF61i zUuP2ogPXSQh5(&S42&Q~pw1>nut<>3CPuJGFi4HNw(f=ykQxvp6r={k2m`4BF~W5= zGcYhXYwK=^(Af-XgKO(9fV+5&-0jUGCVnOP_tT>Q5Fe@Hp zr>nN^h6IqEAVwm{P7osrWG9G`3{vBvt-B!wqz1%D1*ris(m-lJjC7EF3@+Nb8!|xl zfmxX#`@pO$kbPiQHb@;KNL>y{9hj91QU_+`fz*Ln`8pdJY~T@C0OoCQ(AM2h2w^&E z>ux9l1v*IC1}AOZ4aGVe8Ev$|YDyqXkeX7k8ju7?O_|O{23xqP>nUIvW}6;O5nVd0_MEAxx0+1_%?RyisQ(qaDP&CI}Ox zpc%phDQMBz$Y2jQuNBM#o7V}gHIvW}7A?9^Lm>>mR5GF`Lx6USRaLaZB zoAf3YCT7`o5c zlbeefVgnr+bU7Kcz#<|Y92FcH97#-JY;GJ@Y(^|9?3`RssR?WiYz1to3^6QzYz{1D zOxmnqDFG)(ZY2jf2Pp@M1EL265AYq}Il$?_z{$X^wUI&6$&s7E0Rw_0cQPFfg(>bR>s>RWi6lf`Efa7h4ru r7F!Bq1d9io4T}kr8Y?HC)(*!1ts7W+H!?A}Y-UYiV{ma{U|;|M6KIBE literal 0 HcmV?d00001 diff --git a/tests/FontAdvanceCache/main.cpp b/tests/FontAdvanceCache/main.cpp new file mode 100644 index 0000000..d123790 --- /dev/null +++ b/tests/FontAdvanceCache/main.cpp @@ -0,0 +1,144 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Issue #57: Font::GetLineWidth used to call stbtt_GetCodepointHMetrics for +// every glyph on every call. Caret hit-testing (InputField_HitTestCursor) +// rescans the whole field once per character, so that is O(n²) HMetrics calls. +// Advances are now memoised per Font in *font units* (Font::AdvanceUnits): +// ASCII in a flat array, the rest in a map, rescaled per call. +// +// The cache must be in unscaled units, NOT pixels — the FontAtlas's +// Glyph::advance is baked at kBaseSize and is wrong at any other size, so it +// cannot be reused here. What is asserted: +// +// - AdvanceUnits is size-independent: the value for a codepoint is identical +// no matter which size was queried first (proves it stores units, not px). +// - GetLineWidth equals the documented pipeline: sum of per-glyph +// (int)(units * scaleForSize), with the truncation applied PER GLYPH. +// - Repeated calls (cache hits) are byte-identical to the first (miss). +// - Width scales with size — a 2× size is wider, ruling out a baked-at-one- +// size advance that would make every size render the same width. +// - The empty string is zero. +// - Codepoints above ASCII (the map path) behave like the array path. +// +// Pure CPU: Font only touches stb_truetype, so no Vulkan device is needed. + +#include + +import Crafter.Graphics; +import std; + +using namespace Crafter; + +namespace { + +int failures = 0; + +void Check(bool ok, std::string_view what) { + std::println("{} {}", ok ? "PASS" : "FAIL", what); + if (!ok) ++failures; +} + +std::filesystem::path FindFont() { + for (const char* cand : { + "font.ttf", + "tests/FontAdvanceCache/font.ttf", + "../../examples/HelloUI/font.ttf" }) { + if (std::filesystem::exists(cand)) return cand; + } + return "font.ttf"; +} + +// The exact contract GetLineWidth must honour: truncate each glyph's scaled +// advance to int independently, then accumulate. Rescaling the cached unit +// advance per call is what keeps it correct at sizes other than kBaseSize. +std::uint32_t ExpectedWidth(Font& font, std::string_view text, float size) { + float scale = font.ScaleForSize(size); + std::uint32_t w = 0; + std::size_t i = 0; + while (i < text.size()) { + std::uint32_t cp = DecodeUtf8(text, i); + if (cp == 0) break; + w += static_cast(font.AdvanceUnits(cp) * scale); + } + return w; +} + +} // namespace + +int main() { + Font font(FindFont()); + + const std::string_view text = "Hello, World!"; + + // ── 1. AdvanceUnits is size-independent (stored in font units). ───────── + // Prime the cache via a large size, capture the unit advance, then prime + // again via a tiny size. The unit value must not move — if the cache stored + // pixels it would differ wildly between the two priming sizes. + font.GetLineWidth(text, 96.0f); + std::int32_t aBig = font.AdvanceUnits(static_cast('A')); + font.GetLineWidth(text, 6.0f); + std::int32_t aSmall = font.AdvanceUnits(static_cast('A')); + Check(aBig == aSmall && aBig > 0, + "AdvanceUnits('A') is a stable, positive font-unit value across sizes"); + + // ── 2. GetLineWidth matches the per-glyph-truncated, rescaled pipeline. ─ + bool pipelineOk = true; + for (float size : {6.0f, 11.0f, 18.0f, 32.0f, 64.0f, 100.0f}) { + std::uint32_t got = font.GetLineWidth(text, size); + std::uint32_t exp = ExpectedWidth(font, text, size); + if (got != exp) { + pipelineOk = false; + std::println(" size {}: got {} expected {}", size, got, exp); + } + } + Check(pipelineOk, "GetLineWidth == sum of per-glyph (int)(units * scale) at every size"); + + // ── 3. Cache hits are byte-identical to the first (miss) call. ────────── + std::uint32_t first = font.GetLineWidth(text, 18.0f); + bool stable = true; + for (int k = 0; k < 8; ++k) stable = stable && (font.GetLineWidth(text, 18.0f) == first); + Check(stable && first > 0, "repeated GetLineWidth calls are identical (cache hit == miss)"); + + // ── 4. Width scales with size — not baked at a single size. ───────────── + std::uint32_t w18 = font.GetLineWidth(text, 18.0f); + std::uint32_t w36 = font.GetLineWidth(text, 36.0f); + Check(w36 > w18 && w36 > w18 + (w18 / 2), + "doubling the size roughly doubles the width (advance is rescaled, not baked)"); + + // ── 5. Empty string is zero. ──────────────────────────────────────────── + Check(font.GetLineWidth("", 18.0f) == 0, "empty string has zero width"); + + // ── 6. Non-ASCII codepoints (the map path) behave like the array path. ── + // U+00E9 'é' (UTF-8 0xC3 0xA9) is > 127 so it lands in advanceUnits_, not + // the flat ASCII array. Its advance must be stable and a prefix narrower + // than the whole. + const std::string_view accented = "café"; // 'é' is two UTF-8 bytes + std::uint32_t accWidth = font.GetLineWidth(accented, 24.0f); + std::uint32_t accExp = ExpectedWidth(font, accented, 24.0f); + Check(accWidth == accExp && accWidth > font.GetLineWidth("caf", 24.0f), + "non-ASCII codepoint goes through the map path and widens the line"); + std::uint32_t eFirst = static_cast(font.AdvanceUnits(0x00E9)); + Check(static_cast(font.AdvanceUnits(0x00E9)) == eFirst, + "AdvanceUnits for a mapped codepoint is stable across calls"); + + if (failures == 0) std::println("\nAll font advance-cache checks passed."); + else std::println("\n{} font advance-cache check(s) FAILED.", failures); + return failures == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} From 574242dd30a2a949dd5c9d4d3a7595064d34a713 Mon Sep 17 00:00:00 2001 From: catbot Date: Tue, 16 Jun 2026 17:03:24 +0000 Subject: [PATCH 03/47] perf(ui): add additive DispatchFused to collapse consecutive UI passes (#47) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a fifth UI compute path — DispatchFused — alongside the four per-element Dispatch* calls. It composites quads → circles → images → text (canonical back-to-front order) in ONE dispatch that loads the destination image once and stores once, eliminating the per-pass load+store and the inter-pass VkMemoryBarriers a run of consecutive Dispatch* calls would pay. The win materialises at >= 2 non-empty categories; an absent category is a zero-trip, push-constant-uniform loop (~free). The new uber-kernel (shaders/ui-fused.comp.glsl) reuses each category's exact cooperative shared-memory tile-cull + per-pixel accumulate, so a fused category is pixel-identical to its standalone pass. Categories run sequentially per thread and share one set of shared-memory scratch, so the VGPR/shared high-water mark stays at the per-category level, not the sum. Additive and non-breaking: the per-element Dispatch* calls and the frozen 48-byte UIDispatchHeader are untouched. DispatchFused gets its own 128-byte push-constant layout (UIFusedHeader: four uvec4 headers + four per-category clip vec4s). To interleave a custom ui.Dispatch() between standard passes, bracket it with two DispatchFused calls — the dispatch boundary is the explicit, app-declared flush point. WebGPU (Vulkan-second): DispatchFused falls back to the per-element Dispatch* calls in canonical order — one texture/sampler per dispatch there can't feed the bindless image phase — so the result matches; only the load/store fusion is Vulkan-only. HelloUI now composites its background quads, mouse circle, and label text in a single DispatchFused. Verified rendering identical on a live Vulkan/Wayland session. Resolves #47 Co-Authored-By: Claude Opus 4.8 --- examples/HelloUI/main.cpp | 17 +- .../Crafter.Graphics-UI-WebGPU.cpp | 22 +- implementations/Crafter.Graphics-UI.cpp | 61 +++- interfaces/Crafter.Graphics-UI.cppm | 63 +++- project.cpp | 30 ++ shaders/ui-fused.comp.glsl | 319 ++++++++++++++++++ shaders/ui-shared.glsl | 13 + tests/UIFusedShader/main.cpp | 203 +++++++++++ 8 files changed, 720 insertions(+), 8 deletions(-) create mode 100644 shaders/ui-fused.comp.glsl create mode 100644 tests/UIFusedShader/main.cpp diff --git a/examples/HelloUI/main.cpp b/examples/HelloUI/main.cpp index 1a73824..48948f8 100644 --- a/examples/HelloUI/main.cpp +++ b/examples/HelloUI/main.cpp @@ -170,14 +170,20 @@ int main() { 0, 0, 0, 0, }; - // Flush + dispatch. The library inserts the inter-dispatch barriers. + // Flush the buffers the GPU is about to read, then composite all three + // categories — background quads, the mouse circle, and the button/label + // text — in ONE fused dispatch (issue #47). The frame runs these passes + // back-to-back with no custom shader interleaved, so DispatchFused loads + // and stores the swapchain image once instead of three times and skips + // the two inter-pass barriers DispatchQuads→Circles→Text would insert. + // Canonical order is quads → circles → images → text; here there are no + // images, so that category is a free no-op. if (qc > 0) { #ifndef CRAFTER_GRAPHICS_WINDOW_DOM quadsBuf.FlushDevice(cmd, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); #else quadsBuf.FlushDevice(); #endif - ui.DispatchQuads(cmd, quadsSlot, qc); } if (cc > 0) { #ifndef CRAFTER_GRAPHICS_WINDOW_DOM @@ -185,7 +191,6 @@ int main() { #else circlesBuf.FlushDevice(); #endif - ui.DispatchCircles(cmd, circlesSlot, cc); } if (gc > 0) { #ifndef CRAFTER_GRAPHICS_WINDOW_DOM @@ -193,8 +198,12 @@ int main() { #else glyphsBuf.FlushDevice(); #endif - ui.DispatchText(cmd, glyphsSlot, gc); } + ui.DispatchFused(cmd, + {quadsSlot, qc}, + {circlesSlot, cc}, + {}, // no images this frame + {glyphsSlot, gc}); }); window.FinishInit(); diff --git a/implementations/Crafter.Graphics-UI-WebGPU.cpp b/implementations/Crafter.Graphics-UI-WebGPU.cpp index ef3d90e..e7cac71 100644 --- a/implementations/Crafter.Graphics-UI-WebGPU.cpp +++ b/implementations/Crafter.Graphics-UI-WebGPU.cpp @@ -26,7 +26,8 @@ void UIRenderer::Initialize(Window& window, GraphicsDescriptorHeap& heap, Graphi std::filesystem::path /*quadsSpv*/, std::filesystem::path /*circlesSpv*/, std::filesystem::path /*imagesSpv*/, - std::filesystem::path /*textSpv*/) { + std::filesystem::path /*textSpv*/, + std::filesystem::path /*fusedSpv*/) { (void)initCmd; window_ = &window; heap_ = &heap; @@ -144,6 +145,25 @@ void UIRenderer::DispatchText(GraphicsCommandBuffer /*cmd*/, std::uint32_t buffe texHandle, sampHandle); } +void UIRenderer::DispatchFused(GraphicsCommandBuffer cmd, + const FusedBatch& quads, + const FusedBatch& circles, + const FusedBatch& images, + const FusedBatch& text) { + // WebGPU is Vulkan-second here (issue #47): one bind group can carry only + // one texture/sampler per dispatch, so the single-load/store uber-kernel + // can't bind per-item image textures the way Vulkan bindless does. Fall + // back to the per-element Dispatch* calls in the same canonical order + // (quads → circles → images → text), which produces the identical result — + // it just pays the per-pass load/store + barriers the Vulkan path fuses + // away. The common quads+circles+text screen still works perfectly; only + // the image category leans on DispatchImages' font-atlas fallback. + DispatchQuads(cmd, quads.bufferSlot, quads.itemCount, quads.clipRectPx); + DispatchCircles(cmd, circles.bufferSlot, circles.itemCount, circles.clipRectPx); + DispatchImages(cmd, images.bufferSlot, images.itemCount, images.clipRectPx); + DispatchText(cmd, text.bufferSlot, text.itemCount, text.clipRectPx); +} + SamplerSlot UIRenderer::RegisterLinearClampSampler() { auto range = heap_->AllocateSamplerSlots(1); heap_->samplerTable[range.firstElement] = WebGPU::wgpuCreateLinearClampSampler(); diff --git a/implementations/Crafter.Graphics-UI.cpp b/implementations/Crafter.Graphics-UI.cpp index e8ce871..3ca9ff5 100644 --- a/implementations/Crafter.Graphics-UI.cpp +++ b/implementations/Crafter.Graphics-UI.cpp @@ -39,15 +39,17 @@ void UIRenderer::Initialize(Window& window, GraphicsDescriptorHeap& heap, Graphi std::filesystem::path quadsSpv, std::filesystem::path circlesSpv, std::filesystem::path imagesSpv, - std::filesystem::path textSpv) { + std::filesystem::path textSpv, + std::filesystem::path fusedSpv) { window_ = &window; heap_ = &heap; - // Load the four standard pipelines. + // Load the four standard pipelines plus the fused uber-kernel (issue #47). drawQuads.Load(quadsSpv); drawCircles.Load(circlesSpv); drawImages.Load(imagesSpv); drawText.Load(textSpv); + drawFused.Load(fusedSpv); // Allocate one image slot for the swapchain output. Each per-frame heap // copy will hold ITS frame's image at this slot. @@ -176,6 +178,61 @@ void UIRenderer::DispatchText(GraphicsCommandBuffer cmd, std::uint32_t bufferSlo TilesFor(window_->width), TilesFor(window_->height), 1u); } +// ─── fused dispatch (issue #47) ───────────────────────────────────────── +// +// One uber-kernel composites quads → circles → images → text into the output +// image with a single load and a single store. Routed through the generic +// Dispatch() so it still gets the standard write-after-write barrier when it +// is not the first dispatch of the frame, and a following dispatch barriers +// against it — the only barriers it removes are the INTRA-fuse ones a run of +// Dispatch* calls would have inserted between its categories. + +void UIRenderer::DispatchFused(GraphicsCommandBuffer cmd, + const FusedBatch& quads, + const FusedBatch& circles, + const FusedBatch& images, + const FusedBatch& text) { + // Nothing to draw → no dispatch (and, crucially, no barrier bump). + if (quads.itemCount == 0 && circles.itemCount == 0 && + images.itemCount == 0 && text.itemCount == 0) { + return; + } + + if (text.itemCount != 0 && !fontAtlasImageSlot_) { + throw std::runtime_error("UIRenderer::DispatchFused: text batch given but no FontAtlas registered (set fontAtlas before Initialize)"); + } + // Flush any glyphs ShapeText rasterised during this onBuild, mirroring + // DispatchText — only relevant when there is text to draw. + if (text.itemCount != 0 && fontAtlas != nullptr && fontAtlas->dirty) { + fontAtlas->Update(cmd); + } + + UIFusedHeader pc{}; + pc.itemBuffers[0] = quads.bufferSlot; + pc.itemBuffers[1] = circles.bufferSlot; + pc.itemBuffers[2] = images.bufferSlot; + pc.itemBuffers[3] = text.bufferSlot; + pc.itemCounts[0] = quads.itemCount; + pc.itemCounts[1] = circles.itemCount; + pc.itemCounts[2] = images.itemCount; + pc.itemCounts[3] = text.itemCount; + pc.outImage = outImageSlot_; + pc.fontTexture = fontAtlasImageSlot_; + pc.fontSampler = fontAtlasSamplerSlot_; + pc.flags = 0; + pc.surfaceWidth = window_->width; + pc.surfaceHeight = window_->height; + pc.frameIdx = window_->currentBuffer; + pc._pad = 0; + std::copy(quads.clipRectPx.begin(), quads.clipRectPx.end(), pc.clipQuads); + std::copy(circles.clipRectPx.begin(), circles.clipRectPx.end(), pc.clipCircles); + std::copy(images.clipRectPx.begin(), images.clipRectPx.end(), pc.clipImages); + std::copy(text.clipRectPx.begin(), text.clipRectPx.end(), pc.clipText); + + Dispatch(cmd, drawFused, &pc, sizeof(pc), + TilesFor(window_->width), TilesFor(window_->height), 1u); +} + // ─── generic Dispatch (with barrier) ──────────────────────────────────── void UIRenderer::Dispatch(GraphicsCommandBuffer cmd, const GraphicsComputeShader& shader, diff --git a/interfaces/Crafter.Graphics-UI.cppm b/interfaces/Crafter.Graphics-UI.cppm index b5760f9..a0d904d 100644 --- a/interfaces/Crafter.Graphics-UI.cppm +++ b/interfaces/Crafter.Graphics-UI.cppm @@ -59,6 +59,39 @@ export namespace Crafter { }; static_assert(sizeof(UIDispatchHeader) == 48); + // ─── fused-dispatch push-constant header (issue #47) ──────────────── + // Mirrors the PC block in shaders/ui-fused.comp.glsl byte-for-byte: every + // member is vec4-aligned, no padding holes, exactly 128 bytes (the + // guaranteed push-constant minimum). This is a SEPARATE contract — the + // per-element Dispatch* path and the frozen 48-byte UIDispatchHeader are + // untouched. DispatchFused builds this; users never fill it directly. + struct UIFusedHeader { + std::uint32_t itemBuffers[4]; // heap slots: quads, circles, images, text + std::uint32_t itemCounts[4]; // item counts: quads, circles, images, text + std::uint32_t outImage; // swapchain image heap slot + std::uint32_t fontTexture; // font-atlas image slot (text phase) + std::uint32_t fontSampler; // font-atlas sampler slot (text phase) + std::uint32_t flags; // reserved — keep zeroed + std::uint32_t surfaceWidth; + std::uint32_t surfaceHeight; + std::uint32_t frameIdx; + std::uint32_t _pad; // reserved — keep zeroed + float clipQuads[4]; // (x, y, w, h) clip per category + float clipCircles[4]; + float clipImages[4]; + float clipText[4]; + }; + static_assert(sizeof(UIFusedHeader) == 128); + + // One category's input to UIRenderer::DispatchFused. A zero itemCount + // makes that category a free no-op (its kernel loop is zero-trip). + // clipRectPx defaults to "no clip", matching the Dispatch* default. + struct FusedBatch { + std::uint32_t bufferSlot = 0; + std::uint32_t itemCount = 0; + std::array clipRectPx = {0.0f, 0.0f, 1e9f, 1e9f}; + }; + // ─── standard item PODs (match GLSL std430) ───────────────────────── struct QuadItem { float x, y, w, h; @@ -143,6 +176,7 @@ export namespace Crafter { GraphicsComputeShader drawCircles; GraphicsComputeShader drawImages; GraphicsComputeShader drawText; + GraphicsComputeShader drawFused; FontAtlas* fontAtlas = nullptr; @@ -156,7 +190,8 @@ export namespace Crafter { std::filesystem::path quadsSpv = "ui-quads.comp.spv", std::filesystem::path circlesSpv = "ui-circles.comp.spv", std::filesystem::path imagesSpv = "ui-images.comp.spv", - std::filesystem::path textSpv = "ui-text.comp.spv"); + std::filesystem::path textSpv = "ui-text.comp.spv", + std::filesystem::path fusedSpv = "ui-fused.comp.spv"); void Record(GraphicsCommandBuffer cmd, std::uint32_t frameIdx, Window& window) override; @@ -186,6 +221,32 @@ export namespace Crafter { void DispatchText(GraphicsCommandBuffer cmd, std::uint32_t bufferSlot, std::uint32_t itemCount, std::array clipRectPx = {0.0f, 0.0f, 1e9f, 1e9f}); + // Fused UI dispatch (issue #47). Composites up to four standard + // categories — quads → circles → images → text, canonical + // back-to-front order — in ONE compute dispatch that loads the + // destination image once and stores once, eliminating the per-pass + // load+store and the inter-pass memory barriers a run of consecutive + // Dispatch* calls would pay. The win materialises at >= 2 non-empty + // categories; an empty category (itemCount 0) is a free no-op. + // + // Additive: the per-element Dispatch* calls and UIDispatchHeader are + // untouched. Keep using those for single-category or fine-grained + // work. To interleave a custom ui.Dispatch() between standard passes, + // bracket it with two DispatchFused calls — the dispatch boundary is + // the explicit, app-declared flush point. + // + // The text category uses the registered font atlas (set fontAtlas + // before Initialize); passing a non-empty text batch without one + // throws, exactly like DispatchText. On the WebGPU backend this falls + // back to issuing the per-element Dispatch* calls in canonical order + // (one texture/sampler per dispatch there — see DispatchImages), + // so the result matches; the load/store fusion is Vulkan-only. + void DispatchFused(GraphicsCommandBuffer cmd, + const FusedBatch& quads, + const FusedBatch& circles, + const FusedBatch& images, + const FusedBatch& text); + // Generic dispatch for user-authored shaders. On Vulkan, `shader` is // a SPIR-V compute pipeline (bindless via VK_EXT_descriptor_heap, so // any resource indices baked into push data resolve through the diff --git a/project.cpp b/project.cpp index d678456..fe5f67e 100644 --- a/project.cpp +++ b/project.cpp @@ -204,6 +204,7 @@ extern "C" Configuration CrafterBuildProject(std::span a cfg.shaders.emplace_back(fs::path("shaders/ui-circles.comp.glsl"), std::string("main"), ShaderType::Compute); cfg.shaders.emplace_back(fs::path("shaders/ui-images.comp.glsl"), std::string("main"), ShaderType::Compute); cfg.shaders.emplace_back(fs::path("shaders/ui-text.comp.glsl"), std::string("main"), ShaderType::Compute); + cfg.shaders.emplace_back(fs::path("shaders/ui-fused.comp.glsl"), std::string("main"), ShaderType::Compute); cfg.buildFiles.emplace_back(fs::path("shaders/ui-shared.glsl")); // Regression test for issue #18: drive the NVIDIA descriptor-heap @@ -413,6 +414,35 @@ extern "C" Configuration CrafterBuildProject(std::span a memImpls.emplace_back("tests/MemoryTypeFallback/main"); mc.GetInterfacesAndImplementations(ifaces, memImpls); cfg.tests.push_back(std::move(memTest)); + + // Issue #47: the fused UI uber-kernel (shaders/ui-fused.comp.glsl) and + // its C++ push-constant mirror UIFusedHeader. Compiles the real shader + // with glslang, validates with spirv-val, and pins the push-constant + // member offsets to UIFusedHeader's layout so a GLSL/C++ drift can't + // slip through (the C++ static_assert only guards the C++ side). No GPU + // device at runtime, but glslang + spirv-val are required tools. + Test fusedTest; + Configuration& fc = fusedTest.config; + fc.path = cfg.path; + fc.name = "UIFusedShader"; + fc.outputName = "UIFusedShader"; + fc.type = ConfigurationType::Executable; + fc.target = cfg.target; + fc.march = cfg.march; + fc.mtune = cfg.mtune; + fc.debug = cfg.debug; + fc.sysroot = cfg.sysroot; + fc.dependencies = cfg.dependencies; + fc.externalDependencies = cfg.externalDependencies; + fc.compileFlags = cfg.compileFlags; + fc.linkFlags = cfg.linkFlags; + fc.defines = cfg.defines; + fc.cFiles = cfg.cFiles; + std::vector fusedImpls(impls.begin(), impls.end()); + fusedImpls.emplace_back("tests/UIFusedShader/main"); + fc.GetInterfacesAndImplementations(ifaces, fusedImpls); + fusedTest.requires_ = { "tool:glslang", "tool:spirv-val" }; + cfg.tests.push_back(std::move(fusedTest)); } return cfg; diff --git a/shaders/ui-fused.comp.glsl b/shaders/ui-fused.comp.glsl new file mode 100644 index 0000000..f43bbf2 --- /dev/null +++ b/shaders/ui-fused.comp.glsl @@ -0,0 +1,319 @@ +#version 460 +#extension GL_GOOGLE_include_directive : enable +#include "ui-shared.glsl" + +// ─── fused UI uber-kernel (issue #47) ──────────────────────────────────── +// One dispatch that composites up to four standard categories in canonical +// back-to-front order — quads → circles → images → text — into a single +// per-pixel register, loading the destination image ONCE and storing it ONCE. +// A run of consecutive Dispatch* calls would instead load+store the image per +// category and drain compute with a VkMemoryBarrier between each; this kernel +// collapses all of that to 1 load + 1 store + 0 inter-pass barriers. +// +// Each category keeps the exact cooperative shared-memory tile-cull + per-pixel +// accumulate of its standalone shader (see ui-quads/circles/images/text), so a +// fused category is pixel-identical to its standalone Dispatch* pass (the only +// difference is that intermediate results stay in full float precision between +// categories instead of round-tripping through the storage image — strictly +// more accurate). Categories run sequentially within each thread, so the VGPR +// high-water mark is ~max(per-category), not the sum. +// +// An absent category (itemCount 0) is a zero-trip, push-constant-uniform loop: +// no divergence, no memory traffic — using DispatchFused for only quads+text +// costs ~nothing for the unused circle/image phases. +// +// This is ADDITIVE: it has its own push-constant layout and does NOT touch the +// frozen 48-byte UIDispatchHeader or the per-element Dispatch* contract. + +// Push-constant block. Mirrors Crafter::UIFusedHeader byte-for-byte: every +// member is vec4-aligned, no padding holes, exactly 128 bytes (the guaranteed +// push-constant minimum). +layout(push_constant) uniform PC { + uvec4 itemBuffers; // heap slots: (quads, circles, images, text) + uvec4 itemCounts; // item counts: (quads, circles, images, text) + uvec4 misc; // (outImage, fontTexture, fontSampler, flags) + uvec4 surface; // (surfaceWidth, surfaceHeight, frameIdx, _pad) + vec4 clipQuads; + vec4 clipCircles; + vec4 clipImages; + vec4 clipText; +} pc; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +// SDF tuning for the text phase — must match Crafter::FontAtlas constants +// (same values as ui-text.comp.glsl). +const float ON_EDGE = 128.0 / 255.0; +const float DIST_SCALE = 32.0; + +// Generic per-chunk cooperative-cull scratch, REUSED across the four phases +// (they composite sequentially with a barrier between, so the storage is free +// once a phase's last read completes). Keeping one shared set instead of four +// keeps the shared-memory footprint — and thus occupancy — at the per-category +// level. Member mapping per phase: +// quads: v0=rect v1=color v2=corners v3=outline +// circles: v0=centerRadius v1=color v2=outline +// images: v0=rect v1=uv v2=tint v4=slots +// text: v0=rect v1=uv v2=color +shared vec4 s_v0[UI_CHUNK]; +shared vec4 s_v1[UI_CHUNK]; +shared vec4 s_v2[UI_CHUNK]; +shared vec4 s_v3[UI_CHUNK]; +shared uvec4 s_v4[UI_CHUNK]; +shared uint s_keep[UI_CHUNK]; +shared uint s_order[UI_CHUNK]; +shared uint s_count; + +// Stable in-order compaction of one chunk's survivors. Lane 0 scans s_keep in +// buffer order so the inner per-pixel loop still sees items in draw order. +void uiCompactChunk(uint base, uint count) { + if (gl_LocalInvocationIndex == 0u) { + uint n = 0u; + uint lim = min(UI_CHUNK, count - base); + for (uint k = 0u; k < lim; ++k) + if (s_keep[k] != 0u) s_order[n++] = k; + s_count = n; + } +} + +void main() { + // NOTE: do not early-return — every thread must reach the barriers below. + uvec2 pxu = gl_GlobalInvocationID.xy; + bool inSurface = pxu.x < pc.surface.x && pxu.y < pc.surface.y; + ivec2 screenPx = ivec2(pxu); + vec2 sp = vec2(screenPx) + 0.5; + + // Single load of the destination for the whole surface. + vec4 dst = vec4(0.0); + if (inSurface) dst = imageLoad(uiImages[pc.misc.x], screenPx); + + vec2 tileMin, tileMax; + uiTileBounds(tileMin, tileMax); + uint lid = gl_LocalInvocationIndex; + + // ─── QUADS ──────────────────────────────────────────────────────────── + { + uint heap = pc.itemBuffers.x; + uint count = pc.itemCounts.x; + bool inClip = inSurface && uiPixelInClipRect(pxu, pc.clipQuads); + for (uint base = 0u; base < count; base += UI_CHUNK) { + uint idx = base + lid; + bool keep = false; + if (idx < count) { + s_v0[lid] = uiQuadHeap[heap].items[idx].rect; + s_v1[lid] = uiQuadHeap[heap].items[idx].color; + s_v2[lid] = uiQuadHeap[heap].items[idx].corners; + s_v3[lid] = uiQuadHeap[heap].items[idx].outline; + keep = uiAabbOverlapsTile(s_v0[lid].xy, s_v0[lid].xy + s_v0[lid].zw, + tileMin, tileMax); + } + s_keep[lid] = keep ? 1u : 0u; + barrier(); + uiCompactChunk(base, count); + barrier(); + + if (inClip) { + for (uint j = 0u; j < s_count; ++j) { + uint c = s_order[j]; + + vec2 lo = s_v0[c].xy; + vec2 hi = s_v0[c].xy + s_v0[c].zw; + if (sp.x < lo.x || sp.y < lo.y) continue; + if (sp.x >= hi.x || sp.y >= hi.y) continue; + + vec2 halfSize = s_v0[c].zw * 0.5; + vec2 p = sp - (s_v0[c].xy + halfSize); + float d = uiSdRoundRect(p, halfSize, s_v2[c]); + + vec4 outline = s_v3[c]; + float bodyA = clamp(0.5 - d, 0.0, 1.0); + if (bodyA <= 0.0 && outline.x <= 0.0) continue; + + vec4 col = s_v1[c]; + vec4 src = vec4(col.rgb, col.a * bodyA); + + if (outline.x > 0.0) { + float t = abs(d + outline.x * 0.5) - outline.x * 0.5; + float outlineA = clamp(0.5 - t, 0.0, 1.0); + src.rgb = mix(src.rgb, outline.yzw, outlineA); + src.a = max(src.a, outlineA); + } + + if (src.a <= 0.0) continue; + dst = uiBlendOver(dst, src); + } + } + barrier(); // done reading shared for this chunk before reuse + } + } + + // ─── CIRCLES ────────────────────────────────────────────────────────── + { + uint heap = pc.itemBuffers.y; + uint count = pc.itemCounts.y; + bool inClip = inSurface && uiPixelInClipRect(pxu, pc.clipCircles); + for (uint base = 0u; base < count; base += UI_CHUNK) { + uint idx = base + lid; + bool keep = false; + if (idx < count) { + s_v0[lid] = uiCircleHeap[heap].items[idx].centerRadius; + s_v1[lid] = uiCircleHeap[heap].items[idx].color; + s_v2[lid] = uiCircleHeap[heap].items[idx].outline; + float radius = s_v0[lid].z; + if (radius > 0.0) { + vec2 cen = s_v0[lid].xy; + vec2 r = vec2(radius + 1.0); + keep = uiAabbOverlapsTile(cen - r, cen + r, tileMin, tileMax); + } + } + s_keep[lid] = keep ? 1u : 0u; + barrier(); + uiCompactChunk(base, count); + barrier(); + + if (inClip) { + for (uint j = 0u; j < s_count; ++j) { + uint c = s_order[j]; + + vec2 center = s_v0[c].xy; + float radius = s_v0[c].z; + if (radius <= 0.0) continue; + + if (abs(sp.x - center.x) > radius + 1.0) continue; + if (abs(sp.y - center.y) > radius + 1.0) continue; + + float d = length(sp - center) - radius; + + vec4 outline = s_v2[c]; + float bodyA = clamp(0.5 - d, 0.0, 1.0); + if (bodyA <= 0.0 && outline.x <= 0.0) continue; + + vec4 col = s_v1[c]; + vec4 src = vec4(col.rgb, col.a * bodyA); + + if (outline.x > 0.0) { + float t = abs(d + outline.x * 0.5) - outline.x * 0.5; + float outlineA = clamp(0.5 - t, 0.0, 1.0); + src.rgb = mix(src.rgb, outline.yzw, outlineA); + src.a = max(src.a, outlineA); + } + + if (src.a <= 0.0) continue; + dst = uiBlendOver(dst, src); + } + } + barrier(); + } + } + + // ─── IMAGES ─────────────────────────────────────────────────────────── + { + uint heap = pc.itemBuffers.z; + uint count = pc.itemCounts.z; + bool inClip = inSurface && uiPixelInClipRect(pxu, pc.clipImages); + for (uint base = 0u; base < count; base += UI_CHUNK) { + uint idx = base + lid; + bool keep = false; + if (idx < count) { + s_v0[lid] = uiImageHeap[heap].items[idx].rect; + s_v1[lid] = uiImageHeap[heap].items[idx].uv; + s_v2[lid] = uiImageHeap[heap].items[idx].tint; + s_v4[lid] = uiImageHeap[heap].items[idx].slots; + keep = uiAabbOverlapsTile(s_v0[lid].xy, s_v0[lid].xy + s_v0[lid].zw, + tileMin, tileMax); + } + s_keep[lid] = keep ? 1u : 0u; + barrier(); + uiCompactChunk(base, count); + barrier(); + + if (inClip) { + for (uint j = 0u; j < s_count; ++j) { + uint c = s_order[j]; + + vec2 lo = s_v0[c].xy; + vec2 hi = s_v0[c].xy + s_v0[c].zw; + if (sp.x < lo.x || sp.y < lo.y) continue; + if (sp.x >= hi.x || sp.y >= hi.y) continue; + + vec2 t = (sp - s_v0[c].xy) / s_v0[c].zw; + vec2 uv = mix(s_v1[c].xy, s_v1[c].zw, t); + + uint texSlot = s_v4[c].x; + uint sampSlot = s_v4[c].y; + + vec4 sampled = texture( + sampler2D(uiTextures[nonuniformEXT(texSlot)], + uiSamplers[nonuniformEXT(sampSlot)]), + uv + ); + vec4 src = sampled * s_v2[c]; + if (src.a <= 0.0) continue; + dst = uiBlendOver(dst, src); + } + } + barrier(); + } + } + + // ─── TEXT ───────────────────────────────────────────────────────────── + { + uint heap = pc.itemBuffers.w; + uint count = pc.itemCounts.w; + bool inClip = inSurface && uiPixelInClipRect(pxu, pc.clipText); + for (uint base = 0u; base < count; base += UI_CHUNK) { + uint idx = base + lid; + bool keep = false; + if (idx < count) { + s_v0[lid] = uiGlyphHeap[heap].items[idx].rect; + s_v1[lid] = uiGlyphHeap[heap].items[idx].uv; + s_v2[lid] = uiGlyphHeap[heap].items[idx].color; + keep = uiAabbOverlapsTile(s_v0[lid].xy, s_v0[lid].xy + s_v0[lid].zw, + tileMin, tileMax); + } + s_keep[lid] = keep ? 1u : 0u; + barrier(); + uiCompactChunk(base, count); + barrier(); + + if (inClip) { + for (uint j = 0u; j < s_count; ++j) { + uint c = s_order[j]; + + vec2 lo = s_v0[c].xy; + vec2 hi = s_v0[c].xy + s_v0[c].zw; + if (sp.x < lo.x || sp.y < lo.y) continue; + if (sp.x >= hi.x || sp.y >= hi.y) continue; + + vec2 t = (sp - s_v0[c].xy) / s_v0[c].zw; + vec2 uv = mix(s_v1[c].xy, s_v1[c].zw, t); + + // Font slots are push constants — provably dynamically uniform, + // so no nonuniformEXT (same as ui-text.comp.glsl). + float sdf = texture( + sampler2D(uiTextures[pc.misc.y], + uiSamplers[pc.misc.z]), + uv + ).r; + + float dAtlas = (ON_EDGE - sdf) * DIST_SCALE; + + vec2 uvSpan = s_v1[c].zw - s_v1[c].xy; + vec2 atlasPerScreen = (uvSpan * 1024.0) / s_v0[c].zw; + float scalePx = max(atlasPerScreen.x, atlasPerScreen.y); + float band = max(scalePx, 0.0001); + + float a = clamp(0.5 - dAtlas / band, 0.0, 1.0); + if (a <= 0.0) continue; + + vec4 col = s_v2[c]; + vec4 src = vec4(col.rgb, col.a * a); + dst = uiBlendOver(dst, src); + } + } + barrier(); + } + } + + if (inSurface) imageStore(uiImages[pc.misc.x], screenPx, dst); +} diff --git a/shaders/ui-shared.glsl b/shaders/ui-shared.glsl index 52e29b3..18c4c15 100644 --- a/shaders/ui-shared.glsl +++ b/shaders/ui-shared.glsl @@ -128,6 +128,19 @@ bool uiResolveScreenPixel(UIDispatchHeader hdr, out ivec2 screenPx) { return true; } +// Per-category clip test for the fused kernel (issue #47). The fused shader +// loads/stores the destination image once for the whole surface but each +// category carries its own clip rect, so the clip can only gate compositing — +// not the shared load/store. This mirrors the integer-pixel comparison +// uiResolveScreenPixel does (compare the pixel's top-left, not its center), so +// a fused category is pixel-identical to its standalone Dispatch* pass. +bool uiPixelInClipRect(uvec2 px, vec4 clip) { + if (float(px.x) < clip.x || float(px.y) < clip.y) return false; + if (float(px.x) >= clip.x + clip.z) return false; + if (float(px.y) >= clip.y + clip.w) return false; + return true; +} + // Non-premultiplied "src over dst" blend. Both operands and result are // straight-alpha vec4. Use this when iterating items in a loop with a local // accumulator. diff --git a/tests/UIFusedShader/main.cpp b/tests/UIFusedShader/main.cpp new file mode 100644 index 0000000..9bfa40d --- /dev/null +++ b/tests/UIFusedShader/main.cpp @@ -0,0 +1,203 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Regression test for issue #47: the fused UI uber-kernel +// (shaders/ui-fused.comp.glsl) and its C++ push-constant mirror +// (Crafter::UIFusedHeader). The fused kernel is large and hand-written, and +// its push-constant block must stay byte-for-byte identical to UIFusedHeader +// or DispatchFused silently corrupts every fused frame. This test: +// +// 1. compiles the real ui-fused.comp.glsl with glslang (same target-env the +// engine builds it with), proving the uber-kernel still compiles and its +// GL_GOOGLE_include of ui-shared.glsl resolves; +// 2. runs spirv-val over the result under the engine's block-layout flags; +// 3. asserts exactly one push-constant variable survives (a well-formed +// single block, like every other UI shader); +// 4. parses the push-constant struct's member Offset decorations and asserts +// they are exactly {0,16,32,48,64,80,96,112} — the layout of +// UIFusedHeader (four uvec4 headers at 0/16/32/48 then four vec4 clip +// rects at 64/80/96/112, 128 bytes total). Any add/remove/reorder of a +// member shifts these offsets and fails here, catching a GLSL/C++ drift +// that the C++-side static_assert(sizeof==128) alone cannot. +// +// glslang and spirv-val are invoked at runtime, so the test declares them as +// required tools (see project.cpp). It needs no GPU device. + +#include + +import Crafter.Graphics; // for the static_assert(sizeof(UIFusedHeader)==128) +import std; +using namespace Crafter; + +namespace { + +namespace fs = std::filesystem; + +int RunCommand(const std::string& cmd) { + int status = std::system(cmd.c_str()); + if (status == -1) return -1; + return (status & 0x7f) == 0 ? ((status >> 8) & 0xff) : 128 + (status & 0x7f); +} + +std::vector ReadSpirv(const fs::path& p) { + std::ifstream f(p, std::ios::binary | std::ios::ate); + if (!f) return {}; + std::streamsize size = f.tellg(); + f.seekg(0); + std::vector words(static_cast(size) / sizeof(std::uint32_t)); + f.read(reinterpret_cast(words.data()), size); + return words; +} + +// Count OpVariable instructions in the PushConstant storage class (SC == 9). +int CountPushConstantVariables(const std::vector& words) { + constexpr std::uint32_t OpVariable = 59; + constexpr std::uint32_t StorageClassPushConstant = 9; + int count = 0; + for (std::size_t i = 5; i < words.size();) { + std::uint32_t len = words[i] >> 16; + if (len == 0 || i + len > words.size()) break; + if ((words[i] & 0xFFFFu) == OpVariable && len >= 4 && words[i + 3] == StorageClassPushConstant) + ++count; + i += len; + } + return count; +} + +// The single PushConstant struct's member byte offsets, sorted ascending. +// Finds the OpTypePointer in the PushConstant storage class (SC == 9), takes +// the struct type it points at, and collects every Offset (decoration 35) +// declared on that struct via OpMemberDecorate. +std::vector PushConstantMemberOffsets(const std::vector& words) { + constexpr std::uint32_t OpTypePointer = 32; + constexpr std::uint32_t OpMemberDecorate = 72; + constexpr std::uint32_t StorageClassPushConstant = 9; + constexpr std::uint32_t DecorationOffset = 35; + + std::uint32_t structId = 0; + for (std::size_t i = 5; i < words.size();) { + std::uint32_t len = words[i] >> 16; + if (len == 0 || i + len > words.size()) break; + // OpTypePointer: [op][result id][storage class][pointee type id] + if ((words[i] & 0xFFFFu) == OpTypePointer && len >= 4 && + words[i + 2] == StorageClassPushConstant) { + structId = words[i + 3]; + break; + } + i += len; + } + if (structId == 0) return {}; + + std::vector offsets; + for (std::size_t i = 5; i < words.size();) { + std::uint32_t len = words[i] >> 16; + if (len == 0 || i + len > words.size()) break; + // OpMemberDecorate: [op][struct id][member][decoration][operands...] + if ((words[i] & 0xFFFFu) == OpMemberDecorate && len >= 5 && + words[i + 1] == structId && words[i + 3] == DecorationOffset) { + offsets.push_back(words[i + 4]); + } + i += len; + } + std::ranges::sort(offsets); + offsets.erase(std::ranges::unique(offsets).begin(), offsets.end()); + return offsets; +} + +// Locate shaders/ui-fused.comp.glsl whether the test runs from the build +// output dir or the project root (mirrors ShapeTextCache's font probing). +fs::path LocateShader() { + const std::array candidates = { + "shaders/ui-fused.comp.glsl", + "../shaders/ui-fused.comp.glsl", + "../../shaders/ui-fused.comp.glsl", + "../../../shaders/ui-fused.comp.glsl", + }; + for (const auto& c : candidates) { + std::error_code ec; + if (fs::exists(c, ec)) return c; + } + return {}; +} + +} // namespace + +int main() { + // Sanity: the C++ mirror is the size the shader layout assumes. + static_assert(sizeof(UIFusedHeader) == 128); + + const fs::path src = LocateShader(); + if (src.empty()) { + std::println(std::cerr, "could not locate shaders/ui-fused.comp.glsl from {}", + fs::current_path().string()); + return 1; + } + + const fs::path dir = fs::temp_directory_path() / "crafter-uifused-test"; + std::error_code ec; + fs::create_directories(dir, ec); + const fs::path spv = dir / "ui-fused.comp.spv"; + + // 1. Compile the real uber-kernel. GL_GOOGLE_include resolves + // ui-shared.glsl relative to the source directory. + std::string compile = "glslang --target-env vulkan1.4 -V -S comp \"" + + src.string() + "\" -o \"" + spv.string() + "\" > /dev/null"; + if (RunCommand(compile) != 0) { + std::println(std::cerr, "glslang failed to compile ui-fused.comp.glsl"); + return 1; + } + + std::vector words = ReadSpirv(spv); + if (words.size() < 5) { + std::println(std::cerr, "could not read compiled SPIR-V"); + return 1; + } + + // 2. spirv-val under the engine's relaxed block-layout flags. + std::string validate = "spirv-val \"" + spv.string() + + "\" --relax-block-layout --scalar-block-layout --target-env vulkan1.4"; + if (RunCommand(validate) != 0) { + std::println(std::cerr, "spirv-val rejected ui-fused.comp.spv"); + return 1; + } + + // 3. Exactly one push-constant block. + int pcVars = CountPushConstantVariables(words); + if (pcVars != 1) { + std::println(std::cerr, "expected exactly 1 push-constant variable, found {}", pcVars); + return 1; + } + + // 4. Member offsets must match UIFusedHeader's layout exactly. + const std::vector expected = {0, 16, 32, 48, 64, 80, 96, 112}; + std::vector offsets = PushConstantMemberOffsets(words); + if (offsets != expected) { + std::print(std::cerr, "push-constant member offsets mismatch — expected {{"); + for (auto o : expected) std::print(std::cerr, "{} ", o); + std::print(std::cerr, "}}, got {{"); + for (auto o : offsets) std::print(std::cerr, "{} ", o); + std::println(std::cerr, "}}"); + return 1; + } + + std::println(std::cout, + "ui-fused shader ok (push-constant vars: {}, member offsets pinned to UIFusedHeader, 128 bytes)", + pcVars); + return 0; +} From bda21979fd8fc33a33472fd0c5ca0ca233baa47c Mon Sep 17 00:00:00 2001 From: catbot Date: Tue, 16 Jun 2026 17:04:26 +0000 Subject: [PATCH 04/47] perf(vulkan-buffer): skip flush/invalidate on coherent memory (#60) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FlushDevice/FlushHost called vkFlushMappedMemoryRanges / vkInvalidateMappedMemoryRanges unconditionally. Record the chosen memory type's propertyFlags at Create time (via the index GetMemoryType returns, not the requested flags) and early-return when HOST_COHERENT. The cmd overload still always emits its pipeline barrier — only the redundant host-side flush/invalidate is gated. Co-Authored-By: Claude Opus 4.8 --- interfaces/Crafter.Graphics-VulkanBuffer.cppm | 20 ++++- project.cpp | 28 ++++++ tests/VulkanBufferFlushGate/main.cpp | 89 +++++++++++++++++++ 3 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 tests/VulkanBufferFlushGate/main.cpp diff --git a/interfaces/Crafter.Graphics-VulkanBuffer.cppm b/interfaces/Crafter.Graphics-VulkanBuffer.cppm index c1d0c25..ce0d862 100644 --- a/interfaces/Crafter.Graphics-VulkanBuffer.cppm +++ b/interfaces/Crafter.Graphics-VulkanBuffer.cppm @@ -35,6 +35,11 @@ namespace Crafter { std::uint32_t size; VkBuffer buffer = VK_NULL_HANDLE; VkDeviceMemory memory; + // Property flags of the memory type actually chosen by GetMemoryType — + // not the requested flags. GetMemoryType may land a request without the + // COHERENT bit on a coherent type (and vice versa), so the flush/ + // invalidate paths gate on this recorded value, never on the request. + VkMemoryPropertyFlags memoryPropertyFlagsChosen = 0; }; export template @@ -79,10 +84,12 @@ namespace Crafter { VkMemoryRequirements memReqs; vkGetBufferMemoryRequirements(Device::device, buffer, &memReqs); + std::uint32_t memoryTypeIndex = Device::GetMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags, preferredPropertyFlags); + memoryPropertyFlagsChosen = Device::memoryProperties.memoryTypes[memoryTypeIndex].propertyFlags; VkMemoryAllocateInfo memAlloc { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = memReqs.size, - .memoryTypeIndex = Device::GetMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags, preferredPropertyFlags) + .memoryTypeIndex = memoryTypeIndex }; VkMemoryAllocateFlagsInfoKHR allocFlagsInfo { @@ -163,6 +170,11 @@ namespace Crafter { } void FlushDevice() requires(Mapped) { + // Coherent memory needs no explicit flush — host writes are + // automatically visible to the device. + if (memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) { + return; + } VkMappedMemoryRange range = { .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, .memory = memory, @@ -197,6 +209,11 @@ namespace Crafter { } void FlushHost() requires(Mapped) { + // Coherent memory needs no explicit invalidate — device writes are + // automatically visible to the host. + if (memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) { + return; + } VkMappedMemoryRange range = { .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, .memory = memory, @@ -210,6 +227,7 @@ namespace Crafter { buffer = other.buffer; memory = other.memory; size = other.size; + memoryPropertyFlagsChosen = other.memoryPropertyFlagsChosen; other.buffer = VK_NULL_HANDLE; address = other.address; if constexpr(Mapped) { diff --git a/project.cpp b/project.cpp index ec2748f..c086c3e 100644 --- a/project.cpp +++ b/project.cpp @@ -414,6 +414,34 @@ extern "C" Configuration CrafterBuildProject(std::span a mc.GetInterfacesAndImplementations(ifaces, memImpls); cfg.tests.push_back(std::move(memTest)); + // Issue #60: VulkanBuffer::FlushDevice / FlushHost now record the chosen + // memory type's propertyFlags at Create time and skip the + // flush/invalidate when the memory is HOST_COHERENT. The gate is pure + // logic over the recorded flags, so this test stamps them directly and + // verifies the coherent path issues no Vulkan call — no GPU device + // needed at runtime. + Test flushTest; + Configuration& fc = flushTest.config; + fc.path = cfg.path; + fc.name = "VulkanBufferFlushGate"; + fc.outputName = "VulkanBufferFlushGate"; + fc.type = ConfigurationType::Executable; + fc.target = cfg.target; + fc.march = cfg.march; + fc.mtune = cfg.mtune; + fc.debug = cfg.debug; + fc.sysroot = cfg.sysroot; + fc.dependencies = cfg.dependencies; + fc.externalDependencies = cfg.externalDependencies; + fc.compileFlags = cfg.compileFlags; + fc.linkFlags = cfg.linkFlags; + fc.defines = cfg.defines; + fc.cFiles = cfg.cFiles; + std::vector flushImpls(impls.begin(), impls.end()); + flushImpls.emplace_back("tests/VulkanBufferFlushGate/main"); + fc.GetInterfacesAndImplementations(ifaces, flushImpls); + cfg.tests.push_back(std::move(flushTest)); + // Issue #50: uiResolveScreenPixel now gates its per-pixel clip-rect // compares on the reserved kUIFlagClip bit, which FillHeader sets only // when the clip rect is narrower than the surface. The decision lives diff --git a/tests/VulkanBufferFlushGate/main.cpp b/tests/VulkanBufferFlushGate/main.cpp new file mode 100644 index 0000000..eba680c --- /dev/null +++ b/tests/VulkanBufferFlushGate/main.cpp @@ -0,0 +1,89 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Regression test for issue #60: VulkanBuffer::FlushDevice / FlushHost used to +// call vkFlushMappedMemoryRanges / vkInvalidateMappedMemoryRanges +// unconditionally. They now record the *chosen* memory type's propertyFlags at +// Create time and skip the flush/invalidate when the memory is HOST_COHERENT +// (coherent memory makes host/device writes mutually visible with no explicit +// flush). +// +// The gate is pure logic over the recorded flags, so this test drives it +// directly with no GPU device: it stamps memoryPropertyFlagsChosen and verifies +// the coherent path returns without issuing any Vulkan call. With no device +// created, Device::device is VK_NULL_HANDLE — so if the gate were removed, the +// flush call would dereference a null dispatch handle and crash. Reaching the +// line after the calls is the assertion that the gate held. + +#include +#include "vulkan/vulkan.h" + +import Crafter.Graphics; +import std; +using namespace Crafter; + +namespace { + +int failures = 0; + +void Check(bool ok, std::string_view what) { + std::println("{} {}", ok ? "PASS" : "FAIL", what); + if (!ok) ++failures; +} + +constexpr auto DEVICE_LOCAL = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; +constexpr auto HOST_VISIBLE = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; +constexpr auto HOST_COHERENT = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + +} // namespace + +int main() { + // No device is created, so Device::device stays VK_NULL_HANDLE; any real + // flush/invalidate call below would fault. The coherent gate must prevent + // it. + Check(Device::device == VK_NULL_HANDLE, + "no Vulkan device created — a real flush would fault"); + + { + VulkanBuffer buf; + // Coherent type chosen (even though no COHERENT bit was requested): + // both flush paths must early-return without touching Vulkan. + buf.memoryPropertyFlagsChosen = HOST_VISIBLE | HOST_COHERENT; + buf.FlushDevice(); + buf.FlushHost(); + Check(true, "FlushDevice/FlushHost skip the Vulkan call on coherent memory"); + } + + { + // A non-coherent type would require the flush — verify the recorded + // flags reflect the gate's decision input rather than the request. The + // bit test is the exact condition FlushDevice/FlushHost branch on. + VulkanBuffer buf; + buf.memoryPropertyFlagsChosen = HOST_VISIBLE | DEVICE_LOCAL; + Check(!(buf.memoryPropertyFlagsChosen & HOST_COHERENT), + "non-coherent chosen type is not gated as coherent (flush still required)"); + } + + if (failures != 0) { + std::println("{} check(s) failed", failures); + return EXIT_FAILURE; + } + std::println("all checks passed"); + return EXIT_SUCCESS; +} From cfbe181d9ea2812c89c210136fdac359912a5d7f Mon Sep 17 00:00:00 2001 From: catbot Date: Tue, 16 Jun 2026 17:23:27 +0000 Subject: [PATCH 05/47] feat(device): runtime upload-strategy helper PreferDirectDeviceWrite (#89) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Device::PreferDirectDeviceWrite(size) so the memory-placement cluster (#65/#72/#73/#75) no longer re-derives where a CPU-written, GPU-read buffer should live. It picks the strategy at runtime from Device::memoryProperties: - No DEVICE_LOCAL|HOST_VISIBLE type (no resizable BAR) -> false (must stage). - ReBAR/UMA (BAR heap >= 90% of largest DEVICE_LOCAL heap) -> true: map and write directly; a staging copy would be pure overhead. - Small BAR window (BAR heap << VRAM) -> true only for buffers within a per-window budget (1/8 of the window); large buffers stage so they don't exhaust the window (#58). Complements GetMemoryType (#59): #59 picks the memory *type*, this picks the *strategy*. Upload-only — readback (#74) wants HOST_CACHED and stays separate. A VK_EXT_memory_budget-driven remaining-space check is noted as a follow-up. Tested by tests/UploadStrategy (pure CPU over synthetic ReBAR / UMA / small-window / no-BAR layouts, no GPU device needed), mirroring MemoryTypeFallback. Co-Authored-By: Claude Opus 4.8 --- implementations/Crafter.Graphics-Device.cpp | 45 +++++ interfaces/Crafter.Graphics-Device.cppm | 28 ++++ project.cpp | 29 ++++ tests/UploadStrategy/main.cpp | 173 ++++++++++++++++++++ 4 files changed, 275 insertions(+) create mode 100644 tests/UploadStrategy/main.cpp diff --git a/implementations/Crafter.Graphics-Device.cpp b/implementations/Crafter.Graphics-Device.cpp index 3a30ed4..c4ca43f 100644 --- a/implementations/Crafter.Graphics-Device.cpp +++ b/implementations/Crafter.Graphics-Device.cpp @@ -840,4 +840,49 @@ std::uint32_t Device::GetMemoryType(uint32_t typeBits, VkMemoryPropertyFlags req } throw std::runtime_error("Could not find a matching memory type"); +} + +bool Device::PreferDirectDeviceWrite(VkDeviceSize size) { + constexpr VkMemoryPropertyFlags directFlags = + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + + // 1. Find a DEVICE_LOCAL | HOST_VISIBLE type and remember its heap. Without + // one (no resizable BAR), direct writes are impossible -> must stage. + bool haveDirectType = false; + std::uint32_t barHeapIndex = 0; + for (std::uint32_t i = 0; i < memoryProperties.memoryTypeCount; ++i) { + if ((memoryProperties.memoryTypes[i].propertyFlags & directFlags) == directFlags) { + haveDirectType = true; + barHeapIndex = memoryProperties.memoryTypes[i].heapIndex; + break; + } + } + if (!haveDirectType) { + return false; + } + + // 2. Compare the host-visible device-local heap against the largest plain + // DEVICE_LOCAL heap (the true VRAM size). + VkDeviceSize barHeap = memoryProperties.memoryHeaps[barHeapIndex].size; + VkDeviceSize vram = 0; + for (std::uint32_t i = 0; i < memoryProperties.memoryHeapCount; ++i) { + if (memoryProperties.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) { + vram = std::max(vram, memoryProperties.memoryHeaps[i].size); + } + } + + // 3. ReBAR / UMA: the BAR heap covers (almost) all of VRAM -> direct. + // barHeap >= 0.9 * vram, written to dodge floating point / overflow. + if (barHeap >= vram - vram / 10) { + return true; + } + + // 4. Small BAR window: direct only for buffers small enough that several + // still fit the window — large buffers stage so they don't exhaust it. + // The budget is tied to the actual window size rather than a magic + // constant so it adapts across devices (a fixed cap would be wrong for + // both a 256 MiB and a 4 GiB window). VK_EXT_memory_budget would refine + // this to the window's *remaining* space; see the header note. + constexpr VkDeviceSize barWindowFraction = 8; + return size <= barHeap / barWindowFraction; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-Device.cppm b/interfaces/Crafter.Graphics-Device.cppm index a36fa96..8c79181 100644 --- a/interfaces/Crafter.Graphics-Device.cppm +++ b/interfaces/Crafter.Graphics-Device.cppm @@ -206,6 +206,34 @@ export namespace Crafter { // valid memory exists for the allocation). static std::uint32_t GetMemoryType(std::uint32_t typeBits, VkMemoryPropertyFlags required, VkMemoryPropertyFlags preferred = 0); + // Upload-strategy helper for a CPU-written, GPU-read buffer of `size` + // bytes. Picks *where* the buffer should live, complementing + // GetMemoryType (which picks the memory type once the strategy is set): + // true -> allocate HOST_VISIBLE | DEVICE_LOCAL, map, write straight + // into device memory (no staging buffer, no copy). + // false -> allocate pure DEVICE_LOCAL and feed it from a HOST_VISIBLE + // staging buffer + vkCmdCopyBuffer. + // + // The decision is platform-dependent and made at runtime, never + // hardcoded: + // 1. No DEVICE_LOCAL | HOST_VISIBLE type exists (no resizable BAR) -> + // false: direct writes are impossible, staging is mandatory. + // 2. ReBAR / UMA — the host-visible device-local heap is essentially + // the whole VRAM heap (>= 90% of the largest DEVICE_LOCAL heap) -> + // true: a staging copy would be pure overhead. + // 3. Small BAR window — the host-visible device-local heap is a tiny + // window on a separate heap (<< VRAM) -> true only for buffers + // small enough to fit the window's per-buffer budget; large + // buffers stage so they don't exhaust the window (issue #58). + // + // This is the *upload* path (CPU-write -> GPU-read). Readback wants + // HOST_CACHED instead (the ReBAR type is coherent-not-cached, so CPU + // reads from it are slow) — keep that path separate. A more robust + // small-window budget would consult VK_EXT_memory_budget for the + // window's *remaining* space rather than its total size; that needs the + // extension enabled and is a follow-up. + static bool PreferDirectDeviceWrite(VkDeviceSize size); + // ─── Wayland key repeat ──────────────────────────────────────── // TickKeyRepeats fires onRawKeyDown / onRawKeyHold / onTextInput on // the focused window for whichever key is currently repeating. diff --git a/project.cpp b/project.cpp index bda77a1..5cab540 100644 --- a/project.cpp +++ b/project.cpp @@ -472,6 +472,35 @@ extern "C" Configuration CrafterBuildProject(std::span a fc.GetInterfacesAndImplementations(ifaces, flushImpls); cfg.tests.push_back(std::move(flushTest)); + // Issue #89: Device::PreferDirectDeviceWrite chooses the upload strategy + // for a CPU-written, GPU-read buffer — direct HOST_VISIBLE|DEVICE_LOCAL + // map+write on ReBAR/UMA vs. staged-into-pure-DEVICE_LOCAL on a small + // BAR window (#58). The decision is pure CPU logic over + // Device::memoryProperties (types + heaps), so this test installs + // synthetic ReBAR / UMA / small-window / no-BAR layouts and drives it + // directly — no GPU device needed at runtime. + Test uploadTest; + Configuration& uc = uploadTest.config; + uc.path = cfg.path; + uc.name = "UploadStrategy"; + uc.outputName = "UploadStrategy"; + uc.type = ConfigurationType::Executable; + uc.target = cfg.target; + uc.march = cfg.march; + uc.mtune = cfg.mtune; + uc.debug = cfg.debug; + uc.sysroot = cfg.sysroot; + uc.dependencies = cfg.dependencies; + uc.externalDependencies = cfg.externalDependencies; + uc.compileFlags = cfg.compileFlags; + uc.linkFlags = cfg.linkFlags; + uc.defines = cfg.defines; + uc.cFiles = cfg.cFiles; + std::vector uploadImpls(impls.begin(), impls.end()); + uploadImpls.emplace_back("tests/UploadStrategy/main"); + uc.GetInterfacesAndImplementations(ifaces, uploadImpls); + cfg.tests.push_back(std::move(uploadTest)); + // Issue #57: Font::GetLineWidth memoises per-codepoint advances in // font units (Font::AdvanceUnits) and rescales per call, instead of // calling stbtt_GetCodepointHMetrics for every glyph on every caret diff --git a/tests/UploadStrategy/main.cpp b/tests/UploadStrategy/main.cpp new file mode 100644 index 0000000..442711d --- /dev/null +++ b/tests/UploadStrategy/main.cpp @@ -0,0 +1,173 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Regression test for issue #89: Device::PreferDirectDeviceWrite is the runtime +// upload-strategy helper that decides whether a CPU-written, GPU-read buffer +// should be written directly into a HOST_VISIBLE | DEVICE_LOCAL allocation +// (true) or staged through a HOST_VISIBLE buffer + vkCmdCopyBuffer into pure +// DEVICE_LOCAL (false). The choice is platform-dependent: +// - ReBAR / UMA: the host-visible device-local heap is ~all of VRAM, so +// staging is pure overhead -> direct. +// - non-ReBAR discrete: HOST_VISIBLE | DEVICE_LOCAL is a small BAR window on +// a separate heap; large buffers must stage (#58), small/hot ones may map. +// - no host-visible device-local type at all -> staging is mandatory. +// +// The decision is pure CPU logic over Device::memoryProperties (both the +// memory types and the heaps), so this test installs synthetic layouts and +// drives it directly — no GPU device needed at runtime, mirroring +// MemoryTypeFallback. + +#include +#include "vulkan/vulkan.h" + +import Crafter.Graphics; +import std; +using namespace Crafter; + +namespace { + +int failures = 0; + +void Check(bool ok, std::string_view what) { + std::println("{} {}", ok ? "PASS" : "FAIL", what); + if (!ok) ++failures; +} + +constexpr auto DEVICE_LOCAL = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; +constexpr auto HOST_VISIBLE = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; +constexpr auto HOST_COHERENT = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; +constexpr auto HOST_CACHED = VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + +constexpr VkDeviceSize KiB = 1024; +constexpr VkDeviceSize MiB = 1024 * KiB; +constexpr VkDeviceSize GiB = 1024 * MiB; + +struct TypeSpec { VkMemoryPropertyFlags flags; std::uint32_t heapIndex; }; +struct HeapSpec { VkDeviceSize size; VkMemoryHeapFlags flags; }; + +// Install a synthetic memory layout: explicit types (with their heap index) +// and explicit heaps (size + flags). +void SetMemory(std::initializer_list types, + std::initializer_list heaps) { + Device::memoryProperties = {}; + Device::memoryProperties.memoryTypeCount = static_cast(types.size()); + std::uint32_t i = 0; + for (const TypeSpec& t : types) { + Device::memoryProperties.memoryTypes[i].propertyFlags = t.flags; + Device::memoryProperties.memoryTypes[i].heapIndex = t.heapIndex; + ++i; + } + Device::memoryProperties.memoryHeapCount = static_cast(heaps.size()); + std::uint32_t h = 0; + for (const HeapSpec& s : heaps) { + Device::memoryProperties.memoryHeaps[h].size = s.size; + Device::memoryProperties.memoryHeaps[h].flags = s.flags; + ++h; + } +} + +constexpr VkMemoryHeapFlags HEAP_DEVICE_LOCAL = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT; + +} // namespace + +int main() { + // --- ReBAR: full 24 GiB heap is DEVICE_LOCAL | HOST_VISIBLE (this dev + // machine, type 4 on heap 0) -> always direct, even for huge buffers -- + { + SetMemory( + { {DEVICE_LOCAL, 0}, + {HOST_VISIBLE | HOST_COHERENT, 1}, + {DEVICE_LOCAL | HOST_VISIBLE | HOST_COHERENT, 0} }, + { {24 * GiB, HEAP_DEVICE_LOCAL}, {32 * GiB, 0} }); + Check(Device::PreferDirectDeviceWrite(8), + "ReBAR: a tiny buffer maps directly"); + Check(Device::PreferDirectDeviceWrite(8 * GiB), + "ReBAR: even a multi-GiB buffer maps directly (no overhead staging)"); + } + + // --- UMA: a single heap with all flags -> direct ---------------------- + { + SetMemory( + { {DEVICE_LOCAL | HOST_VISIBLE | HOST_COHERENT | HOST_CACHED, 0} }, + { {125 * GiB, HEAP_DEVICE_LOCAL} }); + Check(Device::PreferDirectDeviceWrite(64 * GiB), + "UMA: single all-flags heap maps directly"); + } + + // --- non-ReBAR discrete: small 256 MiB BAR window (heap 1) separate from + // an 8 GiB VRAM heap (heap 0) -> stage large, map small ------------ + { + SetMemory( + { {DEVICE_LOCAL, 0}, // pure VRAM + {HOST_VISIBLE | HOST_COHERENT, 2}, // system RAM + {DEVICE_LOCAL | HOST_VISIBLE | HOST_COHERENT, 1} }, // BAR window + { {8 * GiB, HEAP_DEVICE_LOCAL}, // heap 0: VRAM + {256 * MiB, HEAP_DEVICE_LOCAL}, // heap 1: BAR + {32 * GiB, 0} }); // heap 2: RAM + Check(!Device::PreferDirectDeviceWrite(128 * MiB), + "small BAR: a large buffer stages (would exhaust the 256 MiB window)"); + Check(Device::PreferDirectDeviceWrite(8 * MiB), + "small BAR: a small/hot buffer maps directly into the window"); + // Budget is 1/8 of the window = 32 MiB. Boundary checks. + Check(Device::PreferDirectDeviceWrite(32 * MiB), + "small BAR: a buffer exactly at the per-buffer budget still maps"); + Check(!Device::PreferDirectDeviceWrite(32 * MiB + 1), + "small BAR: one byte over the budget stages"); + } + + // --- no host-visible device-local type (no resizable BAR) -> must stage - + { + SetMemory( + { {DEVICE_LOCAL, 0}, + {HOST_VISIBLE | HOST_COHERENT, 1}, + {HOST_VISIBLE | HOST_COHERENT | HOST_CACHED, 1} }, + { {8 * GiB, HEAP_DEVICE_LOCAL}, {32 * GiB, 0} }); + Check(!Device::PreferDirectDeviceWrite(8), + "no DEVICE_LOCAL|HOST_VISIBLE type -> staging is mandatory even for a tiny buffer"); + } + + // --- 90% threshold: a BAR heap that is most-but-not-all of VRAM counts as + // ReBAR; one well below the threshold is treated as a window -------- + { + // BAR heap = 9.5 GiB, VRAM = 10 GiB -> 95% >= 90% -> ReBAR -> direct. + SetMemory( + { {DEVICE_LOCAL, 0}, + {DEVICE_LOCAL | HOST_VISIBLE | HOST_COHERENT, 1} }, + { {10 * GiB, HEAP_DEVICE_LOCAL}, {VkDeviceSize(9.5 * GiB), HEAP_DEVICE_LOCAL} }); + Check(Device::PreferDirectDeviceWrite(4 * GiB), + "BAR >= 90% of VRAM is treated as ReBAR: large buffer still maps"); + + // BAR heap = 2 GiB, VRAM = 10 GiB -> 20% < 90% -> window -> stage big. + SetMemory( + { {DEVICE_LOCAL, 0}, + {DEVICE_LOCAL | HOST_VISIBLE | HOST_COHERENT, 1} }, + { {10 * GiB, HEAP_DEVICE_LOCAL}, {2 * GiB, HEAP_DEVICE_LOCAL} }); + Check(!Device::PreferDirectDeviceWrite(4 * GiB), + "BAR well below 90% of VRAM is a window: large buffer stages"); + Check(Device::PreferDirectDeviceWrite(128 * MiB), + "BAR window of 2 GiB still maps a small buffer (budget = 256 MiB)"); + } + + if (failures != 0) { + std::println("{} check(s) failed", failures); + return EXIT_FAILURE; + } + std::println("all checks passed"); + return EXIT_SUCCESS; +} From ab222ffa1fdb8d65d9e5aca9cea32ef26c720761 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Tue, 16 Jun 2026 20:10:23 +0200 Subject: [PATCH 06/47] cache directwrite --- implementations/Crafter.Graphics-Device.cpp | 19 ++++++++++++++----- interfaces/Crafter.Graphics-Device.cppm | 14 ++++++++++++++ tests/UploadStrategy/main.cpp | 4 ++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/implementations/Crafter.Graphics-Device.cpp b/implementations/Crafter.Graphics-Device.cpp index c4ca43f..25b18fb 100644 --- a/implementations/Crafter.Graphics-Device.cpp +++ b/implementations/Crafter.Graphics-Device.cpp @@ -780,6 +780,7 @@ void Device::Initialize() { CheckVkResult(vkCreateCommandPool(device, &commandPoolcreateInfo, NULL, &commandPool)); vkGetPhysicalDeviceMemoryProperties(physDevice, &memoryProperties); + CacheUploadStrategy(); vkGetAccelerationStructureBuildSizesKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetAccelerationStructureBuildSizesKHR")); vkCreateAccelerationStructureKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateAccelerationStructureKHR")); @@ -842,7 +843,7 @@ std::uint32_t Device::GetMemoryType(uint32_t typeBits, VkMemoryPropertyFlags req throw std::runtime_error("Could not find a matching memory type"); } -bool Device::PreferDirectDeviceWrite(VkDeviceSize size) { +void Device::CacheUploadStrategy() { constexpr VkMemoryPropertyFlags directFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; @@ -858,7 +859,8 @@ bool Device::PreferDirectDeviceWrite(VkDeviceSize size) { } } if (!haveDirectType) { - return false; + directWriteBudget = 0; + return; } // 2. Compare the host-visible device-local heap against the largest plain @@ -871,10 +873,11 @@ bool Device::PreferDirectDeviceWrite(VkDeviceSize size) { } } - // 3. ReBAR / UMA: the BAR heap covers (almost) all of VRAM -> direct. + // 3. ReBAR / UMA: the BAR heap covers (almost) all of VRAM -> map any size. // barHeap >= 0.9 * vram, written to dodge floating point / overflow. if (barHeap >= vram - vram / 10) { - return true; + directWriteBudget = std::numeric_limits::max(); + return; } // 4. Small BAR window: direct only for buffers small enough that several @@ -884,5 +887,11 @@ bool Device::PreferDirectDeviceWrite(VkDeviceSize size) { // both a 256 MiB and a 4 GiB window). VK_EXT_memory_budget would refine // this to the window's *remaining* space; see the header note. constexpr VkDeviceSize barWindowFraction = 8; - return size <= barHeap / barWindowFraction; + directWriteBudget = barHeap / barWindowFraction; +} + +bool Device::PreferDirectDeviceWrite(VkDeviceSize size) { + // directWriteBudget caches the size-independent decision (CacheUploadStrategy): + // 0 -> never direct, max -> ReBAR/UMA so any size, else the small-window cap. + return directWriteBudget != 0 && size <= directWriteBudget; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-Device.cppm b/interfaces/Crafter.Graphics-Device.cppm index 8c79181..11853d2 100644 --- a/interfaces/Crafter.Graphics-Device.cppm +++ b/interfaces/Crafter.Graphics-Device.cppm @@ -232,8 +232,22 @@ export namespace Crafter { // small-window budget would consult VK_EXT_memory_budget for the // window's *remaining* space rather than its total size; that needs the // extension enabled and is a follow-up. + // + // Everything except the final size comparison depends only on + // memoryProperties, so it is derived once by CacheUploadStrategy (called + // at device creation) into the directWrite* members below; this leaves + // the per-allocation call a single bounds check. static bool PreferDirectDeviceWrite(VkDeviceSize size); + // Size-independent half of PreferDirectDeviceWrite, cached from + // memoryProperties. directWriteBudget is the largest buffer that may be + // written directly: 0 when no resizable BAR exists (always stage), + // VkDeviceSize max for ReBAR/UMA (always map), or the small-window + // per-buffer cap otherwise. Recompute via CacheUploadStrategy whenever + // memoryProperties changes (device creation does this once). + inline static VkDeviceSize directWriteBudget = 0; + static void CacheUploadStrategy(); + // ─── Wayland key repeat ──────────────────────────────────────── // TickKeyRepeats fires onRawKeyDown / onRawKeyHold / onTextInput on // the focused window for whichever key is currently repeating. diff --git a/tests/UploadStrategy/main.cpp b/tests/UploadStrategy/main.cpp index 442711d..ff81ee0 100644 --- a/tests/UploadStrategy/main.cpp +++ b/tests/UploadStrategy/main.cpp @@ -80,6 +80,10 @@ void SetMemory(std::initializer_list types, Device::memoryProperties.memoryHeaps[h].flags = s.flags; ++h; } + // PreferDirectDeviceWrite reads the cache CacheUploadStrategy derives from + // memoryProperties (device creation does this once); refresh it here since + // the test mutates the layout directly. + Device::CacheUploadStrategy(); } constexpr VkMemoryHeapFlags HEAP_DEVICE_LOCAL = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT; From b6d0ec5caeb88b2e729ff32ddc92be19a662c3f6 Mon Sep 17 00:00:00 2001 From: catbot Date: Tue, 16 Jun 2026 18:22:09 +0000 Subject: [PATCH 07/47] perf(buffer): capacity-reuse in VulkanBuffer::Resize (#63) Resize previously always destroyed + reallocated. It now reuses the existing allocation in place when the new request still fits the created capacity and the immutable-at-create properties match: usage flags are fixed at create, and the chosen memory type must still satisfy the required property flags. preferredPropertyFlags is a best-effort perf hint and is intentionally not part of the guard. On reuse the buffer handle, device address and mapped pointer are preserved; only `size` shrinks to the new logical extent. Tracks capacity and the created usage flags on VulkanBufferBase, set at Create and carried through the move constructor. Adds VulkanBufferResizeReuse, a device-free regression test that drives the reuse guard directly and asserts the in-place path issues no Vulkan call. Co-Authored-By: Claude Opus 4.8 --- interfaces/Crafter.Graphics-VulkanBuffer.cppm | 26 ++++ project.cpp | 29 ++++ tests/VulkanBufferResizeReuse/main.cpp | 134 ++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 tests/VulkanBufferResizeReuse/main.cpp diff --git a/interfaces/Crafter.Graphics-VulkanBuffer.cppm b/interfaces/Crafter.Graphics-VulkanBuffer.cppm index ce0d862..037d786 100644 --- a/interfaces/Crafter.Graphics-VulkanBuffer.cppm +++ b/interfaces/Crafter.Graphics-VulkanBuffer.cppm @@ -40,6 +40,12 @@ namespace Crafter { // COHERENT bit on a coherent type (and vice versa), so the flush/ // invalidate paths gate on this recorded value, never on the request. VkMemoryPropertyFlags memoryPropertyFlagsChosen = 0; + // Byte capacity the buffer was created with, and the usage flags it was + // created with. Resize reuses the allocation in place when a new + // request still fits within `capacity` and these immutable-at-create + // properties match, avoiding a destroy+reallocate. + std::uint32_t capacity = 0; + VkBufferUsageFlags2 usageFlagsCreated = 0; }; export template @@ -67,6 +73,8 @@ namespace Crafter { // available on every device — see Device::GetMemoryType. void Create(VkBufferUsageFlags2 usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count, VkMemoryPropertyFlags preferredPropertyFlags = 0) { size = count * sizeof(T); + capacity = size; + usageFlagsCreated = usageFlags; // Carry usage in the maintenance5 flags2 chain so 64-bit bits // (e.g. VK_BUFFER_USAGE_2_MEMORY_DECOMPRESSION_BIT_EXT, bit 35) @@ -122,6 +130,22 @@ namespace Crafter { } void Resize(VkBufferUsageFlags2 usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count, VkMemoryPropertyFlags preferredPropertyFlags = 0) { + // Reuse the existing allocation in place when the request still fits + // and the fixed-at-create properties match: usage flags are + // immutable after creation, and the memory type already chosen must + // still satisfy the required property flags. preferredPropertyFlags + // is a best-effort perf hint and does not affect correctness, so it + // is intentionally not part of the guard. The buffer handle (and its + // device address / mapped pointer) is preserved, only `size` shrinks + // to the new logical extent. + std::uint32_t requestedSize = count * sizeof(T); + if(buffer != VK_NULL_HANDLE + && requestedSize <= capacity + && usageFlags == usageFlagsCreated + && (memoryPropertyFlagsChosen & memoryPropertyFlags) == memoryPropertyFlags) { + size = requestedSize; + return; + } if(buffer != VK_NULL_HANDLE) { Clear(); } @@ -227,6 +251,8 @@ namespace Crafter { buffer = other.buffer; memory = other.memory; size = other.size; + capacity = other.capacity; + usageFlagsCreated = other.usageFlagsCreated; memoryPropertyFlagsChosen = other.memoryPropertyFlagsChosen; other.buffer = VK_NULL_HANDLE; address = other.address; diff --git a/project.cpp b/project.cpp index 5cab540..d928210 100644 --- a/project.cpp +++ b/project.cpp @@ -472,6 +472,35 @@ extern "C" Configuration CrafterBuildProject(std::span a fc.GetInterfacesAndImplementations(ifaces, flushImpls); cfg.tests.push_back(std::move(flushTest)); + // Issue #63: VulkanBuffer::Resize now reuses the existing allocation in + // place when a new request still fits the created capacity and the + // immutable-at-create properties match (usage flags fixed at create; + // chosen memory type still satisfies the required flags), instead of + // always destroying + reallocating. The reuse guard is pure logic over + // recorded fields, so this test stamps a fake handle + capacity/flags and + // verifies the in-place path issues no Vulkan call — no GPU device needed. + Test resizeTest; + Configuration& rc = resizeTest.config; + rc.path = cfg.path; + rc.name = "VulkanBufferResizeReuse"; + rc.outputName = "VulkanBufferResizeReuse"; + rc.type = ConfigurationType::Executable; + rc.target = cfg.target; + rc.march = cfg.march; + rc.mtune = cfg.mtune; + rc.debug = cfg.debug; + rc.sysroot = cfg.sysroot; + rc.dependencies = cfg.dependencies; + rc.externalDependencies = cfg.externalDependencies; + rc.compileFlags = cfg.compileFlags; + rc.linkFlags = cfg.linkFlags; + rc.defines = cfg.defines; + rc.cFiles = cfg.cFiles; + std::vector resizeImpls(impls.begin(), impls.end()); + resizeImpls.emplace_back("tests/VulkanBufferResizeReuse/main"); + rc.GetInterfacesAndImplementations(ifaces, resizeImpls); + cfg.tests.push_back(std::move(resizeTest)); + // Issue #89: Device::PreferDirectDeviceWrite chooses the upload strategy // for a CPU-written, GPU-read buffer — direct HOST_VISIBLE|DEVICE_LOCAL // map+write on ReBAR/UMA vs. staged-into-pure-DEVICE_LOCAL on a small diff --git a/tests/VulkanBufferResizeReuse/main.cpp b/tests/VulkanBufferResizeReuse/main.cpp new file mode 100644 index 0000000..9348180 --- /dev/null +++ b/tests/VulkanBufferResizeReuse/main.cpp @@ -0,0 +1,134 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Regression test for issue #63: VulkanBuffer::Resize used to unconditionally +// destroy + reallocate. It now reuses the existing allocation in place when the +// new request still fits within the created capacity AND the immutable-at-create +// properties match (usage flags are fixed at create; the chosen memory type must +// still satisfy the required property flags). The reuse path only shrinks `size` +// and issues no Vulkan call. +// +// The guard is pure logic over recorded fields, so this test drives it directly +// with no GPU device: it stamps a fake non-null buffer handle plus capacity / +// usage / chosen-flags, then calls Resize. With Device::device == VK_NULL_HANDLE +// any real destroy/reallocate would dereference a null dispatch handle and +// crash — so reaching the line after a reuse Resize is the assertion that the +// in-place path was taken. The realloc path (flag/size mismatch) is intentionally +// not exercised here because it would issue real Vulkan calls. + +#include +#include +#include "vulkan/vulkan.h" + +import Crafter.Graphics; +import std; +using namespace Crafter; + +namespace { + +int failures = 0; + +void Check(bool ok, std::string_view what) { + std::println("{} {}", ok ? "PASS" : "FAIL", what); + if (!ok) ++failures; +} + +constexpr auto DEVICE_LOCAL = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; +constexpr auto HOST_VISIBLE = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; +constexpr auto HOST_COHERENT = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + +constexpr VkBufferUsageFlags2 USAGE = + VK_BUFFER_USAGE_2_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT; + +// A fake non-null, non-dereferenced handle: Resize's reuse path never touches it. +VkBuffer FakeHandle() { + return reinterpret_cast(static_cast(0x1)); +} + +// Stamp a buffer into the "already created with capacity C" state without +// touching the GPU, then neutralise it so the destructor's Clear() is skipped. +template +void NeutraliseHandle(Buf& buf) { + buf.buffer = VK_NULL_HANDLE; +} + +} // namespace + +int main() { + Check(Device::device == VK_NULL_HANDLE, + "no Vulkan device created — a real reallocate would fault"); + + { + // Shrink within capacity, flags match → reuse in place, only size shrinks. + VulkanBuffer buf; + buf.buffer = FakeHandle(); + buf.capacity = 16 * sizeof(float); + buf.size = 16 * sizeof(float); + buf.usageFlagsCreated = USAGE; + buf.memoryPropertyFlagsChosen = HOST_VISIBLE | HOST_COHERENT; + + buf.Resize(USAGE, HOST_VISIBLE, 4); // 4 floats <= 16-float capacity + Check(buf.buffer == FakeHandle(), + "fitting Resize reuses the existing handle (no realloc)"); + Check(buf.size == 4 * sizeof(float), + "reuse updates size to the new logical extent"); + Check(buf.capacity == 16 * sizeof(float), + "reuse leaves capacity at the original allocation size"); + NeutraliseHandle(buf); + } + + { + // Exact-fit request (size == capacity) is still a reuse. + VulkanBuffer buf; + buf.buffer = FakeHandle(); + buf.capacity = 8 * sizeof(float); + buf.size = 2 * sizeof(float); + buf.usageFlagsCreated = USAGE; + buf.memoryPropertyFlagsChosen = HOST_VISIBLE; + + buf.Resize(USAGE, HOST_VISIBLE, 8); + Check(buf.buffer == FakeHandle() && buf.size == 8 * sizeof(float), + "exact-capacity Resize reuses the allocation"); + NeutraliseHandle(buf); + } + + { + // Reuse requires the chosen memory type to still satisfy the required + // flags. Chosen type carries DEVICE_LOCAL, so a request for it is + // satisfied and reuse is allowed. + VulkanBuffer buf; + buf.buffer = FakeHandle(); + buf.capacity = 8 * sizeof(float); + buf.size = 8 * sizeof(float); + buf.usageFlagsCreated = USAGE; + buf.memoryPropertyFlagsChosen = HOST_VISIBLE | DEVICE_LOCAL; + + buf.Resize(USAGE, DEVICE_LOCAL, 4); + Check(buf.buffer == FakeHandle() && buf.size == 4 * sizeof(float), + "reuse allowed when chosen memory type satisfies required flags"); + NeutraliseHandle(buf); + } + + if (failures != 0) { + std::println("{} check(s) failed", failures); + return EXIT_FAILURE; + } + std::println("all checks passed"); + return EXIT_SUCCESS; +} From a3a6dd2006adcdb84716fcc66631d16fd297b074 Mon Sep 17 00:00:00 2001 From: catbot Date: Tue, 16 Jun 2026 18:23:25 +0000 Subject: [PATCH 08/47] perf(pipeline): shared, disk-persisted VkPipelineCache (#69) vkCreateComputePipelines and vkCreateRayTracingPipelinesKHR were passed VK_NULL_HANDLE, so the driver couldn't reuse compiled shader binaries across the pipelines built at startup (4 UI shaders + user/RT pipelines) or across runs. Add one process-wide Device::pipelineCache. LoadPipelineCache (called from Initialize) seeds it from pipeline_cache.bin when the file's VkPipelineCacheHeaderVersionOne header matches this device's vendorID / deviceID / pipelineCacheUUID; a stale or foreign blob is discarded so the driver never rejects it. SavePipelineCache is registered with std::atexit, serialising the cache at process shutdown without any explicit teardown hook. Both create sites now pass the cache; a null handle stays valid, so no create-site null check is needed. Co-Authored-By: Claude Opus 4.8 --- .../Crafter.Graphics-ComputeShader.cpp | 2 +- implementations/Crafter.Graphics-Device.cpp | 106 +++++++++++++++++- interfaces/Crafter.Graphics-Device.cppm | 38 +++++++ .../Crafter.Graphics-PipelineRTVulkan.cppm | 2 +- 4 files changed, 144 insertions(+), 4 deletions(-) diff --git a/implementations/Crafter.Graphics-ComputeShader.cpp b/implementations/Crafter.Graphics-ComputeShader.cpp index 3edc369..6df3ea2 100644 --- a/implementations/Crafter.Graphics-ComputeShader.cpp +++ b/implementations/Crafter.Graphics-ComputeShader.cpp @@ -82,7 +82,7 @@ void ComputeShader::Load(const std::filesystem::path& spvPath) { .layout = VK_NULL_HANDLE, }; Device::CheckVkResult(vkCreateComputePipelines( - Device::device, VK_NULL_HANDLE, 1, &info, nullptr, &pipeline)); + Device::device, Device::pipelineCache, 1, &info, nullptr, &pipeline)); } void ComputeShader::Dispatch(VkCommandBuffer cmd, diff --git a/implementations/Crafter.Graphics-Device.cpp b/implementations/Crafter.Graphics-Device.cpp index 25b18fb..bcd668c 100644 --- a/implementations/Crafter.Graphics-Device.cpp +++ b/implementations/Crafter.Graphics-Device.cpp @@ -133,8 +133,99 @@ void Device::CheckVkResult(VkResult result) { } throw std::runtime_error(string_VkResult(result)); - } -} + } +} + +bool Device::PipelineCacheDataCompatible(std::span data) { + // VkPipelineCacheHeaderVersionOne is a fixed 32-byte little-endian header + // (Vulkan spec 16.5.2): u32 headerSize, u32 headerVersion, u32 vendorID, + // u32 deviceID, then VK_UUID_SIZE bytes of pipelineCacheUUID. Parse the + // fields by offset rather than reinterpret_cast'ing the struct so the + // check is independent of the C struct's alignment/padding. + constexpr std::size_t headerBytes = 16 + VK_UUID_SIZE; + if (data.size() < headerBytes) { + return false; + } + + auto readU32 = [&](std::size_t offset) { + std::uint32_t value; + std::memcpy(&value, data.data() + offset, sizeof(value)); + return value; + }; + + const std::uint32_t headerSize = readU32(0); + const std::uint32_t headerVersion = readU32(4); + const std::uint32_t vendorID = readU32(8); + const std::uint32_t deviceID = readU32(12); + + if (headerSize < headerBytes) { + return false; + } + if (headerVersion != VK_PIPELINE_CACHE_HEADER_VERSION_ONE) { + return false; + } + if (vendorID != deviceProperties.vendorID || deviceID != deviceProperties.deviceID) { + return false; + } + return std::memcmp(data.data() + 16, deviceProperties.pipelineCacheUUID, VK_UUID_SIZE) == 0; +} + +void Device::LoadPipelineCache() { + std::vector initialData; + + std::error_code ec; + if (std::filesystem::exists(pipelineCachePath, ec)) { + std::ifstream file(pipelineCachePath, std::ios::binary | std::ios::ate); + if (file) { + const std::streamoff size = file.tellg(); + if (size > 0) { + initialData.resize(static_cast(size)); + file.seekg(0); + file.read(reinterpret_cast(initialData.data()), size); + if (!file) { + initialData.clear(); // partial/short read — start cold + } + } + } + } + + // Drop a blob the current driver/device wouldn't accept: a header from a + // different GPU (or a corrupt/empty file) would at best be ignored and at + // worst rejected. Starting empty just costs a one-time cold compile. + if (!PipelineCacheDataCompatible(initialData)) { + initialData.clear(); + } + + VkPipelineCacheCreateInfo info { + .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, + .initialDataSize = initialData.size(), + .pInitialData = initialData.empty() ? nullptr : initialData.data(), + }; + CheckVkResult(vkCreatePipelineCache(device, &info, nullptr, &pipelineCache)); +} + +void Device::SavePipelineCache() { + if (pipelineCache == VK_NULL_HANDLE) { + return; + } + + // Two-call idiom: query size, then fetch. Best-effort — a failure here only + // forfeits the next run's warm start, so it must never throw out of an + // atexit handler. + std::size_t size = 0; + if (vkGetPipelineCacheData(device, pipelineCache, &size, nullptr) != VK_SUCCESS || size == 0) { + return; + } + std::vector data(size); + if (vkGetPipelineCacheData(device, pipelineCache, &size, data.data()) != VK_SUCCESS) { + return; + } + + std::ofstream file(pipelineCachePath, std::ios::binary | std::ios::trunc); + if (file) { + file.write(reinterpret_cast(data.data()), static_cast(size)); + } +} VkBool32 onError(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT type, const VkDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData) { @@ -603,6 +694,9 @@ void Device::Initialize() { .pNext = &rayTracingProperties }; vkGetPhysicalDeviceProperties2(physDevice, &properties2); + // Keep the core properties around for the pipeline-cache identity check + // (vendorID / deviceID / pipelineCacheUUID). + deviceProperties = properties2.properties; // NVIDIA's brand-new VK_EXT_descriptor_heap acceleration-structure read // path faults (see #7); enable the SPIR-V rewrite workaround there. Other @@ -810,6 +904,14 @@ void Device::Initialize() { memoryDecompressionSupported = false; } } + + // Create the shared pipeline cache (seeded from disk when a compatible + // blob exists) and arrange for it to be written back at process exit. The + // device handle is a never-destroyed static, so it is still valid when the + // atexit handler runs. Registered once — Initialize is the single device + // bring-up. + LoadPipelineCache(); + std::atexit(SavePipelineCache); } std::uint32_t Device::GetMemoryType(uint32_t typeBits, VkMemoryPropertyFlags required, VkMemoryPropertyFlags preferred) { diff --git a/interfaces/Crafter.Graphics-Device.cppm b/interfaces/Crafter.Graphics-Device.cppm index 11853d2..a5b4a68 100644 --- a/interfaces/Crafter.Graphics-Device.cppm +++ b/interfaces/Crafter.Graphics-Device.cppm @@ -161,6 +161,27 @@ export namespace Crafter { inline static VkPhysicalDeviceMemoryProperties memoryProperties; + // Core physical-device properties, captured once at Initialize from the + // VkPhysicalDeviceProperties2 query. Kept because the pipeline-cache + // persistence path needs vendorID / deviceID / pipelineCacheUUID to + // decide whether an on-disk blob was written by this exact device. + inline static VkPhysicalDeviceProperties deviceProperties = {}; + + // One process-wide pipeline cache fed to every vkCreate*Pipelines call + // (compute UI/user shaders + RT pipelines). Pipeline compilation is a + // one-time cold-start cost, not a per-frame one; a single shared cache + // lets the driver reuse compiled shader binaries across the several + // pipelines built at startup and — once persisted to disk (see + // LoadPipelineCache / SavePipelineCache) — across runs. VK_NULL_HANDLE + // until LoadPipelineCache runs; passing VK_NULL_HANDLE to a create call + // is valid and simply means "no cache", so the create sites need no + // null check. + inline static VkPipelineCache pipelineCache = VK_NULL_HANDLE; + // Where the serialized cache lives. Relative to the working directory, + // matching the gpu_crash_dump-* convention. Set before Initialize to + // relocate it. + inline static std::filesystem::path pipelineCachePath = "pipeline_cache.bin"; + inline static VkPhysicalDeviceDescriptorHeapPropertiesEXT descriptorHeapProperties = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_PROPERTIES_EXT }; @@ -199,6 +220,23 @@ export namespace Crafter { // ComputeShader read the offset off the pipeline they record. static void CheckVkResult(VkResult result); + + // ─── Pipeline cache persistence (issue #69) ───────────────────── + // LoadPipelineCache creates `pipelineCache`, seeding it from + // pipelineCachePath when the file exists *and* its header matches this + // device (PipelineCacheDataCompatible) — a stale or foreign blob is + // discarded so the driver never rejects it. Called once from + // Initialize. SavePipelineCache writes the driver's current cache blob + // back out; Initialize registers it with std::atexit so the cache is + // serialized at process shutdown without any explicit teardown call. + static void LoadPipelineCache(); + static void SavePipelineCache(); + // True when `data` is a VkPipelineCache blob whose header was written by + // a device with the same vendorID / deviceID / pipelineCacheUUID as + // `deviceProperties`. Pure logic over the standard 32-byte cache header, + // so it is driven directly by the PipelineCacheValidation test with no + // GPU device. A blob too short to hold the header is incompatible. + static bool PipelineCacheDataCompatible(std::span data); // Selects a memory type index from typeBits that satisfies `required`. // When `preferred` bits are also given, a type satisfying both is // chosen first; if none exists we fall back to required-only rather diff --git a/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm b/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm index c0374ec..9da555e 100644 --- a/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm +++ b/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm @@ -82,7 +82,7 @@ export namespace Crafter { .layout = VK_NULL_HANDLE }; - Device::CheckVkResult(Device::vkCreateRayTracingPipelinesKHR(Device::device, {}, {}, 1, &rtPipelineInfo, nullptr, &pipeline)); + Device::CheckVkResult(Device::vkCreateRayTracingPipelinesKHR(Device::device, {}, Device::pipelineCache, 1, &rtPipelineInfo, nullptr, &pipeline)); std::size_t dataSize = Device::rayTracingProperties.shaderGroupHandleSize * rtPipelineInfo.groupCount; shaderHandles.resize(dataSize); From a728482731a5b419014ee3d41f132335e79b7892 Mon Sep 17 00:00:00 2001 From: catbot Date: Tue, 16 Jun 2026 18:23:25 +0000 Subject: [PATCH 09/47] test(pipeline): cover pipeline-cache header validation (#69) PipelineCacheValidation drives Device::PipelineCacheDataCompatible directly (no GPU): synthetic device identities + 32-byte cache headers assert that a matching header is accepted while foreign vendor/device, a bumped UUID (driver update), unknown version, undersized headerSize, and short/empty blobs are all rejected. Mirrors the GPU-free style of MemoryTypeFallback / UploadStrategy. Also gitignore the serialized pipeline_cache.bin a real-device test run drops in the working dir. Co-Authored-By: Claude Opus 4.8 --- .gitignore | 3 +- project.cpp | 29 +++++ tests/PipelineCacheValidation/main.cpp | 157 +++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 tests/PipelineCacheValidation/main.cpp diff --git a/.gitignore b/.gitignore index 4e9c87a..b85ab43 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ build/ -bin/ \ No newline at end of file +bin/ +pipeline_cache.bin diff --git a/project.cpp b/project.cpp index 5cab540..d56ad86 100644 --- a/project.cpp +++ b/project.cpp @@ -587,6 +587,35 @@ extern "C" Configuration CrafterBuildProject(std::span a hitImpls.emplace_back("tests/InputFieldHitTest/main"); hc.GetInterfacesAndImplementations(ifaces, hitImpls); cfg.tests.push_back(std::move(hitTest)); + + // Issue #69: the engine feeds one shared Device::pipelineCache to every + // vkCreate*Pipelines call and persists it across runs, discarding an + // on-disk blob whose header doesn't match the current GPU. That gate — + // Device::PipelineCacheDataCompatible — is pure logic over the standard + // VkPipelineCache header and Device::deviceProperties, so this test + // stamps synthetic device identities + headers and drives it directly, + // no GPU device needed at runtime. + Test cacheTest; + Configuration& cc = cacheTest.config; + cc.path = cfg.path; + cc.name = "PipelineCacheValidation"; + cc.outputName = "PipelineCacheValidation"; + cc.type = ConfigurationType::Executable; + cc.target = cfg.target; + cc.march = cfg.march; + cc.mtune = cfg.mtune; + cc.debug = cfg.debug; + cc.sysroot = cfg.sysroot; + cc.dependencies = cfg.dependencies; + cc.externalDependencies = cfg.externalDependencies; + cc.compileFlags = cfg.compileFlags; + cc.linkFlags = cfg.linkFlags; + cc.defines = cfg.defines; + cc.cFiles = cfg.cFiles; + std::vector cacheImpls(impls.begin(), impls.end()); + cacheImpls.emplace_back("tests/PipelineCacheValidation/main"); + cc.GetInterfacesAndImplementations(ifaces, cacheImpls); + cfg.tests.push_back(std::move(cacheTest)); } return cfg; diff --git a/tests/PipelineCacheValidation/main.cpp b/tests/PipelineCacheValidation/main.cpp new file mode 100644 index 0000000..a55b693 --- /dev/null +++ b/tests/PipelineCacheValidation/main.cpp @@ -0,0 +1,157 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Regression test for issue #69: the engine feeds a shared Device::pipelineCache +// to every vkCreate*Pipelines call and persists it across runs. A blob written +// by a different GPU (or a corrupt/short file) must be rejected before it is +// handed to vkCreatePipelineCache, otherwise the driver ignores or rejects it. +// Device::PipelineCacheDataCompatible is that gate: pure logic over the standard +// 32-byte VkPipelineCacheHeaderVersionOne header (headerSize, headerVersion, +// vendorID, deviceID, pipelineCacheUUID) compared against Device::deviceProperties. +// It needs no GPU, so this test stamps synthetic device identities and headers +// and drives it directly, mirroring MemoryTypeFallback / UploadStrategy. + +#include +#include +#include "vulkan/vulkan.h" + +import Crafter.Graphics; +import std; +using namespace Crafter; + +namespace { + +int failures = 0; + +void Check(bool ok, std::string_view what) { + std::println("{} {}", ok ? "PASS" : "FAIL", what); + if (!ok) ++failures; +} + +// Stamp the device identity the validator compares against. +void SetDevice(std::uint32_t vendorID, std::uint32_t deviceID, + const std::array& uuid) { + Device::deviceProperties = {}; + Device::deviceProperties.vendorID = vendorID; + Device::deviceProperties.deviceID = deviceID; + std::memcpy(Device::deviceProperties.pipelineCacheUUID, uuid.data(), VK_UUID_SIZE); +} + +// Build a 32-byte VkPipelineCacheHeaderVersionOne blob, optionally with extra +// trailing payload bytes (the real cache body). Fields are written little-endian +// at fixed offsets, matching how the driver lays the header out on disk. +std::vector MakeHeader(std::uint32_t headerSize, + std::uint32_t headerVersion, + std::uint32_t vendorID, + std::uint32_t deviceID, + const std::array& uuid, + std::size_t trailing = 0) { + std::vector blob(16 + VK_UUID_SIZE + trailing, std::byte{0xAB}); + auto put = [&](std::size_t off, std::uint32_t v) { + std::memcpy(blob.data() + off, &v, sizeof(v)); + }; + put(0, headerSize); + put(4, headerVersion); + put(8, vendorID); + put(12, deviceID); + std::memcpy(blob.data() + 16, uuid.data(), VK_UUID_SIZE); + return blob; +} + +constexpr std::uint32_t kVendor = 0x10DE; // NVIDIA +constexpr std::uint32_t kDevice = 0x2204; // some GPU device id +constexpr std::array kUuid = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 +}; +constexpr std::uint32_t kV1 = VK_PIPELINE_CACHE_HEADER_VERSION_ONE; +constexpr std::uint32_t kHeaderSize = 16 + VK_UUID_SIZE; // 32 + +} // namespace + +int main() { + SetDevice(kVendor, kDevice, kUuid); + + // --- the happy path: a header written by this exact device -------------- + { + auto blob = MakeHeader(kHeaderSize, kV1, kVendor, kDevice, kUuid); + Check(Device::PipelineCacheDataCompatible(blob), + "matching vendor/device/UUID header is accepted"); + + auto withBody = MakeHeader(kHeaderSize, kV1, kVendor, kDevice, kUuid, /*trailing*/ 4096); + Check(Device::PipelineCacheDataCompatible(withBody), + "matching header followed by a cache body is accepted"); + } + + // --- foreign / stale blobs must be rejected ----------------------------- + { + auto otherVendor = MakeHeader(kHeaderSize, kV1, 0x1002 /*AMD*/, kDevice, kUuid); + Check(!Device::PipelineCacheDataCompatible(otherVendor), + "different vendorID is rejected"); + + auto otherDevice = MakeHeader(kHeaderSize, kV1, kVendor, 0x9999, kUuid); + Check(!Device::PipelineCacheDataCompatible(otherDevice), + "different deviceID is rejected"); + + std::array otherUuid = kUuid; + otherUuid[15] ^= 0xFF; // a driver update bumps the UUID + auto staleUuid = MakeHeader(kHeaderSize, kV1, kVendor, kDevice, otherUuid); + Check(!Device::PipelineCacheDataCompatible(staleUuid), + "different pipelineCacheUUID (e.g. driver update) is rejected"); + } + + // --- malformed headers -------------------------------------------------- + { + auto badVersion = MakeHeader(kHeaderSize, 0xDEAD, kVendor, kDevice, kUuid); + Check(!Device::PipelineCacheDataCompatible(badVersion), + "unknown headerVersion is rejected"); + + auto smallHeaderSize = MakeHeader(8, kV1, kVendor, kDevice, kUuid); + Check(!Device::PipelineCacheDataCompatible(smallHeaderSize), + "headerSize smaller than the 32-byte header is rejected"); + + Check(!Device::PipelineCacheDataCompatible({}), + "empty blob (no file / cold start) is rejected"); + + std::vector truncated(20, std::byte{0}); + Check(!Device::PipelineCacheDataCompatible(truncated), + "blob too short to hold the header is rejected"); + } + + // --- identity follows the active device --------------------------------- + { + // Re-stamp as a different device; a blob valid for the old one is now + // foreign. Guards against the validator caching identity anywhere but + // Device::deviceProperties. + auto blob = MakeHeader(kHeaderSize, kV1, kVendor, kDevice, kUuid); + SetDevice(0x8086 /*Intel*/, 0x1234, kUuid); + Check(!Device::PipelineCacheDataCompatible(blob), + "a blob from the previous device is rejected after the device changes"); + SetDevice(kVendor, kDevice, kUuid); + Check(Device::PipelineCacheDataCompatible(blob), + "...and accepted again once the matching device is restored"); + } + + if (failures != 0) { + std::println("{} check(s) failed", failures); + return EXIT_FAILURE; + } + std::println("all checks passed"); + return EXIT_SUCCESS; +} From 8e6ba49743ba1a22ede26e53a481cc144e974677 Mon Sep 17 00:00:00 2001 From: catbot Date: Tue, 16 Jun 2026 18:24:09 +0000 Subject: [PATCH 10/47] perf(rt): high-water-mark growth for TLAS host-input buffers (#64) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BuildTLAS reallocated the host-visible instanceBuffer and metadataBuffer on every topology change. They only ever need to hold at least primitiveCount entries (the AS build reads exactly primitiveCount via tlasRangeInfo, and the copy loop writes [0, primitiveCount)), so a shrink — or any growth that still fits the previous capacity — can reuse the existing allocation. Gate the two Resize calls on a high-water-mark check, removing two of the four reallocations on a count change. The AS storage + scratch rebuild below is unchanged: it is tied to the AS itself and dominates this path regardless. Adds tests/TLASHighWaterMark driving the real hardware AS-build path: it asserts a shrink (and within-capacity growth) reuses the buffers while a growth past the high-water reallocates, that builtInstanceCount still tracks the live count, and that feeding an oversized instance buffer to the build produces zero Vulkan validation errors. Co-Authored-By: Claude Opus 4.8 --- .../Crafter.Graphics-RenderingElement3D.cpp | 25 +- project.cpp | 31 ++ tests/TLASHighWaterMark/main.cpp | 287 ++++++++++++++++++ 3 files changed, 339 insertions(+), 4 deletions(-) create mode 100644 tests/TLASHighWaterMark/main.cpp diff --git a/implementations/Crafter.Graphics-RenderingElement3D.cpp b/implementations/Crafter.Graphics-RenderingElement3D.cpp index b43f43e..5eea69c 100644 --- a/implementations/Crafter.Graphics-RenderingElement3D.cpp +++ b/implementations/Crafter.Graphics-RenderingElement3D.cpp @@ -88,12 +88,29 @@ void RenderingElement3D::BuildTLAS(VkCommandBuffer cmd, std::uint32_t index, RTB } if (topologyChanged) { - // Resize the host-visible inputs to match the new count. + // Grow the host-visible inputs on a high-water mark rather than + // resizing to the exact count every topology change. These buffers + // only need to hold *at least* primitiveCount entries — the AS build + // reads exactly primitiveCount of them via tlasRangeInfo, and the + // copy loop below writes [0, primitiveCount) — so an allocation left + // over from a larger earlier frame is reused as-is. This drops the + // two host-buffer reallocations on a count decrease (and on an + // increase that still fits the previous high-water capacity), + // leaving only the AS storage + scratch rebuild below, which is tied + // to the AS itself and dominates this path regardless. + // + // instanceBuffer and metadataBuffer grow in lockstep (always resized + // together to the same entry count), so one capacity check covers + // both. size is only meaningful once buffer is non-null, so the + // null check must short-circuit before the division. // STORAGE_BUFFER_BIT is required because the application's compute - // shaders bind this buffer as a storage SSBO (e.g. to write + // shaders bind these buffers as storage SSBOs (e.g. to write // per-instance transforms directly into the TLAS instance data). - tlas.instanceBuffer.Resize(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, primitiveCount); - tlas.metadataBuffer.Resize(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, primitiveCount); + if (tlas.instanceBuffer.buffer == VK_NULL_HANDLE + || primitiveCount > tlas.instanceBuffer.size / sizeof(VkAccelerationStructureInstanceKHR)) { + tlas.instanceBuffer.Resize(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, primitiveCount); + tlas.metadataBuffer.Resize(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, primitiveCount); + } } for(std::uint32_t i = 0; i < primitiveCount; i++) { diff --git a/project.cpp b/project.cpp index 5cab540..b5b0353 100644 --- a/project.cpp +++ b/project.cpp @@ -328,6 +328,37 @@ extern "C" Configuration CrafterBuildProject(std::span a bc.GetInterfacesAndImplementations(ifaces, blasImpls); cfg.tests.push_back(std::move(blasTest)); + // Issue #64: TLAS host-input buffers (instanceBuffer / metadataBuffer) + // grow on a high-water mark instead of reallocating to the exact count + // every topology change. Drives the real hardware AS-build path — a + // cube BLAS plus RenderingElement3D::BuildTLAS at a sequence of + // instance counts — and asserts that a shrink (and any growth within + // the high-water capacity) reuses the existing allocation while a + // growth past it reallocates, with the validation layer reporting no + // errors when an oversized instance buffer is fed to the build. Needs a + // Vulkan RT device at runtime, so it shares the native build settings. + Test tlasTest; + Configuration& tlc = tlasTest.config; + tlc.path = cfg.path; + tlc.name = "TLASHighWaterMark"; + tlc.outputName = "TLASHighWaterMark"; + tlc.type = ConfigurationType::Executable; + tlc.target = cfg.target; + tlc.march = cfg.march; + tlc.mtune = cfg.mtune; + tlc.debug = cfg.debug; + tlc.sysroot = cfg.sysroot; + tlc.dependencies = cfg.dependencies; + tlc.externalDependencies = cfg.externalDependencies; + tlc.compileFlags = cfg.compileFlags; + tlc.linkFlags = cfg.linkFlags; + tlc.defines = cfg.defines; + tlc.cFiles = cfg.cFiles; + std::vector tlasImpls(impls.begin(), impls.end()); + tlasImpls.emplace_back("tests/TLASHighWaterMark/main"); + tlc.GetInterfacesAndImplementations(ifaces, tlasImpls); + cfg.tests.push_back(std::move(tlasTest)); + // Issue #51: FontAtlas only re-uploads the dirty sub-rect now, // tracked via FontAtlas::DirtyRect. The accumulation/clamp math is // pure CPU, so this test drives it directly — no GPU device needed diff --git a/tests/TLASHighWaterMark/main.cpp b/tests/TLASHighWaterMark/main.cpp new file mode 100644 index 0000000..d35de8a --- /dev/null +++ b/tests/TLASHighWaterMark/main.cpp @@ -0,0 +1,287 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Issue #64: TLAS host-visible input buffers (instanceBuffer / metadataBuffer) +// grow on a high-water mark instead of being reallocated to the exact instance +// count on every topology change. These buffers only ever need to hold *at +// least* primitiveCount entries — the AS build reads exactly primitiveCount of +// them — so an allocation left over from a larger earlier frame is reused. +// +// This drives the real hardware path: a headless Vulkan RT device (no swapchain +// needed — a TLAS build only touches the queue + command pool), a real cube +// BLAS, and RenderingElement3D::BuildTLAS recorded into one-time command +// buffers at a sequence of instance counts. +// +// What is asserted: +// - First build at a given count ALLOCATES the host inputs (non-null handle, +// non-zero device address, size == count·sizeof(entry)). +// - Growing PAST the current capacity REALLOCATES (new VkBuffer handle, new +// address, larger size) — the only case that still pays the realloc. +// - SHRINKING reuses the existing allocation unchanged (same handle, same +// address, same size) — the core win of this issue, and the case the +// pre-fix code reallocated on. +// - Growing back up but still WITHIN the high-water capacity also reuses it. +// - Growing to EXACTLY the capacity reuses it (boundary: count > capacity, +// not >=). +// - instanceBuffer and metadataBuffer grow in lockstep (one capacity check +// governs both). +// - A same-count rebuild takes the refit (UPDATE) path: builtInstanceCount +// is unchanged and the inputs are obviously not reallocated. +// - builtInstanceCount tracks the live count across every build (the AS +// itself is still rebuilt on a count change — the fix only spares the two +// host buffers, not the AS storage/scratch). +// - The Vulkan validation layer reports ZERO errors across all of the above +// — the strongest check that feeding an oversized instance buffer to the +// AS build (with tlasRangeInfo.primitiveCount < capacity) is spec-correct. +// +// Validation layers are required for the last check to be meaningful; the build +// marks this test as needing the SDK layers. + +#include "vulkan/vulkan.h" +#include + +import Crafter.Graphics; +import Crafter.Math; +import std; + +using namespace Crafter; + +namespace { + +int failures = 0; + +void Check(bool ok, std::string_view what) { + std::println("{} {}", ok ? "PASS" : "FAIL", what); + if (!ok) ++failures; +} + +// One-time command buffer helpers — record a build, submit, block. +VkCommandBuffer BeginCmd() { + VkCommandBufferAllocateInfo allocInfo { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = Device::commandPool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }; + VkCommandBuffer cmd = VK_NULL_HANDLE; + Device::CheckVkResult(vkAllocateCommandBuffers(Device::device, &allocInfo, &cmd)); + VkCommandBufferBeginInfo beginInfo { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + Device::CheckVkResult(vkBeginCommandBuffer(cmd, &beginInfo)); + return cmd; +} + +void SubmitWait(VkCommandBuffer cmd) { + Device::CheckVkResult(vkEndCommandBuffer(cmd)); + VkSubmitInfo submitInfo { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &cmd, + }; + Device::CheckVkResult(vkQueueSubmit(Device::queue, 1, &submitInfo, VK_NULL_HANDLE)); + Device::CheckVkResult(vkQueueWaitIdle(Device::queue)); + vkFreeCommandBuffers(Device::device, Device::commandPool, 1, &cmd); +} + +// A unit cube (8 verts, 12 triangles) — enough topology for a real BLAS. +std::vector> CubeVerts(float s) { + return { + {-s,-s,-s}, { s,-s,-s}, { s, s,-s}, {-s, s,-s}, + {-s,-s, s}, { s,-s, s}, { s, s, s}, {-s, s, s}, + }; +} +std::vector CubeIndices() { + return { + 0,1,2, 0,2,3, 4,6,5, 4,7,6, + 0,4,5, 0,5,1, 3,2,6, 3,6,7, + 1,5,6, 1,6,2, 0,3,7, 0,7,4, + }; +} + +// One TLAS instance referencing `blasAddr`, identity transform, visible mask. +VkAccelerationStructureInstanceKHR MakeInstance(VkDeviceAddress blasAddr) { + VkAccelerationStructureInstanceKHR inst{}; + inst.transform = VkTransformMatrixKHR{{ + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + }}; + inst.mask = 0xFF; + inst.accelerationStructureReference = blasAddr; + return inst; +} + +// Register exactly `n` elements from a stable (reserved, never-reallocated) +// pool. Clears the current registration first so the live count is exactly n. +void SetCount(std::vector& pool, std::uint32_t n) { + while (!RenderingElement3D::elements.empty()) { + RenderingElement3D::Remove(RenderingElement3D::elements.back()); + } + for (std::uint32_t i = 0; i < n; ++i) { + RenderingElement3D::Add(&pool[i]); + } +} + +// Build the frame-0 TLAS for the currently-registered elements. +void BuildOnce() { + VkCommandBuffer cmd = BeginCmd(); + RenderingElement3D::BuildTLAS(cmd, 0); + SubmitWait(cmd); +} + +// Snapshot of the host-input buffer identity, for before/after comparison. +struct InputSnapshot { + VkBuffer instBuf; + VkDeviceAddress instAddr; + std::uint32_t instSize; + VkBuffer metaBuf; + VkDeviceAddress metaAddr; + std::uint32_t metaSize; +}; +InputSnapshot Snapshot() { + auto& tlas = RenderingElement3D::tlases[0]; + return { + tlas.instanceBuffer.buffer, tlas.instanceBuffer.address, tlas.instanceBuffer.size, + tlas.metadataBuffer.buffer, tlas.metadataBuffer.address, tlas.metadataBuffer.size, + }; +} + +// Both host inputs untouched (same VkBuffer handle, address and size). +bool Reused(const InputSnapshot& a, const InputSnapshot& b) { + return a.instBuf == b.instBuf && a.instAddr == b.instAddr && a.instSize == b.instSize + && a.metaBuf == b.metaBuf && a.metaAddr == b.metaAddr && a.metaSize == b.metaSize; +} + +constexpr std::uint32_t kEntrySize = sizeof(VkAccelerationStructureInstanceKHR); + +} // namespace + +int main() { + Device::Initialize(); + Device::validationErrorCount = 0; + + // One real cube BLAS that every TLAS instance references. + Mesh cube; + { + auto verts = CubeVerts(1.0f); + auto idx = CubeIndices(); + VkCommandBuffer cmd = BeginCmd(); + cube.Build(verts, idx, cmd, RTBuildOptions{ .allowUpdate = true }); + SubmitWait(cmd); + } + Check(cube.blasAddr != 0, "cube BLAS produced a non-zero blasAddr"); + + // Stable backing store for the elements. Reserve to the max count this + // test ever registers so the vector never reallocates (the static + // `elements` array holds raw pointers into it). + constexpr std::uint32_t kMaxElems = 32; + std::vector pool(kMaxElems); + for (auto& e : pool) e.instance = MakeInstance(cube.blasAddr); + + // ── 1. First build at 4 instances → allocates the host inputs. ────────── + SetCount(pool, 4); + BuildOnce(); + InputSnapshot s4 = Snapshot(); + Check(s4.instBuf != VK_NULL_HANDLE, "first build allocated the instance buffer"); + Check(s4.metaBuf != VK_NULL_HANDLE, "first build allocated the metadata buffer"); + Check(s4.instAddr != 0, "instance buffer has a non-zero device address"); + Check(s4.instSize == 4 * kEntrySize, "instance buffer sized for 4 entries"); + Check(RenderingElement3D::tlases[0].builtInstanceCount == 4, + "builtInstanceCount == 4 after first build"); + + // ── 2. Grow to 16 (past capacity) → REALLOCATES. ──────────────────────── + SetCount(pool, 16); + BuildOnce(); + InputSnapshot s16 = Snapshot(); + Check(s16.instBuf != s4.instBuf, "growing past capacity reallocated the instance buffer"); + Check(s16.metaBuf != s4.metaBuf, "growing past capacity reallocated the metadata buffer"); + Check(s16.instSize == 16 * kEntrySize, "instance buffer grew to 16 entries"); + Check(RenderingElement3D::tlases[0].builtInstanceCount == 16, + "builtInstanceCount == 16 after growth"); + + // ── 3. Shrink to 2 → REUSES the 16-entry allocation (the core win). ───── + SetCount(pool, 2); + BuildOnce(); + InputSnapshot s2 = Snapshot(); + Check(Reused(s16, s2), + "shrinking to 2 reused the existing buffers (no realloc — high-water mark)"); + Check(s2.instSize == 16 * kEntrySize, + "instance buffer kept its 16-entry high-water size after shrink"); + Check(RenderingElement3D::tlases[0].builtInstanceCount == 2, + "builtInstanceCount tracks the live count (2) even on the reuse path"); + + // ── 4. Grow to 10 (still within the high-water capacity) → REUSES. ────── + SetCount(pool, 10); + BuildOnce(); + InputSnapshot s10 = Snapshot(); + Check(Reused(s16, s10), + "growing to 10 (≤ capacity 16) reused the existing buffers"); + + // ── 5. Grow to exactly 16 (== capacity) → REUSES (boundary: > not >=). ── + SetCount(pool, 16); + BuildOnce(); + InputSnapshot s16b = Snapshot(); + Check(Reused(s16, s16b), + "growing to exactly the capacity (16) reused the existing buffers"); + + // ── 6. Same count again → refit (UPDATE) path, inputs untouched. ──────── + BuildOnce(); + InputSnapshot s16c = Snapshot(); + Check(Reused(s16, s16c), "same-count rebuild left the inputs untouched"); + Check(RenderingElement3D::tlases[0].builtInstanceCount == 16, + "same-count rebuild kept builtInstanceCount == 16 (took the refit path)"); + + // ── 7. Grow to 32 (past the high-water) → REALLOCATES again. ──────────── + SetCount(pool, 32); + BuildOnce(); + InputSnapshot s32 = Snapshot(); + Check(s32.instBuf != s16.instBuf, "growing past the high-water (32) reallocated again"); + Check(s32.instSize == 32 * kEntrySize, "instance buffer grew to 32 entries"); + + // Unregister everything so nothing dangles past the pool's lifetime. + SetCount(pool, 0); + + Check(Device::validationErrorCount == 0, + std::format("no Vulkan validation errors ({} seen)", Device::validationErrorCount)); + + // Tear down the frame-0 TLAS while the Vulkan device is still alive. The + // static tlases[] array is destroyed at process exit, by which point the + // device (also static) may already be gone — so its VulkanBuffer + // destructors would fault. Releasing here keeps the exit path clean. + { + auto& t0 = RenderingElement3D::tlases[0]; + if (t0.accelerationStructure != VK_NULL_HANDLE) { + Device::vkDestroyAccelerationStructureKHR(Device::device, t0.accelerationStructure, nullptr); + t0.accelerationStructure = VK_NULL_HANDLE; + } + if (t0.instanceBuffer.buffer != VK_NULL_HANDLE) t0.instanceBuffer.Clear(); + if (t0.metadataBuffer.buffer != VK_NULL_HANDLE) t0.metadataBuffer.Clear(); + if (t0.scratchBuffer.buffer != VK_NULL_HANDLE) t0.scratchBuffer.Clear(); + if (t0.buffer.buffer != VK_NULL_HANDLE) t0.buffer.Clear(); + } + + if (failures != 0) { + std::println("{} check(s) failed", failures); + return EXIT_FAILURE; + } + std::println("all checks passed"); + return EXIT_SUCCESS; +} From 6d7ad87e38c2561ab019bb41c69281ee862f1b27 Mon Sep 17 00:00:00 2001 From: catbot Date: Tue, 16 Jun 2026 18:29:24 +0000 Subject: [PATCH 11/47] perf(ui): flush only the written descriptor range, not the whole heap (#61) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WriteBufferDescriptor / WriteSampledImageDescriptor / RegisterSampler / WriteSwapchainDescriptors each FlushDevice()'d the entire multi-KB per-frame descriptor heap after writing one few-byte descriptor. Add a ranged VulkanBuffer::FlushDevice(offset, bytes) that flushes only the touched bytes, rounding the range outward to nonCoherentAtomSize as vkFlushMappedMemoryRanges requires (the WHOLE_SIZE path sidestepped that). The coherent-memory gate from issue #60 is preserved — coherent memory still skips the flush entirely. The descriptor writers now self-flush their written range, so the redundant whole-heap flushes in UIRenderer::Initialize and the resize callback are gone. Rounding lives in AlignMappedFlushRange (pure math) and is unit-tested without a device in the new VulkanBufferRangedFlush test; the change was also verified end-to-end by running HelloUI on a real GPU with validation layers (font-atlas glyphs, sampler and storage-image descriptors all flush correctly, no VUIDs). Co-Authored-By: Claude Opus 4.8 --- implementations/Crafter.Graphics-Device.cpp | 4 + implementations/Crafter.Graphics-UI.cpp | 32 ++++- interfaces/Crafter.Graphics-Device.cppm | 8 ++ interfaces/Crafter.Graphics-VulkanBuffer.cppm | 48 +++++++ project.cpp | 27 ++++ tests/VulkanBufferRangedFlush/main.cpp | 124 ++++++++++++++++++ 6 files changed, 236 insertions(+), 7 deletions(-) create mode 100644 tests/VulkanBufferRangedFlush/main.cpp diff --git a/implementations/Crafter.Graphics-Device.cpp b/implementations/Crafter.Graphics-Device.cpp index 25b18fb..7428f29 100644 --- a/implementations/Crafter.Graphics-Device.cpp +++ b/implementations/Crafter.Graphics-Device.cpp @@ -604,6 +604,10 @@ void Device::Initialize() { }; vkGetPhysicalDeviceProperties2(physDevice, &properties2); + // Cache the flush/invalidate alignment for ranged host-memory flushes + // (VulkanBuffer::FlushDevice(offset,bytes)). + nonCoherentAtomSize = properties2.properties.limits.nonCoherentAtomSize; + // NVIDIA's brand-new VK_EXT_descriptor_heap acceleration-structure read // path faults (see #7); enable the SPIR-V rewrite workaround there. Other // drivers (and any future fixed NVIDIA driver, once this gate is removed) diff --git a/implementations/Crafter.Graphics-UI.cpp b/implementations/Crafter.Graphics-UI.cpp index 4ec52e8..00f131e 100644 --- a/implementations/Crafter.Graphics-UI.cpp +++ b/implementations/Crafter.Graphics-UI.cpp @@ -67,16 +67,16 @@ void UIRenderer::Initialize(Window& window, GraphicsDescriptorHeap& heap, Graphi WriteFontAtlasDescriptor(); } - // Flush the host-mapped descriptor heaps so the GPU sees what we wrote. - for (auto& h : heap_->resourceHeap) h.FlushDevice(); - for (auto& h : heap_->samplerHeap) h.FlushDevice(); + // No whole-heap flush here: WriteSwapchainDescriptors / WriteFontAtlasDescriptor + // / RegisterSampler each flush their own written byte range above, so the GPU + // already sees everything written during setup. // Re-bind the swapchain image descriptors after every resize. Window // already drained the queue and rebuilt the imageViews[] before // firing the event, so vkWriteResourceDescriptorsEXT is safe here. + // WriteSwapchainDescriptors flushes its own range, so no extra flush needed. resizeSub_.SetEvent(&window.onResize, [this]() { WriteSwapchainDescriptors(); - for (auto& h : heap_->resourceHeap) h.FlushDevice(); }); (void)initCmd; // reserved for future image-layout tweaks @@ -315,6 +315,12 @@ void UIRenderer::WriteSwapchainDescriptors() { Device::vkWriteResourceDescriptorsEXT( Device::device, Window::numFrames, resources.data(), destinations.data() ); + // Only the one image descriptor at outImageSlot_ changed in each per-frame + // heap (same slot offset in every frame), so flush just that byte range + // rather than the whole multi-KB heap. + const std::uint32_t off = heap_->ImageByteOffset(outImageSlot_); + const std::uint32_t sz = Device::descriptorHeapProperties.imageDescriptorSize; + for (auto& h : heap_->resourceHeap) h.FlushDevice(off, sz); } void UIRenderer::WriteFontAtlasDescriptor() { @@ -366,7 +372,11 @@ void UIRenderer::WriteSampledImageDescriptor(std::uint16_t slot, Device::vkWriteResourceDescriptorsEXT( Device::device, Window::numFrames, resources.data(), destinations.data() ); - for (auto& h : heap_->resourceHeap) h.FlushDevice(); + // One image descriptor written per heap, at the same slot offset — flush + // just that range rather than the whole heap. + const std::uint32_t off = heap_->ImageByteOffset(slot); + const std::uint32_t sz = Device::descriptorHeapProperties.imageDescriptorSize; + for (auto& h : heap_->resourceHeap) h.FlushDevice(off, sz); } void UIRenderer::WriteBufferDescriptor(std::uint16_t slot, VkDeviceAddress address, std::uint32_t size) { @@ -389,7 +399,11 @@ void UIRenderer::WriteBufferDescriptor(std::uint16_t slot, VkDeviceAddress addre Device::vkWriteResourceDescriptorsEXT( Device::device, Window::numFrames, resources.data(), destinations.data() ); - for (auto& h : heap_->resourceHeap) h.FlushDevice(); + // One buffer descriptor written per heap, at the same slot offset — flush + // just that range rather than the whole heap. + const std::uint32_t off = heap_->BufferByteOffset(slot); + const std::uint32_t sz = Device::descriptorHeapProperties.bufferDescriptorSize; + for (auto& h : heap_->resourceHeap) h.FlushDevice(off, sz); } SamplerSlot UIRenderer::RegisterSampler(const VkSamplerCreateInfo& info) { @@ -406,7 +420,11 @@ SamplerSlot UIRenderer::RegisterSampler(const VkSamplerCreateInfo& info) { Device::vkWriteSamplerDescriptorsEXT( Device::device, Window::numFrames, infos.data(), destinations.data() ); - for (auto& h : heap_->samplerHeap) h.FlushDevice(); + // One sampler descriptor written per heap, at the same slot offset — flush + // just that range rather than the whole sampler heap. + const std::uint32_t off = heap_->SamplerByteOffset(range.firstElement); + const std::uint32_t sz = Device::descriptorHeapProperties.samplerDescriptorSize; + for (auto& h : heap_->samplerHeap) h.FlushDevice(off, sz); return SamplerSlot{heap_, range.firstElement}; } diff --git a/interfaces/Crafter.Graphics-Device.cppm b/interfaces/Crafter.Graphics-Device.cppm index 11853d2..be8b8e2 100644 --- a/interfaces/Crafter.Graphics-Device.cppm +++ b/interfaces/Crafter.Graphics-Device.cppm @@ -161,6 +161,14 @@ export namespace Crafter { inline static VkPhysicalDeviceMemoryProperties memoryProperties; + // VkPhysicalDeviceLimits::nonCoherentAtomSize — the alignment (in bytes, + // a power of two) that vkFlushMappedMemoryRanges / vkInvalidateMappedMemoryRanges + // require for the offset and size of a sub-buffer range. Whole-buffer + // (VK_WHOLE_SIZE) flushes sidestep it; ranged flushes must round outward + // to it (see VulkanBuffer::FlushDevice(offset,bytes)). Populated at device + // creation; defaults to 1 so the rounding math is well-defined before then. + inline static VkDeviceSize nonCoherentAtomSize = 1; + inline static VkPhysicalDeviceDescriptorHeapPropertiesEXT descriptorHeapProperties = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_PROPERTIES_EXT }; diff --git a/interfaces/Crafter.Graphics-VulkanBuffer.cppm b/interfaces/Crafter.Graphics-VulkanBuffer.cppm index ce0d862..ae66111 100644 --- a/interfaces/Crafter.Graphics-VulkanBuffer.cppm +++ b/interfaces/Crafter.Graphics-VulkanBuffer.cppm @@ -29,10 +29,35 @@ import std; import :Device; namespace Crafter { + // Round a host-write flush range outward to nonCoherentAtomSize boundaries — + // the alignment vkFlushMappedMemoryRanges demands for a sub-buffer range + // (whole-buffer VK_WHOLE_SIZE calls sidestep it). `atom` is + // VkPhysicalDeviceLimits::nonCoherentAtomSize (a power of two ≥ 1) and + // `mappingSize` is the byte size the memory was mapped with (the allocation + // size). The start rounds down and the end rounds up to atom multiples; the + // end is then clamped to `mappingSize`. Clamping is what keeps the range + // valid even when `mappingSize` itself is not atom-aligned: the spec permits + // a non-atom-multiple size only when offset+size equals the mapping size, and + // the clamp lands exactly on that exception. Pure math, so it is unit-tested + // without a device. + export struct MappedFlushRange { VkDeviceSize offset; VkDeviceSize size; }; + export constexpr MappedFlushRange AlignMappedFlushRange( + VkDeviceSize offset, VkDeviceSize bytes, + VkDeviceSize atom, VkDeviceSize mappingSize) { + VkDeviceSize begin = (offset / atom) * atom; + VkDeviceSize end = ((offset + bytes + atom - 1) / atom) * atom; + if (end > mappingSize) end = mappingSize; + return { begin, end - begin }; + } + export class VulkanBufferBase { public: VkDeviceAddress address; std::uint32_t size; + // Byte size the memory was mapped with — equal to the allocation's + // VkMemoryRequirements::size, which is ≥ `size` and what ranged flushes + // clamp their upper bound to. Only meaningful for mapped buffers. + VkDeviceSize mappedSize = 0; VkBuffer buffer = VK_NULL_HANDLE; VkDeviceMemory memory; // Property flags of the memory type actually chosen by GetMemoryType — @@ -108,6 +133,7 @@ namespace Crafter { address = vkGetBufferDeviceAddress(Device::device, &addressInfo); if constexpr(Mapped) { + mappedSize = memReqs.size; Device::CheckVkResult(vkMapMemory(Device::device, memory, 0, memReqs.size, 0, reinterpret_cast(&(VulkanBufferMappedConditional::value)))); } } @@ -184,6 +210,27 @@ namespace Crafter { vkFlushMappedMemoryRanges(Device::device, 1, &range); } + // Flush only the host writes in [offset, offset+bytes) to the device, + // instead of the whole buffer. Use after touching a small sub-range + // (e.g. one descriptor in a multi-KB descriptor heap) so cache + // maintenance scales with the bytes actually written. The range is + // rounded outward to nonCoherentAtomSize as the Vulkan spec requires. + // No-op on coherent memory, same gate as the whole-buffer FlushDevice(). + void FlushDevice(VkDeviceSize offset, VkDeviceSize bytes) requires(Mapped) { + if (memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) { + return; + } + MappedFlushRange r = AlignMappedFlushRange( + offset, bytes, Device::nonCoherentAtomSize, mappedSize); + VkMappedMemoryRange range = { + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = memory, + .offset = r.offset, + .size = r.size + }; + vkFlushMappedMemoryRanges(Device::device, 1, &range); + } + void FlushDevice(VkCommandBuffer cmd, VkAccessFlags dstAccessMask, VkPipelineStageFlags dstStageMask) requires(Mapped) { FlushDevice(); VkBufferMemoryBarrier barrier = { @@ -227,6 +274,7 @@ namespace Crafter { buffer = other.buffer; memory = other.memory; size = other.size; + mappedSize = other.mappedSize; memoryPropertyFlagsChosen = other.memoryPropertyFlagsChosen; other.buffer = VK_NULL_HANDLE; address = other.address; diff --git a/project.cpp b/project.cpp index 5cab540..e1e63f8 100644 --- a/project.cpp +++ b/project.cpp @@ -472,6 +472,33 @@ extern "C" Configuration CrafterBuildProject(std::span a fc.GetInterfacesAndImplementations(ifaces, flushImpls); cfg.tests.push_back(std::move(flushTest)); + // Ranged FlushDevice: UI descriptor registration now flushes only the + // written descriptor byte range instead of the whole heap, rounding the + // range outward to nonCoherentAtomSize via AlignMappedFlushRange. The + // rounding is pure math and the coherent gate is pure logic, so this + // test drives both directly with no GPU device. + Test rangedFlushTest; + Configuration& rfc = rangedFlushTest.config; + rfc.path = cfg.path; + rfc.name = "VulkanBufferRangedFlush"; + rfc.outputName = "VulkanBufferRangedFlush"; + rfc.type = ConfigurationType::Executable; + rfc.target = cfg.target; + rfc.march = cfg.march; + rfc.mtune = cfg.mtune; + rfc.debug = cfg.debug; + rfc.sysroot = cfg.sysroot; + rfc.dependencies = cfg.dependencies; + rfc.externalDependencies = cfg.externalDependencies; + rfc.compileFlags = cfg.compileFlags; + rfc.linkFlags = cfg.linkFlags; + rfc.defines = cfg.defines; + rfc.cFiles = cfg.cFiles; + std::vector rangedFlushImpls(impls.begin(), impls.end()); + rangedFlushImpls.emplace_back("tests/VulkanBufferRangedFlush/main"); + rfc.GetInterfacesAndImplementations(ifaces, rangedFlushImpls); + cfg.tests.push_back(std::move(rangedFlushTest)); + // Issue #89: Device::PreferDirectDeviceWrite chooses the upload strategy // for a CPU-written, GPU-read buffer — direct HOST_VISIBLE|DEVICE_LOCAL // map+write on ReBAR/UMA vs. staged-into-pure-DEVICE_LOCAL on a small diff --git a/tests/VulkanBufferRangedFlush/main.cpp b/tests/VulkanBufferRangedFlush/main.cpp new file mode 100644 index 0000000..2dd5ff8 --- /dev/null +++ b/tests/VulkanBufferRangedFlush/main.cpp @@ -0,0 +1,124 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Regression test for the ranged FlushDevice (UI descriptor registration): +// WriteBufferDescriptor / WriteSampledImageDescriptor / RegisterSampler used to +// FlushDevice() the whole multi-KB descriptor heap after writing one few-byte +// descriptor. They now flush only the written byte range via +// VulkanBuffer::FlushDevice(offset, bytes), which rounds the range outward to +// VkPhysicalDeviceLimits::nonCoherentAtomSize (vkFlushMappedMemoryRanges rejects +// an unaligned sub-buffer offset/size). +// +// The rounding lives in AlignMappedFlushRange — pure math with no Vulkan +// dependency — so this drives it directly with no GPU device, and also confirms +// the ranged FlushDevice keeps the issue-#60 coherent-memory gate (a real flush +// would fault with Device::device == VK_NULL_HANDLE). + +#include +#include "vulkan/vulkan.h" + +import Crafter.Graphics; +import std; +using namespace Crafter; + +namespace { + +int failures = 0; + +void Check(bool ok, std::string_view what) { + std::println("{} {}", ok ? "PASS" : "FAIL", what); + if (!ok) ++failures; +} + +constexpr auto HOST_VISIBLE = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; +constexpr auto HOST_COHERENT = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; +constexpr auto DEVICE_LOCAL = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + +} // namespace + +int main() { + Check(Device::device == VK_NULL_HANDLE, + "no Vulkan device created — a real flush would fault"); + + // ── AlignMappedFlushRange: the spec demands offset and size be multiples + // of nonCoherentAtomSize (atom). Start rounds down, end rounds up. ── + { + // A descriptor wholly inside one atom expands to cover that atom. + auto r = AlignMappedFlushRange(/*offset*/ 80, /*bytes*/ 16, + /*atom*/ 64, /*mappingSize*/ 4096); + Check(r.offset == 64 && r.size == 64, + "sub-atom range rounds out to the enclosing atom [64,128)"); + Check(r.offset % 64 == 0 && r.size % 64 == 0, + "rounded offset and size are atom multiples"); + } + { + // A range straddling an atom boundary expands both ways. + auto r = AlignMappedFlushRange(/*offset*/ 60, /*bytes*/ 8, + /*atom*/ 64, /*mappingSize*/ 4096); + Check(r.offset == 0 && r.size == 128, + "straddling range rounds out to [0,128)"); + } + { + // Already-aligned range is left exactly as is. + auto r = AlignMappedFlushRange(/*offset*/ 128, /*bytes*/ 64, + /*atom*/ 64, /*mappingSize*/ 4096); + Check(r.offset == 128 && r.size == 64, + "already atom-aligned range is unchanged"); + } + { + // atom == 1 (no coherence granularity): range passes through verbatim. + auto r = AlignMappedFlushRange(/*offset*/ 80, /*bytes*/ 16, + /*atom*/ 1, /*mappingSize*/ 4096); + Check(r.offset == 80 && r.size == 16, + "atom==1 leaves the range exact"); + } + { + // Near the end of a non-atom-aligned mapping: the rounded-up end is + // clamped to mappingSize so offset+size == mapping size (the spec's + // exception that permits a non-atom-multiple size). + auto r = AlignMappedFlushRange(/*offset*/ 4030, /*bytes*/ 8, + /*atom*/ 64, /*mappingSize*/ 4040); + Check(r.offset == 3968 && r.offset + r.size == 4040, + "tail range clamps its end to the mapping size"); + Check(r.offset % 64 == 0, + "clamped range still has an atom-aligned offset"); + } + + // ── Ranged FlushDevice keeps the coherent-memory gate (issue #60). ── + { + VulkanBuffer buf; + buf.memoryPropertyFlagsChosen = HOST_VISIBLE | HOST_COHERENT; + buf.mappedSize = 4096; + buf.FlushDevice(/*offset*/ 80, /*bytes*/ 16); // must early-return + Check(true, "ranged FlushDevice skips the Vulkan call on coherent memory"); + } + { + VulkanBuffer buf; + buf.memoryPropertyFlagsChosen = HOST_VISIBLE | DEVICE_LOCAL; + Check(!(buf.memoryPropertyFlagsChosen & HOST_COHERENT), + "non-coherent chosen type is not gated as coherent (ranged flush still required)"); + } + + if (failures != 0) { + std::println("{} check(s) failed", failures); + return EXIT_FAILURE; + } + std::println("all checks passed"); + return EXIT_SUCCESS; +} From 518509aa0e9c3acbedb92dcdabc0dfd34e889002 Mon Sep 17 00:00:00 2001 From: catbot Date: Tue, 16 Jun 2026 18:30:44 +0000 Subject: [PATCH 12/47] perf(image): batch final mip-chain layout transition (#70) The mip-generation upload path issued N+1 layout-transition barriers per chain. The final blit's destination level is never read again, so its dedicated TRANSFER_DST -> TRANSFER_SRC barrier was redundant: the level was transitioned to SRC only to keep the final SRC -> consumer transition uniform. Skip that last per-level barrier and fold the final level into the closing transition, which now batches two VkImageMemoryBarrier entries ([0, mipLevels-1) from TRANSFER_SRC, the last level from TRANSFER_DST) into a single vkCmdPipelineBarrier. One fewer barrier call per chain; the N-1 interleaved per-level barriers stay one-at-a-time since each is read by the next blit. Applied to both the host-upload and GPU-decompress Update paths. Adds the MipChainBarrierBatch regression test driving the pure barrier builder (no GPU device needed). Co-Authored-By: Claude Opus 4.8 --- interfaces/Crafter.Graphics-ImageVulkan.cppm | 57 ++++++++- project.cpp | 27 ++++ tests/MipChainBarrierBatch/main.cpp | 122 +++++++++++++++++++ 3 files changed, 202 insertions(+), 4 deletions(-) create mode 100644 tests/MipChainBarrierBatch/main.cpp diff --git a/interfaces/Crafter.Graphics-ImageVulkan.cppm b/interfaces/Crafter.Graphics-ImageVulkan.cppm index 9ba72a6..6630e2c 100644 --- a/interfaces/Crafter.Graphics-ImageVulkan.cppm +++ b/interfaces/Crafter.Graphics-ImageVulkan.cppm @@ -31,6 +31,41 @@ import :Decompress; import :VulkanBuffer; export namespace Crafter { + // Builds the batched final layout transition for a freshly-generated mip + // chain (mipLevels >= 2). After the blit loop, levels [0, mipLevels-1) sit + // in TRANSFER_SRC_OPTIMAL — each was read as a blit source — while the last + // level is still in TRANSFER_DST_OPTIMAL: it was written by the final blit + // and is never read, so it never needed a DST->SRC barrier of its own. + // Both groups move to the consumer `layout` in one vkCmdPipelineBarrier + // (two VkImageMemoryBarrier entries sharing src=TRANSFER / dst=consumer + // stage), shaving one barrier call off the previous N+1-per-chain count. + // The interleaved per-level barriers stay one-at-a-time — each is mandated + // because the next blit reads the level it transitions. Writes the entries + // into `out` and returns the count (always 2). + std::uint32_t BuildMipChainFinalBarriers(VkImage image, VkImageLayout layout, std::uint32_t mipLevels, std::array& out) { + auto fill = [&](VkImageMemoryBarrier& b, VkImageLayout oldLayout, std::uint32_t baseMip, std::uint32_t count, VkAccessFlags srcAccess) { + b = {}; + b.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + b.oldLayout = oldLayout; + b.newLayout = layout; + b.image = image; + b.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + b.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + b.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + b.subresourceRange.baseMipLevel = baseMip; + b.subresourceRange.levelCount = count; + b.subresourceRange.baseArrayLayer = 0; + b.subresourceRange.layerCount = 1; + b.srcAccessMask = srcAccess; + b.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + }; + // Levels [0, mipLevels-1): blit sources, currently TRANSFER_SRC. + fill(out[0], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 0, mipLevels - 1u, VK_ACCESS_TRANSFER_READ_BIT); + // Final level: the last blit's destination, still TRANSFER_DST. + fill(out[1], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipLevels - 1u, 1u, VK_ACCESS_TRANSFER_WRITE_BIT); + return 2; + } + template class ImageVulkan { public: @@ -160,10 +195,17 @@ export namespace Crafter { blit.dstOffsets[1] = { (int32_t)mipWidth, (int32_t)mipHeight, 1 }; vkCmdBlitImage(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR); - TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, i, 1); + // The final blit's destination is never read again, so it + // skips the DST->SRC barrier and is taken straight to the + // consumer layout by the batched final transition below. + if (i + 1 < mipLevels) { + TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, i, 1); + } } - TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, layout, VK_PIPELINE_STAGE_TRANSFER_BIT, consumerStage, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, 0, mipLevels); + std::array finalBarriers; + std::uint32_t finalCount = BuildMipChainFinalBarriers(image, layout, mipLevels, finalBarriers); + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, consumerStage, 0, 0, nullptr, 0, nullptr, finalCount, finalBarriers.data()); } else { TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, layout, VK_PIPELINE_STAGE_TRANSFER_BIT, consumerStage, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, 0, mipLevels); } @@ -297,9 +339,16 @@ export namespace Crafter { blit.dstOffsets[0] = { 0, 0, 0 }; blit.dstOffsets[1] = { (int32_t)mipWidth, (int32_t)mipHeight, 1 }; vkCmdBlitImage(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR); - TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, i, 1); + // The final blit's destination is never read again, so it + // skips the DST->SRC barrier and is taken straight to the + // consumer layout by the batched final transition below. + if (i + 1 < mipLevels) { + TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, i, 1); + } } - TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, layout, VK_PIPELINE_STAGE_TRANSFER_BIT, consumerStage, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, 0, mipLevels); + std::array finalBarriers; + std::uint32_t finalCount = BuildMipChainFinalBarriers(image, layout, mipLevels, finalBarriers); + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, consumerStage, 0, 0, nullptr, 0, nullptr, finalCount, finalBarriers.data()); } else { TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, layout, VK_PIPELINE_STAGE_TRANSFER_BIT, consumerStage, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, 0, mipLevels); } diff --git a/project.cpp b/project.cpp index d928210..ad47601 100644 --- a/project.cpp +++ b/project.cpp @@ -415,6 +415,33 @@ extern "C" Configuration CrafterBuildProject(std::span a mc.GetInterfacesAndImplementations(ifaces, memImpls); cfg.tests.push_back(std::move(memTest)); + // Issue #70: ImageVulkan's mip-chain upload folds the never-read final + // blit destination into a single batched final-transition barrier + // instead of giving it a dedicated DST->SRC barrier. The barrier set is + // built by BuildMipChainFinalBarriers, pure CPU logic over mip count and + // layout, so this test drives it directly — no GPU device at runtime. + Test mipBarrierTest; + Configuration& mbc = mipBarrierTest.config; + mbc.path = cfg.path; + mbc.name = "MipChainBarrierBatch"; + mbc.outputName = "MipChainBarrierBatch"; + mbc.type = ConfigurationType::Executable; + mbc.target = cfg.target; + mbc.march = cfg.march; + mbc.mtune = cfg.mtune; + mbc.debug = cfg.debug; + mbc.sysroot = cfg.sysroot; + mbc.dependencies = cfg.dependencies; + mbc.externalDependencies = cfg.externalDependencies; + mbc.compileFlags = cfg.compileFlags; + mbc.linkFlags = cfg.linkFlags; + mbc.defines = cfg.defines; + mbc.cFiles = cfg.cFiles; + std::vector mipBarrierImpls(impls.begin(), impls.end()); + mipBarrierImpls.emplace_back("tests/MipChainBarrierBatch/main"); + mbc.GetInterfacesAndImplementations(ifaces, mipBarrierImpls); + cfg.tests.push_back(std::move(mipBarrierTest)); + // Issue #47: the fused UI uber-kernel (shaders/ui-fused.comp.glsl) and // its C++ push-constant mirror UIFusedHeader. Compiles the real shader // with glslang, validates with spirv-val, and pins the push-constant diff --git a/tests/MipChainBarrierBatch/main.cpp b/tests/MipChainBarrierBatch/main.cpp new file mode 100644 index 0000000..4b12e9e --- /dev/null +++ b/tests/MipChainBarrierBatch/main.cpp @@ -0,0 +1,122 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Regression test for issue #70: ImageVulkan's mip-chain upload used to issue +// N+1 layout-transition barriers per chain, one vkCmdPipelineBarrier per call. +// The final blit's destination level is never read again, so its dedicated +// TRANSFER_DST -> TRANSFER_SRC barrier is redundant; the chain now folds that +// last level straight into the final transition, which batches two +// VkImageMemoryBarrier entries into a single vkCmdPipelineBarrier: +// - levels [0, mipLevels-1): TRANSFER_SRC_OPTIMAL -> consumer layout +// - level mipLevels-1 : TRANSFER_DST_OPTIMAL -> consumer layout +// BuildMipChainFinalBarriers is the pure builder for that batched barrier, so +// this test drives it directly with a sentinel image handle — no GPU device +// needed at runtime, mirroring MemoryTypeFallback / UploadStrategy. + +#include +#include "vulkan/vulkan.h" + +import Crafter.Graphics; +import std; +using namespace Crafter; + +namespace { + +int failures = 0; + +void Check(bool ok, std::string_view what) { + std::println("{} {}", ok ? "PASS" : "FAIL", what); + if (!ok) ++failures; +} + +// A non-null sentinel so we can assert the builder propagates the image handle +// into every barrier without owning a real VkImage. +VkImage SentinelImage() { + return reinterpret_cast(static_cast(0xC0FFEE)); +} + +// Common invariants every emitted barrier must satisfy regardless of which +// level group it covers. +void CheckCommon(const VkImageMemoryBarrier& b, VkImageLayout layout, std::string_view tag) { + Check(b.sType == VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, std::format("{}: sType", tag)); + Check(b.image == SentinelImage(), std::format("{}: image handle propagated", tag)); + Check(b.newLayout == layout, std::format("{}: newLayout is the consumer layout", tag)); + Check(b.dstAccessMask == VK_ACCESS_SHADER_READ_BIT, std::format("{}: dst access is shader read", tag)); + Check(b.srcQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED, std::format("{}: src queue ignored", tag)); + Check(b.dstQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED, std::format("{}: dst queue ignored", tag)); + Check(b.subresourceRange.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT, std::format("{}: color aspect", tag)); + Check(b.subresourceRange.baseArrayLayer == 0, std::format("{}: base array layer 0", tag)); + Check(b.subresourceRange.layerCount == 1, std::format("{}: single array layer", tag)); +} + +} // namespace + +int main() { + constexpr VkImageLayout LAYOUT = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + for (std::uint32_t mipLevels : {2u, 3u, 5u, 12u}) { + std::array barriers; + std::uint32_t count = BuildMipChainFinalBarriers(SentinelImage(), LAYOUT, mipLevels, barriers); + + // The whole point: the final transition is a single batched call of two + // barriers, never one-per-level. + Check(count == 2, std::format("mip={}: batched into exactly two barriers", mipLevels)); + + const VkImageMemoryBarrier& src = barriers[0]; + const VkImageMemoryBarrier& dst = barriers[1]; + CheckCommon(src, LAYOUT, std::format("mip={} src-group", mipLevels)); + CheckCommon(dst, LAYOUT, std::format("mip={} dst-group", mipLevels)); + + // Group 0: the blit sources, [0, mipLevels-1), coming from TRANSFER_SRC. + Check(src.oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + std::format("mip={}: source group transitions from TRANSFER_SRC", mipLevels)); + Check(src.srcAccessMask == VK_ACCESS_TRANSFER_READ_BIT, + std::format("mip={}: source group src access is transfer read", mipLevels)); + Check(src.subresourceRange.baseMipLevel == 0, + std::format("mip={}: source group starts at level 0", mipLevels)); + Check(src.subresourceRange.levelCount == mipLevels - 1, + std::format("mip={}: source group covers all but the last level", mipLevels)); + + // Group 1: the final blit's destination, still TRANSFER_DST, never read. + Check(dst.oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + std::format("mip={}: last level transitions from TRANSFER_DST", mipLevels)); + Check(dst.srcAccessMask == VK_ACCESS_TRANSFER_WRITE_BIT, + std::format("mip={}: last level src access is transfer write", mipLevels)); + Check(dst.subresourceRange.baseMipLevel == mipLevels - 1, + std::format("mip={}: last level starts at the final mip", mipLevels)); + Check(dst.subresourceRange.levelCount == 1, + std::format("mip={}: last level covers a single mip", mipLevels)); + + // The two groups must tile [0, mipLevels) with no gap and no overlap, or + // some level would be left in a transfer layout when the shader samples. + std::uint32_t covered = src.subresourceRange.levelCount + dst.subresourceRange.levelCount; + Check(covered == mipLevels, + std::format("mip={}: the two groups cover every level exactly once", mipLevels)); + Check(src.subresourceRange.baseMipLevel + src.subresourceRange.levelCount + == dst.subresourceRange.baseMipLevel, + std::format("mip={}: groups are contiguous (no gap/overlap)", mipLevels)); + } + + if (failures != 0) { + std::println("{} check(s) failed", failures); + return EXIT_FAILURE; + } + std::println("all checks passed"); + return EXIT_SUCCESS; +} From cb012d7068cf5e0effa23fc7c7194c1e6feb32b8 Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 13:21:12 +0000 Subject: [PATCH 13/47] fix(device): fence-keyed deferred resource-deletion queue (#101) Since #40 dropped the per-frame vkQueueWaitIdle, frames are pipelined: a resource the CPU is done with may still be read by the GPU for up to numFrames-1 more frames. VulkanBuffer::Resize's destroy-and-recreate path was therefore a live use-after-free (#63), no longer masked by the wait-idle. Device gains a monotonic, frame-counter-keyed deletion queue: EnqueueDeletion tags {buffer, memory} with the current frameCounter; ReclaimDeletions (called per frame after the fence wait) frees entries once framesInFlight frames have elapsed; DrainDeletions frees everything after a wait-idle. VulkanBuffer::DeferredClear hands handles to the queue and nulls the handle, and Resize uses it instead of immediate Clear(). Window::Render sets framesInFlight at init, reclaims after the per-image fence wait, bumps Device::frameCounter once per frame, and drains on the resize / OUT_OF_DATE wait-idle paths. The destructor keeps immediate Clear() (callers destroying mid-flight remain responsible, unchanged). Adds the DeferredDeletion test: drives the retire timing on a real headless device with real buffers, stepping Device::frameCounter to pin the exact reclaim frame, asserting validation stays silent. Co-Authored-By: Claude Opus 4.8 --- implementations/Crafter.Graphics-Device.cpp | 35 ++++ implementations/Crafter.Graphics-Window.cpp | 20 ++ interfaces/Crafter.Graphics-Device.cppm | 38 ++++ interfaces/Crafter.Graphics-VulkanBuffer.cppm | 18 +- project.cpp | 31 ++++ tests/DeferredDeletion/main.cpp | 171 ++++++++++++++++++ 6 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 tests/DeferredDeletion/main.cpp diff --git a/implementations/Crafter.Graphics-Device.cpp b/implementations/Crafter.Graphics-Device.cpp index cb09fe5..f96ddc8 100644 --- a/implementations/Crafter.Graphics-Device.cpp +++ b/implementations/Crafter.Graphics-Device.cpp @@ -1000,4 +1000,39 @@ bool Device::PreferDirectDeviceWrite(VkDeviceSize size) { // directWriteBudget caches the size-independent decision (CacheUploadStrategy): // 0 -> never direct, max -> ReBAR/UMA so any size, else the small-window cap. return directWriteBudget != 0 && size <= directWriteBudget; +} + +void Device::EnqueueDeletion(VkBuffer buffer, VkDeviceMemory memory) { + // Nothing to free for an already-null handle — avoids parking dead + // entries that ReclaimDeletions would have to skip over. + if (buffer == VK_NULL_HANDLE) return; + deletionQueue.push_back({ frameCounter, buffer, memory }); +} + +void Device::ReclaimDeletions() { + // Free every entry whose retire frame has been reached, compacting the + // survivors down in place. An entry tagged at frame F retires at + // F + framesInFlight: by the time the CPU has begun that many later + // frames (each gated by a per-image fence wait), single-queue submission + // order guarantees all GPU work from frame F is complete. + std::size_t kept = 0; + for (PendingDeletion& entry : deletionQueue) { + if (entry.retireAfter + framesInFlight <= frameCounter) { + vkDestroyBuffer(device, entry.buffer, nullptr); + vkFreeMemory(device, entry.memory, nullptr); + } else { + deletionQueue[kept++] = entry; + } + } + deletionQueue.resize(kept); +} + +void Device::DrainDeletions() { + // Unconditional: the caller has just wait-idled, so no in-flight GPU work + // can still reference any queued resource. + for (PendingDeletion& entry : deletionQueue) { + vkDestroyBuffer(device, entry.buffer, nullptr); + vkFreeMemory(device, entry.memory, nullptr); + } + deletionQueue.clear(); } \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Window.cpp b/implementations/Crafter.Graphics-Window.cpp index 5138839..0afe9d8 100644 --- a/implementations/Crafter.Graphics-Window.cpp +++ b/implementations/Crafter.Graphics-Window.cpp @@ -452,6 +452,11 @@ Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height Device::CheckVkResult(vkCreateSemaphore(Device::device, &semaphoreCreateInfo, nullptr, &imageAcquiredSemaphores[i])); } + // The deferred-deletion queue (issue #101) retires a resource after this + // many recorded frames — exactly the depth the per-frame fences (#40) + // allow in flight. Set it here, where numFrames is in scope. + Device::framesInFlight = numFrames; + // Per-frame info structs: everything that never varies between frames is // set here once. Render() patches the barriers' image/oldLayout and the // sync handles that follow currentBuffer/frameCounter (submitInfo's @@ -542,6 +547,9 @@ void Window::Resize(std::uint32_t newWidth, std::uint32_t newHeight) { // Caller (configure handler / WM_SIZE) runs between frames, but be // defensive: ensure no in-flight commands reference the old swapchain. Device::CheckVkResult(vkQueueWaitIdle(Device::queue)); + // The queue is idle, so every deferred resource is now safe to free + // regardless of its retire frame (issue #101). + Device::DrainDeletions(); #ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND if (wpViewport) { @@ -768,6 +776,7 @@ void Window::Render() { imageAcquired, (VkFence)nullptr, ¤tBuffer); if (acquire == VK_ERROR_OUT_OF_DATE_KHR) { Device::CheckVkResult(vkQueueWaitIdle(Device::queue)); + Device::DrainDeletions(); RecreateSwapchainAndImages(); onResize.Invoke(); acquire = vkAcquireNextImageKHR(Device::device, swapChain, UINT64_MAX, @@ -786,6 +795,12 @@ void Window::Render() { Device::CheckVkResult(vkWaitForFences(Device::device, 1, &waitFences[currentBuffer], VK_TRUE, UINT64_MAX)); Device::CheckVkResult(vkResetFences(Device::device, 1, &waitFences[currentBuffer])); + // Now that a frame's fence has been waited, free any deferred resources + // whose retire frame has been reached (issue #101). frameCounter is bumped + // at end of frame, so it still holds this frame's index here — exactly the + // value entries enqueued this frame are tagged with. + Device::ReclaimDeletions(); + Device::CheckVkResult(vkBeginCommandBuffer(drawCmdBuffers[currentBuffer], &cmdBufInfo)); // On an image's first use after (re)creating the swapchain it is still in @@ -902,6 +917,7 @@ void Window::Render() { // frame. The wait-idle here also re-settles every per-frame fence to a // signaled state, so the next Render()'s fence wait passes through. Device::CheckVkResult(vkQueueWaitIdle(Device::queue)); + Device::DrainDeletions(); RecreateSwapchainAndImages(); onResize.Invoke(); } else { @@ -911,6 +927,10 @@ void Window::Render() { // command-buffer reuse, so the CPU is free to acquire/record frame N+1 // while the GPU is still executing frame N. frameCounter++; + // Advance the global deferred-deletion clock too (issue #101). Kept + // separate from the per-window frameCounter above (which also keys the + // acquire-semaphore slot) so non-window code can enqueue against it. + Device::frameCounter++; } #ifdef CRAFTER_TIMING diff --git a/interfaces/Crafter.Graphics-Device.cppm b/interfaces/Crafter.Graphics-Device.cppm index e7530b9..95f7583 100644 --- a/interfaces/Crafter.Graphics-Device.cppm +++ b/interfaces/Crafter.Graphics-Device.cppm @@ -294,6 +294,44 @@ export namespace Crafter { inline static VkDeviceSize directWriteBudget = 0; static void CacheUploadStrategy(); + // ─── Fence-keyed deferred resource deletion (issue #101) ──────── + // Since #40 dropped the per-frame vkQueueWaitIdle, frames are + // pipelined: a buffer the CPU is "done" with may still be read by the + // GPU for up to framesInFlight-1 more frames. Destroying it + // immediately (as Clear() does) is then a use-after-free. Callers that + // free a buffer which may still be in flight — VulkanBuffer::Resize's + // reallocate path, VulkanBuffer::DeferredClear — hand the handles here + // instead, tagged with the current frameCounter; ReclaimDeletions frees + // an entry only once framesInFlight frames have elapsed, by which point + // single-queue submission order guarantees all GPU work that could + // reference it has completed. + struct PendingDeletion { + std::uint64_t retireAfter; // frameCounter value at enqueue time + VkBuffer buffer; + VkDeviceMemory memory; + }; + // Monotonic recorded-frame index, bumped once per Window::Render. Not + // the same as Window::frameCounter (which is per-window and also drives + // the acquire-semaphore slot) — this one is global so Mesh/Device code + // can enqueue too, not just the window loop. + inline static std::uint64_t frameCounter = 0; + // = Window::numFrames; set at window init. The number of frames that + // must elapse after an enqueue before the resource is safe to free. + // Zero until set, which only makes ReclaimDeletions more eager — and it + // is never called before a Window (which sets this) starts rendering. + inline static std::uint8_t framesInFlight = 0; + inline static std::vector deletionQueue; + // Tag {buffer, memory} for deletion after framesInFlight more frames. + // A VK_NULL_HANDLE buffer is ignored (nothing to free). + static void EnqueueDeletion(VkBuffer buffer, VkDeviceMemory memory); + // Free every entry whose retire frame has been reached + // (retireAfter + framesInFlight <= frameCounter). Call once per frame + // after waiting that frame's fence. + static void ReclaimDeletions(); + // Free every queued entry unconditionally. Call only after a wait-idle + // (resize / teardown), when no in-flight GPU work can reference them. + static void DrainDeletions(); + // ─── Wayland key repeat ──────────────────────────────────────── // TickKeyRepeats fires onRawKeyDown / onRawKeyHold / onTextInput on // the focused window for whichever key is currently repeating. diff --git a/interfaces/Crafter.Graphics-VulkanBuffer.cppm b/interfaces/Crafter.Graphics-VulkanBuffer.cppm index a130c1d..a499062 100644 --- a/interfaces/Crafter.Graphics-VulkanBuffer.cppm +++ b/interfaces/Crafter.Graphics-VulkanBuffer.cppm @@ -155,6 +155,18 @@ namespace Crafter { buffer = VK_NULL_HANDLE; } + // Like Clear(), but hands the destroy+free to Device's fence-keyed + // deletion queue instead of doing it immediately. Use when the buffer + // may still be read by an in-flight frame's GPU work — destroying it + // now would be a use-after-free, since frames are pipelined up to + // framesInFlight deep (issue #101). The handle is nulled immediately so + // this VulkanBuffer no longer owns it; vkFreeMemory (deferred) implicitly + // unmaps mapped memory, so no explicit vkUnmapMemory is needed here. + void DeferredClear() { + Device::EnqueueDeletion(buffer, memory); + buffer = VK_NULL_HANDLE; + } + void Resize(VkBufferUsageFlags2 usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count, VkMemoryPropertyFlags preferredPropertyFlags = 0) { // Reuse the existing allocation in place when the request still fits // and the fixed-at-create properties match: usage flags are @@ -172,8 +184,12 @@ namespace Crafter { size = requestedSize; return; } + // Defer the old allocation's destruction: an in-flight frame may + // still reference it (the #63 hazard, no longer masked by a + // per-frame wait-idle since #40). DeferredClear nulls the handle, + // so the Create below starts from a clean slate. if(buffer != VK_NULL_HANDLE) { - Clear(); + DeferredClear(); } Create(usageFlags, memoryPropertyFlags, count, preferredPropertyFlags); } diff --git a/project.cpp b/project.cpp index a08ce34..879108f 100644 --- a/project.cpp +++ b/project.cpp @@ -586,6 +586,37 @@ extern "C" Configuration CrafterBuildProject(std::span a rc.GetInterfacesAndImplementations(ifaces, resizeImpls); cfg.tests.push_back(std::move(resizeTest)); + // Issue #101: fence-keyed deferred resource-deletion queue. Since #40 + // dropped the per-frame wait-idle, destroying a buffer the GPU may + // still read (Resize's reallocate path) is a use-after-free. + // VulkanBuffer::DeferredClear / Resize now hand handles to Device's + // queue, which ReclaimDeletions frees only after framesInFlight frames + // and DrainDeletions frees on a wait-idle. The retire timing is driven + // on a real headless device (real buffers so the frees execute and the + // validation layer can object) by stepping Device::frameCounter — no + // swapchain/window needed, so it shares the native build settings. + Test deferredTest; + Configuration& dc = deferredTest.config; + dc.path = cfg.path; + dc.name = "DeferredDeletion"; + dc.outputName = "DeferredDeletion"; + dc.type = ConfigurationType::Executable; + dc.target = cfg.target; + dc.march = cfg.march; + dc.mtune = cfg.mtune; + dc.debug = cfg.debug; + dc.sysroot = cfg.sysroot; + dc.dependencies = cfg.dependencies; + dc.externalDependencies = cfg.externalDependencies; + dc.compileFlags = cfg.compileFlags; + dc.linkFlags = cfg.linkFlags; + dc.defines = cfg.defines; + dc.cFiles = cfg.cFiles; + std::vector deferredImpls(impls.begin(), impls.end()); + deferredImpls.emplace_back("tests/DeferredDeletion/main"); + dc.GetInterfacesAndImplementations(ifaces, deferredImpls); + cfg.tests.push_back(std::move(deferredTest)); + // Issue #89: Device::PreferDirectDeviceWrite chooses the upload strategy // for a CPU-written, GPU-read buffer — direct HOST_VISIBLE|DEVICE_LOCAL // map+write on ReBAR/UMA vs. staged-into-pure-DEVICE_LOCAL on a small diff --git a/tests/DeferredDeletion/main.cpp b/tests/DeferredDeletion/main.cpp new file mode 100644 index 0000000..49e8b18 --- /dev/null +++ b/tests/DeferredDeletion/main.cpp @@ -0,0 +1,171 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Issue #101: fence-keyed deferred resource-deletion queue. Since #40 dropped +// the per-frame vkQueueWaitIdle, a buffer the CPU is done with may still be +// read by the GPU for up to framesInFlight-1 more frames — so destroying it +// immediately (VulkanBuffer::Clear / the old Resize path) is a use-after-free. +// VulkanBuffer::DeferredClear() / Resize() now hand the handles to Device's +// deletion queue, tagged with the current frameCounter; ReclaimDeletions frees +// an entry only once framesInFlight frames have elapsed, and DrainDeletions +// (called after a wait-idle) frees everything unconditionally. +// +// The retire-timing is the load-bearing logic: free too early and it's the +// very UAF the queue exists to prevent; free too late and resources leak. This +// test drives it on a real headless Vulkan device (real VkBuffer/VkDeviceMemory +// so the frees actually execute and the validation layer can object), manually +// stepping Device::frameCounter to pin down the exact frame an entry is +// reclaimed on. It needs no swapchain/window — the queue itself is window-free. + +#include "vulkan/vulkan.h" +#include + +import Crafter.Graphics; +import std; + +using namespace Crafter; + +namespace { + +int failures = 0; + +void Check(bool ok, std::string_view what) { + std::println("{} {}", ok ? "PASS" : "FAIL", what); + if (!ok) ++failures; +} + +constexpr VkBufferUsageFlags2 USAGE = + VK_BUFFER_USAGE_2_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT; +constexpr auto DEVICE_LOCAL = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + +} // namespace + +int main() { + Device::Initialize(); + + // No Window here, so set the in-flight depth ourselves (Window::Render + // normally does this at init). Use the real window depth so the timing + // matches production. + Device::framesInFlight = Window::numFrames; + Device::frameCounter = 0; + Device::deletionQueue.clear(); + + // ── DeferredClear enqueues, nulls the handle, and retires after exactly + // framesInFlight frames ────────────────────────────────────────────── + { + VulkanBuffer buf; + buf.Create(USAGE, DEVICE_LOCAL, 16); + Check(buf.buffer != VK_NULL_HANDLE, "Create produced a real buffer handle"); + + buf.DeferredClear(); + Check(buf.buffer == VK_NULL_HANDLE, + "DeferredClear nulls the handle so the buffer no longer owns it"); + Check(Device::deletionQueue.size() == 1, + "DeferredClear enqueues exactly one pending deletion"); + + // Walk right up to the retire frame: still queued at every frame before + // frameCounter reaches retireAfter + framesInFlight. + for (std::uint64_t f = 0; f < Window::numFrames; ++f) { + Device::frameCounter = f; + Device::ReclaimDeletions(); + Check(Device::deletionQueue.size() == 1, + std::format("entry survives ReclaimDeletions at frame {} " + "(< retire frame {})", f, Window::numFrames)); + } + + // At retireAfter(0) + framesInFlight it is finally freed. + Device::frameCounter = Window::numFrames; + Device::ReclaimDeletions(); + Check(Device::deletionQueue.empty(), + std::format("entry is reclaimed once frameCounter reaches {}", + Window::numFrames)); + } + + // ── Resize's reallocate path defers the OLD allocation, not the new one ── + { + Device::frameCounter = 100; + Device::deletionQueue.clear(); + + VulkanBuffer buf; + buf.Create(USAGE, DEVICE_LOCAL, 4); + VkBuffer original = buf.buffer; + + // Grow past capacity → forces destroy+recreate, which must DEFER the old + // buffer (the #63 UAF this whole queue exists to close). + buf.Resize(USAGE, DEVICE_LOCAL, 64); + Check(buf.buffer != VK_NULL_HANDLE && buf.buffer != original, + "Resize past capacity allocates a fresh buffer"); + Check(Device::deletionQueue.size() == 1 + && Device::deletionQueue.front().buffer == original, + "Resize defers the OLD allocation to the deletion queue"); + Check(Device::deletionQueue.front().retireAfter == 100, + "deferred entry is tagged with the frameCounter at enqueue time"); + + // Not yet due (tagged at 100, retires at 100 + framesInFlight). + Device::frameCounter = 100 + Window::numFrames - 1; + Device::ReclaimDeletions(); + Check(Device::deletionQueue.size() == 1, + "old allocation is not freed before its retire frame"); + + Device::frameCounter = 100 + Window::numFrames; + Device::ReclaimDeletions(); + Check(Device::deletionQueue.empty(), + "old allocation is freed on its retire frame"); + + buf.Clear(); // immediate cleanup of the live buffer (no GPU work pending) + } + + // ── DrainDeletions frees everything regardless of retire frame ─────────── + { + Device::frameCounter = 0; + Device::deletionQueue.clear(); + + VulkanBuffer a; + VulkanBuffer b; + a.Create(USAGE, DEVICE_LOCAL, 8); + b.Create(USAGE, DEVICE_LOCAL, 8); + a.DeferredClear(); + b.DeferredClear(); + Check(Device::deletionQueue.size() == 2, "two entries queued"); + + // Far from any retire frame — ReclaimDeletions would free nothing here. + Device::DrainDeletions(); + Check(Device::deletionQueue.empty(), + "DrainDeletions frees every queued entry unconditionally"); + } + + // ── EnqueueDeletion ignores a null handle (nothing to free) ────────────── + { + Device::deletionQueue.clear(); + Device::EnqueueDeletion(VK_NULL_HANDLE, VK_NULL_HANDLE); + Check(Device::deletionQueue.empty(), + "EnqueueDeletion drops a VK_NULL_HANDLE buffer"); + } + + Check(Device::validationErrorCount == 0, + std::format("no Vulkan validation errors across the run ({} seen)", + Device::validationErrorCount)); + + if (failures != 0) { + std::println("{} check(s) failed", failures); + return EXIT_FAILURE; + } + std::println("all checks passed"); + return EXIT_SUCCESS; +} From a55f15d3324e3cde2c248b4530288383aecd9018 Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 17:34:29 +0000 Subject: [PATCH 14/47] perf(mesh): skip immutable index re-upload on RT refit UPDATE (#68) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A hardware acceleration-structure UPDATE cannot change topology, so the index buffer is immutable on the Refit UPDATE path. Re-uploading it was a wasted memcpy + FlushDevice + barrier of unchanged data every refit — genuinely per-frame for deforming meshes. Skip the index memcpy and its FlushDevice on the UPDATE branch; the RecordBLASBuild call still reads the stable, unchanged indexBuffer.address. The `indicies` span is now read only for its count. Document the API nuance: a bitwise-different but topologically-equivalent index array is ignored on refit, consistent with "a refit may only move vertex positions." The full-rebuild fallback path still re-uploads both buffers. Co-Authored-By: Claude Opus 4.8 --- implementations/Crafter.Graphics-Mesh.cpp | 12 ++++++++---- interfaces/Crafter.Graphics-Mesh.cppm | 11 +++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/implementations/Crafter.Graphics-Mesh.cpp b/implementations/Crafter.Graphics-Mesh.cpp index 203e028..baf1207 100644 --- a/implementations/Crafter.Graphics-Mesh.cpp +++ b/implementations/Crafter.Graphics-Mesh.cpp @@ -357,12 +357,16 @@ void Mesh::Refit(std::span> verticies, std::span)); - std::memcpy(indexBuffer.value, indicies.data(), indicies.size() * sizeof(std::uint32_t)); vertexBuffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); - indexBuffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); RecordBLASBuild(*this, static_cast(verticies.size()), static_cast(indicies.size()), buildFlags, /*update*/ true, cmd); } diff --git a/interfaces/Crafter.Graphics-Mesh.cppm b/interfaces/Crafter.Graphics-Mesh.cppm index c04f819..e5f1c6a 100644 --- a/interfaces/Crafter.Graphics-Mesh.cppm +++ b/interfaces/Crafter.Graphics-Mesh.cppm @@ -148,10 +148,13 @@ export namespace Crafter { // a hardware UPDATE-mode build — much cheaper than a rebuild, and the // BLAS handle / blasAddr are preserved, so TLAS instances referencing // it stay valid. A hardware update may only move vertex positions, - // not change connectivity; the indices are re-uploaded for symmetry - // with the rebuild path but must describe the same topology. Without - // allowUpdate (or if the counts changed) it falls back to a full - // rebuild. Lifetime contract matches Build: the spans need only + // not change connectivity, so on the UPDATE path only the vertex + // buffer is re-uploaded; the index buffer is left untouched (its + // contents are immutable for an UPDATE). The `indicies` span is read + // only for its count — a bitwise-different but topologically-equivalent + // index array is ignored on refit. Without allowUpdate (or if the + // counts changed) it falls back to a full rebuild, which does re-upload + // both buffers. Lifetime contract matches Build: the spans need only // outlive this call. Call this per frame to track a deforming mesh. void Refit(std::span> verticies, std::span indicies, VkCommandBuffer cmd); // Procedural analog of Refit: new object-space boxes, same count. From 8b008b49d58688cf5acfe90091ac39d3c513d7be Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 17:36:10 +0000 Subject: [PATCH 15/47] perf(mesh): release per-mesh scratch buffer for static BLAS (#66) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A static (!allowUpdate) mesh can never refit, so its per-mesh DEVICE_LOCAL scratch buffer is dead weight the moment the build's GPU work completes — yet it was kept alive for the mesh's whole lifetime. In many-mesh scenes (where static meshes dominate) this is real, accumulating VRAM waste. Release it via scratchBuffer.DeferredClear() right after recording a fresh build for a static mesh. The old "free-after-build is a UAF, the GPU is still building" hazard is gone: the fence-keyed deferred-deletion queue (#101/#102) retires the allocation only once the build's frame has passed its fence. A later Refit on a static mesh takes the rebuild fallback, whose Resize sees the nulled handle and re-Creates the scratch. Refit-capable (allowUpdate) meshes keep their scratch as before — an in-place UPDATE reuses it each frame. Extends the BLASBuildOptions hardware test to assert the scratch is retained for allowUpdate meshes and released for static ones (including across a static-mesh refit rebuild), with the validation layer confirming zero errors. Co-Authored-By: Claude Opus 4.8 --- implementations/Crafter.Graphics-Mesh.cpp | 17 +++++++++++++++++ tests/BLASBuildOptions/main.cpp | 16 ++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/implementations/Crafter.Graphics-Mesh.cpp b/implementations/Crafter.Graphics-Mesh.cpp index 203e028..a61c6b7 100644 --- a/implementations/Crafter.Graphics-Mesh.cpp +++ b/implementations/Crafter.Graphics-Mesh.cpp @@ -152,6 +152,23 @@ namespace { self.buildFlags = flags; self.builtPrimitiveCount = primitiveCount; + + // Static (!allowUpdate) meshes can never refit, so their per-mesh + // scratch is dead weight the moment this build's GPU work completes — + // release it instead of keeping a persistent DEVICE_LOCAL allocation + // per mesh (the waste that dominates many-mesh scenes, issue #66). The + // fresh build recorded above still reads scratchBuffer.address from + // this command buffer, so a plain Clear() would be a use-after-free; + // DeferredClear() retires the allocation only once the build's frame + // has passed its fence (the queue from #101/#102). A later Refit on a + // static mesh takes the rebuild fallback, whose Resize sees the nulled + // handle and re-Creates the scratch. Refit-capable meshes keep theirs: + // an in-place UPDATE reuses it every frame (build scratch ≥ update + // scratch, so no resize). Only applies to a fresh build — an UPDATE + // never reaches here for a static mesh. + if (!update && !self.allowUpdate) { + self.scratchBuffer.DeferredClear(); + } } void RecordBLASBuild(Mesh& self, std::uint32_t vertexCount, std::uint32_t indexCount, VkBuildAccelerationStructureFlagsKHR flags, bool update, VkCommandBuffer cmd) { diff --git a/tests/BLASBuildOptions/main.cpp b/tests/BLASBuildOptions/main.cpp index f4fed5f..e8520d8 100644 --- a/tests/BLASBuildOptions/main.cpp +++ b/tests/BLASBuildOptions/main.cpp @@ -128,6 +128,10 @@ int main() { Check((tri.buildFlags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR) != 0, "allowUpdate=true → ALLOW_UPDATE bit set"); Check(tri.builtPrimitiveCount == 12, "triangle BLAS reports 12 primitives"); + // allowUpdate=true keeps the scratch buffer alive so the in-place UPDATE + // refit below can reuse it (build scratch ≥ update scratch, no resize). + Check(tri.scratchBuffer.buffer != VK_NULL_HANDLE, + "allowUpdate=true → scratch retained for refit (issue #66)"); const VkDeviceAddress triAddrBefore = tri.blasAddr; const VkAccelerationStructureKHR triHandleBefore = tri.accelerationStructure; @@ -157,6 +161,12 @@ int main() { "FastBuild preference → PREFER_FAST_BUILD bit set"); Check((triFast.buildFlags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR) == 0, "allowUpdate=false → ALLOW_UPDATE bit clear"); + // A static mesh can never refit, so its scratch is released right after + // the build completes rather than kept as persistent VRAM (issue #66). + // DeferredClear nulls the handle immediately (the allocation itself is + // retired by the fence-keyed queue once the build's frame passes). + Check(triFast.scratchBuffer.buffer == VK_NULL_HANDLE, + "allowUpdate=false → scratch released after build (issue #66)"); // Refit without allowUpdate must still succeed via the rebuild fallback. { @@ -167,6 +177,10 @@ int main() { SubmitWait(cmd); } Check(triFast.blasAddr != 0, "Refit fallback rebuild still produced a valid BLAS"); + // The fallback rebuild re-Created the scratch (Resize saw the nulled + // handle) and, still being a static mesh, released it again afterwards. + Check(triFast.scratchBuffer.buffer == VK_NULL_HANDLE, + "static-mesh refit rebuild re-released its scratch (issue #66)"); // ── Procedural (AABB) BLAS: build with options, then refit. ───────── Mesh proc; @@ -182,6 +196,8 @@ int main() { } Check(proc.blasAddr != 0, "procedural Build produced a non-zero blasAddr"); Check(proc.builtPrimitiveCount == 2, "procedural BLAS reports 2 primitives"); + Check(proc.scratchBuffer.buffer != VK_NULL_HANDLE, + "procedural allowUpdate=true → scratch retained for refit (issue #66)"); const VkDeviceAddress procAddrBefore = proc.blasAddr; const VkAccelerationStructureKHR procHandleBefore = proc.accelerationStructure; From d8bc8a4e450914a0be75c4fb36aaa3da7ba037c3 Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 17:37:39 +0000 Subject: [PATCH 16/47] perf(rt): allocate TLAS instance buffer in BAR/VRAM, not system RAM (#65) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TLAS instance buffer is rebuilt/refit every frame and is co-written by both the CPU (host-authored fields) and the GPU (the compute shader writes the transform field in place for transformOwnedByGpu elements). Allocated HOST_VISIBLE | HOST_COHERENT (system RAM), both GPU accesses crossed PCIe: the compute write of the transform, and the AS build's read of the instances. Upgrade the request to HOST_VISIBLE with a best-effort DEVICE_LOCAL preference (BAR/VRAM), keeping the single shared, persistently-mapped buffer — no staging, no copy, no extra barrier. A whole-buffer staging copy was rejected: it would clobber the GPU-written transforms in this in-place co-write design. Now the compute write and AS build stay in local VRAM; the CPU's write-only, sequential, never-read-back field writes are the ideal write-combined BAR load. Correctness: - DEVICE_LOCAL is a preference only (depends on #59): GetMemoryType falls back to plain HOST_VISIBLE (the previous behaviour) on GPUs without resizable BAR. - HOST_COHERENT is dropped from the required set — the combined type may not be coherent. The existing FlushDevice(cmd, ...) already establishes host->build visibility and gates its flush-skip on the *chosen* type's flags (#60), so a non-coherent BAR type still flushes correctly. The barrier is never removed. metadataBuffer gets the same upgrade separately (#75). Extends the TLASHighWaterMark hardware test to assert the chosen memory type matches the HOST_VISIBLE | prefer-DEVICE_LOCAL request and lands on a DEVICE_LOCAL type where one is reachable; the existing zero-validation-errors check covers the dropped HOST_COHERENT path. Co-Authored-By: Claude Opus 4.8 --- .../Crafter.Graphics-RenderingElement3D.cpp | 17 +++++- tests/TLASHighWaterMark/main.cpp | 53 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/implementations/Crafter.Graphics-RenderingElement3D.cpp b/implementations/Crafter.Graphics-RenderingElement3D.cpp index 5eea69c..1da2a3a 100644 --- a/implementations/Crafter.Graphics-RenderingElement3D.cpp +++ b/implementations/Crafter.Graphics-RenderingElement3D.cpp @@ -106,9 +106,24 @@ void RenderingElement3D::BuildTLAS(VkCommandBuffer cmd, std::uint32_t index, RTB // STORAGE_BUFFER_BIT is required because the application's compute // shaders bind these buffers as storage SSBOs (e.g. to write // per-instance transforms directly into the TLAS instance data). + // + // instanceBuffer prefers HOST_VISIBLE | DEVICE_LOCAL (BAR/VRAM) so the + // two per-frame GPU accesses stay on-chip instead of crossing PCIe: the + // compute shader writes the transform field in place, and the AS build + // reads the whole instance. The CPU still writes the host-authored + // fields below — those are write-only, sequential, never-read-back + // writes, the ideal write-combined BAR workload. DEVICE_LOCAL is a + // best-effort preference: GetMemoryType falls back to plain + // HOST_VISIBLE (the previous behaviour) when no combined type exists + // (no resizable BAR). HOST_COHERENT is intentionally dropped from the + // required set — the combined type may not be coherent, and the + // FlushDevice(cmd, ...) below already establishes host->build + // visibility, gating its flush-skip on the *chosen* type's flags. The + // single shared buffer is preserved: a whole-buffer copy would clobber + // the GPU-written transforms, so staging is deliberately avoided. if (tlas.instanceBuffer.buffer == VK_NULL_HANDLE || primitiveCount > tlas.instanceBuffer.size / sizeof(VkAccelerationStructureInstanceKHR)) { - tlas.instanceBuffer.Resize(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, primitiveCount); + tlas.instanceBuffer.Resize(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, primitiveCount, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); tlas.metadataBuffer.Resize(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, primitiveCount); } } diff --git a/tests/TLASHighWaterMark/main.cpp b/tests/TLASHighWaterMark/main.cpp index d35de8a..55bd544 100644 --- a/tests/TLASHighWaterMark/main.cpp +++ b/tests/TLASHighWaterMark/main.cpp @@ -50,6 +50,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // — the strongest check that feeding an oversized instance buffer to the // AS build (with tlasRangeInfo.primitiveCount < capacity) is spec-correct. // +// Issue #65: this test also guards the instance buffer's memory-type request — +// HOST_VISIBLE (required) with a best-effort DEVICE_LOCAL preference and no +// mandatory HOST_COHERENT, so the per-frame GPU accesses stay in local VRAM +// (BAR) rather than crossing PCIe. See the assertion block after the first +// build. The zero-validation-errors check below also covers the dropped +// HOST_COHERENT: the FlushDevice(cmd, ...) in BuildTLAS gates on the chosen +// type's coherency flag, so a non-coherent BAR type still flushes correctly. +// // Validation layers are required for the last check to be meaningful; the build // marks this test as needing the SDK layers. @@ -207,6 +215,51 @@ int main() { Check(RenderingElement3D::tlases[0].builtInstanceCount == 4, "builtInstanceCount == 4 after first build"); + // ── Issue #65: the instance buffer is allocated HOST_VISIBLE (required, + // mappable) with a best-effort DEVICE_LOCAL preference (BAR/VRAM) and the + // mandatory HOST_COHERENT dropped — so the per-frame compute-write of the + // transform field and the AS build's read of the instances both stay in + // local VRAM instead of crossing PCIe. The chosen type must match exactly + // what GetMemoryType resolves for this buffer's memoryTypeBits with that + // prefer-but-don't-require request: DEVICE_LOCAL when a host-visible + // device-local type is reachable (resizable BAR), plain HOST_VISIBLE + // otherwise. Asserting equality with the recomputed selection makes the + // check device-independent — it tracks the call-site request, not the + // particular GPU this runs on. ──────────────────────────────────────────── + { + auto& ib = RenderingElement3D::tlases[0].instanceBuffer; + VkMemoryRequirements req{}; + vkGetBufferMemoryRequirements(Device::device, ib.buffer, &req); + std::uint32_t expectIdx = Device::GetMemoryType( + req.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VkMemoryPropertyFlags expectFlags = + Device::memoryProperties.memoryTypes[expectIdx].propertyFlags; + Check((ib.memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0, + "instance buffer memory is HOST_VISIBLE (mappable — the required flag)"); + Check(ib.memoryPropertyFlagsChosen == expectFlags, + "instance buffer memory matches the HOST_VISIBLE | prefer-DEVICE_LOCAL request (#65)"); + + bool barReachable = false; + constexpr VkMemoryPropertyFlags kBar = + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + for (std::uint32_t i = 0; i < Device::memoryProperties.memoryTypeCount; ++i) { + if ((req.memoryTypeBits & (1u << i)) + && (Device::memoryProperties.memoryTypes[i].propertyFlags & kBar) == kBar) { + barReachable = true; + break; + } + } + if (barReachable) { + Check((ib.memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0, + "instance buffer landed on a DEVICE_LOCAL (BAR/VRAM) type where one is reachable"); + } else { + std::println("INFO no host-visible device-local type reachable — " + "fell back to plain HOST_VISIBLE (expected on non-BAR GPUs)"); + } + } + // ── 2. Grow to 16 (past capacity) → REALLOCATES. ──────────────────────── SetCount(pool, 16); BuildOnce(); From 8a32f0d5453d8587b501cefa57c65e06b15df577 Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 17:40:39 +0000 Subject: [PATCH 17/47] perf(decompress): release compressed staging after submit, not for the resource's life MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The compressed Mesh::Build / ImageVulkan::Update paths kept their host-visible `compressedStaging` (holding the GDeflate streams) alive for the whole life of the mesh/image, pinning host-visible memory long after the single GPU decompress that reads it has retired. Release it via VulkanBuffer::DeferredClear() right after recording the decompress. The recorded vkCmdDecompressMemoryEXT still references compressedStaging.address, so it must outlive the submit — exactly what the fence-keyed deletion queue (#101/#102) guarantees: it retires the allocation only after framesInFlight frames have elapsed, by which point the submit's fence has cleared. Between Builds/Updates the handle is null; the next call's Resize re-creates it. Co-Authored-By: Claude Opus 4.8 --- implementations/Crafter.Graphics-Mesh.cpp | 10 ++++++++++ interfaces/Crafter.Graphics-ImageVulkan.cppm | 19 ++++++++++++++++--- interfaces/Crafter.Graphics-Mesh.cppm | 13 +++++++------ 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/implementations/Crafter.Graphics-Mesh.cpp b/implementations/Crafter.Graphics-Mesh.cpp index 203e028..570511d 100644 --- a/implementations/Crafter.Graphics-Mesh.cpp +++ b/implementations/Crafter.Graphics-Mesh.cpp @@ -261,6 +261,16 @@ void Mesh::Build(const CompressedMeshAsset& asset, VkCommandBuffer cmd, RTBuildO VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR); + // The compressed staging is only read by the decompress recorded above; the + // subsequent BLAS build reads the decompressed vertex/index buffers, never + // this. So hand it to the fence-keyed deletion queue (#101/#102) now rather + // than pinning host-visible memory for the mesh's whole life. The recorded + // vkCmdDecompressMemoryEXT still references compressedStaging.address, so it + // must outlive this submit — which the queue guarantees: it retires the + // allocation only after framesInFlight frames have elapsed, by which point + // the decompress submit's fence has cleared. + compressedStaging.DeferredClear(); + allowUpdate = options.allowUpdate; builtInputCount = asset.vertexCount; RecordBLASBuild(*this, asset.vertexCount, asset.indexCount, BlasFlags(options), /*update*/ false, cmd); diff --git a/interfaces/Crafter.Graphics-ImageVulkan.cppm b/interfaces/Crafter.Graphics-ImageVulkan.cppm index 6630e2c..2c40a01 100644 --- a/interfaces/Crafter.Graphics-ImageVulkan.cppm +++ b/interfaces/Crafter.Graphics-ImageVulkan.cppm @@ -75,9 +75,12 @@ export namespace Crafter { VkImage image; VkDeviceMemory imageMemory; VulkanBuffer buffer; - // Lives until the compressed Update path's cmd buffer completes. - // Same lifetime contract as Mesh::compressedStaging — caller must - // not destroy / re-Update before the submit fence is signaled. + // Transient host-visible staging for the compressed Update path. Same + // lifetime contract as Mesh::compressedStaging: the compressed Update + // releases it via DeferredClear() right after recording the decompress, + // so the fence-keyed deletion queue (#101/#102) frees it once that + // submit's frame has cleared instead of pinning it for the image's life. + // Between Updates the handle is null; the next Update's Resize re-creates it. VulkanBuffer compressedStaging; VkImageView imageView; VkDescriptorImageInfo descriptor; @@ -299,6 +302,16 @@ export namespace Crafter { VK_PIPELINE_STAGE_2_COPY_BIT, VK_ACCESS_2_TRANSFER_READ_BIT); + // Compressed staging is read only by the decompress recorded above; + // the buffer→image copy below reads `buffer` (the decompress dst), + // never this. Release it to the fence-keyed deletion queue + // (#101/#102) now instead of pinning host-visible memory for the + // image's whole life. The recorded vkCmdDecompressMemoryEXT still + // references compressedStaging.address, so it must outlive this + // submit — which the queue guarantees by retiring the allocation + // only after framesInFlight frames (i.e. after the submit's fence). + compressedStaging.DeferredClear(); + // Continue with the existing buffer→image upload + layout transitions. // We've already inserted the decompress→transfer-read barrier, // so we skip the FlushDevice host-write barrier the regular Update diff --git a/interfaces/Crafter.Graphics-Mesh.cppm b/interfaces/Crafter.Graphics-Mesh.cppm index c04f819..1fc126e 100644 --- a/interfaces/Crafter.Graphics-Mesh.cppm +++ b/interfaces/Crafter.Graphics-Mesh.cppm @@ -77,12 +77,13 @@ export namespace Crafter { // Lifetime contract matches vertexBuffer/indexBuffer: must stay // alive until the build submitted on `cmd` completes. VulkanBuffer aabbBuffer; - // Lives until the cmd buffer issued by the compressed Build path - // completes execution. Kept as a member so the recorded - // vkCmdDecompressMemoryEXT references valid memory until the queue - // submit signals — caller must not re-Build or destroy the Mesh - // before that submit's fence is signaled (same contract as the - // existing uncompressed path). + // Transient host-visible staging for the compressed Build path. Kept as + // a member only so the recorded vkCmdDecompressMemoryEXT has a stable + // address to reference; the compressed Build releases it via + // DeferredClear() right after recording the decompress, so it is freed + // by the fence-keyed deletion queue (#101/#102) once that submit's frame + // has cleared — no longer pinned for the mesh's life. Between Builds the + // handle is null; the next Build's Resize re-creates it. VulkanBuffer compressedStaging; VkAccelerationStructureGeometryTrianglesDataKHR blasData; VkAccelerationStructureGeometryKHR blas; From b29b8f03717372fe57284a7519cace46abf5d974 Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 17:40:39 +0000 Subject: [PATCH 18/47] test(decompress): cover compressed-staging release on the real GPU path MeshDecompressStagingRelease drives the real VK_EXT_memory_decompression / GDeflate path on a headless device and asserts the staging is handed to the deferred-deletion queue (not pinned), the build is validation-clean with byte-correct decompressed data, and the entry retires only after framesInFlight frames. Skips the GPU-path assertions when the extension is absent (CPU fallback never allocates the staging). Co-Authored-By: Claude Opus 4.8 --- project.cpp | 34 ++++ tests/MeshDecompressStagingRelease/main.cpp | 197 ++++++++++++++++++++ 2 files changed, 231 insertions(+) create mode 100644 tests/MeshDecompressStagingRelease/main.cpp diff --git a/project.cpp b/project.cpp index 879108f..97d51b5 100644 --- a/project.cpp +++ b/project.cpp @@ -617,6 +617,40 @@ extern "C" Configuration CrafterBuildProject(std::span a dc.GetInterfacesAndImplementations(ifaces, deferredImpls); cfg.tests.push_back(std::move(deferredTest)); + // Issue #67: the compressed Mesh::Build path no longer pins its + // host-visible `compressedStaging` for the mesh's life — it releases it + // via DeferredClear() right after recording the GPU decompress, so the + // fence-keyed deletion queue (#101/#102) frees it once that submit's + // frame has cleared. Drives the real VK_EXT_memory_decompression / + // GDeflate path on a headless device (no swapchain/window — a decompress + // + BLAS build only needs the queue + command pool) and asserts the + // staging is enqueued (not pinned), the build is validation-clean with + // correct decompressed data, and the entry retires only after + // framesInFlight frames. Shares the native build settings; needs the + // asset pipeline for SaveCompressed/LoadCompressedMesh (cfg.dependencies + // already carries Crafter.Asset). + Test meshStagingTest; + Configuration& msc = meshStagingTest.config; + msc.path = cfg.path; + msc.name = "MeshDecompressStagingRelease"; + msc.outputName = "MeshDecompressStagingRelease"; + msc.type = ConfigurationType::Executable; + msc.target = cfg.target; + msc.march = cfg.march; + msc.mtune = cfg.mtune; + msc.debug = cfg.debug; + msc.sysroot = cfg.sysroot; + msc.dependencies = cfg.dependencies; + msc.externalDependencies = cfg.externalDependencies; + msc.compileFlags = cfg.compileFlags; + msc.linkFlags = cfg.linkFlags; + msc.defines = cfg.defines; + msc.cFiles = cfg.cFiles; + std::vector meshStagingImpls(impls.begin(), impls.end()); + meshStagingImpls.emplace_back("tests/MeshDecompressStagingRelease/main"); + msc.GetInterfacesAndImplementations(ifaces, meshStagingImpls); + cfg.tests.push_back(std::move(meshStagingTest)); + // Issue #89: Device::PreferDirectDeviceWrite chooses the upload strategy // for a CPU-written, GPU-read buffer — direct HOST_VISIBLE|DEVICE_LOCAL // map+write on ReBAR/UMA vs. staged-into-pure-DEVICE_LOCAL on a small diff --git a/tests/MeshDecompressStagingRelease/main.cpp b/tests/MeshDecompressStagingRelease/main.cpp new file mode 100644 index 0000000..9f93e77 --- /dev/null +++ b/tests/MeshDecompressStagingRelease/main.cpp @@ -0,0 +1,197 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Issue #67: the compressed Mesh::Build path used to keep `compressedStaging` +// — a per-mesh host-visible staging buffer holding the GDeflate streams — +// alive for the whole life of the mesh, pinning host-visible memory long after +// the one decompress that reads it has retired. Build now releases it via +// VulkanBuffer::DeferredClear() right after recording the decompress, so the +// fence-keyed deletion queue (#101/#102) frees it once that submit's frame has +// cleared instead of pinning it forever. +// +// This drives the real hardware GPU-decompress path (VK_EXT_memory_decompression, +// GDeflate 1.0) on a headless device — no swapchain/window needed, a decompress + +// BLAS build only touches the queue + command pool — and asserts: +// - After Build, mesh.compressedStaging owns no handle (released). +// - Exactly that one allocation was handed to Device's deletion queue, tagged +// with the current frameCounter (so it retires on a later frame, not now). +// - The decompress + BLAS build still complete with ZERO validation errors and +// a non-zero BLAS address — proof the staging genuinely outlived the submit +// (the queue had not yet retired it), so this is no use-after-free. +// - The GPU-decompressed vertex/index data read back byte-equal to the source, +// so releasing the staging didn't corrupt the result. +// - The deferred entry actually retires (and frees) once framesInFlight frames +// elapse, and not before — the whole point of routing through the queue. +// +// On hardware without VK_EXT_memory_decompression the compressed Build takes the +// CPU-decode fallback, which never allocates compressedStaging, so the new +// behavior is moot — the test reports that and skips the GPU-path assertions. + +#include "vulkan/vulkan.h" +#include + +import Crafter.Graphics; +import Crafter.Asset; +import Crafter.Math; +import std; + +using namespace Crafter; +namespace fs = std::filesystem; + +namespace { + +int failures = 0; + +void Check(bool ok, std::string_view what) { + std::println("{} {}", ok ? "PASS" : "FAIL", what); + if (!ok) ++failures; +} + +// One-time command buffer helpers — record, submit, block. Mirrors the other +// headless RT tests (BLASBuildOptions). +VkCommandBuffer BeginCmd() { + VkCommandBufferAllocateInfo allocInfo { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = Device::commandPool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }; + VkCommandBuffer cmd = VK_NULL_HANDLE; + Device::CheckVkResult(vkAllocateCommandBuffers(Device::device, &allocInfo, &cmd)); + VkCommandBufferBeginInfo beginInfo { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + Device::CheckVkResult(vkBeginCommandBuffer(cmd, &beginInfo)); + return cmd; +} + +void SubmitWait(VkCommandBuffer cmd) { + Device::CheckVkResult(vkEndCommandBuffer(cmd)); + VkSubmitInfo submitInfo { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &cmd, + }; + Device::CheckVkResult(vkQueueSubmit(Device::queue, 1, &submitInfo, VK_NULL_HANDLE)); + Device::CheckVkResult(vkQueueWaitIdle(Device::queue)); + vkFreeCommandBuffers(Device::device, Device::commandPool, 1, &cmd); +} + +// A unit cube (8 verts, 36 indices) — small enough to fit one GDeflate tile, +// enough topology for a real BLAS. +MeshAsset MakeCubeMesh() { + MeshAsset mesh; + mesh.vertexes = { + {-1.f, -1.f, -1.f}, { 1.f, -1.f, -1.f}, + { 1.f, 1.f, -1.f}, {-1.f, 1.f, -1.f}, + {-1.f, -1.f, 1.f}, { 1.f, -1.f, 1.f}, + { 1.f, 1.f, 1.f}, {-1.f, 1.f, 1.f}, + }; + mesh.indexes = { + 0,1,2, 0,2,3, 4,6,5, 4,7,6, + 0,4,5, 0,5,1, 3,2,6, 3,6,7, + 1,5,6, 1,6,2, 0,3,7, 0,7,4, + }; + return mesh; +} + +} // namespace + +int main() { + Device::Initialize(); + Device::validationErrorCount = 0; + + // Build → compress → load a cube via the real asset pipeline. + MeshAsset srcMesh = MakeCubeMesh(); + const fs::path meshPath = + fs::temp_directory_path() / "crafter_meshdecompress_staging_release.cmesh"; + srcMesh.SaveCompressed(meshPath); + CompressedMeshAsset asset = LoadCompressedMesh(meshPath); + fs::remove(meshPath); + + Check(asset.vertexCount == srcMesh.vertexes.size() + && asset.indexCount == srcMesh.indexes.size(), + "LoadCompressedMesh round-trips the cube header"); + + if (!Device::memoryDecompressionSupported) { + // No GPU codec → compressed Build takes the CPU-decode fallback, which + // never allocates compressedStaging. The release behavior under test + // only exists on the GPU path, so there is nothing to assert here. + std::println("VK_EXT_memory_decompression absent — GPU decompress path " + "not exercised; skipping staging-release assertions."); + return failures == 0 ? 0 : 1; + } + + // Control the deferred-deletion clock: enqueue at frame 0, retire after 2. + Device::framesInFlight = 2; + Device::frameCounter = 0; + Device::deletionQueue.clear(); + + Mesh mesh; + VkCommandBuffer cmd = BeginCmd(); + mesh.Build(asset, cmd); + + // ── The fix: staging is released to the queue at record time, not pinned. ── + Check(mesh.compressedStaging.buffer == VK_NULL_HANDLE, + "Build releases compressedStaging (no handle pinned to the mesh)"); + Check(Device::deletionQueue.size() == 1, + "exactly one allocation (the staging) handed to the deletion queue"); + const bool taggedNow = !Device::deletionQueue.empty() + && Device::deletionQueue.front().retireAfter == 0 + && Device::deletionQueue.front().buffer != VK_NULL_HANDLE; + Check(taggedNow, + "deferred staging is tagged with the enqueue frame and still a live handle"); + + // The decompress recorded above still references the staging's address; it + // must outlive THIS submit. The queue hasn't retired it (frame 0, retires at + // 2), so the GPU reads valid memory. Zero validation errors + a real BLAS + // address prove there's no use-after-free. + SubmitWait(cmd); + Check(mesh.blasAddr != 0, "compressed Build produced a non-zero BLAS address"); + Check(Device::validationErrorCount == 0, + "decompress + BLAS build raised no validation errors"); + + // The GPU decompress wrote into the host-visible vertex/index buffers; + // invalidate and read them back — releasing the staging must not corrupt + // the decompressed result. + mesh.vertexBuffer.FlushHost(); + mesh.indexBuffer.FlushHost(); + const bool vertsMatch = std::memcmp( + mesh.vertexBuffer.value, srcMesh.vertexes.data(), + srcMesh.vertexes.size() * sizeof(srcMesh.vertexes[0])) == 0; + const bool idxMatch = std::memcmp( + mesh.indexBuffer.value, srcMesh.indexes.data(), + srcMesh.indexes.size() * sizeof(srcMesh.indexes[0])) == 0; + Check(vertsMatch, "GPU-decompressed vertices are byte-equal to the source"); + Check(idxMatch, "GPU-decompressed indices are byte-equal to the source"); + + // ── Retire timing: the staging frees once framesInFlight frames elapse, + // and not a frame before. We've already wait-idled, so freeing is safe. ── + Device::ReclaimDeletions(); + Check(Device::deletionQueue.size() == 1, + "staging is NOT freed before framesInFlight frames have elapsed"); + Device::frameCounter = 2; // retireAfter(0) + framesInFlight(2) <= 2 + Device::ReclaimDeletions(); + Check(Device::deletionQueue.empty(), + "staging is freed once its retire frame is reached"); + + std::println("{}", failures == 0 ? "ALL PASS" : "FAILURES PRESENT"); + return failures == 0 ? 0 : 1; +} From b410f2d5aa8c8c58f2d9c39ce0f9b8d35a5b4c63 Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 17:41:34 +0000 Subject: [PATCH 19/47] perf(rt): place SBT in device-local memory via upload-strategy helper (#72) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The shader binding table is written once at pipeline creation and read by the GPU on every vkCmdTraceRaysKHR thereafter, yet it was allocated plain HOST_VISIBLE (system RAM) — so every trace dispatch read raygen/miss/hit records over PCIe. Route the SBT allocation through Device::PreferDirectDeviceWrite (#89): when a DEVICE_LOCAL|HOST_VISIBLE type exists, prefer DEVICE_LOCAL on top of the required HOST_VISIBLE so the buffer lands in mappable device memory (GPU reads hit local VRAM; the one-time memcpy goes write-combined over PCIe). When no combined type exists, the preferred hint is dropped and GetMemoryType (#59) falls back to plain HOST_VISIBLE — current behaviour. The SBT is tiny so a small BAR window's budget is never the constraint. The buffer stays mapped and the existing FlushDevice gates on the chosen memory type's coherency flags (#60), so a direct-write type lacking HOST_COHERENT is still flushed correctly. Co-Authored-By: Claude Opus 4.8 --- .../Crafter.Graphics-PipelineRTVulkan.cppm | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm b/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm index 9da555e..527753d 100644 --- a/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm +++ b/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm @@ -103,7 +103,30 @@ export namespace Crafter { hitRegion.size = hitGroups.size() * sbtStride; std::size_t bufferSize = hitRegion.deviceAddress + hitRegion.size; - sbtBuffer.Create(VK_BUFFER_USAGE_2_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferSize); + // The SBT is written once here (the memcpys below) and read by the + // GPU on every vkCmdTraceRaysKHR for the pipeline's lifetime — the + // textbook write-once/read-many buffer (issue #72). Get it into + // device-local memory so trace dispatches read raygen/miss/hit + // records out of VRAM instead of over PCIe from system RAM. + // + // Route the placement through #89: when a DEVICE_LOCAL|HOST_VISIBLE + // type exists (ReBAR/UMA, or a BAR window — the SBT is tiny so the + // window budget is never the constraint), prefer DEVICE_LOCAL on top + // of the required HOST_VISIBLE so the allocation lands in device + // memory we can still map. The one-time memcpy goes write-combined + // over PCIe (write-only, sequential — ideal); GPU reads then hit + // local VRAM. When no combined type exists at all (no spec + // guarantee), PreferDirectDeviceWrite returns false and the preferred + // hint is dropped, so GetMemoryType falls back to plain HOST_VISIBLE + // (current behaviour, the per-trace PCIe read) — the cheaper fallback + // the issue blesses while staging isn't wired here. + // + // Either way the buffer stays mapped, and the FlushDevice below gates + // its flush on the *chosen* memory type's flags (issue #60), so a + // direct-write type lacking HOST_COHERENT is still flushed correctly. + VkMemoryPropertyFlags sbtPreferred = Device::PreferDirectDeviceWrite(bufferSize) + ? VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT : 0; + sbtBuffer.Create(VK_BUFFER_USAGE_2_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferSize, sbtPreferred); std::uint8_t* offset = sbtBuffer.value; std::uint8_t* handleOffset = shaderHandles.data(); From 1553f585a287fd0417b58b3ef9667651adadbb9a Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 17:44:37 +0000 Subject: [PATCH 20/47] perf(window): use HOST_CACHED readback staging in SaveFrame (#74) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SaveFrame's readback staging buffer was allocated HOST_VISIBLE | HOST_COHERENT, whose first matching type is write-combined (uncached) on most discrete GPUs. The CPU then reads the entire frame back from it — and reads from write-combined memory are pathologically slow. Select a HOST_CACHED type instead (preferring a cached-coherent type when one exists), falling back to any HOST_VISIBLE type if no cached type is advertised (#59 mechanism). When the chosen type is cached-but-not- coherent, invalidate the mapped range before the CPU read — the read-path counterpart of FlushDevice (#60) — gated on the chosen type's actual coherency, not the requested flags. Whole-range invalidate sidesteps nonCoherentAtomSize. Deliberately not routed through the #89 upload-strategy helper, which is upload-only and steers toward write-combined DEVICE_LOCAL|HOST_VISIBLE — the worst type for readback. Verified by driving SaveFrame from the VulkanTriangle example: the barycentric RGB triangle reads back with correct pixels and channel order on an RTX 4090. Co-Authored-By: Claude Opus 4.8 --- implementations/Crafter.Graphics-Window.cpp | 40 +++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/implementations/Crafter.Graphics-Window.cpp b/implementations/Crafter.Graphics-Window.cpp index 0afe9d8..7be3d4e 100644 --- a/implementations/Crafter.Graphics-Window.cpp +++ b/implementations/Crafter.Graphics-Window.cpp @@ -1200,11 +1200,29 @@ void Window::SaveFrame(const std::filesystem::path& path) { VkMemoryRequirements memReqs; vkGetBufferMemoryRequirements(Device::device, stagingBuf, &memReqs); + + // Readback path: the CPU *reads* this whole buffer back. The first + // HOST_VISIBLE|HOST_COHERENT type is usually write-combined (uncached) on + // discrete GPUs, where CPU reads are pathologically slow. Ask for + // HOST_CACHED so reads hit the CPU cache, preferring a cached-coherent type + // when one exists. HOST_CACHED is near-universal but not spec-guaranteed, + // so fall back to any HOST_VISIBLE type (#59) if absent — that path then + // relies on the coherency-gated invalidate below. (Deliberately NOT routed + // through the upload-strategy helper, which steers toward write-combined + // DEVICE_LOCAL|HOST_VISIBLE — the worst type for readback. See issue #89.) + std::uint32_t memTypeIndex; + try { + memTypeIndex = Device::GetMemoryType(memReqs.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + } catch (const std::runtime_error&) { + memTypeIndex = Device::GetMemoryType(memReqs.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + } VkMemoryAllocateInfo mai{ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = memReqs.size, - .memoryTypeIndex = Device::GetMemoryType(memReqs.memoryTypeBits, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT), + .memoryTypeIndex = memTypeIndex, }; VkDeviceMemory stagingMem = VK_NULL_HANDLE; Device::CheckVkResult(vkAllocateMemory(Device::device, &mai, nullptr, &stagingMem)); @@ -1289,6 +1307,24 @@ void Window::SaveFrame(const std::filesystem::path& path) { // Read back, swizzle BGRA → RGBA if needed, write PNG. void* mapped = nullptr; Device::CheckVkResult(vkMapMemory(Device::device, stagingMem, 0, VK_WHOLE_SIZE, 0, &mapped)); + + // If the chosen type is cached-but-not-coherent, the transfer's writes may + // not yet be visible to the CPU read; invalidate to pull them in. Gate on + // the chosen type's actual coherency (not the requested flags) — a coherent + // type needs no invalidate. Whole-range invalidate sidesteps + // nonCoherentAtomSize. This is the read-path counterpart of FlushDevice (#60). + if (!(Device::memoryProperties.memoryTypes[memTypeIndex].propertyFlags & + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { + VkMappedMemoryRange invalidateRange{ + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = stagingMem, + .offset = 0, + .size = VK_WHOLE_SIZE, + }; + Device::CheckVkResult( + vkInvalidateMappedMemoryRanges(Device::device, 1, &invalidateRange)); + } + const std::uint8_t* src = static_cast(mapped); std::vector rgba(static_cast(width) * height * 4); From 5f858509c81ed0be7ea3480cc08b890d3bcb3c66 Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 17:49:25 +0000 Subject: [PATCH 21/47] perf(mesh): place RT geometry in device-local memory via #89 upload strategy (#73) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vertexBuffer/indexBuffer/aabbBuffer were HOST_VISIBLE (system RAM), so the BLAS build, every refit, and any hit-shader geometry fetch read them over PCIe. The usage flags (SHADER_DEVICE_ADDRESS, plus STORAGE on the index buffer) exist precisely to expose geometry for hit-shader fetch, the dominant RT shading pattern. Add VulkanBuffer::UploadDeviceLocal, which places a CPU-written/GPU-read buffer in device-local memory and picks the upload mechanism at runtime via the #89 strategy (Device::PreferDirectDeviceWrite): - ReBAR/UMA: allocate HOST_VISIBLE|DEVICE_LOCAL (DEVICE_LOCAL preferred), map transiently, memcpy, flush-if-non-coherent. No staging buffer. - no/small BAR: allocate pure DEVICE_LOCAL + TRANSFER_DST, fill a transient HOST_VISIBLE staging buffer, vkCmdCopyBuffer, then DeferredClear the staging buffer onto the fence-keyed deletion queue (#101/#102) so it outlives the copy submit. The geometry buffers become non-mapped so the destination is free to be device-local-only. Same-size re-uploads reuse the allocation, so the device address stays stable across an in-place AS UPDATE refit. The compressed Build path now allocates vertex/index as pure DEVICE_LOCAL — the GPU decompressor fills them directly, no host write. Tests: BLASBuildOptions asserts device-local placement on the triangle, procedural, and (budget-forced) staged paths, and exercises the staged copy + deferred-deletion + in-place refit under GPU-assisted validation with zero validation errors. Co-Authored-By: Claude Opus 4.8 --- implementations/Crafter.Graphics-Mesh.cpp | 45 ++++++----- interfaces/Crafter.Graphics-Mesh.cppm | 12 ++- interfaces/Crafter.Graphics-VulkanBuffer.cppm | 77 +++++++++++++++++++ tests/BLASBuildOptions/main.cpp | 50 ++++++++++++ 4 files changed, 161 insertions(+), 23 deletions(-) diff --git a/implementations/Crafter.Graphics-Mesh.cpp b/implementations/Crafter.Graphics-Mesh.cpp index f324b4d..f9cc990 100644 --- a/implementations/Crafter.Graphics-Mesh.cpp +++ b/implementations/Crafter.Graphics-Mesh.cpp @@ -201,14 +201,13 @@ namespace { } void Mesh::Build(std::span> verticies, std::span indicies, VkCommandBuffer cmd, RTBuildOptions options) { - vertexBuffer.Resize(kVertexUsageBase, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, verticies.size()); - indexBuffer.Resize(kIndexUsageBase, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, indicies.size()); - - std::memcpy(vertexBuffer.value, verticies.data(), verticies.size() * sizeof(Vector)); - std::memcpy(indexBuffer.value, indicies.data(), indicies.size() * sizeof(std::uint32_t)); - - vertexBuffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); - indexBuffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); + // Place both inputs in device-local memory (direct map on ReBAR/UMA, + // staged copy otherwise — UploadDeviceLocal decides) and barrier the + // upload before the BLAS build reads them. Replaces the previous + // HOST_VISIBLE allocation that the build (and any hit-shader fetch) would + // read over PCIe every frame (#73). + vertexBuffer.UploadDeviceLocal(kVertexUsageBase, verticies.data(), static_cast(verticies.size()), cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); + indexBuffer.UploadDeviceLocal(kIndexUsageBase, indicies.data(), static_cast(indicies.size()), cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); allowUpdate = options.allowUpdate; builtInputCount = static_cast(verticies.size()); @@ -235,13 +234,17 @@ void Mesh::Build(const CompressedMeshAsset& asset, VkCommandBuffer cmd, RTBuildO return; } + // The GPU decompressor writes vertex/index directly (the build input is + // never host-written on this path), so they want pure DEVICE_LOCAL — no + // host visibility, no map. This is where the BLAS build and any hit-shader + // fetch then read them from (#73). vertexBuffer.Resize( kVertexUsageBase | VK_BUFFER_USAGE_2_MEMORY_DECOMPRESSION_BIT_EXT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, asset.vertexCount); indexBuffer.Resize( kIndexUsageBase | VK_BUFFER_USAGE_2_MEMORY_DECOMPRESSION_BIT_EXT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, asset.indexCount); compressedStaging.Resize( @@ -317,14 +320,13 @@ namespace { void RecordProceduralBuild(Mesh& self, std::span aabbs, VkBuildAccelerationStructureFlagsKHR flags, bool update, VkCommandBuffer cmd) { // 24-byte-stride VkAabbPositionsKHR-compatible build input // (static_assert'd in the interface). Same usage set as the triangle - // inputs: AS-build read-only + device address. A refit reuses the - // existing same-sized buffer (count is unchanged), so the device - // address — and the geometry it feeds — stays stable. - if (!update) { - self.aabbBuffer.Resize(kVertexUsageBase, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, static_cast(aabbs.size())); - } - std::memcpy(self.aabbBuffer.value, aabbs.data(), aabbs.size() * sizeof(RTAabb)); - self.aabbBuffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); + // inputs: AS-build read-only + device address. UploadDeviceLocal places + // it in device-local memory (direct map on ReBAR/UMA, staged otherwise, + // #73) and barriers the upload before the build reads it. A refit + // re-uploads into the same same-sized buffer: Resize reuses the existing + // allocation (count unchanged), so the device address — and the geometry + // it feeds — stays stable for an in-place UPDATE. + self.aabbBuffer.UploadDeviceLocal(kVertexUsageBase, aabbs.data(), static_cast(aabbs.size()), cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); RecordProceduralBuildFromAddress(self, self.aabbBuffer.address, static_cast(aabbs.size()), sizeof(RTAabb), flags, update, cmd); } @@ -382,8 +384,11 @@ void Mesh::Refit(std::span> verticies, std::span)); - vertexBuffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); + // Re-upload only the vertex positions; UploadDeviceLocal reuses the same + // device-local allocation (count unchanged) so the address the UPDATE reads + // stays stable, then barriers the write before the build. On the staged + // path this re-stages per refit (the deforming-mesh fallback, #73). + vertexBuffer.UploadDeviceLocal(kVertexUsageBase, verticies.data(), static_cast(verticies.size()), cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); RecordBLASBuild(*this, static_cast(verticies.size()), static_cast(indicies.size()), buildFlags, /*update*/ true, cmd); } diff --git a/interfaces/Crafter.Graphics-Mesh.cppm b/interfaces/Crafter.Graphics-Mesh.cppm index e5f1c6a..91341a5 100644 --- a/interfaces/Crafter.Graphics-Mesh.cppm +++ b/interfaces/Crafter.Graphics-Mesh.cppm @@ -71,12 +71,18 @@ export namespace Crafter { public: VulkanBuffer scratchBuffer; VulkanBuffer blasBuffer; - VulkanBuffer, true> vertexBuffer; - VulkanBuffer indexBuffer; + // Non-mapped (device-local) RT geometry: VulkanBuffer::UploadDeviceLocal + // places these in device memory and picks direct-map vs staged-copy per + // the #89 upload strategy, so they live in VRAM for hit-shader fetch and + // BLAS-build/refit reads instead of being read from system RAM over PCIe + // every trace (#73). The compressed Build path allocates them pure + // DEVICE_LOCAL and lets the GPU decompressor fill them directly. + VulkanBuffer, false> vertexBuffer; + VulkanBuffer indexBuffer; // AABB build input for the procedural path (BuildProcedural). // Lifetime contract matches vertexBuffer/indexBuffer: must stay // alive until the build submitted on `cmd` completes. - VulkanBuffer aabbBuffer; + VulkanBuffer aabbBuffer; // Lives until the cmd buffer issued by the compressed Build path // completes execution. Kept as a member so the recorded // vkCmdDecompressMemoryEXT references valid memory until the queue diff --git a/interfaces/Crafter.Graphics-VulkanBuffer.cppm b/interfaces/Crafter.Graphics-VulkanBuffer.cppm index a499062..fe0e60a 100644 --- a/interfaces/Crafter.Graphics-VulkanBuffer.cppm +++ b/interfaces/Crafter.Graphics-VulkanBuffer.cppm @@ -235,6 +235,83 @@ namespace Crafter { ); } + // Upload `count` host elements from `src` into this buffer as + // device-local, GPU-read geometry, choosing placement at runtime via + // the #89 upload strategy (Device::PreferDirectDeviceWrite) and then + // recording a barrier from the upload to (dstStageMask, dstAccessMask) + // on `cmd`: + // ReBAR / UMA (direct) — allocate HOST_VISIBLE with DEVICE_LOCAL as a + // best-effort preference (GetMemoryType lands the host-visible + // device-local type that the strategy already proved exists), map + // transiently, memcpy, flush if the chosen type isn't coherent, + // unmap. No staging buffer: it would be pure overhead on a bar where + // the device-local heap is itself host-writable. + // No / small BAR (staged) — allocate pure DEVICE_LOCAL (+ TRANSFER_DST), + // fill a transient HOST_VISIBLE staging buffer, vkCmdCopyBuffer into + // this buffer, then hand the staging buffer to the fence-keyed + // deferred-deletion queue (#101/#102) so it outlives the copy submit. + // Requires !Mapped: the destination must be free to be device-local-only + // (a persistent map would force HOST_VISIBLE), so the direct path maps + // just long enough to write. A same-size re-upload reuses the allocation + // (Resize), so the device address stays stable across an in-place AS + // UPDATE refit that re-calls this. + void UploadDeviceLocal(VkBufferUsageFlags2 usageFlags, const T* src, std::uint32_t count, VkCommandBuffer cmd, VkAccessFlags dstAccessMask, VkPipelineStageFlags dstStageMask) requires(!Mapped) { + VkDeviceSize bytes = static_cast(count) * sizeof(T); + VkAccessFlags srcAccessMask; + VkPipelineStageFlags srcStageMask; + if (Device::PreferDirectDeviceWrite(bytes)) { + Resize(usageFlags, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, count, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + void* mapped = nullptr; + Device::CheckVkResult(vkMapMemory(Device::device, memory, 0, VK_WHOLE_SIZE, 0, &mapped)); + std::memcpy(mapped, src, bytes); + // Match FlushDevice()'s gate: a non-coherent type needs an + // explicit flush before the device reads the written range. + if (!(memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { + VkMappedMemoryRange range { + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = memory, + .offset = 0, + .size = VK_WHOLE_SIZE + }; + vkFlushMappedMemoryRanges(Device::device, 1, &range); + } + vkUnmapMemory(Device::device, memory); + srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; + srcStageMask = VK_PIPELINE_STAGE_HOST_BIT; + } else { + Resize(usageFlags | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, count); + VulkanBuffer staging; + // SHADER_DEVICE_ADDRESS: Create always queries the buffer device + // address (and allocates with the device-address bit); the + // staging buffer's own address is otherwise unused. + staging.Create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, count); + std::memcpy(staging.value, src, bytes); + staging.FlushDevice(); + VkBufferCopy region { .srcOffset = 0, .dstOffset = 0, .size = bytes }; + vkCmdCopyBuffer(cmd, staging.buffer, buffer, 1, ®ion); + // The queued copy still reads the staging buffer after this call + // returns, so a plain Clear() would be a use-after-free; hand it + // to the fence-keyed queue (#101/#102), which frees it once the + // copy's frame clears its fence. DeferredClear nulls the handle, + // so `staging`'s destructor here is a no-op. + staging.DeferredClear(); + srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + } + + VkBufferMemoryBarrier barrier = { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .srcAccessMask = srcAccessMask, + .dstAccessMask = dstAccessMask, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = buffer, + .offset = 0, + .size = VK_WHOLE_SIZE + }; + vkCmdPipelineBarrier(cmd, srcStageMask, dstStageMask, 0, 0, NULL, 1, &barrier, 0, NULL); + } + void FlushDevice() requires(Mapped) { // Coherent memory needs no explicit flush — host writes are // automatically visible to the device. diff --git a/tests/BLASBuildOptions/main.cpp b/tests/BLASBuildOptions/main.cpp index e8520d8..538fae9 100644 --- a/tests/BLASBuildOptions/main.cpp +++ b/tests/BLASBuildOptions/main.cpp @@ -132,6 +132,15 @@ int main() { // refit below can reuse it (build scratch ≥ update scratch, no resize). Check(tri.scratchBuffer.buffer != VK_NULL_HANDLE, "allowUpdate=true → scratch retained for refit (issue #66)"); + // Geometry is placed in device-local memory regardless of the upload + // strategy taken (issue #73): the direct ReBAR/UMA path lands a + // HOST_VISIBLE | DEVICE_LOCAL type, the staged path a pure DEVICE_LOCAL + // one — either way the DEVICE_LOCAL bit is set, so the BLAS build and any + // hit-shader fetch read from VRAM, not system RAM. + Check((tri.vertexBuffer.memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0, + "triangle vertexBuffer placed in device-local memory (#73)"); + Check((tri.indexBuffer.memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0, + "triangle indexBuffer placed in device-local memory (#73)"); const VkDeviceAddress triAddrBefore = tri.blasAddr; const VkAccelerationStructureKHR triHandleBefore = tri.accelerationStructure; @@ -198,6 +207,8 @@ int main() { Check(proc.builtPrimitiveCount == 2, "procedural BLAS reports 2 primitives"); Check(proc.scratchBuffer.buffer != VK_NULL_HANDLE, "procedural allowUpdate=true → scratch retained for refit (issue #66)"); + Check((proc.aabbBuffer.memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0, + "procedural aabbBuffer placed in device-local memory (#73)"); const VkDeviceAddress procAddrBefore = proc.blasAddr; const VkAccelerationStructureKHR procHandleBefore = proc.accelerationStructure; @@ -293,6 +304,45 @@ int main() { Check(devProc.aabbBuffer.buffer == VK_NULL_HANDLE, "device-buffer refit still never touched the host aabbBuffer"); + // ── Force the STAGED upload path (issue #73). On this ReBAR dev host the + // direct map path is normally taken; temporarily zero the cached upload + // budget so PreferDirectDeviceWrite returns false, exercising Mesh's + // pure-DEVICE_LOCAL + transient-staging-buffer + vkCmdCopyBuffer path + // (and its deferred-deletion of the staging buffer). The validation + // layer check below is the real assertion that the staged copy and its + // barriers are spec-correct on a fresh build and an in-place refit. ─── + { + const VkDeviceSize savedBudget = Device::directWriteBudget; + Device::directWriteBudget = 0; // 0 → PreferDirectDeviceWrite is always false + + Mesh staged; + auto verts = CubeVerts(1.0f); + auto idx = CubeIndices(); + { + VkCommandBuffer cmd = BeginCmd(); + staged.Build(verts, idx, cmd, RTBuildOptions{ + .preference = RTBuildPreference::FastTrace, .allowUpdate = true }); + SubmitWait(cmd); + } + Check(staged.blasAddr != 0, "staged-upload Build produced a non-zero blasAddr (#73)"); + // Staged geometry lives in pure DEVICE_LOCAL — no HOST_VISIBLE bit. + Check((staged.vertexBuffer.memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0, + "staged vertexBuffer is device-local-only, not host-visible (#73)"); + + const VkAccelerationStructureKHR stagedHandle = staged.accelerationStructure; + { + // Same topology, deformed positions → in-place UPDATE, re-staged. + auto verts2 = CubeVerts(1.5f); + VkCommandBuffer cmd = BeginCmd(); + staged.Refit(verts2, idx, cmd); + SubmitWait(cmd); + } + Check(staged.accelerationStructure == stagedHandle, + "staged-upload Refit kept the same AS handle (in-place UPDATE, #73)"); + + Device::directWriteBudget = savedBudget; + } + Check(Device::validationErrorCount == 0, std::format("no Vulkan validation errors ({} seen)", Device::validationErrorCount)); From 801509d9d5e9cf424768523ac6a621445740adb7 Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 17:50:01 +0000 Subject: [PATCH 22/47] perf(rt): allocate TLAS metadata buffer in BAR/VRAM, not system RAM (#75) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TLAS metadata buffer is CPU-written every frame (one userMetadata word per element in BuildTLAS's copy loop) and read by the ray shaders as a STORAGE_BUFFER via device address every frame. Allocated HOST_VISIBLE | HOST_COHERENT (system RAM), every per-frame shader read traversed PCIe — the same cost the sibling instance buffer paid before #65. Upgrade the request to HOST_VISIBLE with a best-effort DEVICE_LOCAL preference (BAR/VRAM), keeping the single persistently-mapped buffer. The CPU's writes are write-only, sequential, never-read-back — the ideal write-combined BAR load — and the shader reads now hit local VRAM. Unlike the instance buffer there is no GPU co-write, so the direct upgrade is unconditional. Staging is still deliberately avoided: this buffer is rewritten by the CPU every frame, so a per-frame stage+copy+barrier would defeat the purpose (#75 caveat 2). Correctness: - DEVICE_LOCAL is a preference only (depends on #59): GetMemoryType falls back to plain HOST_VISIBLE (the previous behaviour) on GPUs without resizable BAR. - HOST_COHERENT is dropped from the required set — the combined type may not be coherent. A FlushDevice() is added after the copy loop to make the host writes available on a non-coherent type; it self-gates to a no-op on a coherent type (#60), so the previous behaviour is preserved wherever the type ends up coherent. The metadata buffer is not an AS build input and has no GPU co-write, so it needs neither the build-stage barrier the instance buffer takes nor a HOST->shader command barrier: the queue submit already orders host writes made available before it against every command in the submission, exactly as it did when this buffer was HOST_COHERENT. Extends the TLASHighWaterMark hardware test with a metadata-buffer assertion block mirroring #65's instance-buffer check: the chosen memory type must equal what GetMemoryType resolves for the HOST_VISIBLE | prefer-DEVICE_LOCAL request, and must be DEVICE_LOCAL wherever a host-visible device-local type is reachable. The zero-validation-errors check covers the dropped HOST_COHERENT path. Co-Authored-By: Claude Opus 4.8 --- .../Crafter.Graphics-RenderingElement3D.cpp | 27 +++++++++- tests/TLASHighWaterMark/main.cpp | 52 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/implementations/Crafter.Graphics-RenderingElement3D.cpp b/implementations/Crafter.Graphics-RenderingElement3D.cpp index 1da2a3a..d06630c 100644 --- a/implementations/Crafter.Graphics-RenderingElement3D.cpp +++ b/implementations/Crafter.Graphics-RenderingElement3D.cpp @@ -121,10 +121,23 @@ void RenderingElement3D::BuildTLAS(VkCommandBuffer cmd, std::uint32_t index, RTB // visibility, gating its flush-skip on the *chosen* type's flags. The // single shared buffer is preserved: a whole-buffer copy would clobber // the GPU-written transforms, so staging is deliberately avoided. + // + // metadataBuffer (#75) gets the same HOST_VISIBLE | prefer-DEVICE_LOCAL + // upgrade: it is CPU-written every frame (the copy loop below) and read + // by the ray shaders as a STORAGE_BUFFER every frame, so it has the same + // per-frame-shader-read-over-PCIe cost. Unlike instanceBuffer there is + // no GPU co-write, so the direct upgrade is unconditional — but staging + // is still wrong here: a per-frame-rewritten buffer would pay a stage+ + // copy+barrier every frame, defeating the point. HOST_COHERENT is + // likewise dropped; the FlushDevice() after the copy loop makes the host + // writes available before the consuming submit on a non-coherent type + // (no-op when the chosen type is coherent — the previous behaviour), + // and the queue submit carries the host-write -> shader-read ordering as + // it always has. if (tlas.instanceBuffer.buffer == VK_NULL_HANDLE || primitiveCount > tlas.instanceBuffer.size / sizeof(VkAccelerationStructureInstanceKHR)) { tlas.instanceBuffer.Resize(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, primitiveCount, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - tlas.metadataBuffer.Resize(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, primitiveCount); + tlas.metadataBuffer.Resize(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, primitiveCount, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); } } @@ -147,6 +160,18 @@ void RenderingElement3D::BuildTLAS(VkCommandBuffer cmd, std::uint32_t index, RTB tlas.instanceBuffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); + // Make the per-frame metadata host writes available before the consuming + // submit. metadataBuffer is not an AS build input (the build never reads it) + // and has no GPU co-write — only the ray shaders read it, in a later pass — + // so it needs neither the build-stage barrier instanceBuffer takes above nor + // a HOST->shader command barrier: the queue submit already orders host + // writes made available before it against every command in the submission, + // exactly as it did when this buffer was HOST_COHERENT. This plain host + // flush is what now makes those writes available on a non-coherent + // (BAR/VRAM) type; it self-gates to a no-op on a coherent type (#60), + // preserving the previous behaviour where the type ends up coherent. + tlas.metadataBuffer.FlushDevice(); + VkAccelerationStructureGeometryInstancesDataKHR instancesData { .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR, .arrayOfPointers = VK_FALSE, diff --git a/tests/TLASHighWaterMark/main.cpp b/tests/TLASHighWaterMark/main.cpp index 55bd544..987b960 100644 --- a/tests/TLASHighWaterMark/main.cpp +++ b/tests/TLASHighWaterMark/main.cpp @@ -58,6 +58,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // HOST_COHERENT: the FlushDevice(cmd, ...) in BuildTLAS gates on the chosen // type's coherency flag, so a non-coherent BAR type still flushes correctly. // +// Issue #75: the metadata buffer gets the identical upgrade — it is CPU-written +// every frame and read by the ray shaders as a STORAGE_BUFFER every frame, so +// the same HOST_VISIBLE | prefer-DEVICE_LOCAL, no-mandatory-HOST_COHERENT +// request keeps those per-frame shader reads in local VRAM. A second assertion +// block after the first build checks its chosen memory type the same way. The +// dropped HOST_COHERENT is covered by the zero-validation-errors check: the +// FlushDevice() added after the copy loop in BuildTLAS makes the host writes +// available on a non-coherent type and self-gates to a no-op on a coherent one. +// // Validation layers are required for the last check to be meaningful; the build // marks this test as needing the SDK layers. @@ -260,6 +269,49 @@ int main() { } } + // ── Issue #75: the metadata buffer carries the same memory-type request as + // the instance buffer — HOST_VISIBLE (required, mappable) with a best-effort + // DEVICE_LOCAL preference (BAR/VRAM) and the mandatory HOST_COHERENT dropped. + // It is CPU-written every frame and read by the ray shaders every frame, so + // landing it in local VRAM keeps those per-frame storage-buffer reads off + // PCIe. Same device-independent check: assert the recorded chosen flags equal + // what GetMemoryType resolves for this buffer's memoryTypeBits with that + // request, and that DEVICE_LOCAL is chosen wherever a host-visible + // device-local type is reachable. ─────────────────────────────────────────── + { + auto& mb = RenderingElement3D::tlases[0].metadataBuffer; + VkMemoryRequirements req{}; + vkGetBufferMemoryRequirements(Device::device, mb.buffer, &req); + std::uint32_t expectIdx = Device::GetMemoryType( + req.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VkMemoryPropertyFlags expectFlags = + Device::memoryProperties.memoryTypes[expectIdx].propertyFlags; + Check((mb.memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0, + "metadata buffer memory is HOST_VISIBLE (mappable — the required flag)"); + Check(mb.memoryPropertyFlagsChosen == expectFlags, + "metadata buffer memory matches the HOST_VISIBLE | prefer-DEVICE_LOCAL request (#75)"); + + bool barReachable = false; + constexpr VkMemoryPropertyFlags kBar = + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + for (std::uint32_t i = 0; i < Device::memoryProperties.memoryTypeCount; ++i) { + if ((req.memoryTypeBits & (1u << i)) + && (Device::memoryProperties.memoryTypes[i].propertyFlags & kBar) == kBar) { + barReachable = true; + break; + } + } + if (barReachable) { + Check((mb.memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0, + "metadata buffer landed on a DEVICE_LOCAL (BAR/VRAM) type where one is reachable"); + } else { + std::println("INFO no host-visible device-local type reachable — " + "metadata fell back to plain HOST_VISIBLE (expected on non-BAR GPUs)"); + } + } + // ── 2. Grow to 16 (past capacity) → REALLOCATES. ──────────────────────── SetCount(pool, 16); BuildOnce(); From ed9b3f67a73df1e723b15ffa33d1d5df533069fb Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 18:05:13 +0000 Subject: [PATCH 23/47] test+usage: device-local geometry readback + isolate #67 staging count MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add TRANSFER_SRC to RT geometry usage so device-local geometry (now in VRAM, no longer host-mappable) stays copyable/inspectable. - MeshDecompressStagingRelease: read decompressed vertex/index back via a device->host copy instead of the removed host-mapped .value/FlushHost, and build the mesh with allowUpdate=true so the retained per-mesh scratch (#66) doesn't also land in the deletion queue — isolating the assertion to the released compressed staging (#67). Co-Authored-By: Claude Opus 4.8 --- implementations/Crafter.Graphics-Mesh.cpp | 10 ++++- tests/MeshDecompressStagingRelease/main.cpp | 41 +++++++++++++++++---- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/implementations/Crafter.Graphics-Mesh.cpp b/implementations/Crafter.Graphics-Mesh.cpp index e518f2d..90649a7 100644 --- a/implementations/Crafter.Graphics-Mesh.cpp +++ b/implementations/Crafter.Graphics-Mesh.cpp @@ -32,10 +32,16 @@ using namespace Crafter; namespace { // Buffer-usage flag set shared by both Build paths. The compressed path - // appends VK_BUFFER_USAGE_2_MEMORY_DECOMPRESSION_BIT_EXT. + // appends VK_BUFFER_USAGE_2_MEMORY_DECOMPRESSION_BIT_EXT, and the staged + // upload path appends VK_BUFFER_USAGE_TRANSFER_DST_BIT. TRANSFER_SRC is in + // the base because the geometry is now device-local (#73): once it no longer + // lives in host-mappable memory, a transfer copy is the only way to read it + // back (debugging, GPU-driven workflows, decompression validation) — a free + // usage flag that keeps device-local geometry inspectable. constexpr VkBufferUsageFlags2 kVertexUsageBase = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - | VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR; + | VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR + | VK_BUFFER_USAGE_TRANSFER_SRC_BIT; constexpr VkBufferUsageFlags2 kIndexUsageBase = kVertexUsageBase | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; diff --git a/tests/MeshDecompressStagingRelease/main.cpp b/tests/MeshDecompressStagingRelease/main.cpp index 9f93e77..9298bf7 100644 --- a/tests/MeshDecompressStagingRelease/main.cpp +++ b/tests/MeshDecompressStagingRelease/main.cpp @@ -146,7 +146,13 @@ int main() { Mesh mesh; VkCommandBuffer cmd = BeginCmd(); - mesh.Build(asset, cmd); + // allowUpdate=true so the per-mesh BLAS scratch is RETAINED for refit + // (issue #66) rather than deferred-cleared after the build. A static + // (allowUpdate=false) build would also hand its scratch to the deletion + // queue, making the count below 2; keeping it isolates this test to the + // one allocation it cares about — the released compressed staging (#67). + // The staging-release behavior under test is independent of allowUpdate. + mesh.Build(asset, cmd, RTBuildOptions{ .allowUpdate = true }); // ── The fix: staging is released to the queue at record time, not pinned. ── Check(mesh.compressedStaging.buffer == VK_NULL_HANDLE, @@ -168,16 +174,35 @@ int main() { Check(Device::validationErrorCount == 0, "decompress + BLAS build raised no validation errors"); - // The GPU decompress wrote into the host-visible vertex/index buffers; - // invalidate and read them back — releasing the staging must not corrupt - // the decompressed result. - mesh.vertexBuffer.FlushHost(); - mesh.indexBuffer.FlushHost(); + // The GPU decompress wrote into the device-local vertex/index buffers + // (issue #73 placed RT geometry in VRAM, so they are no longer host-mapped). + // Copy them back to host-visible staging — the proper way to inspect + // device-local memory — and compare: releasing the compressed staging must + // not have corrupted the decompressed result. The geometry buffers carry + // TRANSFER_SRC for exactly this (#73). + VulkanBuffer, true> vertReadback; + VulkanBuffer idxReadback; + vertReadback.Resize(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, static_cast(srcMesh.vertexes.size())); + idxReadback.Resize(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, static_cast(srcMesh.indexes.size())); + { + // Safe to copy without a barrier: SubmitWait above wait-idled the queue, + // so the decompress + BLAS reads of these buffers have fully retired. + VkCommandBuffer rcmd = BeginCmd(); + VkBufferCopy vRegion { .srcOffset = 0, .dstOffset = 0, .size = vertReadback.size }; + vkCmdCopyBuffer(rcmd, mesh.vertexBuffer.buffer, vertReadback.buffer, 1, &vRegion); + VkBufferCopy iRegion { .srcOffset = 0, .dstOffset = 0, .size = idxReadback.size }; + vkCmdCopyBuffer(rcmd, mesh.indexBuffer.buffer, idxReadback.buffer, 1, &iRegion); + SubmitWait(rcmd); + } + vertReadback.FlushHost(); + idxReadback.FlushHost(); const bool vertsMatch = std::memcmp( - mesh.vertexBuffer.value, srcMesh.vertexes.data(), + vertReadback.value, srcMesh.vertexes.data(), srcMesh.vertexes.size() * sizeof(srcMesh.vertexes[0])) == 0; const bool idxMatch = std::memcmp( - mesh.indexBuffer.value, srcMesh.indexes.data(), + idxReadback.value, srcMesh.indexes.data(), srcMesh.indexes.size() * sizeof(srcMesh.indexes[0])) == 0; Check(vertsMatch, "GPU-decompressed vertices are byte-equal to the source"); Check(idxMatch, "GPU-decompressed indices are byte-equal to the source"); From 3655cd636c3b07b2de45ff0c02b47010b4d7d696 Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 18:50:38 +0000 Subject: [PATCH 24/47] test(mesh): pin static-build deletion count in #67 staging-release test (#110) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue #110 reported `crafter-build test MeshDecompressStagingRelease` failing deterministically on the GPU decompress path: the "exactly one allocation handed to the deletion queue" assertion saw more than one entry. Root cause (confirmed at Crafter.Graphics-Mesh.cpp:175-176): a static (allowUpdate=false) Build cannot refit, so it DeferredClear()s its now-dead per-mesh BLAS scratch in addition to the compressed staging (#67) — two deferred allocations, not one. The original test built statically yet asserted size==1, so the scratch was the unaccounted-for second entry. The behaviour was already corrected on master by PR #109 (the #73 merge, ed9b3f6), which switched the assertion's build to allowUpdate=true so the scratch is retained. This hardens that fix: it adds a sibling case that Builds with allowUpdate=false and asserts the queue holds exactly TWO entries (staging + dead scratch), builds with zero validation errors, and that both retire on schedule. The count the #67 assertion relies on is now a named, explicitly-tested quantity rather than an implicit comment, so a future change to scratch deferral can't silently shift it back. Co-Authored-By: Claude Opus 4.8 --- tests/MeshDecompressStagingRelease/main.cpp | 38 +++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/MeshDecompressStagingRelease/main.cpp b/tests/MeshDecompressStagingRelease/main.cpp index 9298bf7..ccb4643 100644 --- a/tests/MeshDecompressStagingRelease/main.cpp +++ b/tests/MeshDecompressStagingRelease/main.cpp @@ -39,6 +39,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // - The deferred entry actually retires (and frees) once framesInFlight frames // elapse, and not before — the whole point of routing through the queue. // +// As a regression guard for issue #110 it then repeats the Build with +// allowUpdate=false and asserts the queue holds exactly TWO entries — the +// compressed staging PLUS the dead per-mesh BLAS scratch a static build can no +// longer refit. That extra scratch was the unaccounted-for entry behind #110 +// (the original test did a static build yet asserted size==1); pinning the +// breakdown keeps the count above a known, named quantity. +// // On hardware without VK_EXT_memory_decompression the compressed Build takes the // CPU-decode fallback, which never allocates compressedStaging, so the new // behavior is moot — the test reports that and skips the GPU-path assertions. @@ -217,6 +224,37 @@ int main() { Check(Device::deletionQueue.empty(), "staging is freed once its retire frame is reached"); + // ── Regression guard for issue #110 ────────────────────────────────────── + // The "exactly one allocation" assertion above holds ONLY because the + // refit-capable build (allowUpdate=true) RETAINS its per-mesh BLAS scratch + // for in-place updates (#66). A static (allowUpdate=false) build cannot + // refit, so it hands its now-dead scratch to the deletion queue too — the + // SAME Build then lands TWO deferred allocations: the compressed staging + // (#67) AND the dead scratch. Issue #110 was the original test doing a + // static build and asserting size==1: the scratch was the unaccounted-for + // second entry. Pin that breakdown down explicitly so a future change to + // scratch deferral can't silently shift the count the #67 assertion relies + // on, and so the "extra deferred allocation" stays a known, named quantity. + Device::frameCounter = 0; + Device::deletionQueue.clear(); + Mesh staticMesh; + VkCommandBuffer scmd = BeginCmd(); + staticMesh.Build(asset, scmd, RTBuildOptions{ .allowUpdate = false }); + Check(staticMesh.compressedStaging.buffer == VK_NULL_HANDLE, + "static Build also releases compressedStaging (#67 is allowUpdate-independent)"); + Check(Device::deletionQueue.size() == 2, + "static Build enqueues staging + dead BLAS scratch (the #110 extra deletion)"); + SubmitWait(scmd); + Check(staticMesh.blasAddr != 0, "static compressed Build produced a non-zero BLAS address"); + Check(Device::validationErrorCount == 0, + "static decompress + BLAS build raised no validation errors"); + // Retire both entries (enqueued at frame 0, retire at framesInFlight) so the + // staticMesh's scratch/staging are freed and nothing leaks past this test. + Device::frameCounter = Device::framesInFlight; + Device::ReclaimDeletions(); + Check(Device::deletionQueue.empty(), + "static Build's staging + scratch both retire once their frame elapses"); + std::println("{}", failures == 0 ? "ALL PASS" : "FAILURES PRESENT"); return failures == 0 ? 0 : 1; } From 2460a6629668916c5cb592e6ae9410d168889aef Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 19:37:58 +0000 Subject: [PATCH 25/47] perf(device): pop retired deletions off the front in O(ready) (#116) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ReclaimDeletions ran an O(n) walk + survivor compaction over the whole deletion queue every frame. frameCounter is monotonic, so retireAfter is non-decreasing down the queue and the entries ready to reclaim are always a contiguous prefix. Switch deletionQueue from std::vector to std::deque and pop the ready prefix off the front, stopping at the first entry still in flight — O(ready) instead of O(queue). Resolves #116 Co-Authored-By: Claude Opus 4.8 --- implementations/Crafter.Graphics-Device.cpp | 29 +++++++++++---------- interfaces/Crafter.Graphics-Device.cppm | 6 ++++- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/implementations/Crafter.Graphics-Device.cpp b/implementations/Crafter.Graphics-Device.cpp index f96ddc8..8738494 100644 --- a/implementations/Crafter.Graphics-Device.cpp +++ b/implementations/Crafter.Graphics-Device.cpp @@ -1010,21 +1010,22 @@ void Device::EnqueueDeletion(VkBuffer buffer, VkDeviceMemory memory) { } void Device::ReclaimDeletions() { - // Free every entry whose retire frame has been reached, compacting the - // survivors down in place. An entry tagged at frame F retires at - // F + framesInFlight: by the time the CPU has begun that many later - // frames (each gated by a per-image fence wait), single-queue submission - // order guarantees all GPU work from frame F is complete. - std::size_t kept = 0; - for (PendingDeletion& entry : deletionQueue) { - if (entry.retireAfter + framesInFlight <= frameCounter) { - vkDestroyBuffer(device, entry.buffer, nullptr); - vkFreeMemory(device, entry.memory, nullptr); - } else { - deletionQueue[kept++] = entry; - } + // Free every entry whose retire frame has been reached. An entry tagged at + // frame F retires at F + framesInFlight: by the time the CPU has begun that + // many later frames (each gated by a per-image fence wait), single-queue + // submission order guarantees all GPU work from frame F is complete. + // + // frameCounter is monotonic, so retireAfter is non-decreasing down the + // queue and the ready entries are a contiguous prefix: pop them off the + // front and stop at the first one still in flight (everything behind it is + // newer, hence also in flight). O(ready), not O(queue). + while (!deletionQueue.empty() && + deletionQueue.front().retireAfter + framesInFlight <= frameCounter) { + const PendingDeletion& entry = deletionQueue.front(); + vkDestroyBuffer(device, entry.buffer, nullptr); + vkFreeMemory(device, entry.memory, nullptr); + deletionQueue.pop_front(); } - deletionQueue.resize(kept); } void Device::DrainDeletions() { diff --git a/interfaces/Crafter.Graphics-Device.cppm b/interfaces/Crafter.Graphics-Device.cppm index 95f7583..17f77c5 100644 --- a/interfaces/Crafter.Graphics-Device.cppm +++ b/interfaces/Crafter.Graphics-Device.cppm @@ -320,7 +320,11 @@ export namespace Crafter { // Zero until set, which only makes ReclaimDeletions more eager — and it // is never called before a Window (which sets this) starts rendering. inline static std::uint8_t framesInFlight = 0; - inline static std::vector deletionQueue; + // A deque, not a vector: frameCounter is monotonic, so retireAfter is + // non-decreasing down the queue and the entries ready to reclaim are + // always a prefix. ReclaimDeletions pops that prefix off the front in + // O(ready) instead of walking and compacting the whole queue. + inline static std::deque deletionQueue; // Tag {buffer, memory} for deletion after framesInFlight more frames. // A VK_NULL_HANDLE buffer is ignored (nothing to free). static void EnqueueDeletion(VkBuffer buffer, VkDeviceMemory memory); From b4fa6e596e09b169c68e129d5aff288e9e3568cf Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 19:40:35 +0000 Subject: [PATCH 26/47] perf(image): release static texture staging after upload (#114) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ImageVulkan kept its host-visible staging `buffer` (sized w*h, persistently mapped) alive for the whole life of the image and Destroy() never freed it, so static textures (e.g. Sponza albedo) pinned HOST_VISIBLE / small-BAR memory forever. Update now releases the staging via VulkanBuffer::DeferredClear() right after recording the buffer→image copy — the same fence-keyed deletion queue (#101/#102) the compressed Mesh path already uses — so it is freed once the upload submit's frame has cleared. A new `streamed` flag (set by FontAtlas) keeps the persistent map for images re-uploaded from a CPU-side staging buffer every frame; its uploads go through UpdateRegion, which never releases. Destroy now also frees any staging the image still owns, gated on a live handle so a released static texture can't double-free. Adds tests/ImageStagingRelease driving the real upload + readback on a headless device: asserts the staging is enqueued (not pinned), the image reads back byte-equal (released staging outlived the submit), the entry retires only after framesInFlight frames, and a streamed image keeps then frees its staging. Co-Authored-By: Claude Opus 4.8 --- .../Crafter.Graphics-FontAtlas.cpp | 6 +- interfaces/Crafter.Graphics-ImageVulkan.cppm | 37 ++- project.cpp | 34 +++ tests/ImageStagingRelease/main.cpp | 238 ++++++++++++++++++ 4 files changed, 313 insertions(+), 2 deletions(-) create mode 100644 tests/ImageStagingRelease/main.cpp diff --git a/implementations/Crafter.Graphics-FontAtlas.cpp b/implementations/Crafter.Graphics-FontAtlas.cpp index 051d04a..d6f14e7 100644 --- a/implementations/Crafter.Graphics-FontAtlas.cpp +++ b/implementations/Crafter.Graphics-FontAtlas.cpp @@ -39,7 +39,11 @@ void FontAtlas::Initialize(GraphicsCommandBuffer cmd) { VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, // The atlas is sampled by the UI text *compute* shader, not an RT // pipeline — scope the upload barriers to the real consumer. - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + // Streamed: glyphs are rasterized into buffer.value on the CPU and + // re-uploaded every frame via UpdateRegion, so the persistent staging + // map must outlive the first upload rather than being released (#114). + /*streamed*/ true ); std::memset(image.buffer.value, 0, kAtlasSize * kAtlasSize); #else diff --git a/interfaces/Crafter.Graphics-ImageVulkan.cppm b/interfaces/Crafter.Graphics-ImageVulkan.cppm index 2c40a01..8723ea7 100644 --- a/interfaces/Crafter.Graphics-ImageVulkan.cppm +++ b/interfaces/Crafter.Graphics-ImageVulkan.cppm @@ -90,12 +90,24 @@ export namespace Crafter { // consumer. Defaults to RT — the font atlas overrides it to COMPUTE, // since UI text is rendered by a compute shader, not an RT pipeline. VkPipelineStageFlags consumerStage = VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR; + // Whether this image is continuously re-uploaded from a persistent + // CPU-side staging map. False (the default) marks a static texture + // uploaded once — e.g. Sponza albedo: the first Update's buffer→image + // copy is the only reader of the host-visible staging `buffer`, so it + // is released to the fence-keyed deletion queue (#101/#102) right after + // the copy instead of pinning HOST_VISIBLE / small-BAR memory for the + // image's whole life (issue #114). True keeps `buffer` alive: a streamed + // image (the FontAtlas) re-fills buffer.value on the CPU and re-uploads + // every frame, so the persistent map must survive — its uploads go + // through UpdateRegion, which never releases the staging. + bool streamed = false; - void Create(std::uint16_t width, std::uint16_t height, std::uint8_t mipLevels, VkCommandBuffer cmd, VkFormat format, VkImageCreateFlags flags, VkImageLayout layout, VkPipelineStageFlags consumerStage = VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR) { + void Create(std::uint16_t width, std::uint16_t height, std::uint8_t mipLevels, VkCommandBuffer cmd, VkFormat format, VkImageCreateFlags flags, VkImageLayout layout, VkPipelineStageFlags consumerStage = VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, bool streamed = false) { this->width = width; this->height = height; this->mipLevels = mipLevels; this->consumerStage = consumerStage; + this->streamed = streamed; buffer.Create( VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, @@ -212,6 +224,7 @@ export namespace Crafter { } else { TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, layout, VK_PIPELINE_STAGE_TRANSFER_BIT, consumerStage, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, 0, mipLevels); } + ReleaseStaging(); } // Upload only the sub-rectangle [x, x+w) × [y, y+h) of the staging @@ -365,15 +378,37 @@ export namespace Crafter { } else { TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, layout, VK_PIPELINE_STAGE_TRANSFER_BIT, consumerStage, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, 0, mipLevels); } + ReleaseStaging(); } void Destroy() { vkDestroyImageView(Device::device, imageView, nullptr); vkDestroyImage(Device::device, image, nullptr); vkFreeMemory(Device::device, imageMemory, nullptr); + // Free any staging this image still owns: a streamed image's + // persistent `buffer`, or either staging buffer on an image + // destroyed before its first upload released them (issue #114). + // ReleaseStaging()/Update already null released handles, so these + // are no-ops then; Clear() is gated on a live handle so a static + // texture (whose `buffer` was deferred-cleared) can't double-free. + if (buffer.buffer != VK_NULL_HANDLE) buffer.Clear(); + if (compressedStaging.buffer != VK_NULL_HANDLE) compressedStaging.Clear(); } private: + // Release the host-visible staging `buffer` after a one-shot upload, + // unless this is a streamed image (FontAtlas) whose persistent map must + // survive. The recorded buffer→image copy still references buffer.buffer, + // so route through the fence-keyed deletion queue (#101/#102) instead of + // an immediate Clear(): DeferredClear nulls the handle (so re-entry and + // Destroy treat the staging as already gone) while the allocation + // outlives this submit's frame. Issue #114. + void ReleaseStaging() { + if (!streamed) { + buffer.DeferredClear(); + } + } + void TransitionImageLayout(VkCommandBuffer cmd, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, VkPipelineStageFlags sourceStage, VkPipelineStageFlags destinationStage, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, std::uint32_t mipLevel, std::uint32_t count) { VkImageMemoryBarrier barrier = {}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; diff --git a/project.cpp b/project.cpp index 97d51b5..485c8d5 100644 --- a/project.cpp +++ b/project.cpp @@ -651,6 +651,40 @@ extern "C" Configuration CrafterBuildProject(std::span a msc.GetInterfacesAndImplementations(ifaces, meshStagingImpls); cfg.tests.push_back(std::move(meshStagingTest)); + // Issue #114: a static ImageVulkan no longer pins its host-visible + // staging `buffer` for the image's life — Update releases it via + // DeferredClear() right after recording the buffer→image copy, so the + // fence-keyed deletion queue (#101/#102) frees it once that submit's + // frame clears, while a `streamed` image (the FontAtlas) keeps its + // persistent map. Drives the real upload path on a headless device (no + // swapchain/window — a buffer→image copy + readback only needs the queue + // + command pool) and asserts the staging is enqueued (not pinned), the + // image reads back byte-equal (the released staging outlived the submit), + // the entry retires only after framesInFlight frames, and a streamed + // image keeps + then frees its staging on Destroy. Shares the native + // build settings. + Test imageStagingTest; + Configuration& isc = imageStagingTest.config; + isc.path = cfg.path; + isc.name = "ImageStagingRelease"; + isc.outputName = "ImageStagingRelease"; + isc.type = ConfigurationType::Executable; + isc.target = cfg.target; + isc.march = cfg.march; + isc.mtune = cfg.mtune; + isc.debug = cfg.debug; + isc.sysroot = cfg.sysroot; + isc.dependencies = cfg.dependencies; + isc.externalDependencies = cfg.externalDependencies; + isc.compileFlags = cfg.compileFlags; + isc.linkFlags = cfg.linkFlags; + isc.defines = cfg.defines; + isc.cFiles = cfg.cFiles; + std::vector imageStagingImpls(impls.begin(), impls.end()); + imageStagingImpls.emplace_back("tests/ImageStagingRelease/main"); + isc.GetInterfacesAndImplementations(ifaces, imageStagingImpls); + cfg.tests.push_back(std::move(imageStagingTest)); + // Issue #89: Device::PreferDirectDeviceWrite chooses the upload strategy // for a CPU-written, GPU-read buffer — direct HOST_VISIBLE|DEVICE_LOCAL // map+write on ReBAR/UMA vs. staged-into-pure-DEVICE_LOCAL on a small diff --git a/tests/ImageStagingRelease/main.cpp b/tests/ImageStagingRelease/main.cpp new file mode 100644 index 0000000..226226f --- /dev/null +++ b/tests/ImageStagingRelease/main.cpp @@ -0,0 +1,238 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Issue #114: a static ImageVulkan used to keep its host-visible staging +// `buffer` (sized w*h, persistently mapped) alive for the whole life of the +// image, pinning HOST_VISIBLE / small-BAR memory long after the one upload that +// reads it has retired — and Destroy() never freed it. Update now releases it +// via VulkanBuffer::DeferredClear() right after recording the buffer→image +// copy, so the fence-keyed deletion queue (#101/#102) frees it once that +// submit's frame has cleared. A `streamed` image (the FontAtlas) keeps its +// staging, since it re-fills the persistent map and re-uploads every frame. +// +// This drives the real upload path on a headless device — no swapchain/window +// needed, a buffer→image copy only touches the queue + command pool — and +// asserts: +// - After a static Update, image.buffer owns no handle (released), and +// exactly that one allocation was handed to the deletion queue tagged with +// the current frame (so it retires later, not now). +// - The upload + readback still complete with ZERO validation errors and the +// image reads back byte-equal to the staged source — proof the staging +// genuinely outlived the submit (the queue had not yet retired it), so +// releasing it is no use-after-free. +// - The deferred entry retires (and frees) only once framesInFlight frames +// elapse, and not before. +// - A `streamed` image keeps its staging across Update and UpdateRegion (the +// FontAtlas contract), and Destroy() then frees it validation-clean. + +#include "vulkan/vulkan.h" +#include + +import Crafter.Graphics; +import std; + +using namespace Crafter; + +namespace { + +int failures = 0; + +void Check(bool ok, std::string_view what) { + std::println("{} {}", ok ? "PASS" : "FAIL", what); + if (!ok) ++failures; +} + +VkCommandBuffer BeginCmd() { + VkCommandBufferAllocateInfo allocInfo { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = Device::commandPool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }; + VkCommandBuffer cmd = VK_NULL_HANDLE; + Device::CheckVkResult(vkAllocateCommandBuffers(Device::device, &allocInfo, &cmd)); + VkCommandBufferBeginInfo beginInfo { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + Device::CheckVkResult(vkBeginCommandBuffer(cmd, &beginInfo)); + return cmd; +} + +void SubmitWait(VkCommandBuffer cmd) { + Device::CheckVkResult(vkEndCommandBuffer(cmd)); + VkSubmitInfo submitInfo { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &cmd, + }; + Device::CheckVkResult(vkQueueSubmit(Device::queue, 1, &submitInfo, VK_NULL_HANDLE)); + Device::CheckVkResult(vkQueueWaitIdle(Device::queue)); + vkFreeCommandBuffers(Device::device, Device::commandPool, 1, &cmd); +} + +// RGBA8 texel as a 4-byte word, matching VK_FORMAT_R8G8B8A8_UNORM. +using Texel = std::uint32_t; + +constexpr std::uint16_t kW = 4; +constexpr std::uint16_t kH = 4; +constexpr std::uint32_t kCount = static_cast(kW) * kH; + +} // namespace + +int main() { + Device::Initialize(); + Device::validationErrorCount = 0; + + // Control the deferred-deletion clock: enqueue at frame 0, retire after 2. + Device::framesInFlight = 2; + Device::frameCounter = 0; + Device::deletionQueue.clear(); + + // ── Static texture: staging is released right after the upload copy. ────── + ImageVulkan tex; + { + VkCommandBuffer cmd = BeginCmd(); + tex.Create(kW, kH, /*mipLevels*/ 1, cmd, + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT + | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + Check(tex.buffer.buffer != VK_NULL_HANDLE, + "Create allocated the host-visible staging buffer"); + Check(!tex.streamed, "a default ImageVulkan is not streamed"); + + // Fill the staging map with a known pattern, then upload. + for (std::uint32_t i = 0; i < kCount; ++i) { + tex.buffer.value[i] = 0xA0B0C0D0u + i; + } + tex.Update(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + // ── The fix: staging released to the queue at record time, not pinned. ─ + Check(tex.buffer.buffer == VK_NULL_HANDLE, + "static Update releases the staging buffer (no handle pinned)"); + Check(Device::deletionQueue.size() == 1, + "exactly one allocation (the staging) handed to the deletion queue"); + const bool taggedNow = !Device::deletionQueue.empty() + && Device::deletionQueue.front().retireAfter == 0 + && Device::deletionQueue.front().buffer != VK_NULL_HANDLE; + Check(taggedNow, + "deferred staging is tagged with the enqueue frame and still a live handle"); + + // Read the image back to prove the copy saw valid staging. The upload's + // recorded copy still references the now-released staging; it must + // outlive THIS submit (queue retires at frame 2, we're at 0). Move the + // image to TRANSFER_SRC and copy into a host-visible readback buffer in + // the same command buffer. + VkImageMemoryBarrier toSrc { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_SHADER_READ_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = tex.image, + .subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }, + }; + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, + 0, nullptr, 1, &toSrc); + + VulkanBuffer readback; + readback.Create(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, kCount); + VkBufferImageCopy region {}; + region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }; + region.imageExtent = { kW, kH, 1 }; + vkCmdCopyImageToBuffer(cmd, tex.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + readback.buffer, 1, ®ion); + + SubmitWait(cmd); + Check(Device::validationErrorCount == 0, + "static upload + readback raised no validation errors"); + + readback.FlushHost(); + bool match = true; + for (std::uint32_t i = 0; i < kCount; ++i) { + if (readback.value[i] != 0xA0B0C0D0u + i) { match = false; break; } + } + Check(match, + "image reads back byte-equal to the staged source " + "(released staging outlived the submit)"); + } + + // ── Retire timing: the staging frees once framesInFlight frames elapse, + // and not a frame before. We've already wait-idled, so freeing is safe. ─ + Device::ReclaimDeletions(); + Check(Device::deletionQueue.size() == 1, + "staging is NOT freed before framesInFlight frames have elapsed"); + Device::frameCounter = 2; // retireAfter(0) + framesInFlight(2) <= 2 + Device::ReclaimDeletions(); + Check(Device::deletionQueue.empty(), + "staging is freed once its retire frame is reached"); + + // Destroying the image after its staging was released must not double-free + // (buffer handle is already null) and must stay validation-clean. + Device::validationErrorCount = 0; + tex.Destroy(); + Check(Device::validationErrorCount == 0, + "Destroy after a released-staging upload is validation-clean"); + + // ── Streamed image: the persistent staging is kept across uploads. ──────── + Device::frameCounter = 0; + Device::deletionQueue.clear(); + Device::validationErrorCount = 0; + { + ImageVulkan atlas; + VkCommandBuffer cmd = BeginCmd(); + atlas.Create(kW, kH, /*mipLevels*/ 1, cmd, + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + /*streamed*/ true); + Check(atlas.streamed, "streamed=true is recorded on the image"); + + for (std::uint32_t i = 0; i < kCount; ++i) atlas.buffer.value[i] = i; + atlas.Update(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + Check(atlas.buffer.buffer != VK_NULL_HANDLE, + "streamed Update keeps the persistent staging buffer"); + // UpdateRegion (the FontAtlas re-upload path) must also keep it. + atlas.UpdateRegion(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 0, 0, kW, kH); + Check(atlas.buffer.buffer != VK_NULL_HANDLE, + "streamed UpdateRegion keeps the persistent staging buffer"); + Check(Device::deletionQueue.empty(), + "a streamed image enqueues nothing for deferred deletion"); + SubmitWait(cmd); + Check(Device::validationErrorCount == 0, + "streamed upload raised no validation errors"); + + // Destroy frees the still-live persistent staging (the leak #114 names). + atlas.Destroy(); + Check(atlas.buffer.buffer == VK_NULL_HANDLE, + "Destroy frees the streamed image's persistent staging buffer"); + Check(Device::validationErrorCount == 0, + "streamed Destroy (freeing live staging) is validation-clean"); + } + + std::println("{}", failures == 0 ? "ALL PASS" : "FAILURES PRESENT"); + return failures == 0 ? 0 : 1; +} From 9414863cffe5822b473c04989661882b0a2c1591 Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 19:44:33 +0000 Subject: [PATCH 27/47] perf(sync): scope frame-loop barriers to swapchain image + real per-pass stages (#115) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The inter-pass and acquire/present barriers in the frame loop set both stage masks to ALL_COMMANDS, and the inter-pass dependency used a queue-wide VkMemoryBarrier — fully serialising against every pipeline stage and flushing all caches every frame, when all the next pass needs is the swapchain image the previous one wrote. Replace the inter-pass global VkMemoryBarrier with an image memory barrier scoped to the swapchain image's single colour subresource (as the intra-pass UI barrier already does), and derive the barrier stage masks per pass: RenderPass::SwapchainStage() is overridden by UIRenderer (COMPUTE_SHADER) and RTPass (RAY_TRACING_SHADER), so a compute->compute edge only serialises COMPUTE while an RT pass pulls in RAY_TRACING — the acquire/present frame-edge masks use the real union of the frame's passes (SwapchainStageUnion). The base default and the empty-passes fallback are the conservative COMPUTE | RAY_TRACING | TRANSFER union, so a polymorphic or un-overridden pass can only be over- not under-synchronised. Adds SwapchainBarrierScope (pure CPU) pinning the per-pass derivation, the union narrowing, and the inter-pass barrier scope; FrameLoopSync already drives the real GPU frame loop with validation enabled. Co-Authored-By: Claude Opus 4.8 --- implementations/Crafter.Graphics-Window.cpp | 35 +++- interfaces/Crafter.Graphics-RTPass.cppm | 7 + interfaces/Crafter.Graphics-RenderPass.cppm | 70 ++++++++ interfaces/Crafter.Graphics-UI.cppm | 9 + project.cpp | 29 ++++ tests/SwapchainBarrierScope/main.cpp | 174 ++++++++++++++++++++ 6 files changed, 316 insertions(+), 8 deletions(-) create mode 100644 tests/SwapchainBarrierScope/main.cpp diff --git a/implementations/Crafter.Graphics-Window.cpp b/implementations/Crafter.Graphics-Window.cpp index 7be3d4e..19f1f55 100644 --- a/implementations/Crafter.Graphics-Window.cpp +++ b/implementations/Crafter.Graphics-Window.cpp @@ -815,7 +815,16 @@ void Window::Render() { : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; acquireBarrier.image = images[currentBuffer]; - vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &acquireBarrier); + // Real per-pass stage union for this frame's passes — the swapchain image + // is only ever written by compute/RT (and possibly transfer), never by the + // whole pipeline, so the frame-edge barriers wait on exactly those stages + // instead of ALL_COMMANDS. Derived once here and reused for the present + // barrier below (passes don't change within a Render()). + const VkPipelineStageFlags swapWriterStages = SwapchainStageUnion(passes); + + // The acquire->GENERAL transition only needs to be visible before the first + // pass writes the image, i.e. at the writer stages — not ALL_COMMANDS. + vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, swapWriterStages, 0, 0, nullptr, 0, nullptr, 1, &acquireBarrier); // Synthesise key-repeat events before listeners run, so the focused // widget's OnTextInput / OnKeyDown sees them in the same frame. @@ -879,18 +888,28 @@ void Window::Render() { passes[i]->Record(drawCmdBuffers[currentBuffer], currentBuffer, *this); if (i + 1 < passes.size()) { - VkMemoryBarrier mb { - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, - .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, - }; - vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 1, &mb, 0, nullptr, 0, nullptr); + // Make pass i's writes to the swapchain image visible to pass i+1. + // The only resource the inter-pass hazard touches is that one image + // (later passes overdraw earlier ones), so use an image memory + // barrier scoped to its subresource instead of a queue-wide + // VkMemoryBarrier — only this image's caches round-trip, exactly as + // the intra-pass UI barrier already does. The stage masks come from + // each pass's own SwapchainStage(), so a compute->compute edge only + // serialises COMPUTE_SHADER while an RT pass on either side pulls in + // RAY_TRACING_SHADER — never the ALL_COMMANDS full-pipeline stall. + VkImageMemoryBarrier ib = BuildSwapchainInterPassBarrier(images[currentBuffer]); + vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], + passes[i]->SwapchainStage(), passes[i + 1]->SwapchainStage(), + 0, 0, nullptr, 0, nullptr, 1, &ib); } } presentBarrier.image = images[currentBuffer]; - vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &presentBarrier); + // The ->PRESENT_SRC transition only needs to wait on the stages that + // actually wrote the image (the per-pass union), not ALL_COMMANDS; present + // itself is gated by the render-finished semaphore, so dst stays BOTTOM. + vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], swapWriterStages, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &presentBarrier); Device::CheckVkResult(vkEndCommandBuffer(drawCmdBuffers[currentBuffer])); diff --git a/interfaces/Crafter.Graphics-RTPass.cppm b/interfaces/Crafter.Graphics-RTPass.cppm index 37d9bdd..6a5ebef 100644 --- a/interfaces/Crafter.Graphics-RTPass.cppm +++ b/interfaces/Crafter.Graphics-RTPass.cppm @@ -35,6 +35,13 @@ export namespace Crafter { RTPass(PipelineRTVulkan* p) : pipeline(p) {} + // An RT pass writes the swapchain image from the ray-tracing pipeline, + // so the frame loop's barriers must wait on RAY_TRACING_SHADER — using + // the compute stage here would under-synchronise and corrupt the image. + VkPipelineStageFlags SwapchainStage() const override { + return VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR; + } + void Record(VkCommandBuffer cmd, std::uint32_t frameIdx, Window& window) override { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline->pipeline); // NVIDIA descriptor-heap AS-read workaround (issue #15 / #7): feed diff --git a/interfaces/Crafter.Graphics-RenderPass.cppm b/interfaces/Crafter.Graphics-RenderPass.cppm index e49bc5c..c354336 100644 --- a/interfaces/Crafter.Graphics-RenderPass.cppm +++ b/interfaces/Crafter.Graphics-RenderPass.cppm @@ -16,6 +16,10 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +module; +#ifndef CRAFTER_GRAPHICS_WINDOW_DOM +#include "vulkan/vulkan.h" +#endif // !CRAFTER_GRAPHICS_WINDOW_DOM export module Crafter.Graphics:RenderPass; import std; import :GraphicsTypes; @@ -23,8 +27,74 @@ import :GraphicsTypes; export namespace Crafter { struct Window; +#ifndef CRAFTER_GRAPHICS_WINDOW_DOM + // Conservative union of every pipeline stage that can write the swapchain + // storage image: a compute pass via COMPUTE_SHADER, a ray-tracing pass via + // RAY_TRACING_SHADER, plus TRANSFER for any blit/copy writer. Used as the + // RenderPass default (a pass that doesn't narrow its own stage) and as the + // fallback when there are no passes to derive a real union from. Far tighter + // than ALL_COMMANDS, but never under-synchronises a polymorphic pass. + inline constexpr VkPipelineStageFlags kSwapchainWriterStages = + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT + | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR + | VK_PIPELINE_STAGE_TRANSFER_BIT; +#endif // !CRAFTER_GRAPHICS_WINDOW_DOM + struct RenderPass { virtual void Record(GraphicsCommandBuffer cmd, std::uint32_t frameIdx, Window& window) = 0; virtual ~RenderPass() = default; +#ifndef CRAFTER_GRAPHICS_WINDOW_DOM + // Pipeline stage at which this pass reads and writes the swapchain + // storage image. The frame loop uses this to scope the inter-pass and + // frame-edge barriers to the stages that actually touch the image + // instead of ALL_COMMANDS. A subclass MUST narrow this to its real + // stage (RTPass -> RAY_TRACING_SHADER, a compute pass -> COMPUTE_SHADER): + // the default is the conservative writer union so an un-overridden pass + // can never be under-synchronised, only over-synchronised. + virtual VkPipelineStageFlags SwapchainStage() const { return kSwapchainWriterStages; } +#endif // !CRAFTER_GRAPHICS_WINDOW_DOM }; + +#ifndef CRAFTER_GRAPHICS_WINDOW_DOM + // Union of the swapchain-access stages declared by `passes` — the "real" + // per-pass stage union the frame-edge barriers (acquire dst / present src) + // narrow to: an all-compute frame yields COMPUTE_SHADER only, a frame with + // any RT pass folds in RAY_TRACING_SHADER, and so on. Falls back to the + // conservative writer union when there are no passes (the image is still + // transitioned, just never written), so the barrier is always well-formed. + inline VkPipelineStageFlags SwapchainStageUnion(std::span passes) { + if (passes.empty()) return kSwapchainWriterStages; + VkPipelineStageFlags stages = 0; + for (RenderPass* pass : passes) stages |= pass->SwapchainStage(); + return stages; + } + + // The inter-pass swapchain barrier (replacing the old queue-wide + // VkMemoryBarrier at the #115 location). Scoped to the swapchain image's + // single colour subresource so only that image's shader caches round-trip — + // unrelated buffers/images stay resident — exactly as the intra-pass UI + // barrier already does. The image is bound as a storage image and stays in + // VK_IMAGE_LAYOUT_GENERAL, so this is a pure memory dependency (no layout + // transition). The execution scope (stage masks) is supplied per pass pair + // by the caller via vkCmdPipelineBarrier. + inline VkImageMemoryBarrier BuildSwapchainInterPassBarrier(VkImage image) { + return { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + } +#endif // !CRAFTER_GRAPHICS_WINDOW_DOM } diff --git a/interfaces/Crafter.Graphics-UI.cppm b/interfaces/Crafter.Graphics-UI.cppm index 374ed5a..0eec8f2 100644 --- a/interfaces/Crafter.Graphics-UI.cppm +++ b/interfaces/Crafter.Graphics-UI.cppm @@ -201,6 +201,15 @@ export namespace Crafter { void Record(GraphicsCommandBuffer cmd, std::uint32_t frameIdx, Window& window) override; +#ifndef CRAFTER_GRAPHICS_WINDOW_DOM + // A UI pass draws into the swapchain image entirely from compute + // shaders (see Dispatch), so the frame loop's inter-pass / frame-edge + // barriers only need to synchronise the COMPUTE_SHADER stage for it. + VkPipelineStageFlags SwapchainStage() const override { + return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + } +#endif // !CRAFTER_GRAPHICS_WINDOW_DOM + UIDispatchHeader FillHeader(std::uint32_t itemBufferSlot, std::uint32_t itemCount, std::array clipRectPx = {0.0f, 0.0f, 1e9f, 1e9f}, diff --git a/project.cpp b/project.cpp index 97d51b5..04bb68e 100644 --- a/project.cpp +++ b/project.cpp @@ -473,6 +473,35 @@ extern "C" Configuration CrafterBuildProject(std::span a mbc.GetInterfacesAndImplementations(ifaces, mipBarrierImpls); cfg.tests.push_back(std::move(mipBarrierTest)); + // Issue #115: the frame loop's inter-pass and acquire/present barriers + // no longer use ALL_COMMANDS / a queue-wide VkMemoryBarrier. The stage + // masks are derived per pass via RenderPass::SwapchainStage() (compute + // vs ray-tracing) and unioned across the frame by SwapchainStageUnion, + // and the inter-pass dependency is scoped to the swapchain image by + // BuildSwapchainInterPassBarrier — all pure CPU logic over the pass + // list, so this test drives them directly with no GPU at runtime. + Test swapBarrierTest; + Configuration& sbc = swapBarrierTest.config; + sbc.path = cfg.path; + sbc.name = "SwapchainBarrierScope"; + sbc.outputName = "SwapchainBarrierScope"; + sbc.type = ConfigurationType::Executable; + sbc.target = cfg.target; + sbc.march = cfg.march; + sbc.mtune = cfg.mtune; + sbc.debug = cfg.debug; + sbc.sysroot = cfg.sysroot; + sbc.dependencies = cfg.dependencies; + sbc.externalDependencies = cfg.externalDependencies; + sbc.compileFlags = cfg.compileFlags; + sbc.linkFlags = cfg.linkFlags; + sbc.defines = cfg.defines; + sbc.cFiles = cfg.cFiles; + std::vector swapBarrierImpls(impls.begin(), impls.end()); + swapBarrierImpls.emplace_back("tests/SwapchainBarrierScope/main"); + sbc.GetInterfacesAndImplementations(ifaces, swapBarrierImpls); + cfg.tests.push_back(std::move(swapBarrierTest)); + // Issue #47: the fused UI uber-kernel (shaders/ui-fused.comp.glsl) and // its C++ push-constant mirror UIFusedHeader. Compiles the real shader // with glslang, validates with spirv-val, and pins the push-constant diff --git a/tests/SwapchainBarrierScope/main.cpp b/tests/SwapchainBarrierScope/main.cpp new file mode 100644 index 0000000..eb5e4e3 --- /dev/null +++ b/tests/SwapchainBarrierScope/main.cpp @@ -0,0 +1,174 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Issue #115: the frame loop's inter-pass and frame-edge (acquire/present) +// barriers used to set BOTH stage masks to VK_PIPELINE_STAGE_ALL_COMMANDS_BIT +// and the inter-pass dependency to a queue-wide VkMemoryBarrier — flushing +// every cache and fully serialising against the whole pipeline every frame, +// when all the next pass needs is the swapchain image the previous one wrote. +// +// The fix scopes the inter-pass dependency to the swapchain image's single +// colour subresource (BuildSwapchainInterPassBarrier) and narrows the stage +// masks to the stages that actually touch that image, derived per pass via the +// polymorphic RenderPass::SwapchainStage() and unioned across the frame by +// SwapchainStageUnion. The load-bearing correctness constraint is that this +// derivation is per-pass: a compute pass writes via COMPUTE_SHADER, a ray- +// tracing pass via RAY_TRACING_SHADER, so a hardcoded compute mask would +// UNDER-synchronise an RT pass and corrupt the image. All three helpers are +// pure CPU logic over the pass list, so this test drives them directly with no +// GPU device at runtime — mirroring MipChainBarrierBatch / UploadStrategy. + +#include +#include "vulkan/vulkan.h" + +import Crafter.Graphics; +import std; +using namespace Crafter; + +namespace { + +int failures = 0; + +void Check(bool ok, std::string_view what) { + std::println("{} {}", ok ? "PASS" : "FAIL", what); + if (!ok) ++failures; +} + +// A pass that never overrides SwapchainStage(): stands in for any future pass +// that forgets to narrow its stage, and must fall back to the conservative +// writer union rather than silently picking one stage (which could under-sync). +struct DefaultPass : RenderPass { + void Record(GraphicsCommandBuffer, std::uint32_t, Window&) override {} +}; + +VkImage SentinelImage() { + return reinterpret_cast(static_cast(0xC0FFEE)); +} + +} // namespace + +int main() { + // ─── per-pass SwapchainStage() polymorphism ───────────────────────────── + // The whole reason the fix is "moderate, not trivial": these two stages + // MUST differ, or scoping an RT pass's barrier to the compute stage would + // under-synchronise it. Pin that they are distinct and each correct. + Check(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT != VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, + "compute and ray-tracing write stages are distinct (the under-sync trap)"); + + UIRenderer ui; // real compute UI pass + RTPass rt(nullptr); // real RT pass (SwapchainStage doesn't touch the pipeline) + DefaultPass def; // un-overridden fallback + + // Access through the base pointer — the frame loop only ever sees RenderPass*. + RenderPass* uiBase = &ui; + RenderPass* rtBase = &rt; + RenderPass* defBase = &def; + + Check(uiBase->SwapchainStage() == VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + "UIRenderer reports the COMPUTE_SHADER stage"); + Check(rtBase->SwapchainStage() == VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, + "RTPass reports the RAY_TRACING_SHADER stage"); + Check(defBase->SwapchainStage() == kSwapchainWriterStages, + "an un-overridden pass falls back to the conservative writer union"); + + // ─── the conservative writer union constant ───────────────────────────── + constexpr VkPipelineStageFlags expectedUnion = + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT + | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR + | VK_PIPELINE_STAGE_TRANSFER_BIT; + Check(kSwapchainWriterStages == expectedUnion, + "writer union = COMPUTE_SHADER | RAY_TRACING_SHADER | TRANSFER"); + // The point of the whole change: even the conservative fallback is far + // tighter than the ALL_COMMANDS mask it replaces. + Check(kSwapchainWriterStages != VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + "writer union is narrower than ALL_COMMANDS"); + + // ─── SwapchainStageUnion over the frame's pass list ───────────────────── + // Empty frame: no writers to derive from, so fall back to the conservative + // union (the image is still transitioned, just never written). + Check(SwapchainStageUnion(std::span{}) == kSwapchainWriterStages, + "empty pass list falls back to the conservative writer union"); + + // All-compute frame: the union narrows to COMPUTE_SHADER ONLY — this is the + // real perf win, no RAY_TRACING / TRANSFER / ALL_COMMANDS dragged in. + { + std::array computeOnly = {uiBase, uiBase}; + VkPipelineStageFlags u = SwapchainStageUnion(computeOnly); + Check(u == VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + "all-compute frame narrows the union to COMPUTE_SHADER only"); + Check((u & VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR) == 0, + "all-compute frame does NOT pull in RAY_TRACING_SHADER"); + } + + // RT-only frame: narrows to RAY_TRACING_SHADER only. + { + std::array rtOnly = {rtBase}; + Check(SwapchainStageUnion(rtOnly) == VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, + "RT-only frame narrows the union to RAY_TRACING_SHADER only"); + } + + // Mixed frame: the union folds in BOTH stages — never under-syncs the RT + // pass, never over-syncs to ALL_COMMANDS. + { + std::array mixed = {uiBase, rtBase}; + VkPipelineStageFlags u = SwapchainStageUnion(mixed); + Check(u == (VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR), + "mixed compute+RT frame unions COMPUTE_SHADER | RAY_TRACING_SHADER"); + Check((u & VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR) != 0, + "mixed frame keeps RAY_TRACING_SHADER (no RT under-sync)"); + } + + // A frame containing an un-overridden pass conservatively widens to the + // full writer union — safe by construction. + { + std::array withDefault = {uiBase, defBase}; + Check(SwapchainStageUnion(withDefault) == kSwapchainWriterStages, + "a frame with an un-overridden pass widens to the conservative union"); + } + + // ─── BuildSwapchainInterPassBarrier ───────────────────────────────────── + // Replaces the queue-wide VkMemoryBarrier: an image memory barrier scoped + // to the swapchain image's single colour subresource, GENERAL->GENERAL (no + // layout change — the storage image stays bound), matching the intra-pass + // UI barrier's access scope. + { + VkImageMemoryBarrier b = BuildSwapchainInterPassBarrier(SentinelImage()); + Check(b.sType == VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, "inter-pass barrier sType is IMAGE_MEMORY_BARRIER"); + Check(b.image == SentinelImage(), "inter-pass barrier carries the swapchain image handle"); + Check(b.oldLayout == VK_IMAGE_LAYOUT_GENERAL && b.newLayout == VK_IMAGE_LAYOUT_GENERAL, + "inter-pass barrier is GENERAL->GENERAL (pure memory dependency, no transition)"); + Check(b.srcAccessMask == VK_ACCESS_SHADER_WRITE_BIT, + "inter-pass barrier src access is shader write"); + Check(b.dstAccessMask == (VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT), + "inter-pass barrier dst access is shader read|write"); + Check(b.srcQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED && b.dstQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED, + "inter-pass barrier is not a queue-family transfer"); + Check(b.subresourceRange.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT, + "inter-pass barrier covers the colour aspect"); + Check(b.subresourceRange.levelCount == 1 && b.subresourceRange.layerCount == 1, + "inter-pass barrier is scoped to the single swapchain subresource"); + } + + if (failures != 0) { + std::println("{} check(s) failed", failures); + return EXIT_FAILURE; + } + std::println("all checks passed"); + return EXIT_SUCCESS; +} From 1f4c77000a5809ae23271c690638e1e20de5a17e Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 19:50:48 +0000 Subject: [PATCH 28/47] perf(mesh): dirty-range vertex upload for deforming-mesh Refit (#119) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mesh::Refit re-uploaded the entire vertex array every frame on the in-place UPDATE path (full host write + flush + barrier on the direct path; full re-stage + copy on the staged path), even when only a handful of vertices moved. Add VulkanBuffer::UploadDeviceLocalRange — a dirty-range counterpart to UploadDeviceLocal that writes/flushes/stages/copies and barriers only the half-open element range [offset, offset+count) of an already-allocated device-local buffer. It picks direct-map vs staged-copy from the memory type the buffer was actually allocated with (not the sub-range size, which PreferDirectDeviceWrite would mis-route), and the direct path's ranged flush is rounded to nonCoherentAtomSize and clamped to the allocation size (mappedSize is now recorded for every buffer, not just mapped ones). Add a dirty-range Refit overload taking the full vertex/index arrays plus a (dirtyVertexOffset, dirtyVertexCount) window. The full-span Refit now delegates to it with the whole array as the window. On the in-place UPDATE path only the declared window is uploaded — the rest of the device buffer retains last refit's positions; when an UPDATE isn't possible it falls back to the full-span rebuild, which is why the full arrays are still passed. The WebGPU/DOM backend keeps API symmetry: it has no hardware AS, so it ignores the window and rebuilds the host BVH from the full geometry. BLASBuildOptions exercises the dirty-range refit on the direct path, the staged path, and the count-change rebuild fallback, asserting AS-handle / blasAddr stability and zero Vulkan validation errors. Resolves #119 Co-Authored-By: Claude Opus 4.8 --- .../Crafter.Graphics-Mesh-WebGPU.cpp | 12 +++ implementations/Crafter.Graphics-Mesh.cpp | 31 +++++-- interfaces/Crafter.Graphics-Mesh.cppm | 27 ++++++ interfaces/Crafter.Graphics-VulkanBuffer.cppm | 88 ++++++++++++++++++- tests/BLASBuildOptions/main.cpp | 49 +++++++++++ 5 files changed, 199 insertions(+), 8 deletions(-) diff --git a/implementations/Crafter.Graphics-Mesh-WebGPU.cpp b/implementations/Crafter.Graphics-Mesh-WebGPU.cpp index e85317c..edb4065 100644 --- a/implementations/Crafter.Graphics-Mesh-WebGPU.cpp +++ b/implementations/Crafter.Graphics-Mesh-WebGPU.cpp @@ -377,6 +377,18 @@ void Mesh::Refit(std::span> vertices, Build(vertices, indices, cmd); } +void Mesh::Refit(std::span> vertices, + std::span indices, + std::uint32_t /*dirtyVertexOffset*/, + std::uint32_t /*dirtyVertexCount*/, + WebGPUCommandEncoderRef cmd) { + // No hardware AS, so there is no sub-range to update in place — the + // software path rebuilds the host BVH over the full geometry regardless. + // The dirty window only matters to the hardware backend's ranged upload + // (#119); here it is ignored and this is identical to the full-span Refit. + Build(vertices, indices, cmd); +} + void Mesh::RefitProcedural(std::span aabbs, WebGPUCommandEncoderRef cmd) { BuildProcedural(aabbs, opaque, cmd); diff --git a/implementations/Crafter.Graphics-Mesh.cpp b/implementations/Crafter.Graphics-Mesh.cpp index 90649a7..01572da 100644 --- a/implementations/Crafter.Graphics-Mesh.cpp +++ b/implementations/Crafter.Graphics-Mesh.cpp @@ -371,6 +371,11 @@ void Mesh::BuildProcedural(VkDeviceAddress aabbAddress, std::uint32_t count, boo } void Mesh::Refit(std::span> verticies, std::span indicies, VkCommandBuffer cmd) { + // Whole-vertex-array refit: the entire array is the dirty range. + Refit(verticies, indicies, 0, static_cast(verticies.size()), cmd); +} + +void Mesh::Refit(std::span> verticies, std::span indicies, std::uint32_t dirtyVertexOffset, std::uint32_t dirtyVertexCount, VkCommandBuffer cmd) { // A hardware in-place UPDATE is only valid when the original build asked // for it, an AS already exists, and the topology is unchanged. Otherwise // fall back to a full rebuild (mode BUILD) of the same buffers — still @@ -383,7 +388,9 @@ void Mesh::Refit(std::span> verticies, std::span> verticies, std::span> verticies, std::span(verticies.size()), cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); + // + // Re-upload only the dirty sub-range [dirtyVertexOffset, +dirtyVertexCount): + // the rest of the vertex buffer already holds last refit's positions, so a + // ranged upload patches just the vertices that moved (#119). The window is + // clamped to the array — a caller window past the end would otherwise read + // out of bounds / overflow the copy. UploadDeviceLocalRange writes into the + // existing same-sized allocation (no Resize), so the address the UPDATE + // reads stays stable, then barriers just that sub-range before the build. On + // the staged path this re-stages only the dirty slice (the deforming-mesh + // fallback, #73). + std::uint32_t offset = std::min(dirtyVertexOffset, static_cast(verticies.size())); + std::uint32_t count = std::min(dirtyVertexCount, static_cast(verticies.size()) - offset); + if (count != 0) { + vertexBuffer.UploadDeviceLocalRange(verticies.data() + offset, offset, count, cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); + } RecordBLASBuild(*this, static_cast(verticies.size()), static_cast(indicies.size()), buildFlags, /*update*/ true, cmd); } diff --git a/interfaces/Crafter.Graphics-Mesh.cppm b/interfaces/Crafter.Graphics-Mesh.cppm index 1a84574..6176f3c 100644 --- a/interfaces/Crafter.Graphics-Mesh.cppm +++ b/interfaces/Crafter.Graphics-Mesh.cppm @@ -164,6 +164,22 @@ export namespace Crafter { // both buffers. Lifetime contract matches Build: the spans need only // outlive this call. Call this per frame to track a deforming mesh. void Refit(std::span> verticies, std::span indicies, VkCommandBuffer cmd); + // Dirty-range refit: same as Refit above, but only the contiguous block + // of vertices in [dirtyVertexOffset, dirtyVertexOffset + dirtyVertexCount) + // actually moved, so on the in-place UPDATE path only that sub-range is + // re-uploaded — host-write + flush + barrier (direct) or re-stage + copy + // (staged) scale with the moved vertices, not the whole array (#119). Use + // this for a deforming mesh that nudges a small, known window each frame. + // `verticies` / `indicies` are still the *full* arrays (same contract and + // topology rule as the full-span Refit — the index buffer is read only + // for its count on the UPDATE path); the dirty window simply tells the + // upload which slice changed, and is clamped to the array bounds. When an + // in-place UPDATE is not possible (allowUpdate was not set, or the counts + // changed) this falls back to the full-span Refit, which re-uploads + // everything from `verticies` / `indicies` — so passing the full arrays + // keeps that fallback correct. The AS handle / blasAddr are preserved on + // the UPDATE path exactly as in the full-span Refit. + void Refit(std::span> verticies, std::span indicies, std::uint32_t dirtyVertexOffset, std::uint32_t dirtyVertexCount, VkCommandBuffer cmd); // Procedural analog of Refit: new object-space boxes, same count. void RefitProcedural(std::span aabbs, VkCommandBuffer cmd); // Zero-copy procedural refit: the device-buffer counterpart of @@ -307,6 +323,17 @@ export namespace Crafter { void Refit(std::span> vertices, std::span indices, WebGPUCommandEncoderRef cmd = 0); + // Dirty-range refit (#119). The software path has no hardware AS to + // update a sub-range of — it rebuilds the host BVH over the full + // geometry regardless — so the dirty window is ignored here and this + // behaves exactly like the full-span Refit above (a fresh build that + // re-publishes blasAddr). The overload exists so portable deforming-mesh + // code that passes a dirty window compiles and stays correct on WebGPU. + void Refit(std::span> vertices, + std::span indices, + std::uint32_t dirtyVertexOffset, + std::uint32_t dirtyVertexCount, + WebGPUCommandEncoderRef cmd = 0); void RefitProcedural(std::span aabbs, WebGPUCommandEncoderRef cmd = 0); // Zero-copy procedural refit: re-copy the boxes from the device buffer diff --git a/interfaces/Crafter.Graphics-VulkanBuffer.cppm b/interfaces/Crafter.Graphics-VulkanBuffer.cppm index fe0e60a..d606921 100644 --- a/interfaces/Crafter.Graphics-VulkanBuffer.cppm +++ b/interfaces/Crafter.Graphics-VulkanBuffer.cppm @@ -140,8 +140,14 @@ namespace Crafter { }; address = vkGetBufferDeviceAddress(Device::device, &addressInfo); + // Record the allocation's byte size (≥ `size`) for every buffer, + // not just mapped ones: UploadDeviceLocalRange's direct path flushes + // only the dirty sub-range and clamps its rounded-up upper bound to + // this (the nonCoherentAtomSize-aligned-end exception, see + // AlignMappedFlushRange). A non-mapped buffer never maps persistently, + // but its memory is still the allocation a transient map writes into. + mappedSize = memReqs.size; if constexpr(Mapped) { - mappedSize = memReqs.size; Device::CheckVkResult(vkMapMemory(Device::device, memory, 0, memReqs.size, 0, reinterpret_cast(&(VulkanBufferMappedConditional::value)))); } } @@ -312,6 +318,86 @@ namespace Crafter { vkCmdPipelineBarrier(cmd, srcStageMask, dstStageMask, 0, 0, NULL, 1, &barrier, 0, NULL); } + // Re-upload only the half-open sub-range [offset, offset+count) of an + // already-allocated device-local buffer — the dirty-range counterpart of + // UploadDeviceLocal. `src` points at the first changed element (not the + // start of the whole array); `offset` is that element's index in this + // buffer. The buffer must already exist at its full size (a prior + // UploadDeviceLocal / Build sized it): this never Resizes, so the device + // address stays stable and the untouched elements keep their last + // contents. Only the dirty bytes are written + flushed (direct path) or + // staged + copied (staged path), and the post-upload barrier covers only + // that sub-range — so a deforming-mesh refit that nudges a few vertices + // pays for those vertices, not a full-array re-upload + flush + copy (#119). + // + // The direct-vs-staged choice is read from the memory type the buffer was + // actually allocated with (HOST_VISIBLE → map + write in place; + // device-local-only → stage + GPU copy), NOT from the sub-range size: the + // allocation is fixed, so a small dirty range must not be mis-routed to a + // map of a non-host-visible buffer the way UploadDeviceLocal's size-based + // PreferDirectDeviceWrite check would. The staged path's GPU copy needs + // VK_BUFFER_USAGE_TRANSFER_DST_BIT on the destination — already present, + // since UploadDeviceLocal only allocates device-local-only memory on the + // staged path, where it adds that bit. + void UploadDeviceLocalRange(const T* src, std::uint32_t offset, std::uint32_t count, VkCommandBuffer cmd, VkAccessFlags dstAccessMask, VkPipelineStageFlags dstStageMask) requires(!Mapped) { + VkDeviceSize byteOffset = static_cast(offset) * sizeof(T); + VkDeviceSize bytes = static_cast(count) * sizeof(T); + VkAccessFlags srcAccessMask; + VkPipelineStageFlags srcStageMask; + if (memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { + void* mapped = nullptr; + Device::CheckVkResult(vkMapMemory(Device::device, memory, 0, VK_WHOLE_SIZE, 0, &mapped)); + std::memcpy(static_cast(mapped) + byteOffset, src, bytes); + // Non-coherent memory needs an explicit flush — but only of the + // sub-range actually written, rounded outward to + // nonCoherentAtomSize (and clamped to the allocation size). + if (!(memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { + MappedFlushRange r = AlignMappedFlushRange( + byteOffset, bytes, Device::nonCoherentAtomSize, mappedSize); + VkMappedMemoryRange range { + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = memory, + .offset = r.offset, + .size = r.size + }; + vkFlushMappedMemoryRanges(Device::device, 1, &range); + } + vkUnmapMemory(Device::device, memory); + srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; + srcStageMask = VK_PIPELINE_STAGE_HOST_BIT; + } else { + // Device-local-only: stage just the dirty elements and copy them + // into place at byteOffset. The staging buffer outlives the queued + // copy via the fence-keyed deletion queue (#101/#102), exactly as + // the full-buffer staged path does. + VulkanBuffer staging; + staging.Create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, count); + std::memcpy(staging.value, src, bytes); + staging.FlushDevice(); + VkBufferCopy region { .srcOffset = 0, .dstOffset = byteOffset, .size = bytes }; + vkCmdCopyBuffer(cmd, staging.buffer, buffer, 1, ®ion); + staging.DeferredClear(); + srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + } + + // Order only the written sub-range before the consumer reads it. The + // untouched bytes were made visible by their own prior upload's + // barrier and are not written in this submit, so they need no fresh + // dependency even though the build reads the whole buffer. + VkBufferMemoryBarrier barrier = { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .srcAccessMask = srcAccessMask, + .dstAccessMask = dstAccessMask, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = buffer, + .offset = byteOffset, + .size = bytes + }; + vkCmdPipelineBarrier(cmd, srcStageMask, dstStageMask, 0, 0, NULL, 1, &barrier, 0, NULL); + } + void FlushDevice() requires(Mapped) { // Coherent memory needs no explicit flush — host writes are // automatically visible to the device. diff --git a/tests/BLASBuildOptions/main.cpp b/tests/BLASBuildOptions/main.cpp index 538fae9..827b0ca 100644 --- a/tests/BLASBuildOptions/main.cpp +++ b/tests/BLASBuildOptions/main.cpp @@ -157,6 +157,42 @@ int main() { Check(tri.blasAddr == triAddrBefore, "Refit kept the same blasAddr (instances stay valid)"); + // Dirty-range refit (issue #119): move only a sub-window of the vertices + // and pass it as [offset, count). The ranged upload patches just those + // vertices in place; the UPDATE must still keep the same AS handle / + // blasAddr, and the validation layer (checked at the end) is the real proof + // that the ranged host-write / flush / barrier — and the staged variant + // below — are spec-correct. The untouched vertices retain their last values + // in the device buffer, so the refit BLAS is over the full deformed cube. + { + // Full array of moved positions; we declare only the back half [4,8) + // as the dirty window, so only those 4 vertices are re-uploaded. + auto verts = CubeVerts(2.0f); + auto idx = CubeIndices(); + VkCommandBuffer cmd = BeginCmd(); + tri.Refit(verts, idx, /*dirtyVertexOffset*/ 4, /*dirtyVertexCount*/ 4, cmd); + SubmitWait(cmd); + } + Check(tri.accelerationStructure == triHandleBefore, + "dirty-range Refit kept the same AS handle (in-place UPDATE, #119)"); + Check(tri.blasAddr == triAddrBefore, + "dirty-range Refit kept the same blasAddr (#119)"); + + // A dirty-range refit on a mesh whose counts changed must still fall back + // to a full rebuild (the dirty window is irrelevant on that path) and + // succeed — same robustness as the full-span Refit fallback. + { + auto verts = CubeVerts(1.0f); + verts.push_back({2.0f, 2.0f, 2.0f}); // count change → topology change + auto idx = CubeIndices(); + idx.insert(idx.end(), {0u, 1u, 8u}); + VkCommandBuffer cmd = BeginCmd(); + tri.Refit(verts, idx, /*dirtyVertexOffset*/ 8, /*dirtyVertexCount*/ 1, cmd); + SubmitWait(cmd); + } + Check(tri.blasAddr != 0 && tri.builtPrimitiveCount == 13, + "dirty-range Refit with a count change rebuilt the BLAS (13 prims, #119)"); + // ── Triangle BLAS: fast-build, no update → flags reflect the choice. ─ Mesh triFast; { @@ -340,6 +376,19 @@ int main() { Check(staged.accelerationStructure == stagedHandle, "staged-upload Refit kept the same AS handle (in-place UPDATE, #73)"); + // Dirty-range refit on the staged path (#119): the ranged upload must + // take the stage-a-sub-slice + vkCmdCopyBuffer-at-offset path (the + // buffer is pure DEVICE_LOCAL), keep the AS handle, and stay + // validation-clean. Only the front half [0,4) is declared dirty. + { + auto verts3 = CubeVerts(2.0f); + VkCommandBuffer cmd = BeginCmd(); + staged.Refit(verts3, idx, /*dirtyVertexOffset*/ 0, /*dirtyVertexCount*/ 4, cmd); + SubmitWait(cmd); + } + Check(staged.accelerationStructure == stagedHandle, + "staged-upload dirty-range Refit kept the same AS handle (#119)"); + Device::directWriteBudget = savedBudget; } From 85ca08047d1564dc3719042aa8cffd260da04f55 Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 19:51:33 +0000 Subject: [PATCH 29/47] perf(buffer): reuse staging via a per-frame-in-flight ring in UploadDeviceLocal (#120) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The staged branch of VulkanBuffer::UploadDeviceLocal did a full Create (create+getreq+alloc+map) + DeferredClear (later free) per call. Mesh::Refit / RecordProceduralBuild hit this every frame for deforming meshes on no-/small-BAR hardware — a device-memory alloc/free cycle per mesh per frame, on exactly the hardware the staged path targets. Replace it with a persistent per-buffer staging ring sized to a high-water mark, reallocated (via Resize, which defers the outgrown allocation) only on growth — mirroring the TLAS instance/metadata reuse. The ring is a per-frame-in-flight ring, not a single shared buffer: the vkCmdCopyBuffer still reads staging after the call returns, and with frames pipelined framesInFlight deep, overwriting one shared buffer next frame would clobber data the previous frame's copy is still reading. Indexing by frameCounter % framesInFlight gives each in-flight frame its own slot, reused only after framesInFlight frames elapse — the same window the #101 deletion queue relies on for GPU completion. The ring is grown lazily, on first entry into the staged branch, so ReBAR/UMA hardware (which always takes the direct-write branch) never constructs a staging allocation it does not use — keeping this a runtime no-op there and a straight upgrade on non-resizable-BAR machines. Co-Authored-By: Claude Opus 4.8 --- interfaces/Crafter.Graphics-VulkanBuffer.cppm | 56 ++++++++++++++++--- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/interfaces/Crafter.Graphics-VulkanBuffer.cppm b/interfaces/Crafter.Graphics-VulkanBuffer.cppm index fe0e60a..a561487 100644 --- a/interfaces/Crafter.Graphics-VulkanBuffer.cppm +++ b/interfaces/Crafter.Graphics-VulkanBuffer.cppm @@ -280,21 +280,47 @@ namespace Crafter { srcStageMask = VK_PIPELINE_STAGE_HOST_BIT; } else { Resize(usageFlags | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, count); - VulkanBuffer staging; + // Persistent staging instead of a Create+DeferredClear per call + // (issue #120): on no-/small-BAR hardware Mesh::Refit / + // RecordProceduralBuild hit this branch every frame for deforming + // meshes, so a fresh alloc+map+free cycle per upload was pure + // churn on exactly the hardware this path targets. Keep a small + // ring of staging buffers, each grown to a high-water mark and + // reused — reallocated (by Resize, which defers the outgrown + // allocation) only when a larger upload arrives. + // + // The ring is grown lazily, on first entry into this staged + // branch, so ReBAR/UMA hardware (which always takes the direct + // `if` branch above) never constructs a staging allocation it + // does not use. + // + // Why a ring and not one shared buffer: the vkCmdCopyBuffer below + // still reads the staging buffer after this call returns — the + // reason the old code used DeferredClear. With frames pipelined + // framesInFlight deep, overwriting one shared staging buffer next + // frame would clobber data the previous frame's copy is still + // reading. Indexing by frameCounter % framesInFlight gives each + // in-flight frame its own slot; a slot is rewritten only after + // framesInFlight frames have elapsed — the exact window after + // which single-queue submission order (the same guarantee the + // #101 deletion queue relies on) ensures that copy has completed. + const std::uint32_t ringSize = + Device::framesInFlight ? Device::framesInFlight : 1; + if (stagingRing.size() < ringSize) { + stagingRing.resize(ringSize); + } + VulkanBuffer& staging = + stagingRing[Device::frameCounter % ringSize]; // SHADER_DEVICE_ADDRESS: Create always queries the buffer device // address (and allocates with the device-address bit); the - // staging buffer's own address is otherwise unused. - staging.Create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, count); + // staging buffer's own address is otherwise unused. Resize reuses + // the existing allocation (and its mapped pointer) when the + // upload still fits the slot's high-water capacity. + staging.Resize(VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, count); std::memcpy(staging.value, src, bytes); staging.FlushDevice(); VkBufferCopy region { .srcOffset = 0, .dstOffset = 0, .size = bytes }; vkCmdCopyBuffer(cmd, staging.buffer, buffer, 1, ®ion); - // The queued copy still reads the staging buffer after this call - // returns, so a plain Clear() would be a use-after-free; hand it - // to the fence-keyed queue (#101/#102), which frees it once the - // copy's frame clears its fence. DeferredClear nulls the handle, - // so `staging`'s destructor here is a no-op. - staging.DeferredClear(); srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; } @@ -387,6 +413,17 @@ namespace Crafter { vkInvalidateMappedMemoryRanges(Device::device, 1, &range); } + // Persistent per-frame-in-flight staging ring for the staged + // UploadDeviceLocal path (issue #120). Empty — and so holding zero + // host-visible allocations — until the first upload that actually + // stages, which only happens on no-/small-BAR hardware (ReBAR/UMA always + // takes the direct-write branch and never touches this, so the lazy ring + // keeps the change a no-op there). Sized to Device::framesInFlight so + // each in-flight frame copies from its own slot; each slot grows to a + // high-water mark via Resize and is reused across frames rather than + // reallocated every call — mirroring the TLAS instance/metadata reuse. + std::vector> stagingRing; + VulkanBuffer(VulkanBuffer&& other) { buffer = other.buffer; memory = other.memory; @@ -397,6 +434,7 @@ namespace Crafter { memoryPropertyFlagsChosen = other.memoryPropertyFlagsChosen; other.buffer = VK_NULL_HANDLE; address = other.address; + stagingRing = std::move(other.stagingRing); if constexpr(Mapped) { VulkanBufferMappedConditional::value = other.VulkanBufferMappedConditional::value; } From b5b8c042379c8df35e12bf8ed8157fad7d597aef Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 19:58:45 +0000 Subject: [PATCH 30/47] perf(tlas): dirty-track the per-frame TLAS instance+metadata upload (#118) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BuildTLAS rebuilt the host instance+metadata buffers with an O(n) copy of every 64 B VkAccelerationStructureInstanceKHR + metadata entry every frame, unconditionally, then flushed the whole high-water capacity (VK_WHOLE_SIZE) on both buffers. At the millions-of-instances target that copy dominates the CPU frame, and the whole-buffer flush costs on non-coherent BAR/VRAM. Add a generation counter so only changed host-authored fields are copied, and feed the same dirty span into the ranged FlushDevice(offset, bytes) overload: - RenderingElement3D::hostDataVersion + MarkHostDataDirty() (bumps a global monotonic counter). TlasWithBuffer::uploadedVersion records, per frame, the version last copied into each slot. A slot is copied only when its element advanced past the recorded version; version 0 ("untracked") reads dirty every frame, so callers that don't opt in keep the prior copy-every-frame behaviour. Globally-unique versions make this correct under relocation (Remove's swap-and-pop, and remove+add that nets the same count on the refit path) without tracking element identity. The reset on every topology change covers buffer reallocation and the reshuffled element->slot mapping. - The dirty [first, last] envelope drives both the copy and the flush: a new VulkanBuffer::FlushDevice(cmd, access, stage, offset, bytes) overload flushes + barriers just that span for instanceBuffer, and the ranged FlushDevice(offset, bytes) for metadataBuffer. When nothing is dirty both are skipped — the skipped HOST->build barrier only ever ordered host writes, never the application's compute-written GPU-owned transform (that compute->build ordering is the caller's, and is unchanged). Constraint honoured: transformOwnedByGpu transforms are still never host-copied. The API field/method are mirrored on the WebGPU class for source portability (the WebGPU build re-uploads its small mirror wholesale and ignores the version). New test TLASInstanceDirtyTracking drives the real RT device and reads back the host-mapped buffers to assert: tracked elements upload once then skip until re-marked, untracked elements always upload, and relocation on the refit path re-uploads exactly the moved slots — with zero validation-layer errors over the ranged flush. Co-Authored-By: Claude Opus 4.8 --- .../Crafter.Graphics-RenderingElement3D.cpp | 74 ++++- .../Crafter.Graphics-RenderingElement3D.cppm | 57 ++++ interfaces/Crafter.Graphics-VulkanBuffer.cppm | 33 +++ project.cpp | 33 +++ tests/TLASInstanceDirtyTracking/main.cpp | 252 ++++++++++++++++++ 5 files changed, 437 insertions(+), 12 deletions(-) create mode 100644 tests/TLASInstanceDirtyTracking/main.cpp diff --git a/implementations/Crafter.Graphics-RenderingElement3D.cpp b/implementations/Crafter.Graphics-RenderingElement3D.cpp index d06630c..6e90621 100644 --- a/implementations/Crafter.Graphics-RenderingElement3D.cpp +++ b/implementations/Crafter.Graphics-RenderingElement3D.cpp @@ -139,9 +139,37 @@ void RenderingElement3D::BuildTLAS(VkCommandBuffer cmd, std::uint32_t index, RTB tlas.instanceBuffer.Resize(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, primitiveCount, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); tlas.metadataBuffer.Resize(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, primitiveCount, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); } + + // A topology change reshuffles which element occupies each slot (a count + // change, and Remove's swap-and-pop within it) and may have reallocated + // the host buffers just above — so every slot's prior contents are + // stale. Clear the per-slot version record (sized to the live count) so + // the copy loop below re-uploads all primitiveCount slots this frame. + tlas.uploadedVersion.assign(primitiveCount, 0); } + // Copy only the slots whose host-authored data changed since this frame's + // buffers last saw them (#118), tracking the first/last dirty index so the + // flush below covers just that [first, last] envelope rather than the whole + // high-water capacity (the envelope may span a few clean slots between + // sparse dirty ones — flushing a couple of extra entries is far cheaper than + // the per-slot vkFlushMappedMemoryRanges calls true sub-ranges would need). + // A slot is dirty when its element advanced past the version + // recorded for it; an element left at version 0 (never MarkHostDataDirty'd) + // reads dirty every frame, preserving the pre-#118 copy-every-frame + // behaviour for callers that don't opt into dirty tracking. Globally-unique + // versions make this correct under relocation: a slot now holding a + // different element never matches the version recorded for its previous + // occupant. The GPU-owned transform is never copied here regardless — the + // application's compute shader writes it in place earlier in this submission. + bool anyDirty = false; + std::uint32_t dirtyFirst = 0; + std::uint32_t dirtyLast = 0; for(std::uint32_t i = 0; i < primitiveCount; i++) { + const std::uint64_t version = elements[i]->hostDataVersion; + if (version != 0 && version == tlas.uploadedVersion[i]) { + continue; // slot already holds this element's current host data + } if (elements[i]->transformOwnedByGpu) { // Skip the transform field — the application's compute shader // writes it earlier in this submission. Copy everything else. @@ -156,21 +184,43 @@ void RenderingElement3D::BuildTLAS(VkCommandBuffer cmd, std::uint32_t index, RTB tlas.instanceBuffer.value[i] = elements[i]->instance; } tlas.metadataBuffer.value[i] = elements[i]->userMetadata; + tlas.uploadedVersion[i] = version; + if (!anyDirty) { dirtyFirst = i; anyDirty = true; } + dirtyLast = i; } - tlas.instanceBuffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); + if (anyDirty) { + // Flush (and, for the instance buffer, barrier) only the [dirtyFirst, + // dirtyLast] span actually rewritten this frame. When nothing is dirty + // there were no host writes at all, so both flushes — and the + // instanceBuffer HOST->build barrier — are skipped: the AS build reads + // memory already made visible by the submit that last wrote it, and any + // GPU-owned transform write is ordered by the application's own + // compute->build barrier, not this host barrier. + const std::uint32_t count = dirtyLast - dirtyFirst + 1; + constexpr VkDeviceSize instStride = sizeof(VkAccelerationStructureInstanceKHR); + constexpr VkDeviceSize metaStride = sizeof(std::uint32_t); - // Make the per-frame metadata host writes available before the consuming - // submit. metadataBuffer is not an AS build input (the build never reads it) - // and has no GPU co-write — only the ray shaders read it, in a later pass — - // so it needs neither the build-stage barrier instanceBuffer takes above nor - // a HOST->shader command barrier: the queue submit already orders host - // writes made available before it against every command in the submission, - // exactly as it did when this buffer was HOST_COHERENT. This plain host - // flush is what now makes those writes available on a non-coherent - // (BAR/VRAM) type; it self-gates to a no-op on a coherent type (#60), - // preserving the previous behaviour where the type ends up coherent. - tlas.metadataBuffer.FlushDevice(); + tlas.instanceBuffer.FlushDevice( + cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, + static_cast(dirtyFirst) * instStride, + static_cast(count) * instStride); + + // Make the per-frame metadata host writes available before the consuming + // submit. metadataBuffer is not an AS build input (the build never reads + // it) and has no GPU co-write — only the ray shaders read it, in a later + // pass — so it needs neither the build-stage barrier instanceBuffer takes + // above nor a HOST->shader command barrier: the queue submit already + // orders host writes made available before it against every command in + // the submission, exactly as it did when this buffer was HOST_COHERENT. + // This ranged host flush is what now makes those writes available on a + // non-coherent (BAR/VRAM) type; it self-gates to a no-op on a coherent + // type (#60), preserving the previous behaviour where the type ends up + // coherent. + tlas.metadataBuffer.FlushDevice( + static_cast(dirtyFirst) * metaStride, + static_cast(count) * metaStride); + } VkAccelerationStructureGeometryInstancesDataKHR instancesData { .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR, diff --git a/interfaces/Crafter.Graphics-RenderingElement3D.cppm b/interfaces/Crafter.Graphics-RenderingElement3D.cppm index cf88d24..fbc0bc9 100644 --- a/interfaces/Crafter.Graphics-RenderingElement3D.cppm +++ b/interfaces/Crafter.Graphics-RenderingElement3D.cppm @@ -58,6 +58,17 @@ export namespace Crafter { // use flags identical to the originating build, so a change in the // requested preference forces a full rebuild rather than a refit. VkBuildAccelerationStructureFlagsKHR builtFlags = 0; + // Per-slot record of the RenderingElement3D::hostDataVersion that + // BuildTLAS last copied into this frame's instanceBuffer/metadataBuffer + // — parallel to them, sized to the live instance count. The per-frame + // copy re-uploads (and flushes) only the slots whose element advanced + // past the recorded version; an element left at version 0 ("untracked") + // reads dirty every frame, so callers that never opt in keep the + // pre-#118 copy-every-frame behaviour. Reset to all-zero on every + // topology change, since a rebuild reshuffles which element occupies + // each slot (and may have reallocated the buffers). See + // RenderingElement3D::hostDataVersion. + std::vector uploadedVersion; }; class RenderingElement3D { @@ -82,6 +93,43 @@ export namespace Crafter { // already live on the GPU). bool transformOwnedByGpu = false; + // Monotonic version of this element's host-authored TLAS data — the + // instance fields the CPU writes (everything except a GPU-owned + // transform; see transformOwnedByGpu) plus userMetadata. BuildTLAS + // records, per frame, the version it last copied into each buffer slot + // (TlasWithBuffer::uploadedVersion) and re-copies a slot only when its + // element has advanced past the recorded version — so a TLAS dominated + // by instances whose host fields are set once and then left alone (the + // millions-of-GPU-driven-bodies target) pays no per-frame host copy or + // flush after the first upload. + // + // 0 is the "untracked" sentinel: an element left at 0 is copied every + // frame exactly as before this optimization, so code that mutates + // instance/userMetadata without opting in stays correct. Opt in by + // calling MarkHostDataDirty after every host-data change — the standard + // dirty-flag contract (mark on change; the upload clears it until the + // next change). The GPU-owned transform is exempt: BuildTLAS never + // host-copies it, so changing it needs no mark. + std::uint64_t hostDataVersion = 0; + + // Stamp this element's host data with a fresh global version so the next + // BuildTLAS of each frame re-uploads (and flushes) its slot. Call after + // changing any host-authored instance field or userMetadata. See + // hostDataVersion. + void MarkHostDataDirty() { hostDataVersion = ++hostDataVersionCounter; } + + // Source of globally-unique, monotonically-increasing host-data + // versions. Global rather than per-element so a version value names a + // unique (element, edit) pair: a per-slot recorded version then equals + // the slot's current occupant only when that exact element's exact edit + // was the last thing written there. That uniqueness is what makes the + // relocation cases — swap-and-pop in Remove, or a remove+add that nets + // the same instance count and so takes the refit path — fall out + // correctly without tracking element identity: a relocated element's + // version never collides with the (different) element's version recorded + // for that slot. 64-bit, so it does not wrap in practice. + inline static std::uint64_t hostDataVersionCounter = 0; + static std::vector elements; inline static TlasWithBuffer tlases[Window::numFrames]; // Build (or in-place refit) the TLAS for frame `index`. `preference` @@ -181,6 +229,15 @@ export namespace Crafter { // element's instanceBuffer slot directly — BuildTLAS preserves it. bool transformOwnedByGpu = false; + // API-symmetric with the Vulkan side so portable code that opts its + // instances into host-data dirty tracking compiles unchanged. The + // WebGPU BuildTLAS re-uploads the whole CPU mirror every build (the + // counts this path targets are small), so the version is not consulted + // here — it exists purely for cross-backend source compatibility. + std::uint64_t hostDataVersion = 0; + void MarkHostDataDirty() { hostDataVersion = ++hostDataVersionCounter; } + inline static std::uint64_t hostDataVersionCounter = 0; + static std::vector elements; inline static TlasWithBuffer tlases[Window::numFrames]; diff --git a/interfaces/Crafter.Graphics-VulkanBuffer.cppm b/interfaces/Crafter.Graphics-VulkanBuffer.cppm index fe0e60a..b69266b 100644 --- a/interfaces/Crafter.Graphics-VulkanBuffer.cppm +++ b/interfaces/Crafter.Graphics-VulkanBuffer.cppm @@ -372,6 +372,39 @@ namespace Crafter { ); } + // Ranged variant of FlushDevice(cmd, ...): flushes only the host writes + // in [offset, offset+bytes) (rounded outward to nonCoherentAtomSize, and + // a no-op on coherent memory — same gate as the other FlushDevice + // overloads) and records the HOST->(dstStageMask, dstAccessMask) barrier. + // Use after writing a sub-range so the cache-flush cost scales with the + // bytes touched rather than the whole high-water capacity. The barrier + // itself still spans the whole buffer (VK_WHOLE_SIZE): the execution/ + // visibility dependency is cheap regardless of range, and only the + // flush's cache maintenance is bandwidth-sensitive. + void FlushDevice(VkCommandBuffer cmd, VkAccessFlags dstAccessMask, VkPipelineStageFlags dstStageMask, VkDeviceSize offset, VkDeviceSize bytes) requires(Mapped) { + FlushDevice(offset, bytes); + VkBufferMemoryBarrier barrier = { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_HOST_WRITE_BIT, + .dstAccessMask = dstAccessMask, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = buffer, + .offset = 0, + .size = VK_WHOLE_SIZE + }; + + vkCmdPipelineBarrier( + cmd, + VK_PIPELINE_STAGE_HOST_BIT, + dstStageMask, + 0, + 0, NULL, + 1, &barrier, + 0, NULL + ); + } + void FlushHost() requires(Mapped) { // Coherent memory needs no explicit invalidate — device writes are // automatically visible to the host. diff --git a/project.cpp b/project.cpp index 97d51b5..d826380 100644 --- a/project.cpp +++ b/project.cpp @@ -359,6 +359,39 @@ extern "C" Configuration CrafterBuildProject(std::span a tlc.GetInterfacesAndImplementations(ifaces, tlasImpls); cfg.tests.push_back(std::move(tlasTest)); + // Issue #118: the per-frame TLAS instance+metadata host rebuild copies + // (and flushes) only the slots whose element's host-authored data + // changed, tracked via RenderingElement3D::hostDataVersion against the + // per-frame uploadedVersion record; untracked elements (version 0) keep + // the always-copy behaviour. Drives the real AS-build path — a cube + // BLAS plus BuildTLAS at a sequence of marks/mutations — and reads back + // the host-mapped buffers to assert that clean slots are skipped, dirty + // slots re-uploaded, and relocation on the refit path re-uploads exactly + // the moved slots, with the validation layer reporting no errors over + // the ranged FlushDevice the dirty span feeds. Needs a Vulkan RT device + // at runtime, so it shares the native build settings. + Test tlasDirtyTest; + Configuration& tld = tlasDirtyTest.config; + tld.path = cfg.path; + tld.name = "TLASInstanceDirtyTracking"; + tld.outputName = "TLASInstanceDirtyTracking"; + tld.type = ConfigurationType::Executable; + tld.target = cfg.target; + tld.march = cfg.march; + tld.mtune = cfg.mtune; + tld.debug = cfg.debug; + tld.sysroot = cfg.sysroot; + tld.dependencies = cfg.dependencies; + tld.externalDependencies = cfg.externalDependencies; + tld.compileFlags = cfg.compileFlags; + tld.linkFlags = cfg.linkFlags; + tld.defines = cfg.defines; + tld.cFiles = cfg.cFiles; + std::vector tlasDirtyImpls(impls.begin(), impls.end()); + tlasDirtyImpls.emplace_back("tests/TLASInstanceDirtyTracking/main"); + tld.GetInterfacesAndImplementations(ifaces, tlasDirtyImpls); + cfg.tests.push_back(std::move(tlasDirtyTest)); + // Issue #51: FontAtlas only re-uploads the dirty sub-rect now, // tracked via FontAtlas::DirtyRect. The accumulation/clamp math is // pure CPU, so this test drives it directly — no GPU device needed diff --git a/tests/TLASInstanceDirtyTracking/main.cpp b/tests/TLASInstanceDirtyTracking/main.cpp new file mode 100644 index 0000000..2a8bcd9 --- /dev/null +++ b/tests/TLASInstanceDirtyTracking/main.cpp @@ -0,0 +1,252 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Issue #118: the per-frame TLAS instance+metadata host rebuild no longer copies +// every entry unconditionally — it copies (and flushes) only the slots whose +// element's host-authored data changed since this frame's buffers last saw it, +// tracked by RenderingElement3D::hostDataVersion against the per-frame +// TlasWithBuffer::uploadedVersion record. Elements left at version 0 stay +// "untracked" and are copied every frame (the pre-#118 behaviour), so callers +// that don't opt in keep working; MarkHostDataDirty() opts an element in. +// +// This drives the real hardware path (a headless Vulkan RT device, a real cube +// BLAS, RenderingElement3D::BuildTLAS into one-time command buffers) exactly as +// TLASHighWaterMark does, and asserts behaviour by reading back the host-mapped +// instanceBuffer/metadataBuffer (host-visible, and we only inspect host-written +// fields, so the mapping reflects what the copy loop wrote — a skipped slot +// retains its previous bytes, which is the whole point). +// +// What is asserted: +// - A tracked (MarkHostDataDirty'd) element is uploaded on first build. +// - Mutating a tracked element's host data WITHOUT marking it dirty and +// rebuilding leaves the buffer holding the OLD value — the slot was skipped. +// - Marking it dirty and rebuilding uploads the NEW value. +// - An untracked element (version 0) is uploaded on every build even without +// a mark — the backward-compatible always-copy path. +// - Relocation on the refit path: a remove+add that nets the same instance +// count (so BuildTLAS refits rather than rebuilding) re-uploads exactly the +// slots whose occupying element changed, and leaves the unchanged slots +// intact — proving globally-unique versions handle swap-and-pop without +// tracking element identity. +// - The Vulkan validation layer reports ZERO errors across all of the above — +// covering the ranged FlushDevice(offset, bytes) the dirty span feeds. + +#include "vulkan/vulkan.h" +#include + +import Crafter.Graphics; +import Crafter.Math; +import std; + +using namespace Crafter; + +namespace { + +int failures = 0; + +void Check(bool ok, std::string_view what) { + std::println("{} {}", ok ? "PASS" : "FAIL", what); + if (!ok) ++failures; +} + +VkCommandBuffer BeginCmd() { + VkCommandBufferAllocateInfo allocInfo { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = Device::commandPool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }; + VkCommandBuffer cmd = VK_NULL_HANDLE; + Device::CheckVkResult(vkAllocateCommandBuffers(Device::device, &allocInfo, &cmd)); + VkCommandBufferBeginInfo beginInfo { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + Device::CheckVkResult(vkBeginCommandBuffer(cmd, &beginInfo)); + return cmd; +} + +void SubmitWait(VkCommandBuffer cmd) { + Device::CheckVkResult(vkEndCommandBuffer(cmd)); + VkSubmitInfo submitInfo { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &cmd, + }; + Device::CheckVkResult(vkQueueSubmit(Device::queue, 1, &submitInfo, VK_NULL_HANDLE)); + Device::CheckVkResult(vkQueueWaitIdle(Device::queue)); + vkFreeCommandBuffers(Device::device, Device::commandPool, 1, &cmd); +} + +std::vector> CubeVerts(float s) { + return { + {-s,-s,-s}, { s,-s,-s}, { s, s,-s}, {-s, s,-s}, + {-s,-s, s}, { s,-s, s}, { s, s, s}, {-s, s, s}, + }; +} +std::vector CubeIndices() { + return { + 0,1,2, 0,2,3, 4,6,5, 4,7,6, + 0,4,5, 0,5,1, 3,2,6, 3,6,7, + 1,5,6, 1,6,2, 0,3,7, 0,7,4, + }; +} + +VkAccelerationStructureInstanceKHR MakeInstance(VkDeviceAddress blasAddr) { + VkAccelerationStructureInstanceKHR inst{}; + inst.transform = VkTransformMatrixKHR{{ + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + }}; + inst.mask = 0xFF; + inst.accelerationStructureReference = blasAddr; + return inst; +} + +void BuildOnce() { + VkCommandBuffer cmd = BeginCmd(); + RenderingElement3D::BuildTLAS(cmd, 0); + SubmitWait(cmd); +} + +// Read back what the host copy loop last wrote into frame-0's metadata buffer +// slot. Host-visible and host-written, so the mapping reflects the last copy +// (or the slot's prior contents if it was skipped) without an invalidate. +std::uint32_t MetaSlot(std::uint32_t i) { + return RenderingElement3D::tlases[0].metadataBuffer.value[i]; +} +std::uint32_t CustomIndexSlot(std::uint32_t i) { + return RenderingElement3D::tlases[0].instanceBuffer.value[i].instanceCustomIndex; +} + +} // namespace + +int main() { + Device::Initialize(); + Device::validationErrorCount = 0; + + Mesh cube; + { + auto verts = CubeVerts(1.0f); + auto idx = CubeIndices(); + VkCommandBuffer cmd = BeginCmd(); + cube.Build(verts, idx, cmd, RTBuildOptions{ .allowUpdate = true }); + SubmitWait(cmd); + } + Check(cube.blasAddr != 0, "cube BLAS produced a non-zero blasAddr"); + + constexpr std::uint32_t kMaxElems = 8; + std::vector pool(kMaxElems); + for (auto& e : pool) e.instance = MakeInstance(cube.blasAddr); + + // ── 1. A tracked element is uploaded on first build. ──────────────────── + pool[0].userMetadata = 100; + pool[0].instance.instanceCustomIndex = 100; + pool[0].MarkHostDataDirty(); + RenderingElement3D::Add(&pool[0]); + BuildOnce(); + Check(MetaSlot(0) == 100, "tracked element uploaded on first build (metadata)"); + Check(CustomIndexSlot(0) == 100, "tracked element uploaded on first build (customIndex)"); + + // ── 2. Mutating a tracked element WITHOUT marking it dirty and rebuilding + // leaves the old value — the slot is skipped. The instance count is + // unchanged, so this is the refit path (no topology reset). ───────── + pool[0].userMetadata = 200; + pool[0].instance.instanceCustomIndex = 200; + BuildOnce(); + Check(MetaSlot(0) == 100, + "unmarked mutation is NOT uploaded — clean slot skipped (metadata stays 100)"); + Check(CustomIndexSlot(0) == 100, + "unmarked mutation is NOT uploaded — clean slot skipped (customIndex stays 100)"); + + // ── 3. Marking it dirty and rebuilding uploads the new value. ─────────── + pool[0].MarkHostDataDirty(); + BuildOnce(); + Check(MetaSlot(0) == 200, "marking dirty re-uploads the new metadata (200)"); + Check(CustomIndexSlot(0) == 200, "marking dirty re-uploads the new customIndex (200)"); + + // ── 4. An untracked element (version 0) is uploaded every build even + // without a mark — the backward-compatible always-copy path. Add a + // second element to grow the count (topology change), then mutate it + // in place across same-count rebuilds. ───────────────────────────── + pool[1].userMetadata = 300; // left untracked: hostDataVersion == 0 + RenderingElement3D::Add(&pool[1]); + BuildOnce(); + Check(MetaSlot(1) == 300, "untracked element uploaded on first build (300)"); + pool[1].userMetadata = 400; // mutate in place, no MarkHostDataDirty + BuildOnce(); + Check(MetaSlot(1) == 400, + "untracked element re-uploaded every frame without a mark (400) — legacy path"); + // The tracked element [0] must NOT have been disturbed by [1]'s churn. + Check(MetaSlot(0) == 200, "tracked element [0] untouched while [1] churns (still 200)"); + + // ── 5. Relocation on the refit path. Both live elements are tracked; a + // remove+add that nets the same instance count takes the refit path + // (no topology reset), so only the slots whose occupying element + // changed must be re-uploaded — proving globally-unique versions + // handle swap-and-pop without tracking element identity. ─────────── + // Mark both current elements so neither is the always-copy legacy path. + pool[0].userMetadata = 10; pool[0].MarkHostDataDirty(); + pool[1].userMetadata = 11; pool[1].MarkHostDataDirty(); + BuildOnce(); // slots: [0]=10, [1]=11 + Check(MetaSlot(0) == 10 && MetaSlot(1) == 11, "both tracked elements uploaded (10, 11)"); + + // Remove pool[0] (swap-and-pop moves the last element, pool[1], into slot 0), + // then add pool[2] (appended at slot 1). Net count is unchanged at 2, so + // BuildTLAS refits. New mapping: slot 0 -> pool[1] (was pool[0]), + // slot 1 -> pool[2] (was pool[1]). + pool[2].userMetadata = 22; pool[2].MarkHostDataDirty(); + RenderingElement3D::Remove(&pool[0]); + RenderingElement3D::Add(&pool[2]); + BuildOnce(); + Check(MetaSlot(0) == 11, + "relocation: slot 0 re-uploaded to the swapped-in element (pool[1] -> 11)"); + Check(MetaSlot(1) == 22, + "relocation: slot 1 re-uploaded to the appended element (pool[2] -> 22)"); + + // Unregister everything. + while (!RenderingElement3D::elements.empty()) { + RenderingElement3D::Remove(RenderingElement3D::elements.back()); + } + + Check(Device::validationErrorCount == 0, + std::format("no Vulkan validation errors ({} seen)", Device::validationErrorCount)); + + // Tear down the frame-0 TLAS while the device is still alive (same rationale + // as TLASHighWaterMark — the static tlases[] outlive the static device). + { + auto& t0 = RenderingElement3D::tlases[0]; + if (t0.accelerationStructure != VK_NULL_HANDLE) { + Device::vkDestroyAccelerationStructureKHR(Device::device, t0.accelerationStructure, nullptr); + t0.accelerationStructure = VK_NULL_HANDLE; + } + if (t0.instanceBuffer.buffer != VK_NULL_HANDLE) t0.instanceBuffer.Clear(); + if (t0.metadataBuffer.buffer != VK_NULL_HANDLE) t0.metadataBuffer.Clear(); + if (t0.scratchBuffer.buffer != VK_NULL_HANDLE) t0.scratchBuffer.Clear(); + if (t0.buffer.buffer != VK_NULL_HANDLE) t0.buffer.Clear(); + } + + if (failures != 0) { + std::println("{} check(s) failed", failures); + return EXIT_FAILURE; + } + std::println("all checks passed"); + return EXIT_SUCCESS; +} From c8495f548b330ff91657c7bc9e2113078011a161 Mon Sep 17 00:00:00 2001 From: catbot Date: Thu, 18 Jun 2026 13:19:11 +0000 Subject: [PATCH 31/47] perf(ui): transparent shaped-run cache lookup, no string copy on hit (#122) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ShapeText built ShapedRunKey{..., std::string(utf8)} before find() unconditionally — even on cache hits, on the per-frame onBuild path. N labels meant N string copies + hashes (+ frees for non-SSO strings) every frame, partially defeating the run cache. Add is_transparent hash + equality functors on shapedRuns_ and probe with a borrowing ShapedRunViewKey {const Font*, float, array, string_view}. The owning std::string is now materialised only on a miss, for emplace. Cache hits copy and hash no string. Co-Authored-By: Claude Opus 4.8 --- .../Crafter.Graphics-UI-Shared.cpp | 8 ++- interfaces/Crafter.Graphics-UI.cppm | 52 ++++++++++++++++--- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/implementations/Crafter.Graphics-UI-Shared.cpp b/implementations/Crafter.Graphics-UI-Shared.cpp index c3dee39..5c7fcf7 100644 --- a/implementations/Crafter.Graphics-UI-Shared.cpp +++ b/implementations/Crafter.Graphics-UI-Shared.cpp @@ -75,8 +75,11 @@ std::uint32_t UIRenderer::ShapeText(Font& font, float pxSize, // the pen starting at x=0, baseline=0 so a cache hit only needs the // translate below. The whole string is shaped (no outCapacity limit) so // a later call with a larger buffer still gets the full run. - ShapedRunKey key{&font, pxSize, color, std::string(utf8)}; - auto it = shapedRuns_.find(key); + // Probe with a borrowing view key (text is a string_view onto utf8) so a + // cache hit copies and hashes no string. The owning std::string is only + // built on a miss, below, for the emplace. + ShapedRunViewKey viewKey{&font, pxSize, color, utf8}; + auto it = shapedRuns_.find(viewKey); if (it == shapedRuns_.end()) { // Miss: shape from scratch. Ensure() each glyph so brand-new ones // rasterise into the atlas and mark it dirty for the next upload — @@ -110,6 +113,7 @@ std::uint32_t UIRenderer::ShapeText(Font& font, float pxSize, run.advance = cursor; if (shapedRuns_.size() >= kMaxShapedRuns) shapedRuns_.clear(); + ShapedRunKey key{&font, pxSize, color, std::string(utf8)}; it = shapedRuns_.emplace(std::move(key), std::move(run)).first; } diff --git a/interfaces/Crafter.Graphics-UI.cppm b/interfaces/Crafter.Graphics-UI.cppm index 0eec8f2..a47d71f 100644 --- a/interfaces/Crafter.Graphics-UI.cppm +++ b/interfaces/Crafter.Graphics-UI.cppm @@ -339,25 +339,63 @@ export namespace Crafter { // only needs a translate by (x + alignShift, baselineY). The full // string is part of the key (compared on lookup) so hash collisions // can't return the wrong run. + // + // The map owns its key string, but lookups happen every frame on the + // onBuild path. To avoid copying the string into a key on each cache + // hit, the hash and equality functors are transparent (is_transparent) + // and accept a borrowing view key whose text is a std::string_view; + // the owning std::string is only materialised on a miss, for emplace. struct ShapedRunKey { const Font* font; float pxSize; std::array color; std::string text; - bool operator==(const ShapedRunKey&) const = default; + }; + struct ShapedRunViewKey { + const Font* font; + float pxSize; + std::array color; + std::string_view text; }; struct ShapedRunKeyHash { - std::size_t operator()(const ShapedRunKey& k) const noexcept { - std::size_t h = std::hash{}(k.font); + using is_transparent = void; + static std::size_t Hash(const Font* font, float pxSize, + const std::array& color, + std::string_view text) noexcept { + std::size_t h = std::hash{}(font); auto mix = [&h](std::size_t v) noexcept { h ^= v + 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2); }; - mix(std::hash{}(std::bit_cast(k.pxSize))); - for (float c : k.color) + mix(std::hash{}(std::bit_cast(pxSize))); + for (float c : color) mix(std::hash{}(std::bit_cast(c))); - mix(std::hash{}(k.text)); + mix(std::hash{}(text)); return h; } + std::size_t operator()(const ShapedRunKey& k) const noexcept { + return Hash(k.font, k.pxSize, k.color, k.text); + } + std::size_t operator()(const ShapedRunViewKey& k) const noexcept { + return Hash(k.font, k.pxSize, k.color, k.text); + } + }; + struct ShapedRunKeyEqual { + using is_transparent = void; + static bool Eq(const Font* fa, float pa, const std::array& ca, + std::string_view ta, + const Font* fb, float pb, const std::array& cb, + std::string_view tb) noexcept { + return fa == fb && pa == pb && ca == cb && ta == tb; + } + bool operator()(const ShapedRunKey& a, const ShapedRunKey& b) const noexcept { + return Eq(a.font, a.pxSize, a.color, a.text, b.font, b.pxSize, b.color, b.text); + } + bool operator()(const ShapedRunViewKey& a, const ShapedRunKey& b) const noexcept { + return Eq(a.font, a.pxSize, a.color, a.text, b.font, b.pxSize, b.color, b.text); + } + bool operator()(const ShapedRunKey& a, const ShapedRunViewKey& b) const noexcept { + return Eq(a.font, a.pxSize, a.color, a.text, b.font, b.pxSize, b.color, b.text); + } }; struct ShapedRun { std::vector glyphs; // origin-relative @@ -367,7 +405,7 @@ export namespace Crafter { // would grow this without bound; on overflow we drop everything and // rebuild (correctness is unaffected, only the hit rate after a flush). static constexpr std::size_t kMaxShapedRuns = 8192; - std::unordered_map shapedRuns_; + std::unordered_map shapedRuns_; ImageSlot outImageSlot_; ImageSlot fontAtlasImageSlot_; From ac9180076ccf766ffa4e8ca33a1bad08ff84d385 Mon Sep 17 00:00:00 2001 From: catbot Date: Thu, 18 Jun 2026 13:22:07 +0000 Subject: [PATCH 32/47] perf(ui): hoist loop-invariant per-item math out of the per-pixel loop (#125) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cooperative-load section already streams each item into shared memory once per workgroup, but the per-pixel inner loop then recomputed values that are constant for the whole item/glyph. The compiler can't hoist them itself — they read shared memory at a varying index. Precompute them once per item at load time into new shared slots: - ui-images / ui-text / ui-fused (images+text phases): invRectSize = 1.0/rect.zw, turning the per-pixel `(sp-rect.xy)/rect.zw` vec2 divide into a multiply. - ui-text / ui-fused (text phase): the SDF AA `band` (a vec2 divide + two maxes depending only on the glyph's uv span and rect size). In ui-fused the new slots (s_inv, s_band) are reused across phases like the existing s_v* scratch, so the LDS bump is per-category, not summed. Co-Authored-By: Claude Opus 4.8 --- shaders/ui-fused.comp.glsl | 27 +++++++++++++++++++-------- shaders/ui-images.comp.glsl | 8 +++++++- shaders/ui-text.comp.glsl | 30 +++++++++++++++++++----------- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/shaders/ui-fused.comp.glsl b/shaders/ui-fused.comp.glsl index 998061b..28eb5ea 100644 --- a/shaders/ui-fused.comp.glsl +++ b/shaders/ui-fused.comp.glsl @@ -70,6 +70,13 @@ shared vec4 s_v1[UI_CHUNK]; shared vec4 s_v2[UI_CHUNK]; shared vec4 s_v3[UI_CHUNK]; shared uvec4 s_v4[UI_CHUNK]; +// Per-item constants precomputed once at cooperative-load time, REUSED across +// phases like the s_v* scratch (the per-pixel inner loops read them instead of +// recomputing per pixel × item): +// images: s_inv = 1.0/rect.zw +// text: s_inv = 1.0/rect.zw, s_band = SDF AA scale (a divide + two maxes) +shared vec2 s_inv[UI_CHUNK]; +shared float s_band[UI_CHUNK]; shared uint s_keep[UI_CHUNK]; shared uint s_order[UI_CHUNK]; shared uint s_count; @@ -232,6 +239,7 @@ void main() { s_v1[lid] = uiImageHeap[heap].items[idx].uv; s_v2[lid] = uiImageHeap[heap].items[idx].tint; s_v4[lid] = uiImageHeap[heap].items[idx].slots; + s_inv[lid] = 1.0 / s_v0[lid].zw; keep = uiAabbOverlapsTile(s_v0[lid].xy, s_v0[lid].xy + s_v0[lid].zw, tileMin, tileMax); } @@ -249,7 +257,7 @@ void main() { if (sp.x < lo.x || sp.y < lo.y) continue; if (sp.x >= hi.x || sp.y >= hi.y) continue; - vec2 t = (sp - s_v0[c].xy) / s_v0[c].zw; + vec2 t = (sp - s_v0[c].xy) * s_inv[c]; vec2 uv = mix(s_v1[c].xy, s_v1[c].zw, t); uint texSlot = s_v4[c].x; @@ -282,6 +290,13 @@ void main() { s_v0[lid] = uiGlyphHeap[heap].items[idx].rect; s_v1[lid] = uiGlyphHeap[heap].items[idx].uv; s_v2[lid] = uiGlyphHeap[heap].items[idx].color; + s_inv[lid] = 1.0 / s_v0[lid].zw; + // SDF AA band — atlas-px per screen-px, constant per glyph + // (uvSpan * kAtlasSize(1024) / screenSpan), max'd to 1px floor. + vec2 uvSpan = s_v1[lid].zw - s_v1[lid].xy; + vec2 atlasPerScreen = (uvSpan * 1024.0) * s_inv[lid]; + float scalePx = max(atlasPerScreen.x, atlasPerScreen.y); + s_band[lid] = max(scalePx, 0.0001); keep = uiAabbOverlapsTile(s_v0[lid].xy, s_v0[lid].xy + s_v0[lid].zw, tileMin, tileMax); } @@ -299,7 +314,7 @@ void main() { if (sp.x < lo.x || sp.y < lo.y) continue; if (sp.x >= hi.x || sp.y >= hi.y) continue; - vec2 t = (sp - s_v0[c].xy) / s_v0[c].zw; + vec2 t = (sp - s_v0[c].xy) * s_inv[c]; vec2 uv = mix(s_v1[c].xy, s_v1[c].zw, t); // Font slots are push constants — provably dynamically uniform, @@ -312,12 +327,8 @@ void main() { float dAtlas = (ON_EDGE - sdf) * DIST_SCALE; - vec2 uvSpan = s_v1[c].zw - s_v1[c].xy; - vec2 atlasPerScreen = (uvSpan * 1024.0) / s_v0[c].zw; - float scalePx = max(atlasPerScreen.x, atlasPerScreen.y); - float band = max(scalePx, 0.0001); - - float a = clamp(0.5 - dAtlas / band, 0.0, 1.0); + // band (the AA scale) was precomputed once per glyph at load. + float a = clamp(0.5 - dAtlas / s_band[c], 0.0, 1.0); if (a <= 0.0) continue; vec4 col = s_v2[c]; diff --git a/shaders/ui-images.comp.glsl b/shaders/ui-images.comp.glsl index a4424a6..061ee89 100644 --- a/shaders/ui-images.comp.glsl +++ b/shaders/ui-images.comp.glsl @@ -16,6 +16,11 @@ shared vec4 s_rect[UI_CHUNK]; shared vec4 s_uv[UI_CHUNK]; shared vec4 s_tint[UI_CHUNK]; shared uvec4 s_slots[UI_CHUNK]; +// 1.0/rect.zw, precomputed once per item at cooperative-load time so the +// per-pixel inner loop multiplies instead of recomputing a vec2 reciprocal of +// the (loop-invariant, but shared-mem + varying-index, so non-hoistable by the +// compiler) rect size for every pixel × item. +shared vec2 s_invRectSize[UI_CHUNK]; shared uint s_keep[UI_CHUNK]; shared uint s_order[UI_CHUNK]; shared uint s_count; @@ -43,6 +48,7 @@ void main() { s_uv[lid] = uiImageHeap[pc.hdr.itemBuffer].items[idx].uv; s_tint[lid] = uiImageHeap[pc.hdr.itemBuffer].items[idx].tint; s_slots[lid] = uiImageHeap[pc.hdr.itemBuffer].items[idx].slots; + s_invRectSize[lid] = 1.0 / s_rect[lid].zw; keep = uiAabbOverlapsTile(s_rect[lid].xy, s_rect[lid].xy + s_rect[lid].zw, tileMin, tileMax); } @@ -67,7 +73,7 @@ void main() { if (sp.x < lo.x || sp.y < lo.y) continue; if (sp.x >= hi.x || sp.y >= hi.y) continue; - vec2 t = (sp - s_rect[c].xy) / s_rect[c].zw; + vec2 t = (sp - s_rect[c].xy) * s_invRectSize[c]; vec2 uv = mix(s_uv[c].xy, s_uv[c].zw, t); uint texSlot = s_slots[c].x; diff --git a/shaders/ui-text.comp.glsl b/shaders/ui-text.comp.glsl index a64abbf..9c716c2 100644 --- a/shaders/ui-text.comp.glsl +++ b/shaders/ui-text.comp.glsl @@ -23,6 +23,13 @@ const float DIST_SCALE = 32.0; shared vec4 s_rect[UI_CHUNK]; shared vec4 s_uv[UI_CHUNK]; shared vec4 s_color[UI_CHUNK]; +// Per-glyph constants precomputed once at cooperative-load time and read by the +// per-pixel inner loop, instead of being recomputed for every pixel × glyph: +// s_invRectSize = 1.0/rect.zw (turns the per-pixel divide into a multiply) +// s_band = the SDF AA scale (a divide + two maxes that depend only on +// the glyph's uv span and rect size — fully loop-invariant) +shared vec2 s_invRectSize[UI_CHUNK]; +shared float s_band[UI_CHUNK]; shared uint s_keep[UI_CHUNK]; shared uint s_order[UI_CHUNK]; shared uint s_count; @@ -49,6 +56,15 @@ void main() { s_rect[lid] = uiGlyphHeap[pc.hdr.itemBuffer].items[idx].rect; s_uv[lid] = uiGlyphHeap[pc.hdr.itemBuffer].items[idx].uv; s_color[lid] = uiGlyphHeap[pc.hdr.itemBuffer].items[idx].color; + s_invRectSize[lid] = 1.0 / s_rect[lid].zw; + // Atlas-px per screen-px along this glyph's transform — keeps AA + // crisp at any rendering size. uvSpan * atlasSize / screenSpan, + // with FontAtlas::kAtlasSize = 1024. Constant for the whole glyph. + vec2 uvSpan = s_uv[lid].zw - s_uv[lid].xy; + vec2 atlasPerScreen = (uvSpan * 1024.0) * s_invRectSize[lid]; + float scalePx = max(atlasPerScreen.x, atlasPerScreen.y); + // 1-screen-px AA band, expressed in atlas-pixel units of dAtlas. + s_band[lid] = max(scalePx, 0.0001); keep = uiAabbOverlapsTile(s_rect[lid].xy, s_rect[lid].xy + s_rect[lid].zw, tileMin, tileMax); } @@ -73,7 +89,7 @@ void main() { if (sp.x < lo.x || sp.y < lo.y) continue; if (sp.x >= hi.x || sp.y >= hi.y) continue; - vec2 t = (sp - s_rect[c].xy) / s_rect[c].zw; + vec2 t = (sp - s_rect[c].xy) * s_invRectSize[c]; vec2 uv = mix(s_uv[c].xy, s_uv[c].zw, t); // Font slots are push constants — provably dynamically uniform, so no @@ -88,16 +104,8 @@ void main() { // Distance in atlas-pixels (negative inside the glyph). float dAtlas = (ON_EDGE - sdf) * DIST_SCALE; - // Atlas-px per screen-px along this glyph's transform — keeps AA crisp - // at any rendering size. uvSpan * atlasSize / screenSpan. - vec2 uvSpan = s_uv[c].zw - s_uv[c].xy; - // FontAtlas::kAtlasSize = 1024. - vec2 atlasPerScreen = (uvSpan * 1024.0) / s_rect[c].zw; - float scalePx = max(atlasPerScreen.x, atlasPerScreen.y); - // 1-screen-px AA band, expressed in atlas-pixel units of dAtlas. - float band = max(scalePx, 0.0001); - - float a = clamp(0.5 - dAtlas / band, 0.0, 1.0); + // band (the AA scale) was precomputed once per glyph at load. + float a = clamp(0.5 - dAtlas / s_band[c], 0.0, 1.0); if (a <= 0.0) continue; vec4 col = s_color[c]; From 1d7d9f7b8ee92b47a9d1fb2690347279b25a175b Mon Sep 17 00:00:00 2001 From: catbot Date: Thu, 18 Jun 2026 13:23:34 +0000 Subject: [PATCH 33/47] perf(ui): LRU-evict the shaped-run cache instead of clearing it (#123) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At kMaxShapedRuns (8192) UIRenderer::ShapeText did a full shapedRuns_.clear(). A stream of unique strings (FPS counters, timers) alongside many stable labels periodically nuked every stable entry, forcing a full-UI reshape the next frame. Track recency with an auxiliary std::list (front = most recently used) holding pointers to the keys owned by the node-based map. A hit splices its entry to the front; an overflow pops the least-recently-used entry from the back and erases just that one — both O(1). The hot set of labels, reshaped every frame, stays at the front and survives, so the churn of unique strings only recycles the cold tail. Output is byte-identical either way. InvalidateFont now drops matching entries from both the map and the LRU list. Adds ShapedRunCacheSize()/IsShapedRunCached() introspection hooks (the policy isn't observable through output or atlas.dirty) and a ShapeTextCache test that asserts the cache plateaus at a fixed cap (evict-one, not clear-all), the hot label survives a churn past the cap, and an untouched cold string is evicted. Co-Authored-By: Claude Opus 4.8 --- .../Crafter.Graphics-UI-Shared.cpp | 36 ++++++++++-- interfaces/Crafter.Graphics-UI.cppm | 28 ++++++++- tests/ShapeTextCache/main.cpp | 57 +++++++++++++++++++ 3 files changed, 115 insertions(+), 6 deletions(-) diff --git a/implementations/Crafter.Graphics-UI-Shared.cpp b/implementations/Crafter.Graphics-UI-Shared.cpp index c3dee39..3a7be00 100644 --- a/implementations/Crafter.Graphics-UI-Shared.cpp +++ b/implementations/Crafter.Graphics-UI-Shared.cpp @@ -109,8 +109,22 @@ std::uint32_t UIRenderer::ShapeText(Font& font, float pxSize, } run.advance = cursor; - if (shapedRuns_.size() >= kMaxShapedRuns) shapedRuns_.clear(); + // Evict the least-recently-used run (LRU list back) on overflow rather + // than clearing the whole cache. The back is whatever has gone longest + // without a reshape — a stale unique string, never a stable label + // (those are reshaped every frame and sit at the front), so the hot set + // survives and the next frame doesn't trigger a full-UI reshape. + if (shapedRuns_.size() >= kMaxShapedRuns) { + const ShapedRunKey* victim = shapedRunsLru_.back(); + shapedRunsLru_.pop_back(); + shapedRuns_.erase(*victim); + } it = shapedRuns_.emplace(std::move(key), std::move(run)).first; + it->second.lruIt = shapedRunsLru_.insert(shapedRunsLru_.begin(), &it->first); + } else { + // Hit: promote to most-recently-used. splice is O(1) and keeps lruIt + // (and every other iterator) valid. + shapedRunsLru_.splice(shapedRunsLru_.begin(), shapedRunsLru_, it->second.lruIt); } const ShapedRun& run = it->second; @@ -135,7 +149,21 @@ std::uint32_t UIRenderer::ShapeText(Font& font, float pxSize, } void UIRenderer::InvalidateFont(const Font& font) noexcept { - std::erase_if(shapedRuns_, [&font](const auto& kv) { - return kv.first.font == &font; - }); + // Drop matching runs from both the map and the LRU order list, keeping the + // two in sync (the list points at keys owned by the map). + for (auto it = shapedRuns_.begin(); it != shapedRuns_.end(); ) { + if (it->first.font == &font) { + shapedRunsLru_.erase(it->second.lruIt); + it = shapedRuns_.erase(it); + } else { + ++it; + } + } +} + +bool UIRenderer::IsShapedRunCached(const Font& font, float pxSize, + std::array color, + std::string_view utf8) const { + return shapedRuns_.find(ShapedRunKey{&font, pxSize, color, std::string(utf8)}) + != shapedRuns_.end(); } diff --git a/interfaces/Crafter.Graphics-UI.cppm b/interfaces/Crafter.Graphics-UI.cppm index 0eec8f2..d099240 100644 --- a/interfaces/Crafter.Graphics-UI.cppm +++ b/interfaces/Crafter.Graphics-UI.cppm @@ -325,6 +325,17 @@ export namespace Crafter { // reallocated at the same address would otherwise alias stale runs. void InvalidateFont(const Font& font) noexcept; + // Shaped-run cache introspection (issue #123). Hits/misses are + // byte-identical and a re-shape of an already-rasterised string leaves + // the atlas clean, so the LRU eviction policy isn't observable through + // output alone — these let tests assert that the cache stays bounded + // and that the hot set survives an overflow. IsShapedRunCached is a + // pure query: it does not bump recency. + std::size_t ShapedRunCacheSize() const noexcept { return shapedRuns_.size(); } + bool IsShapedRunCached(const Font& font, float pxSize, + std::array color, + std::string_view utf8) const; + std::uint16_t FontAtlasImageSlot() const noexcept { return fontAtlasImageSlot_; } std::uint16_t FontAtlasSamplerSlot() const noexcept { return fontAtlasSamplerSlot_; } @@ -362,12 +373,25 @@ export namespace Crafter { struct ShapedRun { std::vector glyphs; // origin-relative float advance = 0; + // Position of this run's key in shapedRunsLru_ (front = most + // recently used). Lets a hit splice the entry to the front and an + // overflow pop the least-recently-used entry, both O(1). + std::list::iterator lruIt{}; }; // Soft cap. A pathological caller drawing a fresh string every frame - // would grow this without bound; on overflow we drop everything and - // rebuild (correctness is unaffected, only the hit rate after a flush). + // would grow this without bound; on overflow we evict the single + // least-recently-used run (issue #123) rather than clearing the whole + // cache. The hot set of stable labels — reshaped every frame, so always + // near the front — stays resident, while a churn of unique strings + // (FPS counters, timers) only recycles the cold tail. Correctness is + // unaffected either way; this just avoids the periodic full-UI reshape + // spike the old clear() caused. static constexpr std::size_t kMaxShapedRuns = 8192; std::unordered_map shapedRuns_; + // LRU recency order for shapedRuns_; front = most recently used. Holds + // pointers to the keys owned by the map — stable across rehash because + // unordered_map is node-based — so eviction never re-hashes the world. + std::list shapedRunsLru_; ImageSlot outImageSlot_; ImageSlot fontAtlasImageSlot_; diff --git a/tests/ShapeTextCache/main.cpp b/tests/ShapeTextCache/main.cpp index 3ef990b..a877a19 100644 --- a/tests/ShapeTextCache/main.cpp +++ b/tests/ShapeTextCache/main.cpp @@ -225,6 +225,63 @@ int main() { bufBig.data(), bufBig.size(), &advBig); Check(advBig > advHit * 1.5f, "a larger pxSize produces a wider advance"); + // ── 8. Overflow evicts the least-recently-used run, not the whole cache. ─ + // (Issue #123) The old policy did a full clear() at the cap, periodically + // nuking the stable labels and forcing a full-UI reshape the next frame. + // The LRU policy instead pins the cache at its cap (evict one, add one) and + // keeps the hot, reshaped-every-frame set resident. A hit/miss and a + // re-shape of already-rasterised glyphs are not observable through the + // output or atlas.dirty, so this is asserted via the cache introspection + // hooks added for the issue. + ui.InvalidateFont(font); + Check(ui.ShapedRunCacheSize() == 0, "cache empty after InvalidateFont"); + + std::array sink{}; + auto shapeUnique = [&](std::size_t n) { + // Small fixed glyph set (digits + "run#"), so the atlas rasterises once + // and every call is still a distinct cache key. + std::string s = std::format("run#{}", n); + ui.ShapeText(font, kSize, 0, 0, s, white, sink.data(), sink.size(), nullptr); + }; + + // Insert distinct keys until the size stops growing on a brand-new insert. + // Under evict-one LRU that plateau IS the cap (evict one, add one → size + // unchanged). Under the old clear()-all policy a new insert at the cap + // drops the size to 1, so it would never plateau and `cap` would stay 0. + std::size_t cap = 0, prev = 0; + for (std::size_t i = 0; i < 100000 && cap == 0; ++i) { + shapeUnique(i); + std::size_t now = ui.ShapedRunCacheSize(); + if (now == prev) cap = now; + prev = now; + } + Check(cap > 0, "cache size plateaus at a fixed cap (evict-one LRU, not clear-all)"); + + // Hot, every-frame label stays resident across a churn far exceeding the + // cap, because reshaping it each iteration keeps it most-recently-used. + const std::string_view kHot = "Hot Label"; + ui.ShapeText(font, kSize, 0, 0, kHot, white, sink.data(), sink.size(), nullptr); + bool hotStays = ui.IsShapedRunCached(font, kSize, white, kHot); + bool stayedAtCap = true; + for (std::size_t i = 0; i < cap * 2 + 1000 && hotStays; ++i) { + shapeUnique(1'000'000 + i); // fresh keys + ui.ShapeText(font, kSize, 0, 0, kHot, white, sink.data(), sink.size(), nullptr); + hotStays = ui.IsShapedRunCached(font, kSize, white, kHot); + stayedAtCap = stayedAtCap && ui.ShapedRunCacheSize() == cap; + } + Check(hotStays, "hot label survives a churn of unique strings past the cap"); + Check(stayedAtCap, "cache stays pinned at the cap (no full clear)"); + + // A cold string left untouched while the cache churns past it IS evicted — + // confirms the cap is enforced by eviction, not by refusing new inserts. + const std::string_view kCold = "Cold Once"; + ui.ShapeText(font, kSize, 0, 0, kCold, white, sink.data(), sink.size(), nullptr); + Check(ui.IsShapedRunCached(font, kSize, white, kCold), + "cold string cached when first shaped"); + for (std::size_t i = 0; i < cap + 16; ++i) shapeUnique(2'000'000 + i); // never re-touch kCold + Check(!ui.IsShapedRunCached(font, kSize, white, kCold), + "an untouched cold string is eventually evicted (LRU tail recycled)"); + if (failures == 0) std::println("\nAll ShapeText cache checks passed."); else std::println("\n{} ShapeText cache check(s) FAILED.", failures); return failures == 0 ? EXIT_SUCCESS : EXIT_FAILURE; From 225556532adf2b4598526ee962c354be5fe14209 Mon Sep 17 00:00:00 2001 From: catbot Date: Thu, 18 Jun 2026 13:26:32 +0000 Subject: [PATCH 34/47] perf(ui): skip the destination read-modify-write on empty tiles (#127) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The standalone per-category UI shaders (quads/circles/images/text) unconditionally imageLoad the destination pixel up front and imageStore it at the end, paying a full read-modify-write even on tiles no item touches — the common case for a sparse UI. The fused uber-kernel already amortizes a single load/store across all four categories; the standalone Dispatch* path has no such umbrella. Defer the imageLoad to just before the first surviving blend (`loaded` flag) and skip the imageStore unless something was actually blended. Output is bit-identical: before the first blend `dst` is unused, and a skipped store leaves the pixel exactly as a no-op load+store would have rewritten it. Co-Authored-By: Claude Opus 4.8 --- shaders/ui-circles.comp.glsl | 22 +++++++++++++++------- shaders/ui-images.comp.glsl | 22 +++++++++++++++------- shaders/ui-quads.comp.glsl | 22 +++++++++++++++------- shaders/ui-text.comp.glsl | 22 +++++++++++++++------- 4 files changed, 60 insertions(+), 28 deletions(-) diff --git a/shaders/ui-circles.comp.glsl b/shaders/ui-circles.comp.glsl index eea0c93..b5cb547 100644 --- a/shaders/ui-circles.comp.glsl +++ b/shaders/ui-circles.comp.glsl @@ -23,12 +23,16 @@ void main() { ivec2 screenPx; bool valid = uiResolveScreenPixel(pc.hdr, screenPx); - vec4 dst = vec4(0.0); - vec2 sp = vec2(0.0); - if (valid) { - dst = imageLoad(uiImages[pc.hdr.outImage], screenPx); - sp = vec2(screenPx) + 0.5; - } + // Defer the destination read-modify-write: a sparse UI leaves most tiles + // untouched, so only load the pixel when the first surviving item blends + // over it (`loaded`), and only store when something actually touched it. + // The fused kernel amortizes a single load/store across all categories; the + // standalone Dispatch* path has no such umbrella and otherwise pays a full + // read-modify-write per empty tile. + vec4 dst = vec4(0.0); + vec2 sp = vec2(0.0); + bool loaded = false; + if (valid) sp = vec2(screenPx) + 0.5; vec2 tileMin, tileMax; uiTileBounds(tileMin, tileMax); @@ -90,11 +94,15 @@ void main() { } if (src.a <= 0.0) continue; + if (!loaded) { + dst = imageLoad(uiImages[pc.hdr.outImage], screenPx); + loaded = true; + } dst = uiBlendOver(dst, src); } } barrier(); } - if (valid) imageStore(uiImages[pc.hdr.outImage], screenPx, dst); + if (loaded) imageStore(uiImages[pc.hdr.outImage], screenPx, dst); // loaded ⇒ valid } diff --git a/shaders/ui-images.comp.glsl b/shaders/ui-images.comp.glsl index a4424a6..bafb2f5 100644 --- a/shaders/ui-images.comp.glsl +++ b/shaders/ui-images.comp.glsl @@ -24,12 +24,16 @@ void main() { ivec2 screenPx; bool valid = uiResolveScreenPixel(pc.hdr, screenPx); - vec4 dst = vec4(0.0); - vec2 sp = vec2(0.0); - if (valid) { - dst = imageLoad(uiImages[pc.hdr.outImage], screenPx); - sp = vec2(screenPx) + 0.5; - } + // Defer the destination read-modify-write: a sparse UI leaves most tiles + // untouched, so only load the pixel when the first surviving item blends + // over it (`loaded`), and only store when something actually touched it. + // The fused kernel amortizes a single load/store across all categories; the + // standalone Dispatch* path has no such umbrella and otherwise pays a full + // read-modify-write per empty tile. + vec4 dst = vec4(0.0); + vec2 sp = vec2(0.0); + bool loaded = false; + if (valid) sp = vec2(screenPx) + 0.5; vec2 tileMin, tileMax; uiTileBounds(tileMin, tileMax); @@ -80,11 +84,15 @@ void main() { ); vec4 src = sampled * s_tint[c]; if (src.a <= 0.0) continue; + if (!loaded) { + dst = imageLoad(uiImages[pc.hdr.outImage], screenPx); + loaded = true; + } dst = uiBlendOver(dst, src); } } barrier(); } - if (valid) imageStore(uiImages[pc.hdr.outImage], screenPx, dst); + if (loaded) imageStore(uiImages[pc.hdr.outImage], screenPx, dst); // loaded ⇒ valid } diff --git a/shaders/ui-quads.comp.glsl b/shaders/ui-quads.comp.glsl index 24e7daf..6bba7c4 100644 --- a/shaders/ui-quads.comp.glsl +++ b/shaders/ui-quads.comp.glsl @@ -27,12 +27,16 @@ void main() { ivec2 screenPx; bool valid = uiResolveScreenPixel(pc.hdr, screenPx); - vec4 dst = vec4(0.0); - vec2 sp = vec2(0.0); - if (valid) { - dst = imageLoad(uiImages[pc.hdr.outImage], screenPx); - sp = vec2(screenPx) + 0.5; - } + // Defer the destination read-modify-write: a sparse UI leaves most tiles + // untouched, so only load the pixel when the first surviving item blends + // over it (`loaded`), and only store when something actually touched it. + // The fused kernel amortizes a single load/store across all categories; the + // standalone Dispatch* path has no such umbrella and otherwise pays a full + // read-modify-write per empty tile. + vec4 dst = vec4(0.0); + vec2 sp = vec2(0.0); + bool loaded = false; + if (valid) sp = vec2(screenPx) + 0.5; vec2 tileMin, tileMax; uiTileBounds(tileMin, tileMax); @@ -93,11 +97,15 @@ void main() { } if (src.a <= 0.0) continue; + if (!loaded) { + dst = imageLoad(uiImages[pc.hdr.outImage], screenPx); + loaded = true; + } dst = uiBlendOver(dst, src); } } barrier(); // done reading shared for this chunk before it's overwritten } - if (valid) imageStore(uiImages[pc.hdr.outImage], screenPx, dst); + if (loaded) imageStore(uiImages[pc.hdr.outImage], screenPx, dst); // loaded ⇒ valid } diff --git a/shaders/ui-text.comp.glsl b/shaders/ui-text.comp.glsl index a64abbf..b34a485 100644 --- a/shaders/ui-text.comp.glsl +++ b/shaders/ui-text.comp.glsl @@ -31,12 +31,16 @@ void main() { ivec2 screenPx; bool valid = uiResolveScreenPixel(pc.hdr, screenPx); - vec4 dst = vec4(0.0); - vec2 sp = vec2(0.0); - if (valid) { - dst = imageLoad(uiImages[pc.hdr.outImage], screenPx); - sp = vec2(screenPx) + 0.5; - } + // Defer the destination read-modify-write: a sparse UI leaves most tiles + // untouched, so only load the pixel when the first surviving glyph blends + // over it (`loaded`), and only store when something actually touched it. + // The fused kernel amortizes a single load/store across all categories; the + // standalone Dispatch* path has no such umbrella and otherwise pays a full + // read-modify-write per empty tile. + vec4 dst = vec4(0.0); + vec2 sp = vec2(0.0); + bool loaded = false; + if (valid) sp = vec2(screenPx) + 0.5; vec2 tileMin, tileMax; uiTileBounds(tileMin, tileMax); @@ -102,11 +106,15 @@ void main() { vec4 col = s_color[c]; vec4 src = vec4(col.rgb, col.a * a); + if (!loaded) { + dst = imageLoad(uiImages[pc.hdr.outImage], screenPx); + loaded = true; + } dst = uiBlendOver(dst, src); } } barrier(); } - if (valid) imageStore(uiImages[pc.hdr.outImage], screenPx, dst); + if (loaded) imageStore(uiImages[pc.hdr.outImage], screenPx, dst); // loaded ⇒ valid } From da9f2a89e828cac8af08b354b37d7a31829ae90d Mon Sep 17 00:00:00 2001 From: catbot Date: Thu, 18 Jun 2026 13:26:37 +0000 Subject: [PATCH 35/47] perf(ui): subgroup prefix-sum stream compaction, not lane-0 serial scan (#124) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit uiCompactChunk ran the survivor scan entirely on gl_LocalInvocationIndex == 0: ~64 serial iterations with 63/64 lanes idle, between two barriers, 4x per chunk in the fused kernel. It must emit a stable in-order (buffer-order) exclusive prefix of the survivors so the per-pixel inner loop still sees items in draw order. Replace it with a per-subgroup subgroupExclusiveAdd of the keep bits plus a carry across subgroups: each subgroup publishes its survivor total to shared memory, then every lane sums the totals of all lower-id subgroups for its base. A survivor's slot in s_order[] therefore equals the number of survivors with a smaller local index — buffer (draw) order preserved exactly, unlike an atomicAdd which would scramble it. The carry makes the result correct for any subgroup width (2 subgroups on the 32-wide descriptor_heap target, up to 8/16 on narrower parts), relying only on the gl_LocalInvocationIndex <-> (gl_SubgroupID, gl_SubgroupInvocationID) linear mapping every Vulkan compute implementation provides. The feature is free at the device baseline (apiVersion 1.4 + VK_EXT_descriptor_heap implies Vulkan 1.1 subgroup arithmetic), so no correctness fallback is needed; WebGPU is unaffected (separate embedded WGSL). Applied to ui-fused.comp.glsl and the same pattern inlined in ui-quads/circles/images/text. Verified: all 23 tests pass (UIFusedShader recompiles + spirv-val + pins push-constant offsets); a 140k-trial CPU simulation of the algorithm matches the serial reference for subgroup sizes 1..64; HelloUI renders correctly on an RTX 4090 (DispatchFused quads+circles+text) with draw order intact. Co-Authored-By: Claude Opus 4.8 --- shaders/ui-circles.comp.glsl | 38 ++++++++++++++++++++----- shaders/ui-fused.comp.glsl | 55 +++++++++++++++++++++++++++--------- shaders/ui-images.comp.glsl | 38 ++++++++++++++++++++----- shaders/ui-quads.comp.glsl | 39 +++++++++++++++++++------ shaders/ui-text.comp.glsl | 38 ++++++++++++++++++++----- 5 files changed, 166 insertions(+), 42 deletions(-) diff --git a/shaders/ui-circles.comp.glsl b/shaders/ui-circles.comp.glsl index eea0c93..65fa935 100644 --- a/shaders/ui-circles.comp.glsl +++ b/shaders/ui-circles.comp.glsl @@ -1,5 +1,7 @@ #version 460 #extension GL_GOOGLE_include_directive : enable +#extension GL_KHR_shader_subgroup_basic : enable +#extension GL_KHR_shader_subgroup_arithmetic : enable #include "ui-shared.glsl" // One workgroup per 8×8 screen tile. The workgroup cooperatively streams the @@ -18,6 +20,34 @@ shared vec4 s_outline[UI_CHUNK]; shared uint s_keep[UI_CHUNK]; shared uint s_order[UI_CHUNK]; shared uint s_count; +shared uint s_subTotals[UI_CHUNK]; // per-subgroup survivor totals (carry step) + +// Stable in-order compaction of one chunk's survivors via a per-subgroup +// exclusive prefix-sum plus a carry across subgroups, so a survivor's slot in +// s_order[] equals the number of survivors with a smaller local index — buffer +// (draw) order preserved. Replaces the old lane-0 serial scan. See +// ui-fused.comp.glsl for the full rationale; the body is identical. +void uiCompactChunk() { + uint lid = gl_LocalInvocationIndex; + uint keep = s_keep[lid]; + uint subPrefix = subgroupExclusiveAdd(keep); + uint subTotal = subgroupAdd(keep); + if (subgroupElect()) + s_subTotals[gl_SubgroupID] = subTotal; + barrier(); + + uint base = 0u; + uint total = 0u; + for (uint s = 0u; s < gl_NumSubgroups; ++s) { + uint t = s_subTotals[s]; + if (s < gl_SubgroupID) base += t; + total += t; + } + if (lid == 0u) s_count = total; + + if (keep != 0u) + s_order[base + subPrefix] = lid; +} void main() { ivec2 screenPx; @@ -52,13 +82,7 @@ void main() { s_keep[lid] = keep ? 1u : 0u; barrier(); - if (lid == 0u) { - uint n = 0u; - uint lim = min(UI_CHUNK, pc.hdr.itemCount - base); - for (uint k = 0u; k < lim; ++k) - if (s_keep[k] != 0u) s_order[n++] = k; - s_count = n; - } + uiCompactChunk(); barrier(); if (valid) { diff --git a/shaders/ui-fused.comp.glsl b/shaders/ui-fused.comp.glsl index 998061b..b8834a7 100644 --- a/shaders/ui-fused.comp.glsl +++ b/shaders/ui-fused.comp.glsl @@ -1,5 +1,7 @@ #version 460 #extension GL_GOOGLE_include_directive : enable +#extension GL_KHR_shader_subgroup_basic : enable +#extension GL_KHR_shader_subgroup_arithmetic : enable #include "ui-shared.glsl" // ─── fused UI uber-kernel (issue #47) ──────────────────────────────────── @@ -73,17 +75,44 @@ shared uvec4 s_v4[UI_CHUNK]; shared uint s_keep[UI_CHUNK]; shared uint s_order[UI_CHUNK]; shared uint s_count; +// Per-subgroup survivor totals, published once per subgroup for the carry step +// of the compaction below. Sized for the worst case of one subgroup per lane. +shared uint s_subTotals[UI_CHUNK]; -// Stable in-order compaction of one chunk's survivors. Lane 0 scans s_keep in -// buffer order so the inner per-pixel loop still sees items in draw order. -void uiCompactChunk(uint base, uint count) { - if (gl_LocalInvocationIndex == 0u) { - uint n = 0u; - uint lim = min(UI_CHUNK, count - base); - for (uint k = 0u; k < lim; ++k) - if (s_keep[k] != 0u) s_order[n++] = k; - s_count = n; +// Stable in-order compaction of one chunk's survivors. Each subgroup computes +// an exclusive prefix-sum of its keep bits at register speed (subgroupExclusiveAdd), +// publishes its survivor total to shared memory, then every lane carries in the +// totals of all lower-id subgroups. A survivor's slot in s_order[] therefore +// equals the number of survivors with a smaller local index, so the per-pixel +// inner loop still sees items in buffer (draw) order. This replaces the old +// lane-0 serial scan (~64 iterations with 63/64 lanes idle, run 4× per chunk). +// +// Order preservation relies on the gl_LocalInvocationIndex ↔ (gl_SubgroupID, +// gl_SubgroupInvocationID) linear mapping that every Vulkan compute +// implementation provides; the carry across subgroups makes the result correct +// for any subgroup width (2 subgroups on the 32-wide descriptor_heap target, up +// to 8 on 8-wide parts). Out-of-range lanes carry s_keep==0, so they neither +// add to a total nor write a slot — no separate bound is needed. +void uiCompactChunk() { + uint lid = gl_LocalInvocationIndex; + uint keep = s_keep[lid]; + uint subPrefix = subgroupExclusiveAdd(keep); // rank within this subgroup + uint subTotal = subgroupAdd(keep); // survivors in this subgroup + if (subgroupElect()) + s_subTotals[gl_SubgroupID] = subTotal; + barrier(); + + uint base = 0u; + uint total = 0u; + for (uint s = 0u; s < gl_NumSubgroups; ++s) { + uint t = s_subTotals[s]; + if (s < gl_SubgroupID) base += t; // survivors in lower subgroups + total += t; } + if (lid == 0u) s_count = total; + + if (keep != 0u) + s_order[base + subPrefix] = lid; } void main() { @@ -120,7 +149,7 @@ void main() { } s_keep[lid] = keep ? 1u : 0u; barrier(); - uiCompactChunk(base, count); + uiCompactChunk(); barrier(); if (inClip) { @@ -180,7 +209,7 @@ void main() { } s_keep[lid] = keep ? 1u : 0u; barrier(); - uiCompactChunk(base, count); + uiCompactChunk(); barrier(); if (inClip) { @@ -237,7 +266,7 @@ void main() { } s_keep[lid] = keep ? 1u : 0u; barrier(); - uiCompactChunk(base, count); + uiCompactChunk(); barrier(); if (inClip) { @@ -287,7 +316,7 @@ void main() { } s_keep[lid] = keep ? 1u : 0u; barrier(); - uiCompactChunk(base, count); + uiCompactChunk(); barrier(); if (inClip) { diff --git a/shaders/ui-images.comp.glsl b/shaders/ui-images.comp.glsl index a4424a6..afe76ff 100644 --- a/shaders/ui-images.comp.glsl +++ b/shaders/ui-images.comp.glsl @@ -1,5 +1,7 @@ #version 460 #extension GL_GOOGLE_include_directive : enable +#extension GL_KHR_shader_subgroup_basic : enable +#extension GL_KHR_shader_subgroup_arithmetic : enable #include "ui-shared.glsl" // One workgroup per 8×8 screen tile. The workgroup cooperatively streams the @@ -19,6 +21,34 @@ shared uvec4 s_slots[UI_CHUNK]; shared uint s_keep[UI_CHUNK]; shared uint s_order[UI_CHUNK]; shared uint s_count; +shared uint s_subTotals[UI_CHUNK]; // per-subgroup survivor totals (carry step) + +// Stable in-order compaction of one chunk's survivors via a per-subgroup +// exclusive prefix-sum plus a carry across subgroups, so a survivor's slot in +// s_order[] equals the number of survivors with a smaller local index — buffer +// (draw) order preserved. Replaces the old lane-0 serial scan. See +// ui-fused.comp.glsl for the full rationale; the body is identical. +void uiCompactChunk() { + uint lid = gl_LocalInvocationIndex; + uint keep = s_keep[lid]; + uint subPrefix = subgroupExclusiveAdd(keep); + uint subTotal = subgroupAdd(keep); + if (subgroupElect()) + s_subTotals[gl_SubgroupID] = subTotal; + barrier(); + + uint base = 0u; + uint total = 0u; + for (uint s = 0u; s < gl_NumSubgroups; ++s) { + uint t = s_subTotals[s]; + if (s < gl_SubgroupID) base += t; + total += t; + } + if (lid == 0u) s_count = total; + + if (keep != 0u) + s_order[base + subPrefix] = lid; +} void main() { ivec2 screenPx; @@ -49,13 +79,7 @@ void main() { s_keep[lid] = keep ? 1u : 0u; barrier(); - if (lid == 0u) { - uint n = 0u; - uint lim = min(UI_CHUNK, pc.hdr.itemCount - base); - for (uint k = 0u; k < lim; ++k) - if (s_keep[k] != 0u) s_order[n++] = k; - s_count = n; - } + uiCompactChunk(); barrier(); if (valid) { diff --git a/shaders/ui-quads.comp.glsl b/shaders/ui-quads.comp.glsl index 24e7daf..a4fa58d 100644 --- a/shaders/ui-quads.comp.glsl +++ b/shaders/ui-quads.comp.glsl @@ -1,5 +1,7 @@ #version 460 #extension GL_GOOGLE_include_directive : enable +#extension GL_KHR_shader_subgroup_basic : enable +#extension GL_KHR_shader_subgroup_arithmetic : enable #include "ui-shared.glsl" // One workgroup per 8×8 screen tile. The workgroup cooperatively streams the @@ -21,6 +23,34 @@ shared vec4 s_outline[UI_CHUNK]; shared uint s_keep[UI_CHUNK]; shared uint s_order[UI_CHUNK]; shared uint s_count; +shared uint s_subTotals[UI_CHUNK]; // per-subgroup survivor totals (carry step) + +// Stable in-order compaction of one chunk's survivors via a per-subgroup +// exclusive prefix-sum plus a carry across subgroups, so a survivor's slot in +// s_order[] equals the number of survivors with a smaller local index — buffer +// (draw) order preserved. Replaces the old lane-0 serial scan. See +// ui-fused.comp.glsl for the full rationale; the body is identical. +void uiCompactChunk() { + uint lid = gl_LocalInvocationIndex; + uint keep = s_keep[lid]; + uint subPrefix = subgroupExclusiveAdd(keep); + uint subTotal = subgroupAdd(keep); + if (subgroupElect()) + s_subTotals[gl_SubgroupID] = subTotal; + barrier(); + + uint base = 0u; + uint total = 0u; + for (uint s = 0u; s < gl_NumSubgroups; ++s) { + uint t = s_subTotals[s]; + if (s < gl_SubgroupID) base += t; + total += t; + } + if (lid == 0u) s_count = total; + + if (keep != 0u) + s_order[base + subPrefix] = lid; +} void main() { // NOTE: do not early-return — every thread must reach the barriers below. @@ -54,14 +84,7 @@ void main() { s_keep[lid] = keep ? 1u : 0u; barrier(); - // Stable in-order compaction of this chunk's survivors. - if (lid == 0u) { - uint n = 0u; - uint lim = min(UI_CHUNK, pc.hdr.itemCount - base); - for (uint k = 0u; k < lim; ++k) - if (s_keep[k] != 0u) s_order[n++] = k; - s_count = n; - } + uiCompactChunk(); barrier(); if (valid) { diff --git a/shaders/ui-text.comp.glsl b/shaders/ui-text.comp.glsl index a64abbf..9f9bab3 100644 --- a/shaders/ui-text.comp.glsl +++ b/shaders/ui-text.comp.glsl @@ -1,5 +1,7 @@ #version 460 #extension GL_GOOGLE_include_directive : enable +#extension GL_KHR_shader_subgroup_basic : enable +#extension GL_KHR_shader_subgroup_arithmetic : enable #include "ui-shared.glsl" // One workgroup per 8×8 screen tile. The workgroup cooperatively streams the @@ -26,6 +28,34 @@ shared vec4 s_color[UI_CHUNK]; shared uint s_keep[UI_CHUNK]; shared uint s_order[UI_CHUNK]; shared uint s_count; +shared uint s_subTotals[UI_CHUNK]; // per-subgroup survivor totals (carry step) + +// Stable in-order compaction of one chunk's survivors via a per-subgroup +// exclusive prefix-sum plus a carry across subgroups, so a survivor's slot in +// s_order[] equals the number of survivors with a smaller local index — buffer +// (draw) order preserved. Replaces the old lane-0 serial scan. See +// ui-fused.comp.glsl for the full rationale; the body is identical. +void uiCompactChunk() { + uint lid = gl_LocalInvocationIndex; + uint keep = s_keep[lid]; + uint subPrefix = subgroupExclusiveAdd(keep); + uint subTotal = subgroupAdd(keep); + if (subgroupElect()) + s_subTotals[gl_SubgroupID] = subTotal; + barrier(); + + uint base = 0u; + uint total = 0u; + for (uint s = 0u; s < gl_NumSubgroups; ++s) { + uint t = s_subTotals[s]; + if (s < gl_SubgroupID) base += t; + total += t; + } + if (lid == 0u) s_count = total; + + if (keep != 0u) + s_order[base + subPrefix] = lid; +} void main() { ivec2 screenPx; @@ -55,13 +85,7 @@ void main() { s_keep[lid] = keep ? 1u : 0u; barrier(); - if (lid == 0u) { - uint n = 0u; - uint lim = min(UI_CHUNK, pc.hdr.itemCount - base); - for (uint k = 0u; k < lim; ++k) - if (s_keep[k] != 0u) s_order[n++] = k; - s_count = n; - } + uiCompactChunk(); barrier(); if (valid) { From a45e793da4866cbbb631f3fe7d4a4db9e59793a2 Mon Sep 17 00:00:00 2001 From: catbot Date: Thu, 18 Jun 2026 13:29:55 +0000 Subject: [PATCH 36/47] perf(ui): memoize the input-field caret prefix width (#128) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DrawInputField re-measured the cursor prefix via Font::GetLineWidth on every frame of a focused field, even though only the blink (caretVisible) changes frame-to-frame — value, cursorPos and fontSize are stable across the vast majority of frames. Cache the measured prefix WIDTH on the InputField, keyed on (prefix bytes, fontSize). A cheap byte compare of the cached prefix guards the expensive per-glyph UTF-8 decode + advance accumulation. The cache is mutable so the const-ref draw fn can refresh it. The absolute caretX is deliberately NOT cached: it adds the layout- dependent textX (rect.x + paddingX) each frame, so caching it would give a relocated field a stale caret. Caching the width keeps that correct. Adds tests/InputFieldCaretCache pinning: the cached caret matches the uncached GetLineWidth oracle across states, a redraw (cache hit) is identical to the miss, the cache invalidates on value/cursorPos/fontSize changes, the blink never moves the caret, and a relocated field shifts the caret by exactly the rect delta. Co-Authored-By: Claude Opus 4.8 --- .../Crafter.Graphics-InputField.cpp | 16 +- interfaces/Crafter.Graphics-InputField.cppm | 14 + project.cpp | 32 +++ tests/InputFieldCaretCache/font.ttf | Bin 0 -> 621572 bytes tests/InputFieldCaretCache/main.cpp | 248 ++++++++++++++++++ 5 files changed, 309 insertions(+), 1 deletion(-) create mode 100644 tests/InputFieldCaretCache/font.ttf create mode 100644 tests/InputFieldCaretCache/main.cpp diff --git a/implementations/Crafter.Graphics-InputField.cpp b/implementations/Crafter.Graphics-InputField.cpp index 433fddf..c527cdd 100644 --- a/implementations/Crafter.Graphics-InputField.cpp +++ b/implementations/Crafter.Graphics-InputField.cpp @@ -150,7 +150,21 @@ void Crafter::DrawInputField(UIBuffer& buf, const InputField& f, Rect rect, if (f.focused && caretVisible && *buf.quadCount < buf.quadCap) { std::string_view sub(f.value.data(), std::min(f.cursorPos, f.value.size())); - float caretX = textX + (sub.empty() ? 0.0f : static_cast(font.GetLineWidth(sub, fontSize))); + // Memoise the prefix width (issue #128): only the blink changes between + // frames, so re-walking the prefix every frame is wasted work. A byte + // compare of the cached prefix (plus fontSize) is far cheaper than the + // per-glyph UTF-8 decode + advance accumulation GetLineWidth does. + float width; + if (f.caretCacheFontSize_ == fontSize && f.caretCachePrefix_ == sub) { + width = f.caretCacheWidth_; + } else { + width = sub.empty() ? 0.0f + : static_cast(font.GetLineWidth(sub, fontSize)); + f.caretCachePrefix_.assign(sub); + f.caretCacheFontSize_ = fontSize; + f.caretCacheWidth_ = width; + } + float caretX = textX + width; float caretH = fontSize * 1.1f; float caretY = rect.y + (rect.h - caretH) * 0.5f; float caretW = std::max(1.0f, fontSize / 16.0f); diff --git a/interfaces/Crafter.Graphics-InputField.cppm b/interfaces/Crafter.Graphics-InputField.cppm index 0812858..b05ab38 100644 --- a/interfaces/Crafter.Graphics-InputField.cppm +++ b/interfaces/Crafter.Graphics-InputField.cppm @@ -58,6 +58,20 @@ export namespace Crafter { InputFieldType type = InputFieldType::Text; bool focused = false; std::size_t cursorPos = 0; // byte offset into `value` + + // Caret-x memo (issue #128). For a focused field DrawInputField measures + // the cursor prefix via Font::GetLineWidth every frame, but only the + // blink (caretVisible) changes frame-to-frame — value/cursorPos/fontSize + // are stable across the vast majority of frames. Cache the measured + // prefix WIDTH (the GetLineWidth result, in px) keyed on the inputs that + // determine it: the prefix bytes and fontSize. The final caretX adds the + // layout-dependent textX each frame, so it is intentionally NOT cached — + // a field that moves on screen with an unchanged value would otherwise + // get a stale caret. `mutable` so the `const InputField&` draw fn can + // refresh it; these fields are internal and need no user initialisation. + mutable std::string caretCachePrefix_; + mutable float caretCacheFontSize_ = -1.0f; // <0 = unset + mutable float caretCacheWidth_ = 0.0f; }; // Returns true if `s` is a valid candidate for the given type, including diff --git a/project.cpp b/project.cpp index f0ec1e9..82fc66b 100644 --- a/project.cpp +++ b/project.cpp @@ -863,6 +863,38 @@ extern "C" Configuration CrafterBuildProject(std::span a hc.GetInterfacesAndImplementations(ifaces, hitImpls); cfg.tests.push_back(std::move(hitTest)); + // Issue #128: DrawInputField re-measured the cursor prefix via + // Font::GetLineWidth every frame of a focused field even though only the + // blink changes frame-to-frame. The prefix WIDTH is now memoised on the + // InputField keyed on (prefix bytes, fontSize); the absolute caretX is + // deliberately not cached so a relocated field can't get a stale caret. + // The memo is transparent presentation logic over a TrueType file — + // DrawText no-ops with a null atlas/renderer — so this test drives + // DrawInputField directly with no Vulkan device or window. The font is + // copied next to the binary; the test also probes the project root. + Test caretTest; + Configuration& crc = caretTest.config; + crc.path = cfg.path; + crc.name = "InputFieldCaretCache"; + crc.outputName = "InputFieldCaretCache"; + crc.type = ConfigurationType::Executable; + crc.target = cfg.target; + crc.march = cfg.march; + crc.mtune = cfg.mtune; + crc.debug = cfg.debug; + crc.sysroot = cfg.sysroot; + crc.dependencies = cfg.dependencies; + crc.externalDependencies = cfg.externalDependencies; + crc.compileFlags = cfg.compileFlags; + crc.linkFlags = cfg.linkFlags; + crc.defines = cfg.defines; + crc.cFiles = cfg.cFiles; + crc.files = { fs::path("tests/InputFieldCaretCache/font.ttf") }; + std::vector caretImpls(impls.begin(), impls.end()); + caretImpls.emplace_back("tests/InputFieldCaretCache/main"); + crc.GetInterfacesAndImplementations(ifaces, caretImpls); + cfg.tests.push_back(std::move(caretTest)); + // Issue #69: the engine feeds one shared Device::pipelineCache to every // vkCreate*Pipelines call and persists it across runs, discarding an // on-disk blob whose header doesn't match the current GPU. That gate — diff --git a/tests/InputFieldCaretCache/font.ttf b/tests/InputFieldCaretCache/font.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f27f4ff59562d58480f1cb94194393484b8da9e9 GIT binary patch literal 621572 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

a=M8|yC%_H`?OKLz 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

BcL7qG2iKj- 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ⅈ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�ExftZ5G<&LFl0T&26%DO+qbVH5@V)l5ghM!_A2 z4>vG4CtB!kFxSx&x6$2TK}hKaM(1P;-3^vH8<`lyR2B3T+(BjB1}0U{t}b_FcjZKd zh$LxHsjKX?$%2tlR76X6gO$z(2C)s|%1#?tR5!4wf|YILVQ|{O?W~>JrKhZ*VAExx zyTKY&nSvfzp~40>WhaZ)Qft6gIG^DZ3?F zNGosPa&`mNSX|CvTM`toS)vTGMR^08a{?$bSXC1@uwt0HfkiD5R9k@@!K|8)0xd}q zc17xLuz`hzf`Y;ZRyAl?DBD=*Zm@+*ZQxRNQczHEQ+D6LsI3f&MLU=r*fWrj-oOwN zp{HP@tO%}enIM7+Aa_BdVgsA%23A#1Q1*beT_BN&neq~pp|Pb5O7iwF+Y~l%IYXVO zkN|4npa&!@iXC*674$%%=(a)JIU+zhC^AC9CQ@&s0i%ev?gmE`*&yiv6j>(}S(sjA z^U!rT>uj*nMpgjR1hddZXM?qNmx6)~D2;Vl;8wbU%~@}Qfwt}jSFo2s$yf~%@`)RG zl${_c6_lKn6>Jprlr5kha?{z!#iHs2O8p=$s<1EuSw}cYyJJgWp!7v*D&D}P>IN#u zY?Lje6_L`5VkFd^?m8P8th8aF;-RyV!CG4p6lWlZLVW`d0#BU{45E;1zli~q!-e%W zm}%*5@WLVPt+SDVQABuyxt8t*ADs=BTDlv2L80iXte~gh)}@>X3oSn#P=emztF61i zUuP2ogPXSQh5(&S42&Q~pw1>nut<>3CPuJGFi4HNw(f=ykQxvp6r={k2m`4BF~W5= zGcYhXYwK=^(Af-XgKO(9fV+5&-0jUGCVnOP_tT>Q5Fe@Hp zr>nN^h6IqEAVwm{P7osrWG9G`3{vBvt-B!wqz1%D1*ris(m-lJjC7EF3@+Nb8!|xl zfmxX#`@pO$kbPiQHb@;KNL>y{9hj91QU_+`fz*Ln`8pdJY~T@C0OoCQ(AM2h2w^&E z>ux9l1v*IC1}AOZ4aGVe8Ev$|YDyqXkeX7k8ju7?O_|O{23xqP>nUIvW}6;O5nVd0_MEAxx0+1_%?RyisQ(qaDP&CI}Ox zpc%phDQMBz$Y2jQuNBM#o7V}gHIvW}7A?9^Lm>>mR5GF`Lx6USRaLaZB zoAf3YCT7`o5c zlbeefVgnr+bU7Kcz#<|Y92FcH97#-JY;GJ@Y(^|9?3`RssR?WiYz1to3^6QzYz{1D zOxmnqDFG)(ZY2jf2Pp@M1EL265AYq}Il$?_z{$X^wUI&6$&s7E0Rw_0cQPFfg(>bR>s>RWi6lf`Efa7h4ru r7F!Bq1d9io4T}kr8Y?HC)(*!1ts7W+H!?A}Y-UYiV{ma{U|;|M6KIBE literal 0 HcmV?d00001 diff --git a/tests/InputFieldCaretCache/main.cpp b/tests/InputFieldCaretCache/main.cpp new file mode 100644 index 0000000..a407017 --- /dev/null +++ b/tests/InputFieldCaretCache/main.cpp @@ -0,0 +1,248 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Regression test for issue #128: DrawInputField re-walked the cursor prefix +// via Font::GetLineWidth on every frame of a focused field, even though only +// the blink (caretVisible) changes frame-to-frame. The fix memoises the prefix +// WIDTH on the InputField, keyed on (prefix bytes, fontSize) — a cheap byte +// compare guards the expensive per-glyph advance accumulation. +// +// The cache must be transparent: the emitted caret quad has to sit at exactly +// the same x as the uncached `textX + GetLineWidth(prefix)` oracle, in every +// state. This test pins: +// - the caret x matches the oracle across many (value, cursorPos, fontSize) +// states, on both the first draw (cache miss) and an immediate redraw +// (cache hit) — a hit must be byte-identical to a miss, +// - the cache invalidates correctly when value, cursorPos, or fontSize change +// (no stale caret after an edit), +// - the blink (caretVisible toggling) never moves the caret, +// - moving the field on screen (rect.x) with an unchanged value still moves +// the caret by the same delta — the WIDTH is cached, not the absolute +// caretX, so a relocated field can't get a stale caret, +// - the internal cache fields hold the measured prefix/fontSize/width after a +// draw (white-box: the memo is actually populated). +// +// Pure CPU + a TrueType file — DrawText no-ops with a null atlas/renderer, so +// only the background + caret quads are emitted and no Vulkan device or window +// is needed. The font is copied next to the binary; the test also probes the +// project-root path. + +#include + +import Crafter.Graphics; +import std; + +using namespace Crafter; + +namespace { + +int failures = 0; + +void Check(bool ok, std::string_view what) { + std::println("{} {}", ok ? "PASS" : "FAIL", what); + if (!ok) ++failures; +} + +std::filesystem::path FindFont() { + for (const char* cand : { + "font.ttf", + "tests/InputFieldCaretCache/font.ttf", + "../../examples/HelloUI/font.ttf" }) { + if (std::filesystem::exists(cand)) return cand; + } + return "font.ttf"; +} + +const InputFieldColors kColors{ + {0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}, + {1, 1, 1, 1}, {1, 1, 1, 1}, +}; + +constexpr float kEps = 1e-3f; + +// Draw the field into a fresh quad buffer and return the caret quad's x, or +// NaN if no caret was emitted (not focused / not visible). The caret is the +// quad emitted after the background quad. atlas/renderer left null so DrawText +// is a no-op — only the bg + caret quads land. +float CaretX(const InputField& f, Rect rect, Font& font, float fontSize, + bool caretVisible) { + std::array quads{}; + std::uint32_t count = 0; + UIBuffer buf; + buf.quads = quads.data(); + buf.quadCount = &count; + buf.quadCap = static_cast(quads.size()); + + DrawInputField(buf, f, rect, font, fontSize, kColors, caretVisible); + + // bg quad always emitted (count>=1); caret quad, if any, is the last one. + if (count < 2) return std::numeric_limits::quiet_NaN(); + return quads[count - 1].x; +} + +// Uncached oracle: where the caret should sit, computed straight from +// GetLineWidth on the prefix — the exact arithmetic DrawInputField uses, with +// no memo in the path. +float OracleCaretX(const InputField& f, Rect rect, Font& font, float fontSize) { + float textX = rect.x + kColors.paddingX; + std::string_view sub(f.value.data(), std::min(f.cursorPos, f.value.size())); + float w = sub.empty() ? 0.0f + : static_cast(font.GetLineWidth(sub, fontSize)); + return textX + w; +} + +bool Near(float a, float b) { return std::abs(a - b) <= kEps; } + +} // namespace + +int main() { + Font font(FindFont()); + + const float rectX = 30.0f; + const std::array samples = { + "Hello, world", + "1234567890", + "iiiiWWWWmmmm", + "caf\xC3\xA9 na\xC3\xAFve \xE2\x9C\x93", // "café naïve ✓" — multibyte + }; + const std::array sizes = {12.0f, 18.0f, 27.5f}; + + // 1. Across every (value, cursorPos, fontSize), the cached caret matches the + // uncached oracle — and a redraw (guaranteed cache hit) matches the first + // draw exactly. + bool sweepOk = true, hitMatchesMiss = true; + for (std::string_view s : samples) { + for (float size : sizes) { + // Walk every codepoint boundary as a cursor position. + for (std::size_t pos = 0; pos <= s.size(); ++pos) { + InputField f; + f.value = std::string(s); + f.focused = true; + f.cursorPos = pos; + Rect rect{rectX, 0.0f, 200.0f, 24.0f}; + + float miss = CaretX(f, rect, font, size, true); // populates cache + float hit = CaretX(f, rect, font, size, true); // reads cache + float oracle = OracleCaretX(f, rect, font, size); + + if (!Near(miss, oracle)) { + sweepOk = false; + std::println(" caret mismatch \"{}\" pos={} size={}: got={} oracle={}", + s, pos, size, miss, oracle); + } + if (!Near(miss, hit)) hitMatchesMiss = false; + } + } + } + Check(sweepOk, "cached caret x matches the uncached GetLineWidth oracle across states"); + Check(hitMatchesMiss, "a cache hit (redraw) is byte-identical to the cache miss"); + + // 2. The cache invalidates on each keyed input. Reuse ONE field across the + // mutations so a stale memo would survive into the next draw if the key + // check were wrong. + { + InputField f; + f.value = "abcdef"; + f.focused = true; + Rect rect{rectX, 0.0f, 200.0f, 24.0f}; + constexpr float size = 18.0f; + + f.cursorPos = 3; + (void) CaretX(f, rect, font, size, true); // prime cache at (abcdef,3,18) + + // 2a. cursorPos change. + f.cursorPos = 6; + Check(Near(CaretX(f, rect, font, size, true), OracleCaretX(f, rect, font, size)), + "caret updates when cursorPos changes (cache key includes cursorPos)"); + + // 2b. value change with the SAME cursorPos and a prefix of the same + // length — only the bytes differ, so a length-only key would miss + // this. Widen a glyph to force a different width. + f.value = "abcWWW"; + f.cursorPos = 6; + Check(Near(CaretX(f, rect, font, size, true), OracleCaretX(f, rect, font, size)), + "caret updates when value bytes change (cache key compares bytes, not length)"); + + // 2c. fontSize change with value/cursor fixed. + float small = CaretX(f, rect, font, 12.0f, true); + float big = CaretX(f, rect, font, 27.5f, true); + Check(Near(small, OracleCaretX(f, rect, font, 12.0f)) && + Near(big, OracleCaretX(f, rect, font, 27.5f)) && + !Near(small, big), + "caret updates when fontSize changes (cache key includes fontSize)"); + } + + // 3. The blink must never move the caret: drawing with caretVisible=true, + // then false (no caret), then true again yields the same x. + { + InputField f; + f.value = "blink test"; + f.focused = true; + f.cursorPos = 5; + Rect rect{rectX, 0.0f, 200.0f, 24.0f}; + constexpr float size = 18.0f; + + float on1 = CaretX(f, rect, font, size, true); + float off = CaretX(f, rect, font, size, false); // caret suppressed + float on2 = CaretX(f, rect, font, size, true); + Check(std::isnan(off) && Near(on1, on2) && Near(on1, OracleCaretX(f, rect, font, size)), + "blink (caretVisible toggle) does not move or stale the caret"); + } + + // 4. Moving the field on screen with an UNCHANGED value moves the caret by + // exactly the rect delta. This is the guard for caching the prefix WIDTH + // rather than the absolute caretX — a memoised caretX would go stale here. + { + InputField f; + f.value = "relocate"; + f.focused = true; + f.cursorPos = 8; + constexpr float size = 18.0f; + + Rect a{rectX, 0.0f, 200.0f, 24.0f}; + Rect b{rectX + 137.0f, 0.0f, 200.0f, 24.0f}; + float xa = CaretX(f, a, font, size, true); // primes cache + float xb = CaretX(f, b, font, size, true); // same value → cache hit on width + Check(Near(xb - xa, 137.0f) && Near(xb, OracleCaretX(f, b, font, size)), + "moving the field shifts the caret by the rect delta (width cached, not caretX)"); + } + + // 5. White-box: after a draw the memo holds the measured prefix, fontSize + // and width — confirming the fast path is actually wired, not dead code. + { + InputField f; + f.value = "memo check"; + f.focused = true; + f.cursorPos = 4; + Rect rect{rectX, 0.0f, 200.0f, 24.0f}; + constexpr float size = 18.0f; + (void) CaretX(f, rect, font, size, true); + + std::string_view sub(f.value.data(), f.cursorPos); + float w = static_cast(font.GetLineWidth(sub, size)); + Check(f.caretCachePrefix_ == sub && + f.caretCacheFontSize_ == size && + Near(f.caretCacheWidth_, w), + "draw populates the caret memo with the measured prefix/fontSize/width"); + } + + if (failures == 0) std::println("\nAll InputField caret-cache checks passed."); + else std::println("\n{} InputField caret-cache check(s) FAILED.", failures); + return failures == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} From 16d291b0adc116f3c52981e1b05bffb352a80321 Mon Sep 17 00:00:00 2001 From: catbot Date: Thu, 18 Jun 2026 13:35:54 +0000 Subject: [PATCH 37/47] perf(ui): per-shelf dirty spans for tight atlas uploads (#129) FontAtlas::Update used a single union bounding box for the dirty rect. Scattered new glyphs landing on different shelves produced a tall union box that re-uploaded mostly-unchanged texels between the shelves. Track a dirty span per shelf instead and issue one tight UpdateRegion / wgpuWriteAtlasRegion copy per armed span. A shelf packs glyphs left-to-right at a fixed top, so each span stays a contiguous X run capped by the shelf height. The whole-atlas zero-clear in Initialize keeps using the existing dirtyRect span. `dirty` is now the OR of every span and is cleared together with them in Update. Verified: full test suite green (23 passed); HelloUI text renders crisply on the WebGPU backend through the new multi-span upload path. Co-Authored-By: Claude Opus 4.8 --- .../Crafter.Graphics-FontAtlas.cpp | 62 ++++++++++++------- interfaces/Crafter.Graphics-FontAtlas.cppm | 19 ++++-- 2 files changed, 52 insertions(+), 29 deletions(-) diff --git a/implementations/Crafter.Graphics-FontAtlas.cpp b/implementations/Crafter.Graphics-FontAtlas.cpp index d6f14e7..791c03f 100644 --- a/implementations/Crafter.Graphics-FontAtlas.cpp +++ b/implementations/Crafter.Graphics-FontAtlas.cpp @@ -59,11 +59,13 @@ void FontAtlas::Initialize(GraphicsCommandBuffer cmd) { MarkDirty(0, 0, kAtlasSize, kAtlasSize); } -bool FontAtlas::ShelfPlace(int w, int h, int& outX, int& outY) { - for (Shelf& s : shelves_) { +bool FontAtlas::ShelfPlace(int w, int h, int& outX, int& outY, int& outShelf) { + for (std::size_t i = 0; i < shelves_.size(); ++i) { + Shelf& s = shelves_[i]; if (h <= s.height && s.cursorX + w <= kAtlasSize) { outX = s.cursorX; outY = s.y; + outShelf = static_cast(i); s.cursorX += w; return true; } @@ -75,6 +77,7 @@ bool FontAtlas::ShelfPlace(int w, int h, int& outX, int& outY) { s.cursorX = w; outX = 0; outY = s.y; + outShelf = static_cast(shelves_.size()); shelves_.push_back(s); nextShelfY_ += h; return true; @@ -106,8 +109,8 @@ const Glyph* FontAtlas::EnsureAndGet(Font& font, std::uint32_t codepoint) { g.yoff = static_cast(yoff); if (sdf && sw > 0 && sh > 0) { - int px = 0, py = 0; - if (!ShelfPlace(sw, sh, px, py)) { + int px = 0, py = 0, shelf = 0; + if (!ShelfPlace(sw, sh, px, py, shelf)) { stbtt_FreeSDF(sdf, nullptr); return nullptr; } @@ -127,7 +130,10 @@ const Glyph* FontAtlas::EnsureAndGet(Font& font, std::uint32_t codepoint) { g.v0 = static_cast(py) / kAtlasSize; g.u1 = static_cast(px + sw) / kAtlasSize; g.v1 = static_cast(py + sh) / kAtlasSize; - MarkDirty(px, py, sw, sh); + // Mark the *shelf's* span, not one global box — keeps each upload + // tight when glyphs land on different shelves the same frame (#129). + shelves_[shelf].dirty.Add(px, py, sw, sh); + dirty = true; } return &cache_.emplace(key, g).first->second; @@ -141,27 +147,35 @@ const Glyph* FontAtlas::Lookup(Font& font, std::uint32_t codepoint) const { void FontAtlas::Update(GraphicsCommandBuffer cmd) { if (!dirty) return; - // Clamp the accumulated box to the atlas and convert to (origin, - // extent). Add() works in glyph coordinates that are always in-bounds, - // but clamping keeps the copy extent provably valid. The `dirty` guard - // above guarantees the rect is non-empty, so UploadBox always fills the - // outputs. - std::uint32_t x = 0, y = 0, w = 0, h = 0; - dirtyRect.UploadBox(kAtlasSize, x, y, w, h); - + // Issue one tight copy per dirty span and reset it. Clamping to the atlas + // keeps each extent provably valid even though Add() already works in + // in-bounds glyph coordinates; UploadBox skips spans that never armed. + // The spans are the whole-atlas zero-clear (dirtyRect, Initialize only) + // plus each shelf's accumulated glyph run — so scattered glyphs upload as + // several small rects, not one tall union covering the gaps between them. + auto upload = [&](DirtyRect& r) { + std::uint32_t x = 0, y = 0, w = 0, h = 0; + if (!r.UploadBox(kAtlasSize, x, y, w, h)) return; #ifndef CRAFTER_GRAPHICS_WINDOW_DOM - image.UpdateRegion(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, x, y, w, h); + image.UpdateRegion(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, x, y, w, h); #else - (void)cmd; - // The staging buffer keeps the full atlas row stride, so srcBytesPerRow - // stays kAtlasSize and (dstX, dstY) index the sub-rect's first texel. - WebGPU::wgpuWriteAtlasRegion( - textureHandle, staging.data(), - kAtlasSize, kAtlasSize, kAtlasSize, - static_cast(x), static_cast(y), - static_cast(w), static_cast(h) - ); + // The staging buffer keeps the full atlas row stride, so srcBytesPerRow + // stays kAtlasSize and (dstX, dstY) index the sub-rect's first texel. + WebGPU::wgpuWriteAtlasRegion( + textureHandle, staging.data(), + kAtlasSize, kAtlasSize, kAtlasSize, + static_cast(x), static_cast(y), + static_cast(w), static_cast(h) + ); #endif + r.Reset(); + }; + +#ifdef CRAFTER_GRAPHICS_WINDOW_DOM + (void)cmd; +#endif + upload(dirtyRect); + for (Shelf& s : shelves_) upload(s.dirty); + dirty = false; - dirtyRect.Reset(); } diff --git a/interfaces/Crafter.Graphics-FontAtlas.cppm b/interfaces/Crafter.Graphics-FontAtlas.cppm index 105f011..7f2ebef 100644 --- a/interfaces/Crafter.Graphics-FontAtlas.cppm +++ b/interfaces/Crafter.Graphics-FontAtlas.cppm @@ -102,9 +102,12 @@ export namespace Crafter { std::vector staging; #endif // `dirty` stays the cheap "is there anything to flush?" flag the - // renderer polls each frame; `dirtyRect` carries the bounds Update - // copies. The two are always set and cleared together (dirty == - // !dirtyRect.Empty()). + // renderer polls each frame; it is the OR of every dirty span Update + // copies. `dirtyRect` is the *whole-atlas* span — used only for the + // one-shot zero-clear in Initialize; per-glyph dirt is tracked tight + // per shelf (Shelf::dirty) instead of inflating one tall union box + // across scattered shelves (#129). `dirty` and the spans are always + // armed and cleared together. bool dirty = false; DirtyRect dirtyRect; @@ -131,7 +134,11 @@ export namespace Crafter { } private: - struct Shelf { int y = 0; int height = 0; int cursorX = 0; }; + // A shelf packs glyphs left-to-right at a fixed top (`y`). Its `dirty` + // span therefore stays naturally tight: a contiguous X run capped by + // the shelf height — far smaller than a union box spanning every + // shelf a frame happened to touch. + struct Shelf { int y = 0; int height = 0; int cursorX = 0; DirtyRect dirty; }; std::vector shelves_; int nextShelfY_ = 0; @@ -149,6 +156,8 @@ export namespace Crafter { }; std::unordered_map cache_; - bool ShelfPlace(int w, int h, int& outX, int& outY); + // On success, outShelf is the index into shelves_ of the placed + // glyph, so the caller can mark that shelf's dirty span. + bool ShelfPlace(int w, int h, int& outX, int& outY, int& outShelf); }; } From cb650f965d3f4099c9c68a777ef3db9e3d9cab0c Mon Sep 17 00:00:00 2001 From: catbot Date: Thu, 18 Jun 2026 13:54:09 +0000 Subject: [PATCH 38/47] perf(ui): reuse handles vector in WebGPU custom Dispatch (#132) UIRenderer::Dispatch built a fresh std::vector with reserve on every call, paying one malloc+free per dispatch. Custom compute dispatches run per-frame, so make the vector thread_local and clear() it each call: capacity grows to the high-water mark once and is reused thereafter, eliminating the per-frame allocation churn. Behavior is identical. Co-Authored-By: Claude Opus 4.8 --- implementations/Crafter.Graphics-UI-WebGPU.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/implementations/Crafter.Graphics-UI-WebGPU.cpp b/implementations/Crafter.Graphics-UI-WebGPU.cpp index e7cac71..865bb19 100644 --- a/implementations/Crafter.Graphics-UI-WebGPU.cpp +++ b/implementations/Crafter.Graphics-UI-WebGPU.cpp @@ -177,7 +177,12 @@ void UIRenderer::Dispatch(GraphicsCommandBuffer /*cmd*/, const GraphicsComputeSh // data at the recorded offset, look up the GPU handle in the heap, // and assemble a list of handles in the same order the JS bridge // expects (matching shader.customBindings). - std::vector handles; + // Reuse one vector across dispatches: custom compute runs per-frame, so a + // fresh malloc+free each call is pure churn. thread_local keeps it valid + // even if dispatches ever cross threads; its capacity grows to the high + // water mark once and is reused thereafter. + thread_local std::vector handles; + handles.clear(); handles.reserve(shader.customBindings.size()); const std::uint8_t* p = static_cast(push); for (const auto& b : shader.customBindings) { From db35e78eaf7a6da2efb5c296fa8e6d79be6c6835 Mon Sep 17 00:00:00 2001 From: catbot Date: Thu, 18 Jun 2026 13:56:21 +0000 Subject: [PATCH 39/47] perf(webgpu): range-flush only live TLAS metadata slots (#130) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The metadata mirror is padded to kNPadded (65536) but only primitiveCount slots are live. metadataBuffer.FlushDevice() wgpuWriteBuffer'd all 256 KB through the WASM->JS staging path every frame (~100-250x waste for a few-hundred-instance scene). Switch to the existing FlushDeviceRange overload — the same one the instanceBuffer loop directly above uses — sized to primitiveCount * sizeof(uint32_t). The Vulkan parallel already sizes its flush to primitiveCount, so this was WebGPU-specific. Co-Authored-By: Claude Opus 4.8 --- .../Crafter.Graphics-RenderingElement3D-WebGPU.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/implementations/Crafter.Graphics-RenderingElement3D-WebGPU.cpp b/implementations/Crafter.Graphics-RenderingElement3D-WebGPU.cpp index 326f049..2cafd67 100644 --- a/implementations/Crafter.Graphics-RenderingElement3D-WebGPU.cpp +++ b/implementations/Crafter.Graphics-RenderingElement3D-WebGPU.cpp @@ -159,7 +159,11 @@ void RenderingElement3D::BuildTLASUpload(WebGPUCommandEncoderRef /*cmd*/, std::u runOwned = currOwned; } - tlas.metadataBuffer.FlushDevice(); + // Only primitiveCount slots are live; the buffer is padded to kNPadded + // (65536). Flushing the whole thing wgpuWriteBuffers all 256 KB through + // the WASM->JS staging path every frame — ~100-250x waste for a + // few-hundred-instance scene. Range-flush just the live entries. + tlas.metadataBuffer.FlushDeviceRange(0, 0, primitiveCount * sizeof(std::uint32_t)); } void RenderingElement3D::BuildTLASBuild(WebGPUCommandEncoderRef /*cmd*/, std::uint32_t index) { From 10e07575fbb3cd9ad5758c5983f05e75532c775b Mon Sep 17 00:00:00 2001 From: catbot Date: Thu, 18 Jun 2026 13:59:26 +0000 Subject: [PATCH 40/47] perf(rt): batch the WebGPU TLAS instance upload into one writeBuffer (#131) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BuildTLASUpload pushed GPU-driven (transformOwnedByGpu) runs one element at a time — a separate FlushDeviceRange of the 16 strided metadata bytes per element — each paying WebGPU validation / encode / JS-boundary cost, while the CPU-driven arm already batched contiguously. Upload the whole active instance range in a single writeBuffer instead. Pushing the (stale) transform bytes for GPU-driven slots is harmless: the only supported way to drive a transform from the GPU is the manual Upload -> physics compute pass -> Build sequence, and that compute pass runs after this upload and rewrites the transform on the GPU before the TLAS build reads it. For the in-repo (all-CPU) usage the bytes uploaded are identical to before — with one CPU-driven run the old loop already emitted exactly this single FlushDeviceRange(0, 0, primitiveCount*64). Verified: native suite (24 passed) and the RTStress wasm example (512 ray-traced instances) render correctly through the new path. Co-Authored-By: Claude Opus 4.8 --- ...ter.Graphics-RenderingElement3D-WebGPU.cpp | 71 +++++++++---------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/implementations/Crafter.Graphics-RenderingElement3D-WebGPU.cpp b/implementations/Crafter.Graphics-RenderingElement3D-WebGPU.cpp index 326f049..961253a 100644 --- a/implementations/Crafter.Graphics-RenderingElement3D-WebGPU.cpp +++ b/implementations/Crafter.Graphics-RenderingElement3D-WebGPU.cpp @@ -108,9 +108,16 @@ void RenderingElement3D::BuildTLASUpload(WebGPUCommandEncoderRef /*cmd*/, std::u auto& dst = tlas.instanceBuffer.value[i]; const auto& src = elements[i]->instance; if (elements[i]->transformOwnedByGpu) { - // Preserve whatever the GPU compute shader most recently - // wrote into dst.transform. Update only the non-transform - // fields. + // GPU owns the transform: copy only the host-authored + // metadata fields and leave the host mirror's transform bytes + // alone (the application doesn't maintain them). The upload + // below pushes the whole struct, including these transform + // bytes, but the physics-tlas-transform compute pass — which + // runs after this Upload and before Build in the supported + // Upload -> compute -> Build flow — overwrites the transform + // on the GPU before the TLAS build reads it, so the host-side + // value is moot. (Nothing reads instanceBuffer between Upload + // and that compute pass.) dst.instanceCustomIndex = src.instanceCustomIndex; dst.mask = src.mask; dst.instanceShaderBindingTableRecordOffset = src.instanceShaderBindingTableRecordOffset; @@ -122,42 +129,28 @@ void RenderingElement3D::BuildTLASUpload(WebGPUCommandEncoderRef /*cmd*/, std::u tlas.metadataBuffer.value[i] = elements[i]->userMetadata; } - // Upload the instance buffer with partial-write semantics: for runs - // of CPU-driven elements (transformOwnedByGpu=false) we push the - // whole 64-byte struct in one writeBuffer call; for GPU-driven runs - // we push only the trailing 16 metadata bytes per element, leaving - // the transform field intact for the physics-tlas-transform compute - // shader to update. The two arms below produce identical GPU state - // when every element is CPU-driven — this is a no-op refactor until - // 3DForts flips its physics elements to transformOwnedByGpu=true. - constexpr std::uint32_t kInstSize = sizeof(RTInstance); // 64 - constexpr std::uint32_t kTransformSize = sizeof(RTTransformMatrix); // 48 - constexpr std::uint32_t kMetaSize = kInstSize - kTransformSize; // 16 - - std::uint32_t runStart = 0; - bool runOwned = elements[0]->transformOwnedByGpu; - for (std::uint32_t i = 1; i <= primitiveCount; ++i) { - const bool atEnd = (i == primitiveCount); - const bool currOwned = atEnd ? !runOwned : elements[i]->transformOwnedByGpu; - if (currOwned == runOwned && !atEnd) continue; - - if (runOwned) { - // GPU-driven run — metadata only, per element. Cannot batch - // because the metadata bytes are non-contiguous in the - // instance buffer (one 16-byte chunk per 64-byte slot). - for (std::uint32_t j = runStart; j < i; ++j) { - const std::uint32_t off = j * kInstSize + kTransformSize; - tlas.instanceBuffer.FlushDeviceRange(off, off, kMetaSize); - } - } else { - // CPU-driven run — one contiguous writeBuffer. - const std::uint32_t startOff = runStart * kInstSize; - const std::uint32_t bytes = (i - runStart) * kInstSize; - tlas.instanceBuffer.FlushDeviceRange(startOff, startOff, bytes); - } - runStart = i; - runOwned = currOwned; - } + // Upload the active instance range in a single contiguous writeBuffer. + // + // An earlier version split this into per-run arms: contiguous runs of + // CPU-driven elements (transformOwnedByGpu=false) were pushed as one + // block, but GPU-driven runs pushed only the trailing 16 metadata + // bytes of each element — one FlushDeviceRange per element — to avoid + // clobbering the GPU-written transform. The metadata bytes are strided + // (one 16-byte chunk per 64-byte slot), so a GPU-driven run couldn't + // batch; each element paid a separate WebGPU validation / encode / + // JS-boundary cost. + // + // Pushing the whole struct for GPU-driven elements too is harmless: + // the only supported way to drive a transform from the GPU is the + // manual Upload -> physics compute pass -> Build sequence (see the + // file header), and that compute pass runs after this upload and + // rewrites the transform on the GPU before the TLAS build reads it. + // So the stale transform bytes this push carries for those slots are + // overwritten before they matter, and the whole run uploads in one + // call. (The combined BuildTLAS path runs no compute pass and is, as + // documented, only valid for CPU-driven transforms.) + constexpr std::uint32_t kInstSize = sizeof(RTInstance); // 64 + tlas.instanceBuffer.FlushDeviceRange(0, 0, primitiveCount * kInstSize); tlas.metadataBuffer.FlushDevice(); } From 931500ddc38d9a5d0e040cfba201d6e80f58c155 Mon Sep 17 00:00:00 2001 From: catbot Date: Thu, 18 Jun 2026 14:00:36 +0000 Subject: [PATCH 41/47] perf(webgpu): byteCount-bounded readback for over-provisioned buffers (#133) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WebGPUBuffer::EnqueueReadback / PollReadback always copied the full buffer `size` GPU→staging→wasm. Over-provisioned event-queue buffers (e.g. Forts3D's GPU physics event drain) paid the full-capacity copy every drain even when only a small live prefix held data. Add an optional `byteCount` parameter (default 0 = whole buffer, so the existing full-buffer callers are unchanged) bounding the readback to the live prefix. Pass the same byteCount to the paired PollReadback so the matching number of bytes lands in `.value`. JS bridge: the staging buffer is now sized to the full device-buffer capacity (not the first call's byteSize) so a varying prefix length never overflows it, while the copyBufferToBuffer + mapAsync/getMappedRange are bounded to the requested prefix — that's where the copy saving lands. Verified end-to-end in the browser via RayQueryPick: the full readback still resolves correctly, and a follow-up 8-byte prefix read copies only hit+customIndex while primitiveIndex keeps its poison sentinel, proving the copy was bounded. Native `crafter-build test` suite: 24 passed. Co-Authored-By: Claude Opus 4.8 --- additional/dom-webgpu.js | 27 ++++++++++++----- examples/RayQueryPick/main.cpp | 25 +++++++++++++++- interfaces/Crafter.Graphics-WebGPUBuffer.cppm | 29 ++++++++++++++----- 3 files changed, 64 insertions(+), 17 deletions(-) diff --git a/additional/dom-webgpu.js b/additional/dom-webgpu.js index 72ed20e..77f5211 100644 --- a/additional/dom-webgpu.js +++ b/additional/dom-webgpu.js @@ -613,7 +613,7 @@ env.wgpuWriteBufferRange = (handle, dstByteOffset, srcPtr, byteSize) => { const READBACK_IDLE = 0; const READBACK_PENDING = 1; const READBACK_READY = 2; -const readbacks = new Map(); // device-buffer handle → { staging, size, state, pendingData } +const readbacks = new Map(); // device-buffer handle → { staging, capacity, readBytes, state, pendingData } // Readbacks scheduled this frame that still need their mapAsync kicked // off — done after the frame's queue.submit so the map waits for the // compute writes that wrote to `buf` to finish, not just the standalone @@ -623,21 +623,30 @@ const pendingReadbackMaps = []; env.wgpuReadbackEnqueue = (handle, byteSize, resetBytes) => { const buf = buffers.get(handle); if (!buf) return; - const aligned = (byteSize + 3) & ~3; const resetAligned = resetBytes > 0 ? ((resetBytes + 3) & ~3) : 0; let rb = readbacks.get(handle); if (!rb) { + // Size the staging buffer to the FULL device-buffer capacity, not + // this first call's byteSize: a prefix readback (byteCount < size) + // is allowed to vary call-to-call, so the staging must cover any + // later full-size drain without re-allocating. + const capacity = Math.max(16, (buf.size + 3) & ~3); rb = { staging: device.createBuffer({ - size: Math.max(16, aligned), + size: capacity, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST, }), - size: aligned, + capacity, + readBytes: 0, // bytes copied/mapped by the in-flight enqueue state: READBACK_IDLE, pendingData: null, }; readbacks.set(handle, rb); } + // Copy/map only the requested prefix — that's the whole point of the + // byteCount path: skip the over-provisioned tail GPU→staging→wasm. + const aligned = Math.min((byteSize + 3) & ~3, rb.capacity); + rb.readBytes = aligned; if (rb.state !== READBACK_IDLE) { // Previous map still in flight (or has data nobody polled yet); // skip this enqueue AND the paired reset. Events written by the @@ -681,8 +690,9 @@ env.wgpuReadbackEnqueue = (handle, byteSize, resetBytes) => { if (resetAligned > 0) enc.clearBuffer(buf, 0, resetAligned); queue.submit([enc.finish()]); rb.state = READBACK_PENDING; - rb.staging.mapAsync(GPUMapMode.READ).then(() => { - rb.pendingData = new Uint8Array(rb.staging.getMappedRange()).slice(); + const mapBytes = rb.readBytes; + rb.staging.mapAsync(GPUMapMode.READ, 0, mapBytes).then(() => { + rb.pendingData = new Uint8Array(rb.staging.getMappedRange(0, mapBytes)).slice(); rb.staging.unmap(); rb.state = READBACK_READY; }).catch(e => { @@ -1010,8 +1020,9 @@ env.wgpuFrameEnd = () => { // writes (not pre-substep state). while (pendingReadbackMaps.length > 0) { const rb = pendingReadbackMaps.pop(); - rb.staging.mapAsync(GPUMapMode.READ).then(() => { - rb.pendingData = new Uint8Array(rb.staging.getMappedRange()).slice(); + const mapBytes = rb.readBytes; + rb.staging.mapAsync(GPUMapMode.READ, 0, mapBytes).then(() => { + rb.pendingData = new Uint8Array(rb.staging.getMappedRange(0, mapBytes)).slice(); rb.staging.unmap(); rb.state = READBACK_READY; }).catch(e => { diff --git a/examples/RayQueryPick/main.cpp b/examples/RayQueryPick/main.cpp index 4cb0606..91001d5 100644 --- a/examples/RayQueryPick/main.cpp +++ b/examples/RayQueryPick/main.cpp @@ -189,8 +189,17 @@ int main() { push.origin[2] = origin0 + float(kHitZ) * kSpacing; push.dir[0] = -1.0f; push.dir[1] = 0.0f; push.dir[2] = 0.0f; + // Drive byteCount-bounded prefix readback (issue #133) once the full read + // has verified the hit: re-read ONLY the first 8 bytes (hit + + // instanceCustomIndex) with byteCount=8 after poisoning the host mirror, + // and confirm the two prefix fields land while primitiveIndex (bytes 8..11) + // stays poisoned — i.e. the copy was actually bounded, not full-capacity. + constexpr std::uint32_t kPoison = 0xEEEEEEEEu; + constexpr std::uint32_t kPrefixLen = 2 * sizeof(std::uint32_t); // hit + customIndex + static int frame = 0; static bool dispatched = false; + static bool prefixSent = false; static bool reported = false; EventListener tick(&window.onBeforeUpdate, [&]() { if (reported) return; @@ -199,7 +208,7 @@ int main() { pickShader.Dispatch(&push, sizeof(push), pickHandles, 1, 1, 1); pickBuf.EnqueueReadback(); dispatched = true; - } else if (dispatched && pickBuf.PollReadback()) { + } else if (dispatched && !prefixSent && pickBuf.PollReadback()) { const PickResult& r = pickBuf.value[0]; const bool ok = (r.hit == 1u) && (r.instanceCustomIndex == kExpectedCustomIndex); std::println("[RayQueryPick] result: hit={} customIndex={} prim={} t={}", @@ -210,6 +219,20 @@ int main() { std::println("[RayQueryPick] FAIL — expected hit=1 customIndex={}, got hit={} customIndex={}", kExpectedCustomIndex, r.hit, r.instanceCustomIndex); } + // Now kick off the bounded prefix re-read. Poison the whole mirror + // first; the prefix poll must overwrite only bytes [0,8). + pickBuf.value[0] = { kPoison, kPoison, kPoison, 0.0f }; + pickBuf.EnqueueReadback(/*resetBytes*/ 0, /*byteCount*/ kPrefixLen); + prefixSent = true; + } else if (prefixSent && pickBuf.PollReadback(/*byteCount*/ kPrefixLen)) { + const PickResult& r = pickBuf.value[0]; + const bool prefixOk = (r.hit == 1u) + && (r.instanceCustomIndex == kExpectedCustomIndex) + && (r.primitiveIndex == kPoison); // tail untouched + std::println("[RayQueryPick] prefix readback (byteCount={}): hit={} customIndex={} prim=0x{:08X}", + kPrefixLen, r.hit, r.instanceCustomIndex, r.primitiveIndex); + std::println("[RayQueryPick] {} — byteCount-bounded readback copied only the live prefix", + prefixOk ? "PASS" : "FAIL"); // The render loop runs after main's _Exit, where stdio is never // flushed implicitly — push the verdict out explicitly. std::fflush(stdout); diff --git a/interfaces/Crafter.Graphics-WebGPUBuffer.cppm b/interfaces/Crafter.Graphics-WebGPUBuffer.cppm index 4dec146..2f43b13 100644 --- a/interfaces/Crafter.Graphics-WebGPUBuffer.cppm +++ b/interfaces/Crafter.Graphics-WebGPUBuffer.cppm @@ -102,10 +102,18 @@ export namespace Crafter { FlushDeviceRange(off, off, kStride); } - // Schedule a GPU→CPU readback of this buffer's entire contents. - // Asynchronous; data isn't ready until a later PollReadback - // returns true. Successive Enqueues without a Poll are dropped - // — they're a no-op while the previous map is in flight. + // Schedule a GPU→CPU readback of this buffer. Asynchronous; data + // isn't ready until a later PollReadback returns true. Successive + // Enqueues without a Poll are dropped — they're a no-op while the + // previous map is in flight. + // + // `byteCount` (0 = the whole buffer) bounds the readback to the + // live prefix: an over-provisioned event-queue buffer need only + // copy its header / used span GPU→staging→wasm, not the full + // capacity, every drain. Clamped to `size`. The staging buffer is + // sized to the full capacity, so the prefix length may vary from + // one Enqueue to the next; pass the SAME byteCount to the matching + // PollReadback so the right number of bytes lands in `.value`. // // `resetBytes` ≥ 0 — if non-zero, the first `resetBytes` bytes // of THIS buffer are clearBuffer-cleared on the GPU command @@ -114,17 +122,22 @@ export namespace Crafter { // The reset is tied to a successful enqueue (skipped enqueue = // skipped reset), preserving accumulated state across missed // drains. - void EnqueueReadback(std::uint32_t resetBytes = 0) { + void EnqueueReadback(std::uint32_t resetBytes = 0, std::uint32_t byteCount = 0) { + const std::uint32_t n = (byteCount == 0 || byteCount > size) ? size : byteCount; WebGPU::wgpuReadbackEnqueue(handle, - static_cast(size), + static_cast(n), static_cast(resetBytes)); } // Try to copy the readback bytes into this->value. Returns true // if the previous EnqueueReadback resolved and the data is now // mirrored into .value; false if the map is still pending. - bool PollReadback() requires(Mapped) { + // `byteCount` must match the value passed to the paired + // EnqueueReadback (0 = whole buffer); it bounds the staging→wasm + // copy to the live prefix that was captured. + bool PollReadback(std::uint32_t byteCount = 0) requires(Mapped) { + const std::uint32_t n = (byteCount == 0 || byteCount > size) ? size : byteCount; return WebGPU::wgpuReadbackPoll(handle, this->value, - static_cast(size)) != 0; + static_cast(n)) != 0; } // Non-consuming readiness probe. Returns true if a subsequent // PollReadback would succeed without changing state otherwise. From 7df82e4fee4204513615864b1a0c337959427589 Mon Sep 17 00:00:00 2001 From: catbot Date: Thu, 18 Jun 2026 14:00:58 +0000 Subject: [PATCH 42/47] perf(window): block message loop while paused on Win32 (#134) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When `updating` is false (paused / minimized / stopped), the FIFO present no longer paces StartSync()'s message loop, so the bare PeekMessage(PM_REMOVE) spin pinned a core at 100% while uselessly re-running Gamepad::Tick() (mutex + per-pad COM reads) and onBeforeUpdate every iteration. Block on MsgWaitForMultipleObjectsEx with a short timeout while !updating: the loop now wakes on input or every ~16ms, eliminating the busy-spin while preserving gamepad polling / onBeforeUpdate at a sane rate (e.g. to detect a controller button asking to resume). The updating path is unchanged — present remains vsync-gated. Co-Authored-By: Claude Opus 4.8 --- implementations/Crafter.Graphics-Window.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/implementations/Crafter.Graphics-Window.cpp b/implementations/Crafter.Graphics-Window.cpp index 19f1f55..3891db7 100644 --- a/implementations/Crafter.Graphics-Window.cpp +++ b/implementations/Crafter.Graphics-Window.cpp @@ -698,7 +698,20 @@ void Window::StartSync() { } #endif #ifdef CRAFTER_GRAPHICS_WINDOW_WIN32 + // While updating, the FIFO present inside Update() paces this loop to the + // vblank, so the PeekMessage poll is vsync-gated. While NOT updating + // (paused / minimized / stopped) nothing throttles the loop, and a bare + // PeekMessage(PM_REMOVE) spin pins a core at 100% while uselessly re-running + // Gamepad::Tick() (mutex + per-pad COM reads) and onBeforeUpdate every + // iteration. Block on the message queue instead, waking on input or after a + // short timeout so gamepad polling / onBeforeUpdate still tick at a sane + // rate (e.g. to detect a controller button asking to resume). + constexpr DWORD kPausedPollIntervalMs = 16; while(open) { + if (!updating) { + MsgWaitForMultipleObjectsEx(0, nullptr, kPausedPollIntervalMs, + QS_ALLINPUT, MWMO_INPUTAVAILABLE); + } MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); From 7316e51dca723e2e5571fefcb9ac85dcd06d59fa Mon Sep 17 00:00:00 2001 From: catbot Date: Thu, 18 Jun 2026 18:04:25 +0000 Subject: [PATCH 43/47] fix(window): silence per-frame and setup-path Vulkan validation errors (#153) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two distinct validation errors the native frame loop emitted, both originating in Crafter.Graphics with no consumer-side influence. Problem 1 — per-frame acquire-barrier access/stage mismatch. The acquire->GENERAL barrier hardcoded dstAccessMask = SHADER_WRITE|TRANSFER_WRITE but used the per-pass stage union as its dst stage mask. For an all-compute frame the union narrows to COMPUTE_SHADER, which does not support TRANSFER_WRITE, so VUID-02820 fired every frame. Derive the access mask from the same stage union via a new SwapchainWriterAccess() helper (mirroring SwapchainStageUnion), and apply it to both the acquire dst and present src masks for symmetry. Problem 2 — mid-session StartInit/FinishInit (and GetCmd/EndCmd) reuse the shared drawCmdBuffers[currentBuffer]. With no steady-state wait-idle the loop's last submission of that buffer may still be in flight when scene setup runs (building map meshes / acceleration structures), so the old code re-began (VUID-00049) and re-submitted (VUID-00071) a pending buffer, and resources freed in the StartInit..FinishInit bracket could still be referenced by it. Drain the queue at the start of StartInit/GetCmd before re-recording; setup is rare, so a wait-idle is fine (FinishInit/EndCmd already wait-idle at the end). Tests: extend SwapchainBarrierScope with SwapchainWriterAccess coverage (pure CPU), and add SetupCmdBufferReuse — a real-frame-loop regression test driving a compute pass plus interleaved mid-session StartInit rounds, asserting the validation layer stays silent. Verified both halves fail (reproducing the exact VUIDs) when their respective fix is reverted. Co-Authored-By: Claude Opus 4.8 --- implementations/Crafter.Graphics-Window.cpp | 44 ++++++- interfaces/Crafter.Graphics-RenderPass.cppm | 20 +++ project.cpp | 35 ++++++ tests/SetupCmdBufferReuse/main.cpp | 131 ++++++++++++++++++++ tests/SwapchainBarrierScope/main.cpp | 39 ++++++ 5 files changed, 265 insertions(+), 4 deletions(-) create mode 100644 tests/SetupCmdBufferReuse/main.cpp diff --git a/implementations/Crafter.Graphics-Window.cpp b/implementations/Crafter.Graphics-Window.cpp index 3891db7..bee7f82 100644 --- a/implementations/Crafter.Graphics-Window.cpp +++ b/implementations/Crafter.Graphics-Window.cpp @@ -482,12 +482,14 @@ Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height .layerCount = VK_REMAINING_ARRAY_LAYERS, }; - // Transition into VK_IMAGE_LAYOUT_GENERAL before recording passes. image - // and oldLayout are patched per frame (oldLayout depends on first use). + // Transition into VK_IMAGE_LAYOUT_GENERAL before recording passes. image, + // oldLayout (depends on first use) and dstAccessMask (derived from the + // per-pass stage union) are patched per frame in Render(); the dstAccessMask + // here is just a well-formed default. acquireBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -496,7 +498,9 @@ Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height .subresourceRange = range, }; - // Transition back to PRESENT_SRC_KHR after the passes. Only image varies. + // Transition back to PRESENT_SRC_KHR after the passes. image and + // srcAccessMask (derived from the per-pass stage union) are patched per + // frame in Render(); the srcAccessMask here is just a well-formed default. presentBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, @@ -835,6 +839,13 @@ void Window::Render() { // barrier below (passes don't change within a Render()). const VkPipelineStageFlags swapWriterStages = SwapchainStageUnion(passes); + // dstAccessMask must only contain access flags supported by the dst stage + // mask (VUID-vkCmdPipelineBarrier-pImageMemoryBarriers-02820). Derive it + // from the same per-pass stage union the barrier uses as its dst stage mask + // instead of hardcoding SHADER_WRITE|TRANSFER_WRITE, which would carry + // TRANSFER_WRITE into a compute-only frame's COMPUTE_SHADER dst stage. + acquireBarrier.dstAccessMask = SwapchainWriterAccess(swapWriterStages); + // The acquire->GENERAL transition only needs to be visible before the first // pass writes the image, i.e. at the writer stages — not ALL_COMMANDS. vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, swapWriterStages, 0, 0, nullptr, 0, nullptr, 1, &acquireBarrier); @@ -919,6 +930,12 @@ void Window::Render() { presentBarrier.image = images[currentBuffer]; + // Mirror of the acquire barrier: srcAccessMask must be supported by the src + // stage mask (swapWriterStages). SHADER_WRITE alone is valid for COMPUTE/RT + // and so never fires today, but a transfer-stage writer would need + // TRANSFER_WRITE here too — derive both from the same union for symmetry. + presentBarrier.srcAccessMask = SwapchainWriterAccess(swapWriterStages); + // The ->PRESENT_SRC transition only needs to wait on the stages that // actually wrote the image (the per-pass union), not ALL_COMMANDS; present // itself is gated by the render-finished semaphore, so dst stays BOTTOM. @@ -1107,6 +1124,20 @@ void Window::CreateSwapchain() VkCommandBuffer Window::StartInit() { VkCommandBufferBeginInfo cmdBufInfo {}; cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + // StartInit reuses drawCmdBuffers[currentBuffer] — the same buffer the + // steady-state Render() loop submits, guarded by waitFences[currentBuffer]. + // When setup runs mid-session (e.g. building map meshes / acceleration + // structures on a scene transition) the loop's last submission of this + // buffer may still be in flight: re-beginning it would trip + // VUID-vkBeginCommandBuffer-commandBuffer-00049, re-submitting it + // VUID-vkQueueSubmit-pCommandBuffers-00071, and any resource the caller + // destroys between here and FinishInit could still be referenced by that + // pending submission (vkDestroyBuffer-in-use). Drain the queue first so the + // buffer is free to re-record and no in-flight frame still references the + // resources this setup is about to replace (issue #153). Setup is rare + // (not the per-frame path), so a full wait-idle here is fine — FinishInit + // already wait-idles at the end. + Device::CheckVkResult(vkQueueWaitIdle(Device::queue)); Device::CheckVkResult(vkBeginCommandBuffer(drawCmdBuffers[currentBuffer], &cmdBufInfo)); // The swapchain images are deliberately NOT transitioned here. They are @@ -1132,6 +1163,11 @@ void Window::FinishInit() { VkCommandBuffer Window::GetCmd() { VkCommandBufferBeginInfo cmdBufInfo {}; cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + // Same lifecycle hazard as StartInit: GetCmd re-begins and EndCmd + // re-submits the shared drawCmdBuffers[currentBuffer], so drain any + // in-flight submission of it (and free the resources it references) before + // re-recording (issue #153). + Device::CheckVkResult(vkQueueWaitIdle(Device::queue)); Device::CheckVkResult(vkBeginCommandBuffer(drawCmdBuffers[currentBuffer], &cmdBufInfo)); VkImageSubresourceRange range{}; diff --git a/interfaces/Crafter.Graphics-RenderPass.cppm b/interfaces/Crafter.Graphics-RenderPass.cppm index c354336..3cae95d 100644 --- a/interfaces/Crafter.Graphics-RenderPass.cppm +++ b/interfaces/Crafter.Graphics-RenderPass.cppm @@ -69,6 +69,26 @@ export namespace Crafter { return stages; } + // The access mask matching a swapchain-writer stage union, for the + // frame-edge barriers (acquire dst / present src). A barrier's access mask + // must only contain access flags supported by its accompanying stage mask + // (VUID-vkCmdPipelineBarrier-pImageMemoryBarriers-02820): TRANSFER_WRITE is + // not a valid access for COMPUTE/RAY_TRACING stages, so hardcoding both + // SHADER_WRITE and TRANSFER_WRITE fires the VUID every frame on an + // all-compute frame. The swapchain image is written as a storage image by + // compute/RT passes (SHADER_WRITE) and only via TRANSFER_WRITE when a + // transfer-stage pass is present, so derive the access bits from the same + // per-pass stage union the barrier's stage mask uses instead of hardcoding. + inline VkAccessFlags SwapchainWriterAccess(VkPipelineStageFlags stages) { + VkAccessFlags access = 0; + if (stages & (VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT + | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR)) + access |= VK_ACCESS_SHADER_WRITE_BIT; + if (stages & VK_PIPELINE_STAGE_TRANSFER_BIT) + access |= VK_ACCESS_TRANSFER_WRITE_BIT; + return access; + } + // The inter-pass swapchain barrier (replacing the old queue-wide // VkMemoryBarrier at the #115 location). Scoped to the swapchain image's // single colour subresource so only that image's shader caches round-trip — diff --git a/project.cpp b/project.cpp index 82fc66b..97fb4de 100644 --- a/project.cpp +++ b/project.cpp @@ -296,6 +296,41 @@ extern "C" Configuration CrafterBuildProject(std::span a frameLoopImpls.emplace_back("tests/FrameLoopSync/main"); fl.GetInterfacesAndImplementations(ifaces, frameLoopImpls); cfg.tests.push_back(std::move(frameLoopTest)); + + // Issue #153: two frame-loop validation errors. (1) The acquire + // barrier hardcoded dstAccessMask = SHADER_WRITE|TRANSFER_WRITE but + // used the per-pass stage union (COMPUTE_SHADER for an all-compute + // frame) as its dst stage, so TRANSFER_WRITE was unsupported and + // VUID-02820 fired every frame — FrameLoopSync can't see it because + // it runs with no passes (the union falls back to the conservative + // writer set, which includes TRANSFER). (2) Mid-session + // StartInit/FinishInit reuse the shared draw command buffer while + // the loop's last submission of it is still in flight, re-beginning + // (00049) and re-submitting (00071) a pending buffer. Drives a real + // compute pass through the loop plus interleaved StartInit rounds + // and asserts the layer stays silent. Needs the Wayland backend + a + // real compositor, so it lives in the !windows block. + Test setupReuseTest; + Configuration& sr = setupReuseTest.config; + sr.path = cfg.path; + sr.name = "SetupCmdBufferReuse"; + sr.outputName = "SetupCmdBufferReuse"; + sr.type = ConfigurationType::Executable; + sr.target = cfg.target; + sr.march = cfg.march; + sr.mtune = cfg.mtune; + sr.debug = cfg.debug; + sr.sysroot = cfg.sysroot; + sr.dependencies = cfg.dependencies; + sr.externalDependencies = cfg.externalDependencies; + sr.compileFlags = cfg.compileFlags; + sr.linkFlags = cfg.linkFlags; + sr.defines = cfg.defines; + sr.cFiles = cfg.cFiles; + std::vector setupReuseImpls(impls.begin(), impls.end()); + setupReuseImpls.emplace_back("tests/SetupCmdBufferReuse/main"); + sr.GetInterfacesAndImplementations(ifaces, setupReuseImpls); + cfg.tests.push_back(std::move(setupReuseTest)); } // Issue #36: BLAS build options. Drives the real hardware AS-build diff --git a/tests/SetupCmdBufferReuse/main.cpp b/tests/SetupCmdBufferReuse/main.cpp new file mode 100644 index 0000000..6e634a7 --- /dev/null +++ b/tests/SetupCmdBufferReuse/main.cpp @@ -0,0 +1,131 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Issue #153: two distinct validation errors the frame loop emitted, both +// reproduced here by driving the *real* loop against a live compositor and +// asserting the Vulkan validation layer stays silent — same harness idea as +// FrameLoopSync (the silent layer is the load-bearing check). +// +// Problem 1 — per-frame acquire-barrier access/stage mismatch. The +// acquire->GENERAL barrier hardcoded dstAccessMask = +// SHADER_WRITE | TRANSFER_WRITE but used the per-pass stage union as its dst +// stage mask. For an all-compute frame the union narrows to COMPUTE_SHADER, +// which does NOT support TRANSFER_WRITE, so VUID-02820 fired every frame. +// FrameLoopSync runs with NO passes, where the union falls back to the +// conservative writer union (which DOES include TRANSFER) and the VUID never +// fires — so it cannot catch this. The key here is a real compute pass that +// narrows SwapchainStage() to COMPUTE_SHADER, exactly like UIRenderer. +// +// Problem 2 — mid-session StartInit/FinishInit reuse the shared +// drawCmdBuffers[currentBuffer]. With no steady-state wait-idle the loop's +// last submission of that buffer is still in flight when setup runs, so the +// old code re-began it (VUID-vkBeginCommandBuffer-commandBuffer-00049) and +// re-submitted it (VUID-vkQueueSubmit-pCommandBuffers-00071) while pending, +// and any resource freed in the StartInit..FinishInit bracket could still be +// referenced by that submission. StartInit/GetCmd now drain the queue first. +// +// Needs a live Wayland compositor + a Vulkan device at runtime (same as the +// windowed examples / FrameLoopSync), so it shares the native build settings +// and is Linux-only. + +#include "vulkan/vulkan.h" +#include + +import Crafter.Graphics; +import std; + +using namespace Crafter; + +namespace { + +int failures = 0; + +void Check(bool ok, std::string_view what) { + std::println("{} {}", ok ? "PASS" : "FAIL", what); + if (!ok) ++failures; +} + +// A minimal compute pass: records nothing, but narrows its swapchain write +// stage to COMPUTE_SHADER like every real menu/game pass. That narrowing is +// what shrinks the frame's stage union to COMPUTE_SHADER and so exposes the +// problem-1 acquire-barrier access/stage mismatch. Record() needs no work — +// the bug is in the frame-edge barriers the loop records around the passes, +// not in the pass body. +struct ComputePass : RenderPass { + void Record(GraphicsCommandBuffer, std::uint32_t, Window&) override {} + VkPipelineStageFlags SwapchainStage() const override { + return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + } +}; + +} // namespace + +int main() { + Device::Initialize(); + + Window window(640, 480, "SetupCmdBufferReuse test"); + + ComputePass pass; + window.passes.push_back(&pass); + + // ── Problem 1: all-compute frames ─────────────────────────────────────── + // Run enough frames that the acquire barrier is recorded many times with + // swapWriterStages == COMPUTE_SHADER. Pre-fix this logged VUID-02820 once + // per frame. + constexpr int kWarmupFrames = 8; + for (int i = 0; i < kWarmupFrames; ++i) window.Render(); + + Check(Device::validationErrorCount == 0, + std::format("no validation errors over {} all-compute frames " + "(acquire-barrier access/stage match) ({} seen)", + kWarmupFrames, Device::validationErrorCount)); + + // ── Problem 2: mid-session setup while a frame is in flight ────────────── + // After Render() returns there is no steady-state wait-idle, so the last + // submission of drawCmdBuffers[currentBuffer] is still pending. Calling + // StartInit()/FinishInit() now (as a scene transition would, to build map + // meshes / acceleration structures) reuses that exact command buffer. The + // setup buffer is left empty on purpose: it's the begin/submit lifecycle + // around the in-flight buffer that the old code got wrong, not the + // contents. Interleave more frames so each StartInit again races a fresh + // in-flight submission. + constexpr int kSetupRounds = 3; + for (int r = 0; r < kSetupRounds; ++r) { + VkCommandBuffer cmd = window.StartInit(); + Check(cmd != VK_NULL_HANDLE, "StartInit returns a usable command buffer"); + window.FinishInit(); + for (int f = 0; f < 4; ++f) window.Render(); + } + + Check(Device::validationErrorCount == 0, + std::format("no validation errors across {} mid-session StartInit/" + "FinishInit rounds interleaved with rendering ({} seen)", + kSetupRounds, Device::validationErrorCount)); + + // Drain before teardown — no steady-state wait-idle means GPU work may + // still be in flight when the loop exits. + Device::CheckVkResult(vkQueueWaitIdle(Device::queue)); + + if (failures != 0) { + std::println("{} check(s) failed", failures); + return EXIT_FAILURE; + } + std::println("all checks passed"); + return EXIT_SUCCESS; +} diff --git a/tests/SwapchainBarrierScope/main.cpp b/tests/SwapchainBarrierScope/main.cpp index eb5e4e3..5c3357e 100644 --- a/tests/SwapchainBarrierScope/main.cpp +++ b/tests/SwapchainBarrierScope/main.cpp @@ -165,6 +165,45 @@ int main() { "inter-pass barrier is scoped to the single swapchain subresource"); } + // ─── SwapchainWriterAccess (issue #153) ───────────────────────────────── + // The frame-edge (acquire dst / present src) barriers must set an access + // mask supported by their accompanying stage mask + // (VUID-vkCmdPipelineBarrier-pImageMemoryBarriers-02820). The old code + // hardcoded SHADER_WRITE|TRANSFER_WRITE on a barrier whose stage mask is the + // per-pass union, so an all-compute frame carried TRANSFER_WRITE into a + // COMPUTE_SHADER dst stage and fired the VUID every frame. The fix derives + // the access from the same stage union, so each stage only pulls in the + // access flags it actually supports. + { + // All-compute frame: COMPUTE_SHADER supports SHADER_WRITE only — and + // crucially NOT TRANSFER_WRITE (the bit that fired the VUID). + VkAccessFlags compute = SwapchainWriterAccess(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + Check(compute == VK_ACCESS_SHADER_WRITE_BIT, + "compute-only writer access is SHADER_WRITE only"); + Check((compute & VK_ACCESS_TRANSFER_WRITE_BIT) == 0, + "compute-only writer access does NOT include TRANSFER_WRITE (the VUID-02820 trap)"); + + // RT frame: RAY_TRACING_SHADER also writes via SHADER_WRITE, no transfer. + Check(SwapchainWriterAccess(VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR) == VK_ACCESS_SHADER_WRITE_BIT, + "RT-only writer access is SHADER_WRITE only"); + + // A transfer-stage writer pulls in TRANSFER_WRITE. + Check(SwapchainWriterAccess(VK_PIPELINE_STAGE_TRANSFER_BIT) == VK_ACCESS_TRANSFER_WRITE_BIT, + "transfer-only writer access is TRANSFER_WRITE only"); + + // The conservative writer union (un-overridden / empty frame) folds in + // both — and every bit it sets is supported by some stage in the union. + Check(SwapchainWriterAccess(kSwapchainWriterStages) + == (VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT), + "conservative writer union access is SHADER_WRITE | TRANSFER_WRITE"); + + // Mixed compute+RT (no transfer): still SHADER_WRITE only, no transfer. + VkAccessFlags mixed = SwapchainWriterAccess( + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR); + Check(mixed == VK_ACCESS_SHADER_WRITE_BIT, + "compute+RT writer access is SHADER_WRITE only (no spurious TRANSFER_WRITE)"); + } + if (failures != 0) { std::println("{} check(s) failed", failures); return EXIT_FAILURE; From 3e116e6e43e0d9296bb96e3783eabbeacfb3e497 Mon Sep 17 00:00:00 2001 From: catbot Date: Thu, 18 Jun 2026 19:35:54 +0000 Subject: [PATCH 44/47] feat(window): env-gated uncapped present mode for benchmarking CRAFTER_PRESENT_IMMEDIATE selects IMMEDIATE (then MAILBOX) when the surface offers it, instead of the default FIFO. Needed to measure steady-state frame throughput without the compositor's vblank cap; FIFO remains the default when the variable is unset. Co-Authored-By: Claude Opus 4.8 --- implementations/Crafter.Graphics-Window.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/implementations/Crafter.Graphics-Window.cpp b/implementations/Crafter.Graphics-Window.cpp index bee7f82..3af8c7b 100644 --- a/implementations/Crafter.Graphics-Window.cpp +++ b/implementations/Crafter.Graphics-Window.cpp @@ -1072,8 +1072,24 @@ void Window::CreateSwapchain() swapchainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swapchainCI.queueFamilyIndexCount = 0; // VK_PRESENT_MODE_FIFO_KHR ("v-sync") is guaranteed available per spec, so - // it is used unconditionally — no present-mode enumeration is needed. + // it is the default. For benchmarking we want frame pacing uncapped from + // the display refresh, so when CRAFTER_PRESENT_IMMEDIATE is set in the + // environment we enumerate the surface's present modes and prefer + // IMMEDIATE (then MAILBOX) when offered, falling back to FIFO otherwise. swapchainCI.presentMode = VK_PRESENT_MODE_FIFO_KHR; + if (std::getenv("CRAFTER_PRESENT_IMMEDIATE")) { + std::uint32_t presentModeCount = 0; + vkGetPhysicalDeviceSurfacePresentModesKHR(Device::physDevice, vulkanSurface, &presentModeCount, nullptr); + std::vector presentModes(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(Device::physDevice, vulkanSurface, &presentModeCount, presentModes.data()); + bool haveImmediate = false, haveMailbox = false; + for (VkPresentModeKHR m : presentModes) { + if (m == VK_PRESENT_MODE_IMMEDIATE_KHR) haveImmediate = true; + if (m == VK_PRESENT_MODE_MAILBOX_KHR) haveMailbox = true; + } + if (haveImmediate) swapchainCI.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + else if (haveMailbox) swapchainCI.presentMode = VK_PRESENT_MODE_MAILBOX_KHR; + } // Setting oldSwapChain to the saved handle of the previous swapchain aids in resource reuse and makes sure that we can still present already acquired images swapchainCI.oldSwapchain = oldSwapchain; // Setting clipped to VK_TRUE allows the implementation to discard rendering outside of the surface area From 619e39369d944a3a478a737e2e96db00ffe3d85c Mon Sep 17 00:00:00 2001 From: catbot Date: Thu, 18 Jun 2026 19:35:54 +0000 Subject: [PATCH 45/47] =?UTF-8?q?test(bench):=20SponzaBench=20harness=20+?= =?UTF-8?q?=20#40=E2=86=92HEAD=20perf=20measurement=20(#155)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Headless benchmark around the native Sponza RT scene: times setup and a measured Render() loop over the full multi-mesh atrium, prints BENCH metrics, and exits. Includes run-bench.sh and a README documenting the methodology and the measured net gain from #40 to current master. Co-Authored-By: Claude Opus 4.8 --- examples/SponzaBench/README.md | 131 ++++++++++++ examples/SponzaBench/closesthit.glsl | 16 ++ examples/SponzaBench/main.cpp | 306 +++++++++++++++++++++++++++ examples/SponzaBench/miss.glsl | 11 + examples/SponzaBench/project.cpp | 60 ++++++ examples/SponzaBench/raygen.glsl | 52 +++++ examples/SponzaBench/run-bench.sh | 40 ++++ 7 files changed, 616 insertions(+) create mode 100644 examples/SponzaBench/README.md create mode 100644 examples/SponzaBench/closesthit.glsl create mode 100644 examples/SponzaBench/main.cpp create mode 100644 examples/SponzaBench/miss.glsl create mode 100644 examples/SponzaBench/project.cpp create mode 100644 examples/SponzaBench/raygen.glsl create mode 100755 examples/SponzaBench/run-bench.sh diff --git a/examples/SponzaBench/README.md b/examples/SponzaBench/README.md new file mode 100644 index 0000000..fdf3426 --- /dev/null +++ b/examples/SponzaBench/README.md @@ -0,0 +1,131 @@ +# SponzaBench — measuring the post-#40 performance work + +This example exists to answer issue **#155**: *starting with #40 a lot of +performance-related issues were merged — what is the net performance gain, +measured in a representative scene (Sponza)?* + +It is a headless benchmark harness built around the **native Vulkan** +Sponza ray-tracing scene. Same asset bundle and camera as +[`examples/Sponza`](../Sponza), but instead of opening an interactive +window it times the work and prints machine-readable `BENCH …` lines, then +exits. + +## What it measures + +* **setup** — process start through the first command submission: asset + decompression, BLAS build per mesh group, the multi-instance TLAS, the + RT pipeline, GPU memory placement and the descriptor writes. This is the + window most of the post-#40 *native* perf work acts on. +* **frames** — a warmup followed by a measured loop calling + `Window::Render()`, reporting per-frame wall-clock stats and throughput. + +Unlike the interactive Sponza example — which is single-material on native +because of the hit-shader dynamic-`descriptor_heap`-index driver fault +(see `examples/Sponza/README.md`) — SponzaBench's closest-hit shades from +barycentric coordinates and samples **no** texture, so it can build the +**full multi-mesh atrium** (25 mesh groups, ~262 k triangles) as one +multi-instance TLAS. The albedo is still decompressed and uploaded during +setup (to keep that path in the measurement) but is not bound. + +## Running + +```bash +cd examples/SponzaBench +crafter-build # native Vulkan +# from the produced bin dir: +VK_LOADER_LAYERS_DISABLE='~all~' \ +CRAFTER_PRESENT_IMMEDIATE=1 \ +BENCH_WARMUP=200 BENCH_FRAMES=2000 BENCH_MESHES=25 ./SponzaBench +``` + +`run-bench.sh