diff options
| -rw-r--r-- | src/video_core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_buffer_cache.cpp | 116 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_buffer_cache.h | 87 |
3 files changed, 205 insertions, 0 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 60529323e..3e9d2b3be 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -104,6 +104,8 @@ add_library(video_core STATIC | |||
| 104 | if (ENABLE_VULKAN) | 104 | if (ENABLE_VULKAN) |
| 105 | target_sources(video_core PRIVATE | 105 | target_sources(video_core PRIVATE |
| 106 | renderer_vulkan/declarations.h | 106 | renderer_vulkan/declarations.h |
| 107 | renderer_vulkan/vk_buffer_cache.cpp | ||
| 108 | renderer_vulkan/vk_buffer_cache.h | ||
| 107 | renderer_vulkan/vk_device.cpp | 109 | renderer_vulkan/vk_device.cpp |
| 108 | renderer_vulkan/vk_device.h | 110 | renderer_vulkan/vk_device.h |
| 109 | renderer_vulkan/vk_memory_manager.cpp | 111 | renderer_vulkan/vk_memory_manager.cpp |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp new file mode 100644 index 000000000..18b7b94a1 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | #include <memory> | ||
| 7 | #include <optional> | ||
| 8 | #include <tuple> | ||
| 9 | |||
| 10 | #include "common/alignment.h" | ||
| 11 | #include "core/core.h" | ||
| 12 | #include "core/memory.h" | ||
| 13 | #include "video_core/renderer_vulkan/declarations.h" | ||
| 14 | #include "video_core/renderer_vulkan/vk_buffer_cache.h" | ||
| 15 | #include "video_core/renderer_vulkan/vk_scheduler.h" | ||
| 16 | #include "video_core/renderer_vulkan/vk_stream_buffer.h" | ||
| 17 | |||
| 18 | namespace Vulkan { | ||
| 19 | |||
| 20 | VKBufferCache::VKBufferCache(Tegra::MemoryManager& tegra_memory_manager, | ||
| 21 | VideoCore::RasterizerInterface& rasterizer, const VKDevice& device, | ||
| 22 | VKMemoryManager& memory_manager, VKScheduler& scheduler, u64 size) | ||
| 23 | : RasterizerCache{rasterizer}, tegra_memory_manager{tegra_memory_manager} { | ||
| 24 | const auto usage = vk::BufferUsageFlagBits::eVertexBuffer | | ||
| 25 | vk::BufferUsageFlagBits::eIndexBuffer | | ||
| 26 | vk::BufferUsageFlagBits::eUniformBuffer; | ||
| 27 | const auto access = vk::AccessFlagBits::eVertexAttributeRead | vk::AccessFlagBits::eIndexRead | | ||
| 28 | vk::AccessFlagBits::eUniformRead; | ||
| 29 | stream_buffer = | ||
| 30 | std::make_unique<VKStreamBuffer>(device, memory_manager, scheduler, size, usage, access, | ||
| 31 | vk::PipelineStageFlagBits::eAllCommands); | ||
| 32 | buffer_handle = stream_buffer->GetBuffer(); | ||
| 33 | } | ||
| 34 | |||
| 35 | VKBufferCache::~VKBufferCache() = default; | ||
| 36 | |||
| 37 | u64 VKBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, u64 alignment, | ||
| 38 | bool cache) { | ||
| 39 | const auto cpu_addr{tegra_memory_manager.GpuToCpuAddress(gpu_addr)}; | ||
| 40 | ASSERT(cpu_addr); | ||
| 41 | |||
| 42 | // Cache management is a big overhead, so only cache entries with a given size. | ||
| 43 | // TODO: Figure out which size is the best for given games. | ||
| 44 | cache &= size >= 2048; | ||
| 45 | |||
| 46 | if (cache) { | ||
| 47 | if (auto entry = TryGet(*cpu_addr); entry) { | ||
| 48 | if (entry->size >= size && entry->alignment == alignment) { | ||
| 49 | return entry->offset; | ||
| 50 | } | ||
| 51 | Unregister(entry); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | AlignBuffer(alignment); | ||
| 56 | const u64 uploaded_offset = buffer_offset; | ||
| 57 | |||
| 58 | Memory::ReadBlock(*cpu_addr, buffer_ptr, size); | ||
| 59 | |||
| 60 | buffer_ptr += size; | ||
| 61 | buffer_offset += size; | ||
| 62 | |||
| 63 | if (cache) { | ||
| 64 | auto entry = std::make_shared<CachedBufferEntry>(); | ||
| 65 | entry->offset = uploaded_offset; | ||
| 66 | entry->size = size; | ||
| 67 | entry->alignment = alignment; | ||
| 68 | entry->addr = *cpu_addr; | ||
| 69 | Register(entry); | ||
| 70 | } | ||
| 71 | |||
| 72 | return uploaded_offset; | ||
| 73 | } | ||
| 74 | |||
| 75 | u64 VKBufferCache::UploadHostMemory(const u8* raw_pointer, std::size_t size, u64 alignment) { | ||
| 76 | AlignBuffer(alignment); | ||
| 77 | std::memcpy(buffer_ptr, raw_pointer, size); | ||
| 78 | const u64 uploaded_offset = buffer_offset; | ||
| 79 | |||
| 80 | buffer_ptr += size; | ||
| 81 | buffer_offset += size; | ||
| 82 | return uploaded_offset; | ||
| 83 | } | ||
| 84 | |||
| 85 | std::tuple<u8*, u64> VKBufferCache::ReserveMemory(std::size_t size, u64 alignment) { | ||
| 86 | AlignBuffer(alignment); | ||
| 87 | u8* const uploaded_ptr = buffer_ptr; | ||
| 88 | const u64 uploaded_offset = buffer_offset; | ||
| 89 | |||
| 90 | buffer_ptr += size; | ||
| 91 | buffer_offset += size; | ||
| 92 | return {uploaded_ptr, uploaded_offset}; | ||
| 93 | } | ||
| 94 | |||
| 95 | void VKBufferCache::Reserve(std::size_t max_size) { | ||
| 96 | bool invalidate; | ||
| 97 | std::tie(buffer_ptr, buffer_offset_base, invalidate) = stream_buffer->Reserve(max_size); | ||
| 98 | buffer_offset = buffer_offset_base; | ||
| 99 | |||
| 100 | if (invalidate) { | ||
| 101 | InvalidateAll(); | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | VKExecutionContext VKBufferCache::Send(VKExecutionContext exctx) { | ||
| 106 | return stream_buffer->Send(exctx, buffer_offset - buffer_offset_base); | ||
| 107 | } | ||
| 108 | |||
| 109 | void VKBufferCache::AlignBuffer(std::size_t alignment) { | ||
| 110 | // Align the offset, not the mapped pointer | ||
| 111 | const u64 offset_aligned = Common::AlignUp(buffer_offset, alignment); | ||
| 112 | buffer_ptr += offset_aligned - buffer_offset; | ||
| 113 | buffer_offset = offset_aligned; | ||
| 114 | } | ||
| 115 | |||
| 116 | } // namespace Vulkan | ||
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h new file mode 100644 index 000000000..6cbe21202 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <tuple> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "video_core/gpu.h" | ||
| 12 | #include "video_core/rasterizer_cache.h" | ||
| 13 | #include "video_core/renderer_vulkan/declarations.h" | ||
| 14 | #include "video_core/renderer_vulkan/vk_scheduler.h" | ||
| 15 | |||
| 16 | namespace Tegra { | ||
| 17 | class MemoryManager; | ||
| 18 | } | ||
| 19 | |||
| 20 | namespace Vulkan { | ||
| 21 | |||
| 22 | class VKDevice; | ||
| 23 | class VKFence; | ||
| 24 | class VKMemoryManager; | ||
| 25 | class VKStreamBuffer; | ||
| 26 | |||
| 27 | struct CachedBufferEntry final : public RasterizerCacheObject { | ||
| 28 | VAddr GetAddr() const override { | ||
| 29 | return addr; | ||
| 30 | } | ||
| 31 | |||
| 32 | std::size_t GetSizeInBytes() const override { | ||
| 33 | return size; | ||
| 34 | } | ||
| 35 | |||
| 36 | // We do not have to flush this cache as things in it are never modified by us. | ||
| 37 | void Flush() override {} | ||
| 38 | |||
| 39 | VAddr addr; | ||
| 40 | std::size_t size; | ||
| 41 | u64 offset; | ||
| 42 | std::size_t alignment; | ||
| 43 | }; | ||
| 44 | |||
| 45 | class VKBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> { | ||
| 46 | public: | ||
| 47 | explicit VKBufferCache(Tegra::MemoryManager& tegra_memory_manager, VideoCore::RasterizerInterface& rasterizer, | ||
| 48 | const VKDevice& device, VKMemoryManager& memory_manager, | ||
| 49 | VKScheduler& scheduler, u64 size); | ||
| 50 | ~VKBufferCache(); | ||
| 51 | |||
| 52 | /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been | ||
| 53 | /// allocated. | ||
| 54 | u64 UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, u64 alignment = 4, | ||
| 55 | bool cache = true); | ||
| 56 | |||
| 57 | /// Uploads from a host memory. Returns host's buffer offset where it's been allocated. | ||
| 58 | u64 UploadHostMemory(const u8* raw_pointer, std::size_t size, u64 alignment = 4); | ||
| 59 | |||
| 60 | /// Reserves memory to be used by host's CPU. Returns mapped address and offset. | ||
| 61 | std::tuple<u8*, u64> ReserveMemory(std::size_t size, u64 alignment = 4); | ||
| 62 | |||
| 63 | /// Reserves a region of memory to be used in subsequent upload/reserve operations. | ||
| 64 | void Reserve(std::size_t max_size); | ||
| 65 | |||
| 66 | /// Ensures that the set data is sent to the device. | ||
| 67 | [[nodiscard]] VKExecutionContext Send(VKExecutionContext exctx); | ||
| 68 | |||
| 69 | /// Returns the buffer cache handle. | ||
| 70 | vk::Buffer GetBuffer() const { | ||
| 71 | return buffer_handle; | ||
| 72 | } | ||
| 73 | |||
| 74 | private: | ||
| 75 | void AlignBuffer(std::size_t alignment); | ||
| 76 | |||
| 77 | Tegra::MemoryManager& tegra_memory_manager; | ||
| 78 | |||
| 79 | std::unique_ptr<VKStreamBuffer> stream_buffer; | ||
| 80 | vk::Buffer buffer_handle; | ||
| 81 | |||
| 82 | u8* buffer_ptr = nullptr; | ||
| 83 | u64 buffer_offset = 0; | ||
| 84 | u64 buffer_offset_base = 0; | ||
| 85 | }; | ||
| 86 | |||
| 87 | } // namespace Vulkan | ||