From 90ceffc84f863b08dae0f4e328d978a56a8482b5 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Wed, 22 Jul 2026 10:58:26 +0100 Subject: [PATCH 01/11] Remeoved unneccesary buffer copy --- src/vsg/nodes/VertexDraw.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vsg/nodes/VertexDraw.cpp b/src/vsg/nodes/VertexDraw.cpp index 628879c617..2d2ce9d07d 100644 --- a/src/vsg/nodes/VertexDraw.cpp +++ b/src/vsg/nodes/VertexDraw.cpp @@ -128,8 +128,7 @@ void VertexDraw::compile(Context& context) if (requiresCreateAndCopy) { - BufferInfoList combinedBufferInfos(arrays); - createBufferAndTransferData(context, combinedBufferInfos, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE); + createBufferAndTransferData(context, arrays, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE); // info("VertexDraw::compile() create and copy ", this); } From 8e958f851cb0ee1c4ff12805df623210b5929847 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Thu, 23 Jul 2026 11:58:37 +0100 Subject: [PATCH 02/11] Added BufferInfo::requiresAllocation(..) method --- include/vsg/state/BufferInfo.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/vsg/state/BufferInfo.h b/include/vsg/state/BufferInfo.h index f592ef608a..8b8407c351 100644 --- a/include/vsg/state/BufferInfo.h +++ b/include/vsg/state/BufferInfo.h @@ -59,7 +59,13 @@ namespace vsg ref_ptr data; ref_ptr parent; - /// return true if the BufferInfo's data has been modified and should be copied to the buffer + /// return true if the BufferInfo's has not been assigned a buffer or that buffer doesn't yet have device memory allocated for it. + bool requiresAllocation(uint32_t deviceID) const + { + return !buffer || buffer->getDeviceMemory(deviceID)==nullptr; + } + + /// returns true if the data and associated buffers are not in sync and the data needs to be transferred to synchronize them bool requiresCopy(uint32_t deviceID) const { return data && data->differentModifiedCount(copiedModifiedCounts[deviceID]); From abd344fea6985c0b0106a2f3ab07f0ebfb7edd6d Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Thu, 23 Jul 2026 12:03:59 +0100 Subject: [PATCH 03/11] Added MemoryBufferPools::reserve(BufferInfoList&) method --- include/vsg/vk/MemoryBufferPools.h | 1 + src/vsg/vk/MemoryBufferPools.cpp | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/include/vsg/vk/MemoryBufferPools.h b/include/vsg/vk/MemoryBufferPools.h index d6c27ebfa7..7487cfb0f2 100644 --- a/include/vsg/vk/MemoryBufferPools.h +++ b/include/vsg/vk/MemoryBufferPools.h @@ -55,6 +55,7 @@ namespace vsg using DeviceMemoryOffset = std::pair, VkDeviceSize>; DeviceMemoryOffset reserveMemory(VkMemoryRequirements memRequirements, VkMemoryPropertyFlags memoryProperties, void* pNextAllocInfo = nullptr); + VkResult reserve(const BufferInfoList& bufferInfoList, VkDeviceSize alignment, VkBufferUsageFlags bufferUsageFlags, VkSharingMode sharingMode, VkMemoryPropertyFlags memoryProperties); VkResult reserve(ResourceRequirements& requirements); void report(LogOutput& out) const; diff --git a/src/vsg/vk/MemoryBufferPools.cpp b/src/vsg/vk/MemoryBufferPools.cpp index e81ca6e227..496046a254 100644 --- a/src/vsg/vk/MemoryBufferPools.cpp +++ b/src/vsg/vk/MemoryBufferPools.cpp @@ -220,6 +220,34 @@ MemoryBufferPools::DeviceMemoryOffset MemoryBufferPools::reserveMemory(VkMemoryR return MemoryBufferPools::DeviceMemoryOffset(deviceMemory, reservedSlot.second); } + +VkResult MemoryBufferPools::reserve(const BufferInfoList& bufferInfos, VkDeviceSize alignment, VkBufferUsageFlags bufferUsageFlags, VkSharingMode sharingMode, VkMemoryPropertyFlags memoryProperties) +{ + bool allocationSuccess = true; + + for (auto& bufferInfo : bufferInfos) + { + if (!bufferInfo->buffer) + { + if (allocationSuccess) + { + auto newBufferInfo = reserveBuffer(bufferInfo->data->dataSize(), alignment, bufferUsageFlags, sharingMode, memoryProperties); + if (newBufferInfo) + { + bufferInfo->take(*newBufferInfo); + } + else + { + debug("MemoryBufferPools::reserve() failed on ", bufferInfo, ", data = ", bufferInfo->data); + allocationSuccess = false; + } + } + } + } + + return allocationSuccess ? VK_SUCCESS : VK_ERROR_OUT_OF_DEVICE_MEMORY; +} + VkResult MemoryBufferPools::reserve(ResourceRequirements& requirements) { //vsg::info("MemoryBufferPools::reserve(ResourceRequirements& requirements) { "); From dc6a53c3969ec6f988c0d5d150a2dc7f1179a01c Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Fri, 31 Jul 2026 13:25:14 +0100 Subject: [PATCH 04/11] Added FrameStamp to CommandBuffer to enable bette tracking of frames being recorded to command buffers --- include/vsg/vk/CommandBuffer.h | 2 ++ src/vsg/app/CommandGraph.cpp | 1 + src/vsg/app/SecondaryCommandGraph.cpp | 1 + 3 files changed, 4 insertions(+) diff --git a/include/vsg/vk/CommandBuffer.h b/include/vsg/vk/CommandBuffer.h index a4091d81d0..e3eef69033 100644 --- a/include/vsg/vk/CommandBuffer.h +++ b/include/vsg/vk/CommandBuffer.h @@ -15,6 +15,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include namespace vsg { @@ -42,6 +43,7 @@ namespace vsg ViewDependentState* viewDependentState = nullptr; State* state = nullptr; const InstanceNode* instanceNode = nullptr; + ref_ptr frameStamp; ref_ptr gpuStats; VkCommandBufferLevel level() const { return _level; } diff --git a/src/vsg/app/CommandGraph.cpp b/src/vsg/app/CommandGraph.cpp index f0f870d7cf..7ab5c3b1e9 100644 --- a/src/vsg/app/CommandGraph.cpp +++ b/src/vsg/app/CommandGraph.cpp @@ -112,6 +112,7 @@ void CommandGraph::record(ref_ptr recordedCommandBuffers commandBuffer->reset(); } + commandBuffer->frameStamp = frameStamp; commandBuffer->numDependentSubmissions().fetch_add(1); recordTraversal->getState()->connect(commandBuffer); diff --git a/src/vsg/app/SecondaryCommandGraph.cpp b/src/vsg/app/SecondaryCommandGraph.cpp index 356cc18c99..8714c618bb 100644 --- a/src/vsg/app/SecondaryCommandGraph.cpp +++ b/src/vsg/app/SecondaryCommandGraph.cpp @@ -109,6 +109,7 @@ void SecondaryCommandGraph::record(ref_ptr recordedComma commandBuffer->reset(); } + commandBuffer->frameStamp = frameStamp; commandBuffer->numDependentSubmissions().fetch_add(1); recordTraversal->getState()->connect(commandBuffer); From 26dd2c8ea0ef913913c60094d0a96ebc760cd8a9 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Fri, 31 Jul 2026 13:32:46 +0100 Subject: [PATCH 05/11] Added Conext::reset(), createBufferAndTransferData(..) and copy method to centralize work previous done in BufferInfo.cpp --- include/vsg/vk/Context.h | 10 ++ src/vsg/vk/Context.cpp | 238 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 247 insertions(+), 1 deletion(-) diff --git a/include/vsg/vk/Context.h b/include/vsg/vk/Context.h index 02d0e51446..11a166298d 100644 --- a/include/vsg/vk/Context.h +++ b/include/vsg/vk/Context.h @@ -72,6 +72,9 @@ namespace vsg ~Context() override; + // clear any recorded commands for next frame to avoid accumulating work to next compile traversal. + void reset(); + const uint32_t deviceID = 0; ref_ptr device; ResourceRequirements resourceRequirements; @@ -126,6 +129,9 @@ namespace vsg std::vector> commands; + BufferInfoList bufferInfosToCopy; + ImageInfoList imageInfosToCopy; + ref_ptr copyImageCmd; void copy(ref_ptr data, ref_ptr dest); void copy(ref_ptr data, ref_ptr dest, uint32_t numMipMapLevels); @@ -146,6 +152,10 @@ namespace vsg std::vector> buildAccelerationStructureCommands; ref_ptr transferTask; + + bool createBufferAndTransferData(const BufferInfoList& bufferInfoList, VkBufferUsageFlags usage, VkSharingMode sharingMode); + bool copy(const ImageInfoList& imageInfoList); + }; VSG_type_name(vsg::Context); diff --git a/src/vsg/vk/Context.cpp b/src/vsg/vk/Context.cpp index 38e4c5ef5b..6dd80d9340 100644 --- a/src/vsg/vk/Context.cpp +++ b/src/vsg/vk/Context.cpp @@ -166,6 +166,22 @@ Context::~Context() } } +void Context::reset() +{ + if ((commands.size()>0) || bufferInfosToCopy.size()>0 || copyImageCmd || copyBufferCmd) + { + vsg::info("Context::reset() commands.size() = ", commands.size(), ", bufferInfosToCopy.size() = ", bufferInfosToCopy.size(), ", copyImageCmd = ", copyBufferCmd, ", copyBufferCmd= ", copyBufferCmd); + } + + commands.clear(); + + bufferInfosToCopy.clear(); + + copyImageCmd.reset(); + copyBufferCmd.reset(); +} + + ref_ptr Context::getOrCreateCommandBuffer() { if (!commandBuffer) @@ -262,7 +278,23 @@ bool Context::record() { CPU_INSTRUMENTATION_L1_NC(instrumentation, "Context record", COLOR_COMPILE) - if (commands.empty() && buildAccelerationStructureCommands.empty()) return false; + // vsg::info("Context::record() ", this, ", commands.size() = ", commands.size(), ", bufferInfosToCopy.size() = ", bufferInfosToCopy.size(), ", imageInfosToCopy.size() = ", imageInfosToCopy.size()); + + if (transferTask) + { + if (!bufferInfosToCopy.empty()) + { + transferTask->assign(bufferInfosToCopy); + bufferInfosToCopy.clear(); + } + if (!imageInfosToCopy.empty()) + { + transferTask->assign(imageInfosToCopy); + imageInfosToCopy.clear(); + } + } + + if (bufferInfosToCopy.empty() && imageInfosToCopy.empty() && commands.empty() && buildAccelerationStructureCommands.empty()) return false; if (!fence) { @@ -284,6 +316,9 @@ bool Context::record() { COMMAND_BUFFER_INSTRUMENTATION(instrumentation, *commandBuffer, "Context record", COLOR_COMPILE) + vsg::warn("Context::record() Need to implement copying of data."); + + // issue commands of interest { for (auto& command : commands) command->record(*commandBuffer); @@ -338,6 +373,8 @@ void Context::waitForCompletion() { CPU_INSTRUMENTATION_L1_NC(instrumentation, "Context waitForCompletion", COLOR_COMPILE) + vsg::info("Context::waitForCompletion() ", this); + if (!requiresWaitForCompletion || !commandBuffer || !fence) { return; @@ -366,3 +403,202 @@ void Context::waitForCompletion() copyImageCmd = nullptr; copyBufferCmd = nullptr; } + +bool Context::createBufferAndTransferData(const BufferInfoList& bufferInfoList, VkBufferUsageFlags usage, VkSharingMode sharingMode) +{ + if (bufferInfoList.empty()) return false; + + VkDeviceSize alignment = 4; + if (usage == VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) + alignment = device->getPhysicalDevice()->getProperties().limits.minUniformBufferOffsetAlignment; + else if (usage == VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) + alignment = device->getPhysicalDevice()->getProperties().limits.minStorageBufferOffsetAlignment; + +#if 1 + if (VkResult result = deviceMemoryBufferPools->reserve(bufferInfoList, alignment, usage | VK_BUFFER_USAGE_TRANSFER_DST_BIT, sharingMode, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); result == VK_SUCCESS) + { +#if 1 + bufferInfosToCopy.reserve(bufferInfosToCopy.size() + bufferInfoList.size()); + bufferInfosToCopy.insert(bufferInfosToCopy.end(), bufferInfoList.begin(), bufferInfoList.end()); +#else + if (transferTask) transferTask->assign(bufferInfoList); + else warn("vsg::createBufferAndTransferData() transfer task missing, need to implement transfer."); +#endif + return true; + } + else + { + return false; + } +#else + + // info("vsg::createBufferAndTransferData(const BufferInfoList& bufferInfoList, VkBufferUsageFlags usage, VkSharingMode sharingMode) usage = ", usage, ", alignment = ", alignment); + + //transferTask = nullptr; + + ref_ptr deviceBufferInfo; + size_t numBuffersRequired = 0; + bool containsMultipleParents = false; + for (auto& bufferInfo : bufferInfoList) + { + if (bufferInfo->data) + { + if (bufferInfo->data->getModifiedCount(bufferInfo->copiedModifiedCounts[deviceID])) + { + ++numBuffersRequired; + } + } + + if (bufferInfo->parent) + { + if (deviceBufferInfo && bufferInfo->parent != deviceBufferInfo) containsMultipleParents = true; + deviceBufferInfo = bufferInfo->parent; + } + } + + if (numBuffersRequired == 0) + { + debug("\nvsg::createBufferAndTransferData(...) already all compiled. deviceID = ", deviceID); + return false; + } + + if (containsMultipleParents) + { + warn("vsg::createBufferAndTransferData(...) does not support multiple parent BufferInfo."); + return false; + } + + VkDeviceSize totalSize = 0; + VkDeviceSize offset = 0; + for (const auto& bufferInfo : bufferInfoList) + { + if (bufferInfo->data) + { + bufferInfo->offset = offset; + bufferInfo->range = bufferInfo->data->dataSize(); + VkDeviceSize endOfEntry = offset + bufferInfo->range; + offset = (alignment == 1 || (endOfEntry % alignment) == 0) ? endOfEntry : ((endOfEntry / alignment) + 1) * alignment; + //info(" BufferInfo.data = ", bufferInfo->data, ", dynamic = ", bufferInfo->data->getLayout().dynamic); + } + } + + totalSize = offset; + if (totalSize == 0) return false; + + if (deviceBufferInfo && deviceBufferInfo->buffer) + { + if (totalSize != deviceBufferInfo->range) + { + warn("Existing deviceBufferInfo, ", deviceBufferInfo, ", deviceBufferInfo->range = ", deviceBufferInfo->range, ", ", totalSize, " NOT compatible"); + return false; + } + else + { + debug("Existing deviceBufferInfo, ", deviceBufferInfo, ", deviceBufferInfo->range = ", deviceBufferInfo->range, ", ", totalSize, " with compatible size"); + + // make sure the VkBuffer is created + deviceBufferInfo->buffer->compile(context); + + if (!deviceBufferInfo->buffer->getDeviceMemory(deviceID)) + { + VkMemoryRequirements memRequirements; + vkGetBufferMemoryRequirements(*device, deviceBufferInfo->buffer->vk(device->deviceID), &memRequirements); + + auto deviceMemoryOffset = context.deviceMemoryBufferPools->reserveMemory(memRequirements, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + if (deviceMemoryOffset.first) + deviceBufferInfo->buffer->bind(deviceMemoryOffset.first, deviceMemoryOffset.second); + else + { + debug("vsg::createBufferAndTransferData() Failure to assign memory to existing BufferInfo"); + return false; + } + } + } + } + + if (!deviceBufferInfo) + { + VkBufferUsageFlags bufferUsageFlags = VK_BUFFER_USAGE_TRANSFER_DST_BIT | usage; + deviceBufferInfo = context.deviceMemoryBufferPools->reserveBuffer(totalSize, alignment, bufferUsageFlags, sharingMode, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + } + + if (!deviceBufferInfo) + { + debug("vsg::createBufferAndTransferData() Failure to assign Buffer"); + return false; + } + + debug("deviceBufferInfo->buffer ", deviceBufferInfo->buffer, ", ", deviceBufferInfo->offset, ", ", deviceBufferInfo->range, ")"); + + // assign the buffer to the bufferData entries and shift the offsets to offset within the buffer + for (const auto& bufferInfo : bufferInfoList) + { + bufferInfo->buffer = deviceBufferInfo->buffer; + bufferInfo->offset += deviceBufferInfo->offset; + } + + if (transferTask) + { + vsg::debug("vsg::createBufferAndTransferData(..)"); + + for (auto& bufferInfo : bufferInfoList) + { + vsg::debug(" ", bufferInfo, ", ", bufferInfo->data, ", ", bufferInfo->buffer, ", ", bufferInfo->offset); + bufferInfo->data->dirty(); + bufferInfo->parent = deviceBufferInfo; + } + + transferTask->assign(bufferInfoList); + + return true; + } + + auto stagingBufferInfo = context.stagingMemoryBufferPools->reserveBuffer(totalSize, alignment, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, sharingMode, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + debug("stagingBufferInfo->buffer ", stagingBufferInfo->buffer.get(), ", ", stagingBufferInfo->offset, ", ", stagingBufferInfo->range, ")"); + + ref_ptr stagingBuffer(stagingBufferInfo->buffer); + ref_ptr stagingMemory(stagingBuffer->getDeviceMemory(context.deviceID)); + + if (!stagingMemory) + { + return false; + } + + void* buffer_data; + stagingMemory->map(stagingBuffer->getMemoryOffset(context.deviceID) + stagingBufferInfo->offset, stagingBufferInfo->range, 0, &buffer_data); + char* ptr = reinterpret_cast(buffer_data); + + debug(" buffer_data ", buffer_data, ", stagingBufferInfo->offset=", stagingBufferInfo->offset, ", ", totalSize); + + for (const auto& bufferInfo : bufferInfoList) + { + const Data* data = bufferInfo->data; + if (data) + { + std::memcpy(ptr + bufferInfo->offset - deviceBufferInfo->offset, data->dataPointer(), data->dataSize()); + if (data->properties.dataVariance == STATIC_DATA_UNREF_AFTER_TRANSFER) + { + bufferInfo->data.reset(); + } + } + bufferInfo->parent = deviceBufferInfo; + } + + stagingMemory->unmap(); + + context.copy(stagingBufferInfo, deviceBufferInfo); + + return true; +#endif +} + +bool Context::copy(const ImageInfoList& imageInfoList) +{ + if (imageInfoList.empty()) return false; + + imageInfosToCopy.reserve(imageInfosToCopy.size() + imageInfoList.size()); + imageInfosToCopy.insert(imageInfosToCopy.end(), imageInfoList.begin(), imageInfoList.end()); + + return true; +} From 6aa799093ac23408e82c6c03a37eabaef36340bf Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Fri, 31 Jul 2026 13:38:44 +0100 Subject: [PATCH 06/11] Added Context::reset() calls to CompileManager to clean up correctly. --- src/vsg/app/CompileManager.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/vsg/app/CompileManager.cpp b/src/vsg/app/CompileManager.cpp index 55a1c53c64..7581d0c098 100644 --- a/src/vsg/app/CompileManager.cpp +++ b/src/vsg/app/CompileManager.cpp @@ -253,6 +253,11 @@ CompileResult CompileManager::compile(ref_ptr object, ContextSelectionFu auto run_compile_traversal = [&]() -> void { try { + for (auto& context : compileTraversal->contexts) + { + context->reset(); + } + for (auto& context : compileTraversal->contexts) { ref_ptr view = context->view; @@ -345,6 +350,11 @@ CompileResult CompileManager::compile(ref_ptr object, ContextSelectionFu else { ++failedCompileCount; + + for (auto& context : compileTraversal->contexts) + { + context->reset(); + } } return result; @@ -362,6 +372,11 @@ CompileResult CompileManager::compileTask(ref_ptr task, Res auto compileTraversal = CompileTraversal::create(task->device, resourceRequirements); auto deviceMemoryBufferPools = task->device->deviceMemoryBufferPools.ref_ptr(); + for (auto& context : compileTraversal->contexts) + { + context->reset(); + } + for (const auto& context : compileTraversal->contexts) { if (resourceRequirements.dataTransferHint == COMPILE_TRAVERSAL_USE_TRANSFER_TASK) From 6bc23b12100cbd18307e29e6fff0a78209b7666d Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Fri, 31 Jul 2026 14:44:06 +0100 Subject: [PATCH 07/11] Changed vsg::createBufferAndTransferData(..) to use the new Context::createBufferAndTransferData(..) method --- src/vsg/state/BufferInfo.cpp | 180 ++--------------------------------- 1 file changed, 6 insertions(+), 174 deletions(-) diff --git a/src/vsg/state/BufferInfo.cpp b/src/vsg/state/BufferInfo.cpp index 7b4c8af496..2a5fc61438 100644 --- a/src/vsg/state/BufferInfo.cpp +++ b/src/vsg/state/BufferInfo.cpp @@ -207,186 +207,18 @@ ref_ptr vsg::copyDataToStagingBuffer(Context& context, const Data* d return stagingBufferInfo; } +VkDeviceSize BufferInfo::computeDataSize() const +{ + return data ? data->dataSize() : 0; +} + ///////////////////////////////////////////////////////////////////////////////////////// // // vsg::createBufferAndTransferData // bool vsg::createBufferAndTransferData(Context& context, const BufferInfoList& bufferInfoList, VkBufferUsageFlags usage, VkSharingMode sharingMode) { - if (bufferInfoList.empty()) return false; - - Device* device = context.device; - auto deviceID = context.deviceID; - auto transferTask = context.transferTask.get(); - VkDeviceSize alignment = 4; - if (usage == VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) - alignment = device->getPhysicalDevice()->getProperties().limits.minUniformBufferOffsetAlignment; - else if (usage == VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) - alignment = device->getPhysicalDevice()->getProperties().limits.minStorageBufferOffsetAlignment; - - debug("vsg::createBufferAndTransferData(Context& context, const BufferInfoList& bufferInfoList, VkBufferUsageFlags usage, VkSharingMode sharingMode) usage = ", usage, ", alignment = ", alignment); - - //transferTask = nullptr; - - ref_ptr deviceBufferInfo; - size_t numBuffersRequired = 0; - bool containsMultipleParents = false; - for (auto& bufferInfo : bufferInfoList) - { - if (bufferInfo->data) - { - if (bufferInfo->data->getModifiedCount(bufferInfo->copiedModifiedCounts[deviceID])) - { - ++numBuffersRequired; - } - } - - if (bufferInfo->parent) - { - if (deviceBufferInfo && bufferInfo->parent != deviceBufferInfo) containsMultipleParents = true; - deviceBufferInfo = bufferInfo->parent; - } - } - - if (numBuffersRequired == 0) - { - debug("\nvsg::createBufferAndTransferData(...) already all compiled. deviceID = ", deviceID); - return false; - } - - if (containsMultipleParents) - { - warn("vsg::createBufferAndTransferData(...) does not support multiple parent BufferInfo."); - return false; - } - - VkDeviceSize totalSize = 0; - VkDeviceSize offset = 0; - for (const auto& bufferInfo : bufferInfoList) - { - if (bufferInfo->data) - { - bufferInfo->offset = offset; - bufferInfo->range = bufferInfo->data->dataSize(); - VkDeviceSize endOfEntry = offset + bufferInfo->range; - offset = (alignment == 1 || (endOfEntry % alignment) == 0) ? endOfEntry : ((endOfEntry / alignment) + 1) * alignment; - //info(" BufferInfo.data = ", bufferInfo->data, ", dynamic = ", bufferInfo->data->getLayout().dynamic); - } - } - - totalSize = offset; - if (totalSize == 0) return false; - - if (deviceBufferInfo && deviceBufferInfo->buffer) - { - if (totalSize != deviceBufferInfo->range) - { - warn("Existing deviceBufferInfo, ", deviceBufferInfo, ", deviceBufferInfo->range = ", deviceBufferInfo->range, ", ", totalSize, " NOT compatible"); - return false; - } - else - { - debug("Existing deviceBufferInfo, ", deviceBufferInfo, ", deviceBufferInfo->range = ", deviceBufferInfo->range, ", ", totalSize, " with compatible size"); - - // make sure the VkBuffer is created - deviceBufferInfo->buffer->compile(context); - - if (!deviceBufferInfo->buffer->getDeviceMemory(deviceID)) - { - VkMemoryRequirements memRequirements; - vkGetBufferMemoryRequirements(*device, deviceBufferInfo->buffer->vk(device->deviceID), &memRequirements); - - auto deviceMemoryOffset = context.deviceMemoryBufferPools->reserveMemory(memRequirements, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - if (deviceMemoryOffset.first) - deviceBufferInfo->buffer->bind(deviceMemoryOffset.first, deviceMemoryOffset.second); - else - { - debug("vsg::createBufferAndTransferData() Failure to assign memory to existing BufferInfo"); - return false; - } - } - } - } - - if (!deviceBufferInfo) - { - VkBufferUsageFlags bufferUsageFlags = VK_BUFFER_USAGE_TRANSFER_DST_BIT | usage; - deviceBufferInfo = context.deviceMemoryBufferPools->reserveBuffer(totalSize, alignment, bufferUsageFlags, sharingMode, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - } - - if (!deviceBufferInfo) - { - debug("vsg::createBufferAndTransferData() Failure to assign Buffer"); - return false; - } - - debug("deviceBufferInfo->buffer ", deviceBufferInfo->buffer, ", ", deviceBufferInfo->offset, ", ", deviceBufferInfo->range, ")"); - - // assign the buffer to the bufferData entries and shift the offsets to offset within the buffer - for (const auto& bufferInfo : bufferInfoList) - { - bufferInfo->buffer = deviceBufferInfo->buffer; - bufferInfo->offset += deviceBufferInfo->offset; - } - - if (transferTask) - { - vsg::debug("vsg::createBufferAndTransferData(..)"); - - for (auto& bufferInfo : bufferInfoList) - { - vsg::debug(" ", bufferInfo, ", ", bufferInfo->data, ", ", bufferInfo->buffer, ", ", bufferInfo->offset); - bufferInfo->data->dirty(); - bufferInfo->parent = deviceBufferInfo; - } - - transferTask->assign(bufferInfoList); - - return true; - } - - auto stagingBufferInfo = context.stagingMemoryBufferPools->reserveBuffer(totalSize, alignment, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, sharingMode, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - - debug("stagingBufferInfo->buffer ", stagingBufferInfo->buffer.get(), ", ", stagingBufferInfo->offset, ", ", stagingBufferInfo->range, ")"); - - ref_ptr stagingBuffer(stagingBufferInfo->buffer); - ref_ptr stagingMemory(stagingBuffer->getDeviceMemory(context.deviceID)); - - if (!stagingMemory) - { - return false; - } - - void* buffer_data; - stagingMemory->map(stagingBuffer->getMemoryOffset(context.deviceID) + stagingBufferInfo->offset, stagingBufferInfo->range, 0, &buffer_data); - char* ptr = reinterpret_cast(buffer_data); - - debug(" buffer_data ", buffer_data, ", stagingBufferInfo->offset=", stagingBufferInfo->offset, ", ", totalSize); - - for (const auto& bufferInfo : bufferInfoList) - { - const Data* data = bufferInfo->data; - if (data) - { - std::memcpy(ptr + bufferInfo->offset - deviceBufferInfo->offset, data->dataPointer(), data->dataSize()); - if (data->properties.dataVariance == STATIC_DATA_UNREF_AFTER_TRANSFER) - { - bufferInfo->data.reset(); - } - } - bufferInfo->parent = deviceBufferInfo; - } - - stagingMemory->unmap(); - - context.copy(stagingBufferInfo, deviceBufferInfo); - - return true; -} - -VkDeviceSize BufferInfo::computeDataSize() const -{ - return data ? data->dataSize() : 0; + return context.createBufferAndTransferData(bufferInfoList, usage, sharingMode); } ///////////////////////////////////////////////////////////////////////////////////////// From 93dfd253fd75c352e2e3ffc5c997c35383b2b110 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Fri, 31 Jul 2026 15:03:11 +0100 Subject: [PATCH 08/11] Fixed build of older code path --- src/vsg/vk/Context.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vsg/vk/Context.cpp b/src/vsg/vk/Context.cpp index 6dd80d9340..f087fc0960 100644 --- a/src/vsg/vk/Context.cpp +++ b/src/vsg/vk/Context.cpp @@ -294,7 +294,7 @@ bool Context::record() } } - if (bufferInfosToCopy.empty() && imageInfosToCopy.empty() && commands.empty() && buildAccelerationStructureCommands.empty()) return false; + if (commands.empty() && buildAccelerationStructureCommands.empty()) return false; if (!fence) { @@ -497,14 +497,14 @@ bool Context::createBufferAndTransferData(const BufferInfoList& bufferInfoList, debug("Existing deviceBufferInfo, ", deviceBufferInfo, ", deviceBufferInfo->range = ", deviceBufferInfo->range, ", ", totalSize, " with compatible size"); // make sure the VkBuffer is created - deviceBufferInfo->buffer->compile(context); + deviceBufferInfo->buffer->compile(*this); if (!deviceBufferInfo->buffer->getDeviceMemory(deviceID)) { VkMemoryRequirements memRequirements; vkGetBufferMemoryRequirements(*device, deviceBufferInfo->buffer->vk(device->deviceID), &memRequirements); - auto deviceMemoryOffset = context.deviceMemoryBufferPools->reserveMemory(memRequirements, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + auto deviceMemoryOffset = deviceMemoryBufferPools->reserveMemory(memRequirements, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); if (deviceMemoryOffset.first) deviceBufferInfo->buffer->bind(deviceMemoryOffset.first, deviceMemoryOffset.second); else @@ -519,7 +519,7 @@ bool Context::createBufferAndTransferData(const BufferInfoList& bufferInfoList, if (!deviceBufferInfo) { VkBufferUsageFlags bufferUsageFlags = VK_BUFFER_USAGE_TRANSFER_DST_BIT | usage; - deviceBufferInfo = context.deviceMemoryBufferPools->reserveBuffer(totalSize, alignment, bufferUsageFlags, sharingMode, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + deviceBufferInfo = deviceMemoryBufferPools->reserveBuffer(totalSize, alignment, bufferUsageFlags, sharingMode, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); } if (!deviceBufferInfo) @@ -553,12 +553,12 @@ bool Context::createBufferAndTransferData(const BufferInfoList& bufferInfoList, return true; } - auto stagingBufferInfo = context.stagingMemoryBufferPools->reserveBuffer(totalSize, alignment, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, sharingMode, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + auto stagingBufferInfo = stagingMemoryBufferPools->reserveBuffer(totalSize, alignment, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, sharingMode, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); debug("stagingBufferInfo->buffer ", stagingBufferInfo->buffer.get(), ", ", stagingBufferInfo->offset, ", ", stagingBufferInfo->range, ")"); ref_ptr stagingBuffer(stagingBufferInfo->buffer); - ref_ptr stagingMemory(stagingBuffer->getDeviceMemory(context.deviceID)); + ref_ptr stagingMemory(stagingBuffer->getDeviceMemory(deviceID)); if (!stagingMemory) { @@ -566,7 +566,7 @@ bool Context::createBufferAndTransferData(const BufferInfoList& bufferInfoList, } void* buffer_data; - stagingMemory->map(stagingBuffer->getMemoryOffset(context.deviceID) + stagingBufferInfo->offset, stagingBufferInfo->range, 0, &buffer_data); + stagingMemory->map(stagingBuffer->getMemoryOffset(deviceID) + stagingBufferInfo->offset, stagingBufferInfo->range, 0, &buffer_data); char* ptr = reinterpret_cast(buffer_data); debug(" buffer_data ", buffer_data, ", stagingBufferInfo->offset=", stagingBufferInfo->offset, ", ", totalSize); @@ -587,7 +587,7 @@ bool Context::createBufferAndTransferData(const BufferInfoList& bufferInfoList, stagingMemory->unmap(); - context.copy(stagingBufferInfo, deviceBufferInfo); + copy(stagingBufferInfo, deviceBufferInfo); return true; #endif From f836cf54384a0145d0dde5c5017e4cff83d4caed Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Sat, 1 Aug 2026 15:31:41 +0100 Subject: [PATCH 09/11] Moved mutex into public scope to enable locking of groups of share(..) --- include/vsg/utils/SharedObjects.h | 11 ++++++----- src/vsg/utils/SharedObjects.cpp | 12 ++++++------ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/include/vsg/utils/SharedObjects.h b/include/vsg/utils/SharedObjects.h index 23f3dd76d6..d94591038d 100644 --- a/include/vsg/utils/SharedObjects.h +++ b/include/vsg/utils/SharedObjects.h @@ -72,10 +72,11 @@ namespace vsg /// write out stats of objects held, types of objects and their reference counts void report(vsg::LogOutput& output); + mutable std::recursive_mutex mutex; + protected: ~SharedObjects() override; - mutable std::recursive_mutex _mutex; std::map> _defaults; std::map, DereferenceLess>> _sharedObjects; }; @@ -127,7 +128,7 @@ namespace vsg template ref_ptr SharedObjects::shared_default() { - std::scoped_lock lock(_mutex); + std::scoped_lock lock(mutex); auto id = std::type_index(typeid(T)); auto& def = _defaults[id]; @@ -155,7 +156,7 @@ namespace vsg template void SharedObjects::share(ref_ptr& object) { - std::scoped_lock lock(_mutex); + std::scoped_lock lock(mutex); if (suitableForSharing && !suitableForSharing->suitable(object.get())) return; @@ -175,7 +176,7 @@ namespace vsg void SharedObjects::share(ref_ptr& object, Func init) { { - std::scoped_lock lock(_mutex); + std::scoped_lock lock(mutex); auto id = std::type_index(typeid(T)); auto& shared_objects = _sharedObjects[id]; @@ -189,7 +190,7 @@ namespace vsg init(object); { - std::scoped_lock lock(_mutex); + std::scoped_lock lock(mutex); auto id = std::type_index(typeid(T)); auto& shared_objects = _sharedObjects[id]; if (suitableForSharing && suitableForSharing->suitable(object.get())) diff --git a/src/vsg/utils/SharedObjects.cpp b/src/vsg/utils/SharedObjects.cpp index 9d455d7e0d..1e15a0ede8 100644 --- a/src/vsg/utils/SharedObjects.cpp +++ b/src/vsg/utils/SharedObjects.cpp @@ -21,7 +21,7 @@ bool SharedObjects::suitable(const Path& filename) const bool SharedObjects::contains(const Path& filename, ref_ptr options) const { - std::scoped_lock lock(_mutex); + std::scoped_lock lock(mutex); auto loadedObject_id = std::type_index(typeid(LoadedObject)); auto itr = _sharedObjects.find(loadedObject_id); @@ -34,7 +34,7 @@ bool SharedObjects::contains(const Path& filename, ref_ptr option void SharedObjects::add(ref_ptr object, const Path& filename, ref_ptr options) { - std::scoped_lock lock(_mutex); + std::scoped_lock lock(mutex); auto loadedObject_id = std::type_index(typeid(LoadedObject)); auto& loadedObjects = _sharedObjects[loadedObject_id]; @@ -45,7 +45,7 @@ void SharedObjects::add(ref_ptr object, const Path& filename, ref_ptr options) { - std::scoped_lock lock(_mutex); + std::scoped_lock lock(mutex); auto loadedObject_id = std::type_index(typeid(LoadedObject)); auto itr = _sharedObjects.find(loadedObject_id); @@ -67,14 +67,14 @@ bool SharedObjects::remove(const Path& filename, ref_ptr options) void SharedObjects::clear() { - std::scoped_lock lock(_mutex); + std::scoped_lock lock(mutex); _defaults.clear(); _sharedObjects.clear(); } void SharedObjects::prune() { - std::scoped_lock lock(_mutex); + std::scoped_lock lock(mutex); auto loadedObject_id = std::type_index(typeid(LoadedObject)); @@ -156,7 +156,7 @@ void SharedObjects::prune() void SharedObjects::report(vsg::LogOutput& output) { - std::scoped_lock lock(_mutex); + std::scoped_lock lock(mutex); output("SharedObjects::report(..) ", this, " {"); output.in(); output("SharedObjects::_defaults ", _defaults.size(), " {"); From 5ba89624d9117c1b99cfece35cb1ddc9f7d56301 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Sat, 1 Aug 2026 16:55:52 +0100 Subject: [PATCH 10/11] Added mutex before SharedObjects usage to make sure that all pipeline state is shared together within the same thread. --- src/vsg/utils/GraphicsPipelineConfigurator.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vsg/utils/GraphicsPipelineConfigurator.cpp b/src/vsg/utils/GraphicsPipelineConfigurator.cpp index 8094d056d5..d517ecbb43 100644 --- a/src/vsg/utils/GraphicsPipelineConfigurator.cpp +++ b/src/vsg/utils/GraphicsPipelineConfigurator.cpp @@ -834,13 +834,13 @@ bool GraphicsPipelineConfigurator::copyTo(StateCommands& stateCommands, ref_ptr< // create StateGroup as the root of the scene/command graph to hold the GraphicsPipeline, and binding of Descriptors to decorate the whole graph if (sharedObjects) { - for (auto& dsl : layout->setLayouts) - { - sharedObjects->share(dsl); - } + std::scoped_lock lock(sharedObjects->mutex); + + sharedObjects->share(layout->setLayouts); sharedObjects->share(layout); + graphicsPipeline->layout = layout; + sharedObjects->share(graphicsPipeline); - layout = graphicsPipeline->layout; sharedObjects->share(bindGraphicsPipeline); } From 9286437af4a0301bf81a15afadd765d3499bce75 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Sat, 1 Aug 2026 18:43:04 +0100 Subject: [PATCH 11/11] Added per ImageView::Instance reference to image used. --- include/vsg/state/ImageView.h | 2 +- src/vsg/state/ImageView.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/vsg/state/ImageView.h b/include/vsg/state/ImageView.h index 828e239a3e..1c4e2b0097 100644 --- a/include/vsg/state/ImageView.h +++ b/include/vsg/state/ImageView.h @@ -50,8 +50,8 @@ namespace vsg { VkImageView imageView = VK_NULL_HANDLE; ref_ptr device; + ref_ptr image; - ~VulkanData() { release(); } void release(); }; diff --git a/src/vsg/state/ImageView.cpp b/src/vsg/state/ImageView.cpp index e8d8a722ea..d4d2606435 100644 --- a/src/vsg/state/ImageView.cpp +++ b/src/vsg/state/ImageView.cpp @@ -40,6 +40,8 @@ void ImageView::VulkanData::release() vkDestroyImageView(*device, imageView, device->getAllocationCallbacks()); imageView = VK_NULL_HANDLE; device = {}; + image = {}; + } } @@ -137,6 +139,8 @@ void ImageView::compile(Device* device) { throw Exception{"Error: Failed to create VkImageView.", result}; } + + vd.image = image; } void ImageView::compile(Context& context) @@ -166,6 +170,8 @@ void ImageView::compile(Context& context) { throw Exception{"Error: Failed to create VkImageView.", result}; } + + vd.image = image; } ref_ptr vsg::createImageView(vsg::Context& context, ref_ptr image, VkImageAspectFlags aspectFlags)