diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c3d5522b..fec2e101f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,11 @@ cmake_minimum_required(VERSION 3.10) project(vsg - VERSION 1.1.13 + VERSION 1.1.14 DESCRIPTION "VulkanSceneGraph library" LANGUAGES CXX ) -set(VSG_SOVERSION 15) +set(VSG_SOVERSION 16) SET(VSG_RELEASE_CANDIDATE 0) set(Vulkan_MIN_VERSION 1.1.70.0) diff --git a/include/vsg/app/CompileManager.h b/include/vsg/app/CompileManager.h index 342ee14d7..dff3c9f90 100644 --- a/include/vsg/app/CompileManager.h +++ b/include/vsg/app/CompileManager.h @@ -30,7 +30,7 @@ namespace vsg Slots maxSlots; bool containsPagedLOD = false; ResourceRequirements::Views views; - ResourceRequirements::DynamicData dynamicData; + DynamicData dynamicData; explicit operator bool() const noexcept { return result == VK_SUCCESS; } diff --git a/include/vsg/app/TransferTask.h b/include/vsg/app/TransferTask.h index b098bbb0b..54a60f825 100644 --- a/include/vsg/app/TransferTask.h +++ b/include/vsg/app/TransferTask.h @@ -51,7 +51,7 @@ namespace vsg ref_ptr device; - void assign(const ResourceRequirements::DynamicData& dynamicData); + void assign(const DynamicData& dynamicData); void assign(const BufferInfoList& bufferInfoList); void assign(const ImageInfoList& imageInfoList); diff --git a/include/vsg/app/Viewer.h b/include/vsg/app/Viewer.h index 4dd2eb207..a2603e0da 100644 --- a/include/vsg/app/Viewer.h +++ b/include/vsg/app/Viewer.h @@ -104,7 +104,7 @@ namespace vsg /// pass the Events into any registered EventHandlers virtual void handleEvents(); - virtual void compile(ref_ptr hints = {}); + virtual CompileResult compile(ref_ptr hints = {}); virtual bool acquireNextFrame(); diff --git a/include/vsg/state/BufferInfo.h b/include/vsg/state/BufferInfo.h index 6d337b427..b58ee63b1 100644 --- a/include/vsg/state/BufferInfo.h +++ b/include/vsg/state/BufferInfo.h @@ -38,6 +38,9 @@ namespace vsg void release(); + /// compute size of associated data + VkDeviceSize computeDataSize() const; + /// Copy data to the VkBuffer(s) for all Devices associated with vsg::Buffer /// Requires associated buffer memory to be host visible, for non host visible buffers you must use a staging buffer void copyDataToBuffer(); diff --git a/include/vsg/state/Image.h b/include/vsg/state/Image.h index 7be6a50c3..9a802e1a7 100644 --- a/include/vsg/state/Image.h +++ b/include/vsg/state/Image.h @@ -67,8 +67,9 @@ namespace vsg /// return true if the Image's data has been modified and should be copied to the buffer, updating the device specific ModifiedCount to the Data's ModifiedCount. bool syncModifiedCount(uint32_t deviceID) { return data && data->getModifiedCount(_vulkanData[deviceID].copiedModifiedCount); } - virtual void compile(Device* device); - virtual void compile(Context& context); + virtual VkResult compile(Device* device); + virtual VkResult compile(Context& context); + virtual VkResult compile(MemoryBufferPools& memoryBufferPools); protected: virtual ~Image(); diff --git a/include/vsg/state/ImageInfo.h b/include/vsg/state/ImageInfo.h index afd8f857e..7d2645018 100644 --- a/include/vsg/state/ImageInfo.h +++ b/include/vsg/state/ImageInfo.h @@ -47,6 +47,9 @@ namespace vsg ref_ptr imageView; VkImageLayout imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + /// compute size of associated data + VkDeviceSize computeDataSize() const; + /// return true if the ImageInfo's data has been modified and should be copied to the buffer bool requiresCopy(uint32_t deviceID) const { diff --git a/include/vsg/state/ResourceHints.h b/include/vsg/state/ResourceHints.h index 3c1fc019f..91fc70437 100644 --- a/include/vsg/state/ResourceHints.h +++ b/include/vsg/state/ResourceHints.h @@ -13,6 +13,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI */ #include +#include +#include #include namespace vsg @@ -30,6 +32,26 @@ namespace vsg DYNAMIC_VIEWPORTSTATE = 1 << 1 }; + struct DynamicData + { + BufferInfoList bufferInfos; + ImageInfoList imageInfos; + + explicit operator bool() const noexcept { return !bufferInfos.empty() || !imageInfos.empty(); } + + void clear() + { + bufferInfos.clear(); + imageInfos.clear(); + } + + void add(const DynamicData& dd) + { + bufferInfos.insert(bufferInfos.end(), dd.bufferInfos.begin(), dd.bufferInfos.end()); + imageInfos.insert(imageInfos.end(), dd.imageInfos.begin(), dd.imageInfos.end()); + } + }; + /// ResourceHints provides settings that help preallocation of Vulkan resources and memory. class VSG_DECLSPEC ResourceHints : public Inherit { @@ -56,6 +78,15 @@ namespace vsg DataTransferHint dataTransferHint = COMPILE_TRAVERSAL_USE_TRANSFER_TASK; uint32_t viewportStateHint = DYNAMIC_VIEWPORTSTATE; + DynamicData dynamicData; + + bool containsPagedLOD = false; + + /// Ratio of available device memory that can be allocated. + /// Ratios less than 1.0 require VK_EXT_memory_budget extension to be supported. + /// Ratio of 1.0 (or greater) will switch off checks for available memory and keep allocating till Vulkan memory allocations fail. + double allocatedMemoryLimit = 1.0; + public: void read(Input& input) override; void write(Output& output) const override; diff --git a/include/vsg/vk/Context.h b/include/vsg/vk/Context.h index 890f17be0..daa5bb1ed 100644 --- a/include/vsg/vk/Context.h +++ b/include/vsg/vk/Context.h @@ -87,7 +87,7 @@ namespace vsg ref_ptr getOrCreateCommandBuffer(); /// reserve resources that may be needed during compile traversal. - void reserve(const ResourceRequirements& requirements); + VkResult reserve(ResourceRequirements& requirements); ref_ptr allocateDescriptorSet(DescriptorSetLayout* descriptorSetLayout); diff --git a/include/vsg/vk/Device.h b/include/vsg/vk/Device.h index a36f4bb45..5e514ddfc 100644 --- a/include/vsg/vk/Device.h +++ b/include/vsg/vk/Device.h @@ -84,6 +84,9 @@ namespace vsg /// return true if Device was created with specified extension bool supportsDeviceExtension(const char* extensionName) const; + /// return the amount of memory available in deviceMemoryBufferPools and allocatable on device + VkDeviceSize availableMemory(bool includeMemoryPools = true) const; + // provide observer_ptr to memory buffer and descriptor pools so that these can be accessed when required observer_ptr deviceMemoryBufferPools; observer_ptr stagingMemoryBufferPools; diff --git a/include/vsg/vk/MemoryBufferPools.h b/include/vsg/vk/MemoryBufferPools.h index 7f5911869..320cf631b 100644 --- a/include/vsg/vk/MemoryBufferPools.h +++ b/include/vsg/vk/MemoryBufferPools.h @@ -34,6 +34,14 @@ namespace vsg VkDeviceSize minimumBufferSize = 16 * 1024 * 1024; VkDeviceSize minimumDeviceMemorySize = 16 * 1024 * 1024; + /// Ratio of available device memory that can be allocated. + /// Ratios less than 1.0 require VK_EXT_memory_budget extension to be supported. + /// Ratio of 1.0 (or greater) will switch off checks for available memory and keep allocating till Vulkan memory allocations fail. + double allocatedMemoryLimit = 1.0; + + /// throw vsg::Exception when reserveMemory() fails to allocated memory on device. + bool throwOutOfDeviceMemoryException = true; + VkDeviceSize computeMemoryTotalAvailable() const; VkDeviceSize computeMemoryTotalReserved() const; VkDeviceSize computeBufferTotalAvailable() const; @@ -44,6 +52,8 @@ namespace vsg using DeviceMemoryOffset = std::pair, VkDeviceSize>; DeviceMemoryOffset reserveMemory(VkMemoryRequirements memRequirements, VkMemoryPropertyFlags memoryProperties, void* pNextAllocInfo = nullptr); + VkResult reserve(ResourceRequirements& requirements); + protected: mutable std::mutex _mutex; diff --git a/include/vsg/vk/ResourceRequirements.h b/include/vsg/vk/ResourceRequirements.h index 332779f3a..8f23fbed8 100644 --- a/include/vsg/vk/ResourceRequirements.h +++ b/include/vsg/vk/ResourceRequirements.h @@ -60,26 +60,6 @@ namespace vsg using Views = std::map; using ViewDetailStack = std::stack; - struct DynamicData - { - BufferInfoList bufferInfos; - ImageInfoList imageInfos; - - explicit operator bool() const noexcept { return !bufferInfos.empty() || !imageInfos.empty(); } - - void clear() - { - bufferInfos.clear(); - imageInfos.clear(); - } - - void add(const DynamicData& dd) - { - bufferInfos.insert(bufferInfos.end(), dd.bufferInfos.begin(), dd.bufferInfos.end()); - imageInfos.insert(imageInfos.end(), dd.imageInfos.begin(), dd.imageInfos.end()); - } - }; - DynamicData dynamicData; Descriptors descriptors; @@ -92,6 +72,17 @@ namespace vsg uint32_t externalNumDescriptorSets = 0; bool containsPagedLOD = false; + struct BufferProperties + { + VkBufferUsageFlags usageFlags = 0; + VkSharingMode sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + bool operator<(const BufferProperties& rhs) const { return (usageFlags < rhs.usageFlags) || ((usageFlags == rhs.usageFlags) && (sharingMode < rhs.sharingMode)); } + }; + + std::map>> bufferInfos; + std::set> imageInfos; + VkDeviceSize minimumBufferSize = 16 * 1024 * 1024; VkDeviceSize minimumDeviceMemorySize = 16 * 1024 * 1024; @@ -141,13 +132,16 @@ namespace vsg void apply(const VertexIndexDraw& vid) override; void apply(const BindVertexBuffers& bvb) override; void apply(const BindIndexBuffer& bib) override; + void apply(const InstanceNode& in) override; + void apply(const InstanceDraw& id) override; + void apply(const InstanceDrawIndexed& idi) override; - virtual void apply(ref_ptr bufferInfo); + using BufferProperties = ResourceRequirements::BufferProperties; + + virtual void apply(ref_ptr bufferInfo, BufferProperties bufferProperties); virtual void apply(ref_ptr imageInfo); protected: - uint32_t _numResourceHintsAbove = 0; - bool registerDescriptor(const Descriptor& descriptor); }; VSG_type_name(vsg::CollectResourceRequirements); diff --git a/src/vsg/app/CompileManager.cpp b/src/vsg/app/CompileManager.cpp index 75d8c24ce..cdd28de2a 100644 --- a/src/vsg/app/CompileManager.cpp +++ b/src/vsg/app/CompileManager.cpp @@ -105,7 +105,6 @@ void CompileManager::add(ref_ptr device, const ResourceRequirements& res for (auto& ct : cts) { ct->add(device, resourceRequirements); - compileTraversals->add(ct); } } @@ -116,7 +115,6 @@ void CompileManager::add(Window& window, ref_ptr viewport, const for (auto& ct : cts) { ct->add(window, viewport, resourceRequirements); - compileTraversals->add(ct); } } @@ -127,7 +125,6 @@ void CompileManager::add(Window& window, ref_ptr view, const ResourceRequi for (auto& ct : cts) { ct->add(window, view, resourceRequirements); - compileTraversals->add(ct); } } @@ -138,7 +135,6 @@ void CompileManager::add(Framebuffer& framebuffer, ref_ptr view, const Res for (auto& ct : cts) { ct->add(framebuffer, view, resourceRequirements); - compileTraversals->add(ct); } } @@ -149,7 +145,6 @@ void CompileManager::add(const Viewer& viewer, const ResourceRequirements& resou for (auto& ct : cts) { ct->add(viewer, resourceRequirements); - compileTraversals->add(ct); } } @@ -160,15 +155,12 @@ void CompileManager::assignInstrumentation(ref_ptr in_instrumen for (auto& ct : cts) { ct->assignInstrumentation(in_instrumentation); - compileTraversals->add(ct); } } CompileResult CompileManager::compile(ref_ptr object, ContextSelectionFunction contextSelection) { - vsg::debug("CompileManager::compile(", object, ", ..)"); - CollectResourceRequirements collectRequirements; object->accept(collectRequirements); @@ -207,13 +199,13 @@ CompileResult CompileManager::compile(ref_ptr object, ContextSelectionFu } } } - context->reserve(requirements); + + auto reserveResult = context->reserve(requirements); + if (reserveResult != VK_SUCCESS) throw vsg::Exception{"Context::reserve() failed", reserveResult}; } object->accept(*compileTraversal); - //debug("Finished compile traversal ", object); - // if required records and submits to queue if (compileTraversal->record()) { @@ -222,13 +214,11 @@ CompileResult CompileManager::compile(ref_ptr object, ContextSelectionFu } catch (const vsg::Exception& ve) { - vsg::debug("CompileManager::compile() exception caught : ", ve.message); result.message = ve.message; result.result = ve.result; } catch (...) { - vsg::debug("CompileManager::compile() exception caught"); result.message = "Exception occurred during compilation."; result.result = VK_ERROR_UNKNOWN; } @@ -236,23 +226,23 @@ CompileResult CompileManager::compile(ref_ptr object, ContextSelectionFu debug("Finished waiting for compile ", object); }; - // assume success, overite this on failures. + // assume success, overwrite this on failures. result.result = VK_SUCCESS; if (contextSelection) { - std::list> contexts; + std::list> activeContexts; for (auto& context : compileTraversal->contexts) { - if (contextSelection(*context)) contexts.push_back(context); + if (contextSelection(*context)) activeContexts.push_back(context); } - compileTraversal->contexts.swap(contexts); + compileTraversal->contexts.swap(activeContexts); run_compile_traversal(); - compileTraversal->contexts.swap(contexts); + compileTraversal->contexts.swap(activeContexts); } else { @@ -266,25 +256,43 @@ CompileResult CompileManager::compile(ref_ptr object, ContextSelectionFu CompileResult CompileManager::compileTask(ref_ptr task, const ResourceRequirements& resourceRequirements) { - auto compileTraversal = CompileTraversal::create(task->device, resourceRequirements); + CompileResult result; + + // assume success, overwrite this on failures. + result.result = VK_SUCCESS; - for (const auto& context : compileTraversal->contexts) + try { - if (resourceRequirements.dataTransferHint == COMPILE_TRAVERSAL_USE_TRANSFER_TASK) + auto compileTraversal = CompileTraversal::create(task->device, resourceRequirements); + + for (const auto& context : compileTraversal->contexts) { - context->transferTask = task->transferTask; + if (resourceRequirements.dataTransferHint == COMPILE_TRAVERSAL_USE_TRANSFER_TASK) + { + context->transferTask = task->transferTask; + } + } + + for (auto& cg : task->commandGraphs) + { + cg->accept(*compileTraversal); } - } - for (auto& cg : task->commandGraphs) + if (compileTraversal->record()) + { + compileTraversal->waitForCompletion(); + } + } + catch (const vsg::Exception& ve) { - cg->accept(*compileTraversal); + result.message = ve.message; + result.result = ve.result; } - - if (compileTraversal->record()) + catch (...) { - compileTraversal->waitForCompletion(); + result.message = "Exception occurred during compilation."; + result.result = VK_ERROR_UNKNOWN; } - return {}; + return result; } diff --git a/src/vsg/app/TransferTask.cpp b/src/vsg/app/TransferTask.cpp index cf320b2a5..1aa2a6e3e 100644 --- a/src/vsg/app/TransferTask.cpp +++ b/src/vsg/app/TransferTask.cpp @@ -51,7 +51,7 @@ void TransferTask::assignTransferConsumedCompletedSemaphore(TransferMask transfe if ((transferMask & TRANSFER_AFTER_RECORD_TRAVERSAL) != 0) _lateDataToCopy.transferConsumerCompletedSemaphore = semaphore; } -void TransferTask::assign(const ResourceRequirements::DynamicData& dynamicData) +void TransferTask::assign(const DynamicData& dynamicData) { CPU_INSTRUMENTATION_L2(instrumentation); diff --git a/src/vsg/app/Viewer.cpp b/src/vsg/app/Viewer.cpp index 9d8374e92..07a599860 100644 --- a/src/vsg/app/Viewer.cpp +++ b/src/vsg/app/Viewer.cpp @@ -275,15 +275,16 @@ void Viewer::handleEvents() } } -void Viewer::compile(ref_ptr hints) +CompileResult Viewer::compile(ref_ptr hints) { CPU_INSTRUMENTATION_L1_NC(instrumentation, "Viewer compile", COLOR_COMPILE); if (recordAndSubmitTasks.empty()) { - return; + return {}; } + CompileResult result; bool containsPagedLOD = false; ref_ptr databasePager; @@ -386,9 +387,17 @@ void Viewer::compile(ref_ptr hints) for (auto& task : recordAndSubmitTasks) { + if (hints) + { + if (auto deviceMemoryBufferPools = task->device->deviceMemoryBufferPools.ref_ptr()) + { + deviceMemoryBufferPools->allocatedMemoryLimit = hints->allocatedMemoryLimit; + } + } + auto& deviceResource = deviceResourceMap[task->device]; auto& resourceRequirements = deviceResource.collectResources.requirements; - compileManager->compileTask(task, resourceRequirements); + result.add(compileManager->compileTask(task, resourceRequirements)); task->transferTask->assign(resourceRequirements.dynamicData); } @@ -403,6 +412,8 @@ void Viewer::compile(ref_ptr hints) task->databasePager->start(); } } + + return result; } void Viewer::assignRecordAndSubmitTaskAndPresentation(CommandGraphs in_commandGraphs) diff --git a/src/vsg/io/DatabasePager.cpp b/src/vsg/io/DatabasePager.cpp index ca96f187b..ee7527b17 100644 --- a/src/vsg/io/DatabasePager.cpp +++ b/src/vsg/io/DatabasePager.cpp @@ -515,17 +515,25 @@ void DatabasePager::start(uint32_t numReadThreads) plod->pending = subgraph; } - // compile plod - if (auto result = databasePager.compileManager->compile(subgraph)) + try { - plod->requestStatus.exchange(PagedLOD::MergeRequest); + // compile plod + if (auto result = databasePager.compileManager->compile(subgraph)) + { + plod->requestStatus.exchange(PagedLOD::MergeRequest); - // move to the merge queue; - databasePager._toMergeQueue->add(plod, result); + // move to the merge queue; + databasePager._toMergeQueue->add(plod, result); + } + else + { + debug("DatabaserPager::start() unable to compile subgraph, discarding request ", subgraph); + databasePager.requestDiscarded(plod); + } } - else + catch (...) { - debug("Failed to compile ", plod, " ", plod->filename); + debug("DatabaserPager::start() compile threw exception, discarding request ", subgraph); databasePager.requestDiscarded(plod); } } diff --git a/src/vsg/state/BufferInfo.cpp b/src/vsg/state/BufferInfo.cpp index 3e3c41821..8f882c415 100644 --- a/src/vsg/state/BufferInfo.cpp +++ b/src/vsg/state/BufferInfo.cpp @@ -256,7 +256,13 @@ bool vsg::createBufferAndTransferData(Context& context, const BufferInfoList& bu vkGetBufferMemoryRequirements(*device, deviceBufferInfo->buffer->vk(device->deviceID), &memRequirements); auto deviceMemoryOffset = context.deviceMemoryBufferPools->reserveMemory(memRequirements, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - deviceBufferInfo->buffer->bind(deviceMemoryOffset.first, deviceMemoryOffset.second); + if (!deviceMemoryOffset.first) + deviceBufferInfo->buffer->bind(deviceMemoryOffset.first, deviceMemoryOffset.second); + else + { + debug("vsg::createBufferAndTransferData() Failure to assign memory to existing BufferInfo"); + return false; + } } } } @@ -267,6 +273,12 @@ bool vsg::createBufferAndTransferData(Context& context, const BufferInfoList& bu 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 @@ -331,6 +343,11 @@ bool vsg::createBufferAndTransferData(Context& context, const BufferInfoList& bu return true; } +VkDeviceSize BufferInfo::computeDataSize() const +{ + return data ? data->dataSize() : 0; +} + ///////////////////////////////////////////////////////////////////////////////////////// // // vsg::createHostVisibleBuffer diff --git a/src/vsg/state/Image.cpp b/src/vsg/state/Image.cpp index c63813fb8..64d156450 100644 --- a/src/vsg/state/Image.cpp +++ b/src/vsg/state/Image.cpp @@ -143,6 +143,8 @@ int Image::compare(const Object& rhs_object) const VkResult Image::bind(DeviceMemory* deviceMemory, VkDeviceSize memoryOffset) { + if (!deviceMemory) return VK_ERROR_OUT_OF_DEVICE_MEMORY; + VulkanData& vd = _vulkanData[deviceMemory->getDevice()->deviceID]; VkResult result = vkBindImageMemory(*vd.device, vd.image, *deviceMemory, memoryOffset); @@ -188,10 +190,10 @@ VkMemoryRequirements Image::getMemoryRequirements(uint32_t deviceID) const return memRequirements; } -void Image::compile(Device* device) +VkResult Image::compile(Device* device) { auto& vd = _vulkanData[device->deviceID]; - if (vd.image != VK_NULL_HANDLE) return; + if (vd.image != VK_NULL_HANDLE) return VK_SUCCESS; VkImageCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; @@ -216,30 +218,41 @@ void Image::compile(Device* device) vd.requiresDataCopy = data.valid(); - if (VkResult result = vkCreateImage(*vd.device, &info, vd.device->getAllocationCallbacks(), &vd.image); result != VK_SUCCESS) + VkResult result = vkCreateImage(*vd.device, &info, vd.device->getAllocationCallbacks(), &vd.image); + + if (result != VK_SUCCESS) { throw Exception{"Error: Failed to create VkImage.", result}; } + + return result; } -void Image::compile(Context& context) +VkResult Image::compile(Context& context) { - auto& vd = _vulkanData[context.deviceID]; - if (vd.image != VK_NULL_HANDLE) return; + return compile(*context.deviceMemoryBufferPools); +} - compile(context.device); +VkResult Image::compile(MemoryBufferPools& memoryBufferPools) +{ + auto device = memoryBufferPools.device; + auto& vd = _vulkanData[device->deviceID]; + if (vd.image != VK_NULL_HANDLE) return VK_SUCCESS; - VkMemoryRequirements memRequirements; - vkGetImageMemoryRequirements(*vd.device, vd.image, &memRequirements); + VkResult result = compile(device); + if (result != VK_SUCCESS) return result; - auto [deviceMemory, offset] = context.deviceMemoryBufferPools->reserveMemory(memRequirements, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VkMemoryRequirements memRequirements; + vkGetImageMemoryRequirements(*device, vd.image, &memRequirements); - if (!deviceMemory) + auto [deviceMemory, offset] = memoryBufferPools.reserveMemory(memRequirements, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + if (deviceMemory) { - throw Exception{"Error: Image failed to reserve slot from deviceMemoryBufferPools.", VK_ERROR_OUT_OF_DEVICE_MEMORY}; + vd.requiresDataCopy = data.valid(); + return bind(deviceMemory, offset); + } + else + { + return VK_ERROR_OUT_OF_DEVICE_MEMORY; } - - vd.requiresDataCopy = data.valid(); - - bind(deviceMemory, offset); } diff --git a/src/vsg/state/ImageInfo.cpp b/src/vsg/state/ImageInfo.cpp index 144edc9f9..3d7e791dd 100644 --- a/src/vsg/state/ImageInfo.cpp +++ b/src/vsg/state/ImageInfo.cpp @@ -414,3 +414,13 @@ void ImageInfo::computeNumMipMapLevels() if (generateMipmaps) image->usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; } } + +VkDeviceSize ImageInfo::computeDataSize() const +{ + if (imageView && imageView->image) + { + const auto& image = imageView->image; + if (image->data) return image->data->computeValueCountIncludingMipmaps(); + } + return 0; +} diff --git a/src/vsg/state/ResourceHints.cpp b/src/vsg/state/ResourceHints.cpp index 9b45dddf0..9c05fe35d 100644 --- a/src/vsg/state/ResourceHints.cpp +++ b/src/vsg/state/ResourceHints.cpp @@ -74,6 +74,15 @@ void ResourceHints::read(Input& input) { input.read("viewportStateHint", viewportStateHint); } + + if (input.version_greater_equal(1, 1, 14)) + { + input.readObjects("dynamicData.bufferInfos", dynamicData.bufferInfos); + input.readObjects("dynamicData.imageInfos", dynamicData.imageInfos); + + input.read("containsPagedLOD", containsPagedLOD); + input.read("allocatedMemoryLimit", allocatedMemoryLimit); + } } void ResourceHints::write(Output& output) const @@ -127,4 +136,13 @@ void ResourceHints::write(Output& output) const { output.write("viewportStateHint", viewportStateHint); } + + if (output.version_greater_equal(1, 1, 14)) + { + output.writeObjects("dynamicData.bufferInfos", dynamicData.bufferInfos); + output.writeObjects("dynamicData.imageInfos", dynamicData.imageInfos); + + output.write("containsPagedLOD", containsPagedLOD); + output.write("allocatedMemoryLimit", allocatedMemoryLimit); + } } diff --git a/src/vsg/vk/Context.cpp b/src/vsg/vk/Context.cpp index fb2fa0215..cfee9f707 100644 --- a/src/vsg/vk/Context.cpp +++ b/src/vsg/vk/Context.cpp @@ -193,13 +193,17 @@ ShaderCompiler* Context::getOrCreateShaderCompiler() return shaderCompiler; } -void Context::reserve(const ResourceRequirements& requirements) +VkResult Context::reserve(ResourceRequirements& requirements) { CPU_INSTRUMENTATION_L2_NC(instrumentation, "Context reserve", COLOR_COMPILE) + VkResult result = deviceMemoryBufferPools->reserve(requirements); + resourceRequirements.maxSlots.merge(requirements.maxSlots); descriptorPools->reserve(requirements); + + return result; } ref_ptr Context::allocateDescriptorSet(DescriptorSetLayout* descriptorSetLayout) diff --git a/src/vsg/vk/Device.cpp b/src/vsg/vk/Device.cpp index c0f86f35d..61f00211b 100644 --- a/src/vsg/vk/Device.cpp +++ b/src/vsg/vk/Device.cpp @@ -205,3 +205,47 @@ bool Device::supportsDeviceExtension(const char* extensionName) const auto compare = [&](const char* rhs) { return strcmp(extensionName, rhs) == 0; }; return (std::find_if(enabledExtensions.begin(), enabledExtensions.end(), compare) != enabledExtensions.end()); } + +VkDeviceSize Device::availableMemory(bool includeMemoryPools) const +{ + VkDeviceSize available = 0; + + VkPhysicalDeviceMemoryBudgetPropertiesEXT memoryBudget; + memoryBudget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT; + memoryBudget.pNext = nullptr; + + VkPhysicalDeviceMemoryProperties2 dmp; + dmp.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2; + dmp.pNext = &memoryBudget; + + vkGetPhysicalDeviceMemoryProperties2(*(getPhysicalDevice()), &dmp); + + auto& memoryProperties = dmp.memoryProperties; + + VkMemoryPropertyFlags requiredPropertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + + std::set compatibleHeaps; + for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; ++i) + { + if ((memoryProperties.memoryTypes[i].propertyFlags & requiredPropertyFlags) == requiredPropertyFlags) // supported + { + compatibleHeaps.insert(memoryProperties.memoryTypes[i].heapIndex); + } + } + + for (const auto& heapIndex : compatibleHeaps) + { + VkDeviceSize heapAvailable = memoryBudget.heapBudget[heapIndex] - memoryBudget.heapUsage[heapIndex]; + available += heapAvailable; + } + + if (includeMemoryPools) + { + if (auto pool = deviceMemoryBufferPools.ref_ptr()) + { + available += pool->computeMemoryTotalAvailable(); + } + } + + return available; +} diff --git a/src/vsg/vk/MemoryBufferPools.cpp b/src/vsg/vk/MemoryBufferPools.cpp index 168aacb9e..10fe7150b 100644 --- a/src/vsg/vk/MemoryBufferPools.cpp +++ b/src/vsg/vk/MemoryBufferPools.cpp @@ -74,7 +74,7 @@ VkDeviceSize MemoryBufferPools::computeBufferTotalReserved() const return totalReservedSize; } -ref_ptr MemoryBufferPools::reserveBuffer(VkDeviceSize totalSize, VkDeviceSize alignment, VkBufferUsageFlags bufferUsageFlags, VkSharingMode sharingMode, VkMemoryPropertyFlags memoryProperties) +ref_ptr MemoryBufferPools::reserveBuffer(VkDeviceSize totalSize, VkDeviceSize alignment, VkBufferUsageFlags bufferUsageFlags, VkSharingMode sharingMode, VkMemoryPropertyFlags memoryPropertiesFlags) { ref_ptr bufferInfo = BufferInfo::create(); @@ -105,12 +105,6 @@ ref_ptr MemoryBufferPools::reserveBuffer(VkDeviceSize totalSize, VkD bufferInfo->range = totalSize; //debug(name, " : Created new Buffer ", bufferInfo->buffer.get(), " totalSize ", totalSize, " deviceSize = ", deviceSize); - - if (!bufferInfo->buffer->full()) - { - //debug(name, " inserting new Buffer into Context.bufferPools"); - bufferPools.push_back(bufferInfo->buffer); - } } //debug(name, " : bufferInfo->offset = ", bufferInfo->offset); @@ -118,26 +112,36 @@ ref_ptr MemoryBufferPools::reserveBuffer(VkDeviceSize totalSize, VkD VkMemoryRequirements memRequirements; vkGetBufferMemoryRequirements(*device, bufferInfo->buffer->vk(device->deviceID), &memRequirements); - auto reservedMemorySlot = reserveMemory(memRequirements, memoryProperties); + auto reservedMemorySlot = reserveMemory(memRequirements, memoryPropertiesFlags); if (!reservedMemorySlot.first) { - //debug(name, " : Completely Failed to space for MemoryBufferPools::reserveBuffer(", totalSize, ", ", alignment, ", ", bufferUsageFlags, ") "); + debug(name, " : Failed to space for MemoryBufferPools::reserveBuffer(", totalSize, ", ", alignment, ", ", bufferUsageFlags, ")"); + //throw Exception{"Error: Failed to allocate Buffer from MemoryBufferPool.", VK_ERROR_OUT_OF_DEVICE_MEMORY}; return {}; } //debug(name, " : Allocated new buffer, MemoryBufferPools::reserveBuffer(", totalSize, ", ", alignment, ", ", bufferUsageFlags, ") "); bufferInfo->buffer->bind(reservedMemorySlot.first, reservedMemorySlot.second); + //if (!bufferInfo->buffer->full()) + { + std::scoped_lock lock(_mutex); + //debug(name, " inserting new Buffer into Context.bufferPools"); + bufferPools.push_back(bufferInfo->buffer); + } + return bufferInfo; } -MemoryBufferPools::DeviceMemoryOffset MemoryBufferPools::reserveMemory(VkMemoryRequirements memRequirements, VkMemoryPropertyFlags memoryProperties, void* pNextAllocInfo) +MemoryBufferPools::DeviceMemoryOffset MemoryBufferPools::reserveMemory(VkMemoryRequirements memRequirements, VkMemoryPropertyFlags memoryPropertiesFlags, void* pNextAllocInfo) { + VkDeviceSize totalSize = memRequirements.size; + // vsg::info("MemoryBufferPools::reserveMemory() ", totalSize, ", device->availableMemory() = ", device->availableMemory()); + std::scoped_lock lock(_mutex); ref_ptr deviceMemory; - VkDeviceSize totalSize = memRequirements.size; MemorySlots::OptionalOffset reservedSlot(false, 0); for (auto& memoryPool : memoryPools) @@ -157,39 +161,158 @@ MemoryBufferPools::DeviceMemoryOffset MemoryBufferPools::reserveMemory(VkMemoryR if (!deviceMemory) { - VkDeviceSize deviceMemorySize = std::max(totalSize, minimumDeviceMemorySize); + VkDeviceSize availableSpace = std::max(minimumDeviceMemorySize, totalSize); + + if (allocatedMemoryLimit < 1.0) + { + VkPhysicalDeviceMemoryBudgetPropertiesEXT memoryBudget; + memoryBudget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT; + memoryBudget.pNext = nullptr; + + VkPhysicalDeviceMemoryProperties2 dmp; + dmp.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2; + dmp.pNext = &memoryBudget; + + vkGetPhysicalDeviceMemoryProperties2(*(device->getPhysicalDevice()), &dmp); + + auto& memoryProperties = dmp.memoryProperties; + + for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; ++i) + { + if ((memoryProperties.memoryTypes[i].propertyFlags & memoryPropertiesFlags) == memoryPropertiesFlags) // supported + { + uint32_t heapIndex = memoryProperties.memoryTypes[i].heapIndex; - // clamp to an aligned size - deviceMemorySize = ((deviceMemorySize + memRequirements.alignment - 1) / memRequirements.alignment) * memRequirements.alignment; + VkDeviceSize heapBudget = static_cast(static_cast(memoryBudget.heapBudget[heapIndex]) * allocatedMemoryLimit); + VkDeviceSize heapUsage = memoryBudget.heapUsage[heapIndex]; + VkDeviceSize heapAvailable = (heapUsage < heapBudget) ? heapBudget - heapUsage : 0; + availableSpace = heapAvailable; - //debug("Creating new local DeviceMemory"); - if (memRequirements.size < deviceMemorySize) memRequirements.size = deviceMemorySize; + break; + } + } + + VkDeviceSize minimumSpare = 0; //16*1024*1024; + if (availableSpace < minimumSpare) + availableSpace = 0; + else + availableSpace -= minimumSpare; + } - deviceMemory = vsg::DeviceMemory::create(device, memRequirements, memoryProperties, pNextAllocInfo); - if (deviceMemory) + if (totalSize <= availableSpace) { - reservedSlot = deviceMemory->reserve(totalSize); - if (!deviceMemory->full()) + if (availableSpace < minimumDeviceMemorySize) { - //debug(" inserting DeviceMemory into memoryPool ", deviceMemory.get()); - memoryPools.push_back(deviceMemory); + debug("MemoryBufferPools::reserveMemory(", totalSize, ") reducing minimumDeviceMemorySize = ", minimumDeviceMemorySize, " to ", availableSpace); + minimumDeviceMemorySize = availableSpace; + } + + VkDeviceSize deviceMemorySize = std::max(totalSize, minimumDeviceMemorySize); + + // clamp to an aligned size + deviceMemorySize = ((deviceMemorySize + memRequirements.alignment - 1) / memRequirements.alignment) * memRequirements.alignment; + + //debug("Creating new local DeviceMemory"); + if (memRequirements.size < deviceMemorySize) memRequirements.size = deviceMemorySize; + + deviceMemory = vsg::DeviceMemory::create(device, memRequirements, memoryPropertiesFlags, pNextAllocInfo); + if (deviceMemory) + { + reservedSlot = deviceMemory->reserve(totalSize); + // if (!deviceMemory->full()) + { + //debug(" inserting DeviceMemory into memoryPool ", deviceMemory.get()); + memoryPools.push_back(deviceMemory); + } } } - } - else - { - if (deviceMemory->full()) + else if (throwOutOfDeviceMemoryException) { - //debug("DeviceMemory is full ", deviceMemory.get()); + throw vsg::Exception{"MemoryBufferPools::reserve() out of memory", VK_ERROR_OUT_OF_DEVICE_MEMORY}; } } if (!reservedSlot.first) { - //debug("MemoryBufferPools::reserveMemory() Failed to reserve slot"); + debug("MemoryBufferPools::reserveMemory(", totalSize, ") failed, insufficient memory available."); return {}; } //debug("MemoryBufferPools::reserveMemory() allocated DeviceMemoryOffset(", deviceMemory, ", ", reservedSlot.second, ")"); return MemoryBufferPools::DeviceMemoryOffset(deviceMemory, reservedSlot.second); } + +VkResult MemoryBufferPools::reserve(ResourceRequirements& requirements) +{ + //vsg::info("MemoryBufferPools::reserve(ResourceRequirements& requirements) { "); + + auto deviceID = device->deviceID; + const auto& limits = device->getPhysicalDevice()->getProperties().limits; + + VkMemoryPropertyFlags memoryPropertiesFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; // VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + + // allocate bufferInfos + VkDeviceSize failedBufferMemory = 0; + for (auto& [properties, bufferInfos] : requirements.bufferInfos) + { + for (auto& bufferInfo : bufferInfos) + { + if (!bufferInfo->buffer) + { + VkDeviceSize alignment = 4; + if ((properties.usageFlags & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0) + alignment = limits.minUniformBufferOffsetAlignment; + else if ((properties.usageFlags & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0) + alignment = limits.minStorageBufferOffsetAlignment; + + auto newBufferInfo = reserveBuffer(bufferInfo->data->dataSize(), alignment, properties.usageFlags, properties.sharingMode, memoryPropertiesFlags); + if (newBufferInfo) + { + bufferInfo->buffer = newBufferInfo->buffer; + bufferInfo->offset = newBufferInfo->offset; + bufferInfo->range = newBufferInfo->range; + + newBufferInfo->release(); + + //info(" ALLOCATED usage = ", properties.usageFlags, ", alignment = ", alignment, ", bufferInfo->data = ", bufferInfo->data, ", offset = ", bufferInfo->offset, ", range = ", bufferInfo->range, ", buffer = ", bufferInfo->buffer, " ----- size = ", computeSize(*bufferInfo)); + } + else + { + failedBufferMemory += bufferInfo->computeDataSize(); + } + } + } + } + + // allocate images + VkDeviceSize failedImageMemory = 0; + for (auto& imageInfo : requirements.imageInfos) + { + if (imageInfo->imageView && imageInfo->imageView->image && imageInfo->imageView->image->getDeviceMemory(deviceID) == 0) + { + if (imageInfo->imageView->image->compile(*this) == VK_SUCCESS && imageInfo->imageView->image->getDeviceMemory(deviceID) != 0) + { + //info(" ALLOCATED imageInfo = ", imageInfo, ", imageView = ", imageInfo->imageView, " ----- size = ", computeSize(*imageInfo), " device memory = ", imageInfo->imageView->image->getDeviceMemory(deviceID), " offset = ", imageInfo->imageView->image->getMemoryOffset(deviceID)); + } + else + { + failedImageMemory += imageInfo->computeDataSize(); + } + } + } + + VkDeviceSize memoryRequired = failedBufferMemory + failedImageMemory; + + // all required resources allocated + if (memoryRequired == 0) + { + //info("MemoryBufferPools::reserve() memoryRequired = ", memoryRequired); + return VK_SUCCESS; + } + else + { + //VkDeviceSize memoryAvailable = device->availableMemory(false); + //info("MemoryBufferPools::reserve() out of memory: memoryAvailable = ", memoryAvailable, " memoryRequired = ", memoryRequired); + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } +} diff --git a/src/vsg/vk/ResourceRequirements.cpp b/src/vsg/vk/ResourceRequirements.cpp index e5e585b1e..6a5bb6ae7 100644 --- a/src/vsg/vk/ResourceRequirements.cpp +++ b/src/vsg/vk/ResourceRequirements.cpp @@ -17,6 +17,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include +#include +#include #include #include #include @@ -85,6 +88,9 @@ void ResourceRequirements::apply(const ResourceHints& resourceHints) dataTransferHint = resourceHints.dataTransferHint; viewportStateHint = resourceHints.viewportStateHint; + + dynamicData.add(resourceHints.dynamicData); + containsPagedLOD = containsPagedLOD | resourceHints.containsPagedLOD; } ////////////////////////////////////////////////////////////////////// @@ -104,6 +110,9 @@ ref_ptr CollectResourceRequirements::createResourceHints(uint32_t poolSize.descriptorCount = poolSize.descriptorCount * tileMultiplier; } + resourceHints->dynamicData = requirements.dynamicData; + resourceHints->containsPagedLOD = requirements.containsPagedLOD; + return resourceHints; } @@ -133,23 +142,18 @@ void CollectResourceRequirements::apply(const ResourceHints& resourceHints) void CollectResourceRequirements::apply(const Node& node) { - bool hasResourceHints = checkForResourceHints(node); - if (hasResourceHints) ++_numResourceHintsAbove; + checkForResourceHints(node); node.traverse(*this); - - if (hasResourceHints) --_numResourceHintsAbove; } void CollectResourceRequirements::apply(const PagedLOD& plod) { - bool hasResourceHints = checkForResourceHints(plod); - if (hasResourceHints) ++_numResourceHintsAbove; - requirements.containsPagedLOD = true; - plod.traverse(*this); - if (hasResourceHints) --_numResourceHintsAbove; + checkForResourceHints(plod); + + plod.traverse(*this); } void CollectResourceRequirements::apply(const StateCommand& stateCommand) @@ -192,8 +196,27 @@ void CollectResourceRequirements::apply(const DescriptorBuffer& descriptorBuffer { if (registerDescriptor(descriptorBuffer)) { + BufferProperties properties; + + switch (descriptorBuffer.descriptorType) + { + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + properties.usageFlags = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + break; + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + properties.usageFlags = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + break; + default: + break; + } + + properties.usageFlags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + properties.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + //info("CollectResourceRequirements::apply(const DescriptorBuffer& descriptorBuffer) ", &descriptorBuffer); - for (const auto& bufferInfo : descriptorBuffer.bufferInfoList) apply(bufferInfo); + for (const auto& bufferInfo : descriptorBuffer.bufferInfoList) apply(bufferInfo, properties); } } @@ -275,48 +298,121 @@ void CollectResourceRequirements::apply(const Bin& bin) void CollectResourceRequirements::apply(const Geometry& geometry) { - for (const auto& bufferInfo : geometry.arrays) apply(bufferInfo); - apply(geometry.indices); + BufferProperties properties{VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE}; + if (requirements.dataTransferHint == COMPILE_TRAVERSAL_USE_TRANSFER_TASK) properties.usageFlags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + if (geometry.indices) properties.usageFlags |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + + for (const auto& bufferInfo : geometry.arrays) apply(bufferInfo, properties); + + if (geometry.indices) apply(geometry.indices, properties); } void CollectResourceRequirements::apply(const VertexDraw& vd) { - for (const auto& bufferInfo : vd.arrays) apply(bufferInfo); + BufferProperties properties{VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE}; + if (requirements.dataTransferHint == COMPILE_TRAVERSAL_USE_TRANSFER_TASK) properties.usageFlags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + for (const auto& bufferInfo : vd.arrays) apply(bufferInfo, properties); } void CollectResourceRequirements::apply(const VertexIndexDraw& vid) { - for (const auto& bufferInfo : vid.arrays) apply(bufferInfo); - apply(vid.indices); + BufferProperties properties{VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE}; + if (requirements.dataTransferHint == COMPILE_TRAVERSAL_USE_TRANSFER_TASK) properties.usageFlags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + for (const auto& bufferInfo : vid.arrays) apply(bufferInfo, properties); + apply(vid.indices, properties); +} + +void CollectResourceRequirements::apply(const InstanceNode& in) +{ + BufferProperties properties{VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE}; + if (requirements.dataTransferHint == COMPILE_TRAVERSAL_USE_TRANSFER_TASK) properties.usageFlags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + if (in.translations) apply(in.translations, properties); + if (in.rotations) apply(in.rotations, properties); + if (in.scales) apply(in.scales, properties); + if (in.colors) apply(in.colors, properties); + + in.traverse(*this); +} + +void CollectResourceRequirements::apply(const InstanceDraw& id) +{ + BufferProperties properties{VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE}; + if (requirements.dataTransferHint == COMPILE_TRAVERSAL_USE_TRANSFER_TASK) properties.usageFlags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + for (const auto& bufferInfo : id.arrays) apply(bufferInfo, properties); +} + +void CollectResourceRequirements::apply(const InstanceDrawIndexed& idi) +{ + BufferProperties properties{VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE}; + if (requirements.dataTransferHint == COMPILE_TRAVERSAL_USE_TRANSFER_TASK) properties.usageFlags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + for (const auto& bufferInfo : idi.arrays) apply(bufferInfo, properties); + apply(idi.indices, properties); } void CollectResourceRequirements::apply(const BindVertexBuffers& bvb) { - for (const auto& bufferInfo : bvb.arrays) apply(bufferInfo); + BufferProperties properties{VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE}; + if (requirements.dataTransferHint == COMPILE_TRAVERSAL_USE_TRANSFER_TASK) properties.usageFlags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + for (const auto& bufferInfo : bvb.arrays) apply(bufferInfo, properties); } void CollectResourceRequirements::apply(const BindIndexBuffer& bib) { - apply(bib.indices); + BufferProperties properties{VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE}; + if (requirements.dataTransferHint == COMPILE_TRAVERSAL_USE_TRANSFER_TASK) properties.usageFlags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + apply(bib.indices, properties); } -void CollectResourceRequirements::apply(ref_ptr bufferInfo) +void CollectResourceRequirements::apply(ref_ptr bufferInfo, BufferProperties bufferProperties) { - if (bufferInfo && bufferInfo->data && bufferInfo->data->dynamic()) + if (bufferInfo) { - requirements.dynamicData.bufferInfos.push_back(bufferInfo); + if (bufferInfo->data && bufferInfo->data->dynamic()) bufferProperties.usageFlags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + auto& bufferInfos = requirements.bufferInfos[bufferProperties]; + if (bufferInfos.count(bufferInfo) == 0) + { + bufferInfos.insert(bufferInfo); + + if (bufferInfo->data) + { + if (bufferInfo->data->dynamic()) + { + requirements.dynamicData.bufferInfos.push_back(bufferInfo); + } + } + } } } void CollectResourceRequirements::apply(ref_ptr imageInfo) { - if (imageInfo && imageInfo->imageView && imageInfo->imageView->image) + if (imageInfo && requirements.imageInfos.count(imageInfo) == 0) { - // check for dynamic data - auto& data = imageInfo->imageView->image->data; - if (data && data->dynamic()) + if (imageInfo->imageView && imageInfo->imageView->image) + { + requirements.imageInfos.insert(imageInfo); + + // check for dynamic data + auto& data = imageInfo->imageView->image->data; + if (data) + { + if (data->dynamic()) + { + requirements.dynamicData.imageInfos.push_back(imageInfo); + } + } + } + else { - requirements.dynamicData.imageInfos.push_back(imageInfo); + vsg::debug("CollectResourceRequirements::apply() problem ImageInfo ", imageInfo, " { ", imageInfo->imageView, "}"); } } }