diff --git a/android/jni/com/mapswithme/vulkan/android_vulkan_context_factory.cpp b/android/jni/com/mapswithme/vulkan/android_vulkan_context_factory.cpp index 3b47aeb23d..6d233a8b6e 100644 --- a/android/jni/com/mapswithme/vulkan/android_vulkan_context_factory.cpp +++ b/android/jni/com/mapswithme/vulkan/android_vulkan_context_factory.cpp @@ -179,10 +179,10 @@ AndroidVulkanContextFactory::AndroidVulkanContextFactory() vkGetPhysicalDeviceProperties(m_gpu, &gpuProperties); VkPhysicalDeviceMemoryProperties memoryProperties; vkGetPhysicalDeviceMemoryProperties(m_gpu, &memoryProperties); - m_objectManager = make_unique_dp(m_device, gpuProperties.limits, memoryProperties, renderingQueueFamilyIndex); + m_drawContext = make_unique_dp(m_vulkanInstance, m_gpu, gpuProperties, m_device, renderingQueueFamilyIndex, make_ref(m_objectManager)); diff --git a/drape/attribute_buffer_mutator.hpp b/drape/attribute_buffer_mutator.hpp index 7155b931e9..776f2c545e 100644 --- a/drape/attribute_buffer_mutator.hpp +++ b/drape/attribute_buffer_mutator.hpp @@ -17,7 +17,7 @@ struct MutateRegion MutateRegion() : m_offset(0), m_count(0) {} MutateRegion(uint32_t offset, uint32_t count) : m_offset(offset), m_count(count) {} - uint32_t m_offset; // Offset from buffer begin in "Elements" not in bytes + uint32_t m_offset; // Offset from buffer begin in "Elements" not in bytes. uint32_t m_count; // Count of "Elements". }; diff --git a/drape/vulkan/vulkan_base_context.cpp b/drape/vulkan/vulkan_base_context.cpp index 666e8405f0..d3925628a1 100644 --- a/drape/vulkan/vulkan_base_context.cpp +++ b/drape/vulkan/vulkan_base_context.cpp @@ -60,5 +60,19 @@ void VulkanBaseContext::ResetSurface() //TODO: reset swapchains, image views and so on. m_surface.reset(); } + +void VulkanBaseContext::Present() +{ + // Resetting of the default staging buffer must be before submitting the queue. + // It guarantees the graphics data coherence. + m_objectManager->FlushDefaultStagingBuffer(); + + // TODO: submit queue, wait for finishing of rendering. + + // Resetting of the default staging buffer and collecting destroyed objects must be + // only after the finishing of rendering. It prevents data collisions. + m_objectManager->ResetDefaultStagingBuffer(); + m_objectManager->CollectObjects(); +} } // namespace vulkan } // namespace dp diff --git a/drape/vulkan/vulkan_base_context.hpp b/drape/vulkan/vulkan_base_context.hpp index 256d259a55..fc40c5493c 100644 --- a/drape/vulkan/vulkan_base_context.hpp +++ b/drape/vulkan/vulkan_base_context.hpp @@ -26,7 +26,7 @@ public: VkDevice device, uint32_t renderingQueueFamilyIndex, ref_ptr objectManager); - void Present() override {} + void Present() override; void MakeCurrent() override {} void DoneCurrent() override {} bool Validate() override { return true; } diff --git a/drape/vulkan/vulkan_gpu_buffer_impl.cpp b/drape/vulkan/vulkan_gpu_buffer_impl.cpp index e559e926b5..dc83d65c52 100644 --- a/drape/vulkan/vulkan_gpu_buffer_impl.cpp +++ b/drape/vulkan/vulkan_gpu_buffer_impl.cpp @@ -1,4 +1,5 @@ #include "drape/vulkan/vulkan_gpu_buffer_impl.hpp" +#include "drape/vulkan/vulkan_utils.hpp" #include "base/macros.hpp" @@ -16,49 +17,108 @@ VulkanGPUBuffer::VulkanGPUBuffer(ref_ptr context, void const Resize(context, data, capacity); } -void VulkanGPUBuffer::UploadData(ref_ptr context, void const * data, - uint32_t elementCount) -{ - //TODO: Upload must be called only, there is no active command buffer. - uint32_t const currentSize = GetCurrentSize(); - uint8_t const elementSize = GetElementSize(); - ASSERT(GetCapacity() >= elementCount + currentSize, - ("Not enough memory to upload ", elementCount, " elements")); - - uint32_t const byteOffset = currentSize * elementSize; - uint32_t const sizeInBytes = elementCount * elementSize; - CHECK(false, ()); -// uint8_t * gpuPtr = static_cast([m_metalBuffer contents]) + byteOffset; -// memcpy(gpuPtr, data, sizeInBytes); - BufferBase::UploadData(elementCount); -} - void * VulkanGPUBuffer::Map(ref_ptr context, uint32_t elementOffset, uint32_t elementCount) { - //TODO: Stage + map. - UNUSED_VALUE(elementCount); + CHECK(m_objectManager != nullptr, ()); + VkDevice device = context->GetDevice(); + uint32_t const elementSize = GetElementSize(); - uint32_t const byteOffset = elementOffset * elementSize; - CHECK(false, ()); - uint8_t * bufferPointer = nullptr;//static_cast([m_metalBuffer contents]) + byteOffset; - return bufferPointer; + m_mappingSizeInBytes = elementCount * elementSize; + m_mappingByteOffset = elementOffset * elementSize; + + VkCommandBuffer commandBuffer = context->GetCurrentCommandBuffer(); + CHECK(commandBuffer != nullptr, ()); + + // Copy to default or temporary staging buffer. + m_defaultStagingBuffer = m_objectManager->GetDefaultStagingBuffer(m_mappingSizeInBytes); + void * gpuPtr; + if (m_defaultStagingBuffer.m_stagingData) + { + gpuPtr = m_defaultStagingBuffer.m_pointer; + } + else + { + m_temporaryStagingBuffer = m_objectManager->CreateBuffer(VulkanMemoryManager::ResourceType::Staging, + m_mappingSizeInBytes, 0 /* batcherHash */); + + CHECK_VK_CALL(vkMapMemory(device, m_temporaryStagingBuffer.m_allocation->m_memory, + m_temporaryStagingBuffer.m_allocation->m_alignedOffset, + m_temporaryStagingBuffer.m_allocation->m_alignedSize, 0, &gpuPtr)); + } + return gpuPtr; } void VulkanGPUBuffer::UpdateData(void * gpuPtr, void const * data, uint32_t elementOffset, uint32_t elementCount) { + CHECK(gpuPtr != nullptr, ()); uint32_t const elementSize = GetElementSize(); uint32_t const byteOffset = elementOffset * elementSize; uint32_t const byteCount = elementCount * elementSize; - - ASSERT(gpuPtr != nullptr, ()); - memcpy((uint8_t *)gpuPtr + byteOffset, data, byteCount); + memcpy(static_cast(gpuPtr) + byteOffset, data, byteCount); } void VulkanGPUBuffer::Unmap(ref_ptr context) { - //TODO: Unmap + barrier. + VkDevice device = context->GetDevice(); + VkCommandBuffer commandBuffer = context->GetCurrentCommandBuffer(); + CHECK(commandBuffer != nullptr, ()); + + VkBuffer stagingBuffer; + uint32_t offset; + if (m_defaultStagingBuffer.m_stagingData) + { + stagingBuffer = m_defaultStagingBuffer.m_stagingData.m_stagingBuffer; + offset = m_defaultStagingBuffer.m_stagingData.m_offset; + } + else + { + CHECK(m_temporaryStagingBuffer.m_buffer != 0, ()); + + stagingBuffer = m_temporaryStagingBuffer.m_buffer; + offset = 0; + + VkMappedMemoryRange mappedRange = {}; + mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + mappedRange.memory = m_temporaryStagingBuffer.m_allocation->m_memory; + mappedRange.offset = m_temporaryStagingBuffer.m_allocation->m_alignedOffset; + mappedRange.size = m_temporaryStagingBuffer.m_allocation->m_alignedSize; + CHECK_VK_CALL(vkFlushMappedMemoryRanges(device, 1, &mappedRange)); + vkUnmapMemory(device, m_temporaryStagingBuffer.m_allocation->m_memory); + CHECK_VK_CALL(vkBindBufferMemory(device, m_temporaryStagingBuffer.m_buffer, + m_temporaryStagingBuffer.m_allocation->m_memory, + m_temporaryStagingBuffer.m_allocation->m_alignedOffset)); + } + + // Schedule command to copy from the staging buffer to the geometry buffer. + VkBufferCopy copyRegion = {}; + copyRegion.dstOffset = m_mappingByteOffset; + copyRegion.srcOffset = offset; + copyRegion.size = m_mappingSizeInBytes; + + vkCmdCopyBuffer(commandBuffer, stagingBuffer, m_geometryBuffer.m_buffer, + 1, ©Region); + + // Set up a barrier to prevent data collisions. + VkBufferMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + barrier.pNext = nullptr; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.buffer = m_geometryBuffer.m_buffer; + barrier.offset = m_mappingByteOffset; + barrier.size = m_mappingSizeInBytes; + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, 0, nullptr, + 1, &barrier, 0, nullptr); + + m_defaultStagingBuffer = {}; + m_objectManager->DestroyObject(m_temporaryStagingBuffer); + m_mappingByteOffset = 0; + m_mappingSizeInBytes = 0; } void VulkanGPUBuffer::Resize(ref_ptr context, void const * data, @@ -66,21 +126,36 @@ void VulkanGPUBuffer::Resize(ref_ptr context, void const * da { BufferBase::Resize(elementCount); - CHECK(false, ()); -// id device = context->GetMetalDevice(); -// uint32_t const sizeInBytes = GetCapacity() * GetElementSize(); -// if (data != nil) -// { -// m_metalBuffer = [device newBufferWithBytes:data -// length:sizeInBytes -// options:MTLResourceCPUCacheModeWriteCombined]; -// } -// else -// { -// m_metalBuffer = [device newBufferWithLength:sizeInBytes -// options:MTLResourceCPUCacheModeWriteCombined]; -// } - + m_objectManager = context->GetObjectManager(); + VkDevice device = context->GetDevice(); + + uint32_t const sizeInBytes = GetCapacity() * GetElementSize(); + + m_geometryBuffer = m_objectManager->CreateBuffer(VulkanMemoryManager::ResourceType::Geometry, + sizeInBytes, 0 /* batcherHash */); + if (data != nullptr) + { + void * gpuPtr = nullptr; + CHECK_VK_CALL(vkMapMemory(device, m_geometryBuffer.m_allocation->m_memory, + m_geometryBuffer.m_allocation->m_alignedOffset, + m_geometryBuffer.m_allocation->m_alignedSize, 0, &gpuPtr)); + memcpy(gpuPtr, data, sizeInBytes); + if (!m_geometryBuffer.m_allocation->m_isCoherent) + { + VkMappedMemoryRange mappedRange = {}; + mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + mappedRange.memory = m_geometryBuffer.m_allocation->m_memory; + mappedRange.offset = m_geometryBuffer.m_allocation->m_alignedOffset; + mappedRange.size = m_geometryBuffer.m_allocation->m_alignedSize; + CHECK_VK_CALL(vkFlushMappedMemoryRanges(device, 1, &mappedRange)); + } + vkUnmapMemory(device, m_geometryBuffer.m_allocation->m_memory); + } + + CHECK_VK_CALL(vkBindBufferMemory(device, m_geometryBuffer.m_buffer, + m_geometryBuffer.m_allocation->m_memory, + m_geometryBuffer.m_allocation->m_alignedOffset)); + // If we have already set up data, we have to call SetDataSize. if (data != nullptr) SetDataSize(elementCount); diff --git a/drape/vulkan/vulkan_gpu_buffer_impl.hpp b/drape/vulkan/vulkan_gpu_buffer_impl.hpp index 7664973408..045dff9054 100644 --- a/drape/vulkan/vulkan_gpu_buffer_impl.hpp +++ b/drape/vulkan/vulkan_gpu_buffer_impl.hpp @@ -4,6 +4,7 @@ #include "drape/data_buffer_impl.hpp" #include "drape/pointers.hpp" #include "drape/vulkan/vulkan_base_context.hpp" +#include "drape/vulkan/vulkan_object_manager.hpp" #include "base/assert.hpp" @@ -22,21 +23,23 @@ class VulkanGPUBuffer : public BufferBase public: VulkanGPUBuffer(ref_ptr context, void const * data, uint8_t elementSize, uint32_t capacity, uint64_t batcherHash); - - void UploadData(ref_ptr context, void const * data, uint32_t elementCount); - + void * Map(ref_ptr context, uint32_t elementOffset, uint32_t elementCount); void UpdateData(void * gpuPtr, void const * data, uint32_t elementOffset, uint32_t elementCount); void Unmap(ref_ptr context); - VkBuffer GetVulkanBuffer() const { return m_vulkanBuffer; } - protected: void Resize(ref_ptr context, void const * data, uint32_t elementCount); - VkBuffer m_vulkanBuffer; + ref_ptr m_objectManager; + VulkanObject m_geometryBuffer; uint64_t m_batcherHash; + + VulkanObjectManager::StagingPointer m_defaultStagingBuffer; + VulkanObject m_temporaryStagingBuffer; + uint32_t m_mappingByteOffset = 0; + uint32_t m_mappingSizeInBytes = 0; }; class VulkanGpuBufferImpl : public DataBufferImpl @@ -56,7 +59,9 @@ public: void UploadData(ref_ptr context, void const * data, uint32_t elementCount) override { - m_buffer->UploadData(context, data, elementCount); + // For Vulkan we can't update buffers by means of UploadData, because UploadData + // can be called from BR, where command buffers are not available. + CHECK(false, ("UploadData is unsupported for Vulkan buffers (use Map-Copy-Unmap).")); } void UpdateData(void * destPtr, void const * srcPtr, uint32_t elementOffset, @@ -76,8 +81,6 @@ public: m_buffer->Unmap(context); } - VkBuffer GetVulkanBuffer() const { return m_buffer->GetVulkanBuffer(); } - void Bind() override {} }; } // namespace vulkan diff --git a/drape/vulkan/vulkan_memory_manager.cpp b/drape/vulkan/vulkan_memory_manager.cpp index 1b4c3cfcba..6aceb99f1d 100644 --- a/drape/vulkan/vulkan_memory_manager.cpp +++ b/drape/vulkan/vulkan_memory_manager.cpp @@ -2,6 +2,8 @@ #include "drape/vulkan/vulkan_utils.hpp" #include "base/assert.hpp" +#include "base/macros.hpp" +#include "base/math.hpp" #include #include @@ -22,9 +24,9 @@ std::array const kMinBlockSizeIn std::array const kDesiredSizeInBytes = { - 50 * 1024 * 1024, // Geometry + 80 * 1024 * 1024, // Geometry std::numeric_limits::max(), // Uniform (unlimited) - 10 * 1024 * 1024, // Staging + 20 * 1024 * 1024, // Staging std::numeric_limits::max(), // Image (unlimited) }; @@ -34,10 +36,13 @@ VkMemoryPropertyFlags GetMemoryPropertyFlags(VulkanMemoryManager::ResourceType r switch (resourceType) { case VulkanMemoryManager::ResourceType::Geometry: - case VulkanMemoryManager::ResourceType::Staging: fallbackTypeBits = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + case VulkanMemoryManager::ResourceType::Staging: + // No fallback. + return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + case VulkanMemoryManager::ResourceType::Uniform: // No fallback. return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; @@ -51,15 +56,15 @@ VkMemoryPropertyFlags GetMemoryPropertyFlags(VulkanMemoryManager::ResourceType r } return 0; } - -uint32_t GetAligned(uint32_t value, uint32_t alignment) -{ - if (alignment == 0) - return value; - return (value + alignment - 1) & ~(alignment - 1); -} } // namespace +VulkanMemoryManager::VulkanMemoryManager(VkDevice device, VkPhysicalDeviceLimits const & deviceLimits, + VkPhysicalDeviceMemoryProperties const & memoryProperties) + : m_device(device) + , m_deviceLimits(deviceLimits) + , m_memoryProperties(memoryProperties) +{} + VulkanMemoryManager::~VulkanMemoryManager() { for (size_t i = 0; i < kResourcesCount; ++i) @@ -93,17 +98,37 @@ boost::optional VulkanMemoryManager::GetMemoryTypeIndex(uint32_t typeB uint32_t VulkanMemoryManager::GetOffsetAlignment(ResourceType resourceType) const { if (resourceType == ResourceType::Uniform) - return static_cast(m_deviceLimits.minUniformBufferOffsetAlignment); + { + static uint32_t const kUniformAlignment = + base::LCM(static_cast(m_deviceLimits.minUniformBufferOffsetAlignment), + static_cast(m_deviceLimits.nonCoherentAtomSize)); + return kUniformAlignment; + } - return static_cast(m_deviceLimits.minMemoryMapAlignment); + static uint32_t const kAlignment = + base::LCM(static_cast(m_deviceLimits.minMemoryMapAlignment), + static_cast(m_deviceLimits.nonCoherentAtomSize)); + return kAlignment; +} + +uint32_t VulkanMemoryManager::GetSizeAlignment(VkMemoryRequirements const & memReqs) const +{ + return base::LCM(static_cast(memReqs.alignment), + static_cast(m_deviceLimits.nonCoherentAtomSize)); +} + +uint32_t VulkanMemoryManager::GetAligned(uint32_t value, uint32_t alignment) const +{ + if (alignment == 0) + return value; + return (value + alignment - 1) & ~(alignment - 1); } VulkanMemoryManager::AllocationPtr VulkanMemoryManager::Allocate(ResourceType resourceType, VkMemoryRequirements memReqs, uint64_t blockHash) { - auto const alignedSize = GetAligned(static_cast(memReqs.size), - static_cast(memReqs.alignment)); + auto const alignedSize = GetAligned(static_cast(memReqs.size), GetSizeAlignment(memReqs)); // Looking for an existed block. { auto & m = m_memory[static_cast(resourceType)]; diff --git a/drape/vulkan/vulkan_memory_manager.hpp b/drape/vulkan/vulkan_memory_manager.hpp index 18449c0838..c95250c643 100644 --- a/drape/vulkan/vulkan_memory_manager.hpp +++ b/drape/vulkan/vulkan_memory_manager.hpp @@ -21,12 +21,7 @@ class VulkanMemoryManager { public: VulkanMemoryManager(VkDevice device, VkPhysicalDeviceLimits const & deviceLimits, - VkPhysicalDeviceMemoryProperties const & memoryProperties) - : m_device(device) - , m_deviceLimits(deviceLimits) - , m_memoryProperties(memoryProperties) - {} - + VkPhysicalDeviceMemoryProperties const & memoryProperties); ~VulkanMemoryManager(); enum class ResourceType : uint8_t @@ -45,8 +40,8 @@ public: { uint64_t const m_blockHash; VkDeviceMemory const m_memory; - uint32_t const m_offset; - uint32_t const m_size; + uint32_t const m_alignedOffset; + uint32_t const m_alignedSize; ResourceType const m_resourceType; bool const m_isCoherent; @@ -54,8 +49,8 @@ public: uint32_t offset, uint32_t size, bool isCoherent) : m_blockHash(blockHash) , m_memory(memory) - , m_offset(offset) - , m_size(size) + , m_alignedOffset(offset) + , m_alignedSize(size) , m_resourceType(resourceType) , m_isCoherent(isCoherent) {} @@ -69,10 +64,13 @@ public: void Deallocate(AllocationPtr ptr); void EndDeallocationSession(); + uint32_t GetOffsetAlignment(ResourceType resourceType) const; + uint32_t GetSizeAlignment(VkMemoryRequirements const & memReqs) const; + uint32_t GetAligned(uint32_t value, uint32_t alignment) const; + private: boost::optional GetMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) const; - uint32_t GetOffsetAlignment(ResourceType resourceType) const; VkDevice const m_device; VkPhysicalDeviceLimits const m_deviceLimits; diff --git a/drape/vulkan/vulkan_mesh_object_impl.cpp b/drape/vulkan/vulkan_mesh_object_impl.cpp index 26d3c6827b..66b693de14 100644 --- a/drape/vulkan/vulkan_mesh_object_impl.cpp +++ b/drape/vulkan/vulkan_mesh_object_impl.cpp @@ -1,7 +1,6 @@ #include "drape/mesh_object.hpp" #include "drape/pointers.hpp" #include "drape/vulkan/vulkan_base_context.hpp" -#include "drape/vulkan/vulkan_device_holder.hpp" #include "drape/vulkan/vulkan_utils.hpp" #include "base/assert.hpp" @@ -27,6 +26,7 @@ public: { ref_ptr vulkanContext = context; m_objectManager = vulkanContext->GetObjectManager(); + VkDevice device = vulkanContext->GetDevice(); m_geometryBuffers.resize(m_mesh->m_buffers.size()); for (size_t i = 0; i < m_mesh->m_buffers.size(); i++) @@ -38,7 +38,24 @@ public: sizeof(m_mesh->m_buffers[i].m_data[0])); m_geometryBuffers[i] = m_objectManager->CreateBuffer(VulkanMemoryManager::ResourceType::Geometry, sizeInBytes, 0 /* batcherHash */); - //TODO: map, copy, unmap, bind. + void * gpuPtr = nullptr; + CHECK_VK_CALL(vkMapMemory(device, m_geometryBuffers[i].m_allocation->m_memory, + m_geometryBuffers[i].m_allocation->m_alignedOffset, + m_geometryBuffers[i].m_allocation->m_alignedSize, 0, &gpuPtr)); + memcpy(gpuPtr, m_mesh->m_buffers[i].m_data.data(), sizeInBytes); + if (!m_geometryBuffers[i].m_allocation->m_isCoherent) + { + VkMappedMemoryRange mappedRange = {}; + mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + mappedRange.memory = m_geometryBuffers[i].m_allocation->m_memory; + mappedRange.offset = m_geometryBuffers[i].m_allocation->m_alignedOffset; + mappedRange.size = m_geometryBuffers[i].m_allocation->m_alignedSize; + CHECK_VK_CALL(vkFlushMappedMemoryRanges(device, 1, &mappedRange)); + } + vkUnmapMemory(device, m_geometryBuffers[i].m_allocation->m_memory); + CHECK_VK_CALL(vkBindBufferMemory(device, m_geometryBuffers[i].m_buffer, + m_geometryBuffers[i].m_allocation->m_memory, + m_geometryBuffers[i].m_allocation->m_alignedOffset)); } } @@ -53,14 +70,44 @@ public: { CHECK_LESS(bufferInd, static_cast(m_geometryBuffers.size()), ()); + ref_ptr vulkanContext = context; + VkCommandBuffer commandBuffer = vulkanContext->GetCurrentCommandBuffer(); + CHECK(commandBuffer != nullptr, ()); + auto & buffer = m_mesh->m_buffers[bufferInd]; CHECK(!buffer.m_data.empty(), ()); - //TODO: stage, map, copy, unmap, barrier. + // Copy to default or temporary staging buffer. + auto const sizeInBytes = static_cast(buffer.m_data.size() * sizeof(buffer.m_data[0])); + auto staging = m_objectManager->CopyWithDefaultStagingBuffer(sizeInBytes, buffer.m_data.data()); + if (!staging) + { + staging = m_objectManager->CopyWithTemporaryStagingBuffer(sizeInBytes, buffer.m_data.data()); + CHECK(staging, ()); + } -// uint8_t * bufferPointer = (uint8_t *)[m_geometryBuffers[bufferInd] contents]; -// auto const sizeInBytes = buffer.m_data.size() * sizeof(buffer.m_data[0]); -// memcpy(bufferPointer, buffer.m_data.data(), sizeInBytes); + // Schedule command to copy from the staging buffer to our geometry buffer. + VkBufferCopy copyRegion = {}; + copyRegion.dstOffset = 0; + copyRegion.srcOffset = staging.m_offset; + copyRegion.size = sizeInBytes; + vkCmdCopyBuffer(commandBuffer, staging.m_stagingBuffer, m_geometryBuffers[bufferInd].m_buffer, + 1, ©Region); + + // Set up a barrier to prevent data collisions. + VkBufferMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + barrier.pNext = nullptr; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.buffer = m_geometryBuffers[bufferInd].m_buffer; + barrier.offset = 0; + barrier.size = sizeInBytes; + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, 0, nullptr, + 1, &barrier, 0, nullptr); } void DrawPrimitives(ref_ptr context, uint32_t verticesCount) override diff --git a/drape/vulkan/vulkan_object_manager.cpp b/drape/vulkan/vulkan_object_manager.cpp index c7e86e4312..52c8da6fbf 100644 --- a/drape/vulkan/vulkan_object_manager.cpp +++ b/drape/vulkan/vulkan_object_manager.cpp @@ -5,6 +5,8 @@ namespace dp { namespace vulkan { +uint32_t constexpr kDefaultStagingBufferSizeInBytes = 10 * 1024 * 1024; + VulkanObjectManager::VulkanObjectManager(VkDevice device, VkPhysicalDeviceLimits const & deviceLimits, VkPhysicalDeviceMemoryProperties const & memoryProperties, uint32_t queueFamilyIndex) @@ -13,10 +15,30 @@ VulkanObjectManager::VulkanObjectManager(VkDevice device, VkPhysicalDeviceLimits , m_memoryManager(device, deviceLimits, memoryProperties) { m_queueToDestroy.reserve(50); + + m_defaultStagingBuffer = CreateBuffer(VulkanMemoryManager::ResourceType::Staging, + kDefaultStagingBufferSizeInBytes, 0 /* batcherHash */); + VkMemoryRequirements memReqs = {}; + vkGetBufferMemoryRequirements(m_device, m_defaultStagingBuffer.m_buffer, &memReqs); + m_defaultStagingBufferAlignment = m_memoryManager.GetSizeAlignment(memReqs); + + CHECK_VK_CALL(vkBindBufferMemory(device, m_defaultStagingBuffer.m_buffer, + m_defaultStagingBuffer.m_allocation->m_memory, + m_defaultStagingBuffer.m_allocation->m_alignedOffset)); + + CHECK_VK_CALL(vkMapMemory(device, m_defaultStagingBuffer.m_allocation->m_memory, + m_defaultStagingBuffer.m_allocation->m_alignedOffset, + m_defaultStagingBuffer.m_allocation->m_alignedSize, 0, + reinterpret_cast(&m_defaultStagingBufferPtr))); + + if (!m_defaultStagingBuffer.m_allocation->m_isCoherent) + m_regionsToFlush.reserve(10); } VulkanObjectManager::~VulkanObjectManager() { + vkUnmapMemory(m_device, m_defaultStagingBuffer.m_allocation->m_memory); + DestroyObject(m_defaultStagingBuffer); CollectObjects(); } @@ -102,6 +124,97 @@ void VulkanObjectManager::DestroyObject(VulkanObject object) m_queueToDestroy.push_back(std::move(object)); } +VulkanObjectManager::StagingPointer VulkanObjectManager::GetDefaultStagingBuffer(uint32_t sizeInBytes) +{ + std::lock_guard lock(m_mutex); + + auto const alignedSize = m_memoryManager.GetAligned(sizeInBytes, m_defaultStagingBufferAlignment); + if (m_defaultStagingBufferOffset + alignedSize > kDefaultStagingBufferSizeInBytes) + return {}; + + auto const alignedOffset = m_defaultStagingBufferOffset; + uint8_t * ptr = m_defaultStagingBufferPtr + alignedOffset; + + if (!m_defaultStagingBuffer.m_allocation->m_isCoherent) + { + VkMappedMemoryRange mappedRange = {}; + mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + mappedRange.memory = m_defaultStagingBuffer.m_allocation->m_memory; + mappedRange.offset = alignedOffset; + mappedRange.size = alignedSize; + m_regionsToFlush.push_back(std::move(mappedRange)); + } + + // Update offset and align it. + m_defaultStagingBufferOffset += alignedSize; + auto const alignment = m_memoryManager.GetOffsetAlignment(VulkanMemoryManager::ResourceType::Staging); + m_defaultStagingBufferOffset = m_memoryManager.GetAligned(m_defaultStagingBufferOffset, alignment); + + StagingPointer stagingDataPtr; + stagingDataPtr.m_stagingData.m_stagingBuffer = m_defaultStagingBuffer.m_buffer; + stagingDataPtr.m_stagingData.m_offset = alignedOffset; + stagingDataPtr.m_pointer = ptr; + return stagingDataPtr; +} + +VulkanObjectManager::StagingData VulkanObjectManager::CopyWithDefaultStagingBuffer(uint32_t sizeInBytes, + void * data) +{ + auto s = GetDefaultStagingBuffer(sizeInBytes); + memcpy(s.m_pointer, data, sizeInBytes); + return s.m_stagingData; +} + +VulkanObjectManager::StagingData VulkanObjectManager::CopyWithTemporaryStagingBuffer(uint32_t sizeInBytes, + void * data) +{ + auto stagingObj = CreateBuffer(VulkanMemoryManager::ResourceType::Staging, + sizeInBytes, 0 /* batcherHash */); + void * gpuPtr = nullptr; + CHECK_VK_CALL(vkMapMemory(m_device, stagingObj.m_allocation->m_memory, + stagingObj.m_allocation->m_alignedOffset, + stagingObj.m_allocation->m_alignedSize, 0, &gpuPtr)); + memcpy(gpuPtr, data, sizeInBytes); + if (!stagingObj.m_allocation->m_isCoherent) + { + VkMappedMemoryRange mappedRange = {}; + mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + mappedRange.memory = stagingObj.m_allocation->m_memory; + mappedRange.offset = stagingObj.m_allocation->m_alignedOffset; + mappedRange.size = stagingObj.m_allocation->m_alignedSize; + CHECK_VK_CALL(vkFlushMappedMemoryRanges(m_device, 1, &mappedRange)); + } + vkUnmapMemory(m_device, stagingObj.m_allocation->m_memory); + CHECK_VK_CALL(vkBindBufferMemory(m_device, stagingObj.m_buffer, + stagingObj.m_allocation->m_memory, + stagingObj.m_allocation->m_alignedOffset)); + + StagingData stagingData; + stagingData.m_stagingBuffer = stagingObj.m_buffer; + stagingData.m_offset = 0; + + // The object will be destroyed on the next CollectObjects(). + DestroyObject(stagingObj); + return stagingData; +} + +void VulkanObjectManager::FlushDefaultStagingBuffer() +{ + std::lock_guard lock(m_mutex); + if (m_defaultStagingBuffer.m_allocation->m_isCoherent) + return; + + CHECK_VK_CALL(vkFlushMappedMemoryRanges(m_device, static_cast(m_regionsToFlush.size()), + m_regionsToFlush.data())); + m_regionsToFlush.clear(); +} + +void VulkanObjectManager::ResetDefaultStagingBuffer() +{ + std::lock_guard lock(m_mutex); + m_defaultStagingBufferOffset = 0; +} + void VulkanObjectManager::CollectObjects() { std::lock_guard lock(m_mutex); diff --git a/drape/vulkan/vulkan_object_manager.hpp b/drape/vulkan/vulkan_object_manager.hpp index 560faffd93..4907bf52e6 100644 --- a/drape/vulkan/vulkan_object_manager.hpp +++ b/drape/vulkan/vulkan_object_manager.hpp @@ -34,14 +34,43 @@ public: uint32_t sizeInBytes, uint64_t batcherHash); VulkanObject CreateImage(VkImageUsageFlagBits usageFlagBits, VkFormat format, VkImageAspectFlags aspectFlags, uint32_t width, uint32_t height); + + struct StagingData + { + VkBuffer m_stagingBuffer = {}; + uint32_t m_offset = 0; + operator bool() const { return m_stagingBuffer != 0; } + }; + struct StagingPointer + { + StagingData m_stagingData; + uint8_t * m_pointer = nullptr; + }; + StagingPointer GetDefaultStagingBuffer(uint32_t sizeInBytes); + StagingData CopyWithDefaultStagingBuffer(uint32_t sizeInBytes, void * data); + void FlushDefaultStagingBuffer(); + void ResetDefaultStagingBuffer(); + + // The result buffer will be destroyed after the nearest command queue submitting. + StagingData CopyWithTemporaryStagingBuffer(uint32_t sizeInBytes, void * data); + void DestroyObject(VulkanObject object); void CollectObjects(); + VulkanMemoryManager const & GetMemoryManager() const { return m_memoryManager; }; + private: VkDevice const m_device; uint32_t const m_queueFamilyIndex; VulkanMemoryManager m_memoryManager; std::vector m_queueToDestroy; + + VulkanObject m_defaultStagingBuffer; + uint8_t * m_defaultStagingBufferPtr = nullptr; + uint32_t m_defaultStagingBufferAlignment = 0; + uint32_t m_defaultStagingBufferOffset = 0; + std::vector m_regionsToFlush; + std::mutex m_mutex; }; } // namespace vulkan