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;