From a55f15d3324e3cde2c248b4530288383aecd9018 Mon Sep 17 00:00:00 2001 From: catbot Date: Wed, 17 Jun 2026 17:34:29 +0000 Subject: [PATCH] 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.