diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_buffer_cache.cpp | 143 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_buffer_cache.h | 73 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_stream_buffer.cpp | 142 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_stream_buffer.h | 44 |
4 files changed, 340 insertions, 62 deletions
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index c36ede898..1ba544943 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp | |||
| @@ -1,3 +1,146 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | 1 | // Copyright 2019 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <cstring> | ||
| 7 | #include <memory> | ||
| 8 | #include <optional> | ||
| 9 | #include <tuple> | ||
| 10 | |||
| 11 | #include "common/assert.h" | ||
| 12 | #include "common/bit_util.h" | ||
| 13 | #include "core/core.h" | ||
| 14 | #include "video_core/renderer_vulkan/declarations.h" | ||
| 15 | #include "video_core/renderer_vulkan/vk_buffer_cache.h" | ||
| 16 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 17 | #include "video_core/renderer_vulkan/vk_scheduler.h" | ||
| 18 | #include "video_core/renderer_vulkan/vk_stream_buffer.h" | ||
| 19 | |||
| 20 | namespace Vulkan { | ||
| 21 | |||
| 22 | namespace { | ||
| 23 | |||
| 24 | const auto BufferUsage = | ||
| 25 | vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eIndexBuffer | | ||
| 26 | vk::BufferUsageFlagBits::eUniformBuffer | vk::BufferUsageFlagBits::eStorageBuffer; | ||
| 27 | |||
| 28 | const auto UploadPipelineStage = | ||
| 29 | vk::PipelineStageFlagBits::eTransfer | vk::PipelineStageFlagBits::eVertexInput | | ||
| 30 | vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eFragmentShader | | ||
| 31 | vk::PipelineStageFlagBits::eComputeShader; | ||
| 32 | |||
| 33 | const auto UploadAccessBarriers = | ||
| 34 | vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eShaderRead | | ||
| 35 | vk::AccessFlagBits::eUniformRead | vk::AccessFlagBits::eVertexAttributeRead | | ||
| 36 | vk::AccessFlagBits::eIndexRead; | ||
| 37 | |||
| 38 | auto CreateStreamBuffer(const VKDevice& device, VKScheduler& scheduler) { | ||
| 39 | return std::make_unique<VKStreamBuffer>(device, scheduler, BufferUsage); | ||
| 40 | } | ||
| 41 | |||
| 42 | } // Anonymous namespace | ||
| 43 | |||
| 44 | CachedBufferBlock::CachedBufferBlock(const VKDevice& device, VKMemoryManager& memory_manager, | ||
| 45 | CacheAddr cache_addr, std::size_t size) | ||
| 46 | : VideoCommon::BufferBlock{cache_addr, size} { | ||
| 47 | const vk::BufferCreateInfo buffer_ci({}, static_cast<vk::DeviceSize>(size), | ||
| 48 | BufferUsage | vk::BufferUsageFlagBits::eTransferSrc | | ||
| 49 | vk::BufferUsageFlagBits::eTransferDst, | ||
| 50 | vk::SharingMode::eExclusive, 0, nullptr); | ||
| 51 | |||
| 52 | const auto& dld{device.GetDispatchLoader()}; | ||
| 53 | const auto dev{device.GetLogical()}; | ||
| 54 | buffer.handle = dev.createBufferUnique(buffer_ci, nullptr, dld); | ||
| 55 | buffer.commit = memory_manager.Commit(*buffer.handle, false); | ||
| 56 | } | ||
| 57 | |||
| 58 | CachedBufferBlock::~CachedBufferBlock() = default; | ||
| 59 | |||
| 60 | VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, | ||
| 61 | const VKDevice& device, VKMemoryManager& memory_manager, | ||
| 62 | VKScheduler& scheduler, VKStagingBufferPool& staging_pool) | ||
| 63 | : VideoCommon::BufferCache<Buffer, vk::Buffer, VKStreamBuffer>{rasterizer, system, | ||
| 64 | CreateStreamBuffer(device, | ||
| 65 | scheduler)}, | ||
| 66 | device{device}, memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{ | ||
| 67 | staging_pool} {} | ||
| 68 | |||
| 69 | VKBufferCache::~VKBufferCache() = default; | ||
| 70 | |||
| 71 | Buffer VKBufferCache::CreateBlock(CacheAddr cache_addr, std::size_t size) { | ||
| 72 | return std::make_shared<CachedBufferBlock>(device, memory_manager, cache_addr, size); | ||
| 73 | } | ||
| 74 | |||
| 75 | const vk::Buffer* VKBufferCache::ToHandle(const Buffer& buffer) { | ||
| 76 | return buffer->GetHandle(); | ||
| 77 | } | ||
| 78 | |||
| 79 | const vk::Buffer* VKBufferCache::GetEmptyBuffer(std::size_t size) { | ||
| 80 | size = std::max(size, std::size_t(4)); | ||
| 81 | const auto& empty = staging_pool.GetUnusedBuffer(size, false); | ||
| 82 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 83 | scheduler.Record([size, buffer = *empty.handle](vk::CommandBuffer cmdbuf, auto& dld) { | ||
| 84 | cmdbuf.fillBuffer(buffer, 0, size, 0, dld); | ||
| 85 | }); | ||
| 86 | return &*empty.handle; | ||
| 87 | } | ||
| 88 | |||
| 89 | void VKBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 90 | const u8* data) { | ||
| 91 | const auto& staging = staging_pool.GetUnusedBuffer(size, true); | ||
| 92 | std::memcpy(staging.commit->Map(size), data, size); | ||
| 93 | |||
| 94 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 95 | scheduler.Record([staging = *staging.handle, buffer = *buffer->GetHandle(), offset, | ||
| 96 | size](auto cmdbuf, auto& dld) { | ||
| 97 | cmdbuf.copyBuffer(staging, buffer, {{0, offset, size}}, dld); | ||
| 98 | cmdbuf.pipelineBarrier( | ||
| 99 | vk::PipelineStageFlagBits::eTransfer, UploadPipelineStage, {}, {}, | ||
| 100 | {vk::BufferMemoryBarrier(vk::AccessFlagBits::eTransferWrite, UploadAccessBarriers, | ||
| 101 | VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, buffer, | ||
| 102 | offset, size)}, | ||
| 103 | {}, dld); | ||
| 104 | }); | ||
| 105 | } | ||
| 106 | |||
| 107 | void VKBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 108 | u8* data) { | ||
| 109 | const auto& staging = staging_pool.GetUnusedBuffer(size, true); | ||
| 110 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 111 | scheduler.Record([staging = *staging.handle, buffer = *buffer->GetHandle(), offset, | ||
| 112 | size](auto cmdbuf, auto& dld) { | ||
| 113 | cmdbuf.pipelineBarrier( | ||
| 114 | vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eFragmentShader | | ||
| 115 | vk::PipelineStageFlagBits::eComputeShader, | ||
| 116 | vk::PipelineStageFlagBits::eTransfer, {}, {}, | ||
| 117 | {vk::BufferMemoryBarrier(vk::AccessFlagBits::eShaderWrite, | ||
| 118 | vk::AccessFlagBits::eTransferRead, VK_QUEUE_FAMILY_IGNORED, | ||
| 119 | VK_QUEUE_FAMILY_IGNORED, buffer, offset, size)}, | ||
| 120 | {}, dld); | ||
| 121 | cmdbuf.copyBuffer(buffer, staging, {{offset, 0, size}}, dld); | ||
| 122 | }); | ||
| 123 | scheduler.Finish(); | ||
| 124 | |||
| 125 | std::memcpy(data, staging.commit->Map(size), size); | ||
| 126 | } | ||
| 127 | |||
| 128 | void VKBufferCache::CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset, | ||
| 129 | std::size_t dst_offset, std::size_t size) { | ||
| 130 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 131 | scheduler.Record([src_buffer = *src->GetHandle(), dst_buffer = *dst->GetHandle(), src_offset, | ||
| 132 | dst_offset, size](auto cmdbuf, auto& dld) { | ||
| 133 | cmdbuf.copyBuffer(src_buffer, dst_buffer, {{src_offset, dst_offset, size}}, dld); | ||
| 134 | cmdbuf.pipelineBarrier( | ||
| 135 | vk::PipelineStageFlagBits::eTransfer, UploadPipelineStage, {}, {}, | ||
| 136 | {vk::BufferMemoryBarrier(vk::AccessFlagBits::eTransferRead, | ||
| 137 | vk::AccessFlagBits::eShaderWrite, VK_QUEUE_FAMILY_IGNORED, | ||
| 138 | VK_QUEUE_FAMILY_IGNORED, src_buffer, src_offset, size), | ||
| 139 | vk::BufferMemoryBarrier(vk::AccessFlagBits::eTransferWrite, UploadAccessBarriers, | ||
| 140 | VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, dst_buffer, | ||
| 141 | dst_offset, size)}, | ||
| 142 | {}, dld); | ||
| 143 | }); | ||
| 144 | } | ||
| 145 | |||
| 146 | } // namespace Vulkan | ||
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index bc6e584cf..3f38eed0c 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h | |||
| @@ -3,3 +3,76 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <unordered_map> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "video_core/buffer_cache/buffer_cache.h" | ||
| 13 | #include "video_core/rasterizer_cache.h" | ||
| 14 | #include "video_core/renderer_vulkan/declarations.h" | ||
| 15 | #include "video_core/renderer_vulkan/vk_memory_manager.h" | ||
| 16 | #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||
| 17 | #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" | ||
| 18 | #include "video_core/renderer_vulkan/vk_stream_buffer.h" | ||
| 19 | |||
| 20 | namespace Core { | ||
| 21 | class System; | ||
| 22 | } | ||
| 23 | |||
| 24 | namespace Vulkan { | ||
| 25 | |||
| 26 | class VKDevice; | ||
| 27 | class VKMemoryManager; | ||
| 28 | class VKScheduler; | ||
| 29 | |||
| 30 | class CachedBufferBlock final : public VideoCommon::BufferBlock { | ||
| 31 | public: | ||
| 32 | explicit CachedBufferBlock(const VKDevice& device, VKMemoryManager& memory_manager, | ||
| 33 | CacheAddr cache_addr, std::size_t size); | ||
| 34 | ~CachedBufferBlock(); | ||
| 35 | |||
| 36 | const vk::Buffer* GetHandle() const { | ||
| 37 | return &*buffer.handle; | ||
| 38 | } | ||
| 39 | |||
| 40 | private: | ||
| 41 | VKBuffer buffer; | ||
| 42 | }; | ||
| 43 | |||
| 44 | using Buffer = std::shared_ptr<CachedBufferBlock>; | ||
| 45 | |||
| 46 | class VKBufferCache final : public VideoCommon::BufferCache<Buffer, vk::Buffer, VKStreamBuffer> { | ||
| 47 | public: | ||
| 48 | explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, | ||
| 49 | const VKDevice& device, VKMemoryManager& memory_manager, | ||
| 50 | VKScheduler& scheduler, VKStagingBufferPool& staging_pool); | ||
| 51 | ~VKBufferCache(); | ||
| 52 | |||
| 53 | const vk::Buffer* GetEmptyBuffer(std::size_t size) override; | ||
| 54 | |||
| 55 | protected: | ||
| 56 | void WriteBarrier() override {} | ||
| 57 | |||
| 58 | Buffer CreateBlock(CacheAddr cache_addr, std::size_t size) override; | ||
| 59 | |||
| 60 | const vk::Buffer* ToHandle(const Buffer& buffer) override; | ||
| 61 | |||
| 62 | void UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 63 | const u8* data) override; | ||
| 64 | |||
| 65 | void DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 66 | u8* data) override; | ||
| 67 | |||
| 68 | void CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset, | ||
| 69 | std::size_t dst_offset, std::size_t size) override; | ||
| 70 | |||
| 71 | private: | ||
| 72 | const VKDevice& device; | ||
| 73 | VKMemoryManager& memory_manager; | ||
| 74 | VKScheduler& scheduler; | ||
| 75 | VKStagingBufferPool& staging_pool; | ||
| 76 | }; | ||
| 77 | |||
| 78 | } // namespace Vulkan | ||
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp index 62f1427f5..d48d3b44c 100644 --- a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp +++ b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp | |||
| @@ -3,86 +3,144 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <memory> | ||
| 7 | #include <optional> | 6 | #include <optional> |
| 7 | #include <tuple> | ||
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | 9 | ||
| 10 | #include "common/alignment.h" | ||
| 10 | #include "common/assert.h" | 11 | #include "common/assert.h" |
| 11 | #include "video_core/renderer_vulkan/declarations.h" | 12 | #include "video_core/renderer_vulkan/declarations.h" |
| 12 | #include "video_core/renderer_vulkan/vk_device.h" | 13 | #include "video_core/renderer_vulkan/vk_device.h" |
| 13 | #include "video_core/renderer_vulkan/vk_memory_manager.h" | ||
| 14 | #include "video_core/renderer_vulkan/vk_resource_manager.h" | 14 | #include "video_core/renderer_vulkan/vk_resource_manager.h" |
| 15 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 15 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 16 | #include "video_core/renderer_vulkan/vk_stream_buffer.h" | 16 | #include "video_core/renderer_vulkan/vk_stream_buffer.h" |
| 17 | 17 | ||
| 18 | namespace Vulkan { | 18 | namespace Vulkan { |
| 19 | 19 | ||
| 20 | namespace { | ||
| 21 | |||
| 20 | constexpr u64 WATCHES_INITIAL_RESERVE = 0x4000; | 22 | constexpr u64 WATCHES_INITIAL_RESERVE = 0x4000; |
| 21 | constexpr u64 WATCHES_RESERVE_CHUNK = 0x1000; | 23 | constexpr u64 WATCHES_RESERVE_CHUNK = 0x1000; |
| 22 | 24 | ||
| 23 | VKStreamBuffer::VKStreamBuffer(const VKDevice& device, VKMemoryManager& memory_manager, | 25 | constexpr u64 STREAM_BUFFER_SIZE = 256 * 1024 * 1024; |
| 24 | VKScheduler& scheduler, u64 size, vk::BufferUsageFlags usage, | 26 | |
| 25 | vk::AccessFlags access, vk::PipelineStageFlags pipeline_stage) | 27 | std::optional<u32> FindMemoryType(const VKDevice& device, u32 filter, |
| 26 | : device{device}, scheduler{scheduler}, buffer_size{size}, access{access}, pipeline_stage{ | 28 | vk::MemoryPropertyFlags wanted) { |
| 27 | pipeline_stage} { | 29 | const auto properties = device.GetPhysical().getMemoryProperties(device.GetDispatchLoader()); |
| 28 | CreateBuffers(memory_manager, usage); | 30 | for (u32 i = 0; i < properties.memoryTypeCount; i++) { |
| 29 | ReserveWatches(WATCHES_INITIAL_RESERVE); | 31 | if (!(filter & (1 << i))) { |
| 32 | continue; | ||
| 33 | } | ||
| 34 | if ((properties.memoryTypes[i].propertyFlags & wanted) == wanted) { | ||
| 35 | return i; | ||
| 36 | } | ||
| 37 | } | ||
| 38 | return {}; | ||
| 39 | } | ||
| 40 | |||
| 41 | } // Anonymous namespace | ||
| 42 | |||
| 43 | VKStreamBuffer::VKStreamBuffer(const VKDevice& device, VKScheduler& scheduler, | ||
| 44 | vk::BufferUsageFlags usage) | ||
| 45 | : device{device}, scheduler{scheduler} { | ||
| 46 | CreateBuffers(usage); | ||
| 47 | ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE); | ||
| 48 | ReserveWatches(previous_watches, WATCHES_INITIAL_RESERVE); | ||
| 30 | } | 49 | } |
| 31 | 50 | ||
| 32 | VKStreamBuffer::~VKStreamBuffer() = default; | 51 | VKStreamBuffer::~VKStreamBuffer() = default; |
| 33 | 52 | ||
| 34 | std::tuple<u8*, u64, bool> VKStreamBuffer::Reserve(u64 size) { | 53 | std::tuple<u8*, u64, bool> VKStreamBuffer::Map(u64 size, u64 alignment) { |
| 35 | ASSERT(size <= buffer_size); | 54 | ASSERT(size <= STREAM_BUFFER_SIZE); |
| 36 | mapped_size = size; | 55 | mapped_size = size; |
| 37 | 56 | ||
| 38 | if (offset + size > buffer_size) { | 57 | if (alignment > 0) { |
| 39 | // The buffer would overflow, save the amount of used buffers, signal an invalidation and | 58 | offset = Common::AlignUp(offset, alignment); |
| 40 | // reset the state. | 59 | } |
| 41 | invalidation_mark = used_watches; | 60 | |
| 42 | used_watches = 0; | 61 | WaitPendingOperations(offset); |
| 62 | |||
| 63 | bool invalidated = false; | ||
| 64 | if (offset + size > STREAM_BUFFER_SIZE) { | ||
| 65 | // The buffer would overflow, save the amount of used watches and reset the state. | ||
| 66 | invalidation_mark = current_watch_cursor; | ||
| 67 | current_watch_cursor = 0; | ||
| 43 | offset = 0; | 68 | offset = 0; |
| 69 | |||
| 70 | // Swap watches and reset waiting cursors. | ||
| 71 | std::swap(previous_watches, current_watches); | ||
| 72 | wait_cursor = 0; | ||
| 73 | wait_bound = 0; | ||
| 74 | |||
| 75 | // Ensure that we don't wait for uncommitted fences. | ||
| 76 | scheduler.Flush(); | ||
| 77 | |||
| 78 | invalidated = true; | ||
| 44 | } | 79 | } |
| 45 | 80 | ||
| 46 | return {mapped_pointer + offset, offset, invalidation_mark.has_value()}; | 81 | const auto dev = device.GetLogical(); |
| 82 | const auto& dld = device.GetDispatchLoader(); | ||
| 83 | const auto pointer = reinterpret_cast<u8*>(dev.mapMemory(*memory, offset, size, {}, dld)); | ||
| 84 | return {pointer, offset, invalidated}; | ||
| 47 | } | 85 | } |
| 48 | 86 | ||
| 49 | void VKStreamBuffer::Send(u64 size) { | 87 | void VKStreamBuffer::Unmap(u64 size) { |
| 50 | ASSERT_MSG(size <= mapped_size, "Reserved size is too small"); | 88 | ASSERT_MSG(size <= mapped_size, "Reserved size is too small"); |
| 51 | 89 | ||
| 52 | if (invalidation_mark) { | 90 | const auto dev = device.GetLogical(); |
| 53 | // TODO(Rodrigo): Find a better way to invalidate than waiting for all watches to finish. | 91 | dev.unmapMemory(*memory, device.GetDispatchLoader()); |
| 54 | scheduler.Flush(); | 92 | |
| 55 | std::for_each(watches.begin(), watches.begin() + *invalidation_mark, | 93 | offset += size; |
| 56 | [&](auto& resource) { resource->Wait(); }); | ||
| 57 | invalidation_mark = std::nullopt; | ||
| 58 | } | ||
| 59 | 94 | ||
| 60 | if (used_watches + 1 >= watches.size()) { | 95 | if (current_watch_cursor + 1 >= current_watches.size()) { |
| 61 | // Ensure that there are enough watches. | 96 | // Ensure that there are enough watches. |
| 62 | ReserveWatches(WATCHES_RESERVE_CHUNK); | 97 | ReserveWatches(current_watches, WATCHES_RESERVE_CHUNK); |
| 63 | } | 98 | } |
| 64 | // Add a watch for this allocation. | 99 | auto& watch = current_watches[current_watch_cursor++]; |
| 65 | watches[used_watches++]->Watch(scheduler.GetFence()); | 100 | watch.upper_bound = offset; |
| 66 | 101 | watch.fence.Watch(scheduler.GetFence()); | |
| 67 | offset += size; | ||
| 68 | } | 102 | } |
| 69 | 103 | ||
| 70 | void VKStreamBuffer::CreateBuffers(VKMemoryManager& memory_manager, vk::BufferUsageFlags usage) { | 104 | void VKStreamBuffer::CreateBuffers(vk::BufferUsageFlags usage) { |
| 71 | const vk::BufferCreateInfo buffer_ci({}, buffer_size, usage, vk::SharingMode::eExclusive, 0, | 105 | const vk::BufferCreateInfo buffer_ci({}, STREAM_BUFFER_SIZE, usage, vk::SharingMode::eExclusive, |
| 72 | nullptr); | 106 | 0, nullptr); |
| 73 | |||
| 74 | const auto dev = device.GetLogical(); | 107 | const auto dev = device.GetLogical(); |
| 75 | const auto& dld = device.GetDispatchLoader(); | 108 | const auto& dld = device.GetDispatchLoader(); |
| 76 | buffer = dev.createBufferUnique(buffer_ci, nullptr, dld); | 109 | buffer = dev.createBufferUnique(buffer_ci, nullptr, dld); |
| 77 | commit = memory_manager.Commit(*buffer, true); | 110 | |
| 78 | mapped_pointer = commit->GetData(); | 111 | const auto requirements = dev.getBufferMemoryRequirements(*buffer, dld); |
| 112 | // Prefer device local host visible allocations (this should hit AMD's pinned memory). | ||
| 113 | auto type = FindMemoryType(device, requirements.memoryTypeBits, | ||
| 114 | vk::MemoryPropertyFlagBits::eHostVisible | | ||
| 115 | vk::MemoryPropertyFlagBits::eHostCoherent | | ||
| 116 | vk::MemoryPropertyFlagBits::eDeviceLocal); | ||
| 117 | if (!type) { | ||
| 118 | // Otherwise search for a host visible allocation. | ||
| 119 | type = FindMemoryType(device, requirements.memoryTypeBits, | ||
| 120 | vk::MemoryPropertyFlagBits::eHostVisible | | ||
| 121 | vk::MemoryPropertyFlagBits::eHostCoherent); | ||
| 122 | ASSERT_MSG(type, "No host visible and coherent memory type found"); | ||
| 123 | } | ||
| 124 | const vk::MemoryAllocateInfo alloc_ci(requirements.size, *type); | ||
| 125 | memory = dev.allocateMemoryUnique(alloc_ci, nullptr, dld); | ||
| 126 | |||
| 127 | dev.bindBufferMemory(*buffer, *memory, 0, dld); | ||
| 79 | } | 128 | } |
| 80 | 129 | ||
| 81 | void VKStreamBuffer::ReserveWatches(std::size_t grow_size) { | 130 | void VKStreamBuffer::ReserveWatches(std::vector<Watch>& watches, std::size_t grow_size) { |
| 82 | const std::size_t previous_size = watches.size(); | 131 | watches.resize(watches.size() + grow_size); |
| 83 | watches.resize(previous_size + grow_size); | 132 | } |
| 84 | std::generate(watches.begin() + previous_size, watches.end(), | 133 | |
| 85 | []() { return std::make_unique<VKFenceWatch>(); }); | 134 | void VKStreamBuffer::WaitPendingOperations(u64 requested_upper_bound) { |
| 135 | if (!invalidation_mark) { | ||
| 136 | return; | ||
| 137 | } | ||
| 138 | while (requested_upper_bound < wait_bound && wait_cursor < *invalidation_mark) { | ||
| 139 | auto& watch = previous_watches[wait_cursor]; | ||
| 140 | wait_bound = watch.upper_bound; | ||
| 141 | watch.fence.Wait(); | ||
| 142 | ++wait_cursor; | ||
| 143 | } | ||
| 86 | } | 144 | } |
| 87 | 145 | ||
| 88 | } // namespace Vulkan | 146 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.h b/src/video_core/renderer_vulkan/vk_stream_buffer.h index 842e54162..187c0c612 100644 --- a/src/video_core/renderer_vulkan/vk_stream_buffer.h +++ b/src/video_core/renderer_vulkan/vk_stream_buffer.h | |||
| @@ -4,28 +4,24 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | ||
| 8 | #include <optional> | 7 | #include <optional> |
| 9 | #include <tuple> | 8 | #include <tuple> |
| 10 | #include <vector> | 9 | #include <vector> |
| 11 | 10 | ||
| 12 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 13 | #include "video_core/renderer_vulkan/declarations.h" | 12 | #include "video_core/renderer_vulkan/declarations.h" |
| 14 | #include "video_core/renderer_vulkan/vk_memory_manager.h" | ||
| 15 | 13 | ||
| 16 | namespace Vulkan { | 14 | namespace Vulkan { |
| 17 | 15 | ||
| 18 | class VKDevice; | 16 | class VKDevice; |
| 19 | class VKFence; | 17 | class VKFence; |
| 20 | class VKFenceWatch; | 18 | class VKFenceWatch; |
| 21 | class VKResourceManager; | ||
| 22 | class VKScheduler; | 19 | class VKScheduler; |
| 23 | 20 | ||
| 24 | class VKStreamBuffer { | 21 | class VKStreamBuffer final { |
| 25 | public: | 22 | public: |
| 26 | explicit VKStreamBuffer(const VKDevice& device, VKMemoryManager& memory_manager, | 23 | explicit VKStreamBuffer(const VKDevice& device, VKScheduler& scheduler, |
| 27 | VKScheduler& scheduler, u64 size, vk::BufferUsageFlags usage, | 24 | vk::BufferUsageFlags usage); |
| 28 | vk::AccessFlags access, vk::PipelineStageFlags pipeline_stage); | ||
| 29 | ~VKStreamBuffer(); | 25 | ~VKStreamBuffer(); |
| 30 | 26 | ||
| 31 | /** | 27 | /** |
| @@ -34,39 +30,47 @@ public: | |||
| 34 | * @returns A tuple in the following order: Raw memory pointer (with offset added), buffer | 30 | * @returns A tuple in the following order: Raw memory pointer (with offset added), buffer |
| 35 | * offset and a boolean that's true when buffer has been invalidated. | 31 | * offset and a boolean that's true when buffer has been invalidated. |
| 36 | */ | 32 | */ |
| 37 | std::tuple<u8*, u64, bool> Reserve(u64 size); | 33 | std::tuple<u8*, u64, bool> Map(u64 size, u64 alignment); |
| 38 | 34 | ||
| 39 | /// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy. | 35 | /// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy. |
| 40 | void Send(u64 size); | 36 | void Unmap(u64 size); |
| 41 | 37 | ||
| 42 | vk::Buffer GetBuffer() const { | 38 | vk::Buffer GetHandle() const { |
| 43 | return *buffer; | 39 | return *buffer; |
| 44 | } | 40 | } |
| 45 | 41 | ||
| 46 | private: | 42 | private: |
| 43 | struct Watch final { | ||
| 44 | VKFenceWatch fence; | ||
| 45 | u64 upper_bound{}; | ||
| 46 | }; | ||
| 47 | |||
| 47 | /// Creates Vulkan buffer handles committing the required the required memory. | 48 | /// Creates Vulkan buffer handles committing the required the required memory. |
| 48 | void CreateBuffers(VKMemoryManager& memory_manager, vk::BufferUsageFlags usage); | 49 | void CreateBuffers(vk::BufferUsageFlags usage); |
| 49 | 50 | ||
| 50 | /// Increases the amount of watches available. | 51 | /// Increases the amount of watches available. |
| 51 | void ReserveWatches(std::size_t grow_size); | 52 | void ReserveWatches(std::vector<Watch>& watches, std::size_t grow_size); |
| 53 | |||
| 54 | void WaitPendingOperations(u64 requested_upper_bound); | ||
| 52 | 55 | ||
| 53 | const VKDevice& device; ///< Vulkan device manager. | 56 | const VKDevice& device; ///< Vulkan device manager. |
| 54 | VKScheduler& scheduler; ///< Command scheduler. | 57 | VKScheduler& scheduler; ///< Command scheduler. |
| 55 | const u64 buffer_size; ///< Total size of the stream buffer. | ||
| 56 | const vk::AccessFlags access; ///< Access usage of this stream buffer. | 58 | const vk::AccessFlags access; ///< Access usage of this stream buffer. |
| 57 | const vk::PipelineStageFlags pipeline_stage; ///< Pipeline usage of this stream buffer. | 59 | const vk::PipelineStageFlags pipeline_stage; ///< Pipeline usage of this stream buffer. |
| 58 | 60 | ||
| 59 | UniqueBuffer buffer; ///< Mapped buffer. | 61 | UniqueBuffer buffer; ///< Mapped buffer. |
| 60 | VKMemoryCommit commit; ///< Memory commit. | 62 | UniqueDeviceMemory memory; ///< Memory allocation. |
| 61 | u8* mapped_pointer{}; ///< Pointer to the host visible commit | ||
| 62 | 63 | ||
| 63 | u64 offset{}; ///< Buffer iterator. | 64 | u64 offset{}; ///< Buffer iterator. |
| 64 | u64 mapped_size{}; ///< Size reserved for the current copy. | 65 | u64 mapped_size{}; ///< Size reserved for the current copy. |
| 65 | 66 | ||
| 66 | std::vector<std::unique_ptr<VKFenceWatch>> watches; ///< Total watches | 67 | std::vector<Watch> current_watches; ///< Watches recorded in the current iteration. |
| 67 | std::size_t used_watches{}; ///< Count of watches, reset on invalidation. | 68 | std::size_t current_watch_cursor{}; ///< Count of watches, reset on invalidation. |
| 68 | std::optional<std::size_t> | 69 | std::optional<std::size_t> invalidation_mark; ///< Number of watches used in the previous cycle. |
| 69 | invalidation_mark{}; ///< Number of watches used in the current invalidation. | 70 | |
| 71 | std::vector<Watch> previous_watches; ///< Watches used in the previous iteration. | ||
| 72 | std::size_t wait_cursor{}; ///< Last watch being waited for completion. | ||
| 73 | u64 wait_bound{}; ///< Highest offset being watched for completion. | ||
| 70 | }; | 74 | }; |
| 71 | 75 | ||
| 72 | } // namespace Vulkan | 76 | } // namespace Vulkan |