Merge pull request #2178 from ReinUsesLisp/vk-buffer-cache
vk_buffer_cache: Implement a buffer cache
This commit is contained in:
		
						commit
						e22670fbc3
					
				| @ -104,6 +104,8 @@ add_library(video_core STATIC | ||||
| if (ENABLE_VULKAN) | ||||
|     target_sources(video_core PRIVATE | ||||
|         renderer_vulkan/declarations.h | ||||
|         renderer_vulkan/vk_buffer_cache.cpp | ||||
|         renderer_vulkan/vk_buffer_cache.h | ||||
|         renderer_vulkan/vk_device.cpp | ||||
|         renderer_vulkan/vk_device.h | ||||
|         renderer_vulkan/vk_memory_manager.cpp | ||||
|  | ||||
							
								
								
									
										116
									
								
								src/video_core/renderer_vulkan/vk_buffer_cache.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								src/video_core/renderer_vulkan/vk_buffer_cache.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,116 @@ | ||||
| // Copyright 2019 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <cstring> | ||||
| #include <memory> | ||||
| #include <optional> | ||||
| #include <tuple> | ||||
| 
 | ||||
| #include "common/alignment.h" | ||||
| #include "core/core.h" | ||||
| #include "core/memory.h" | ||||
| #include "video_core/renderer_vulkan/declarations.h" | ||||
| #include "video_core/renderer_vulkan/vk_buffer_cache.h" | ||||
| #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||
| #include "video_core/renderer_vulkan/vk_stream_buffer.h" | ||||
| 
 | ||||
| namespace Vulkan { | ||||
| 
 | ||||
| VKBufferCache::VKBufferCache(Tegra::MemoryManager& tegra_memory_manager, | ||||
|                              VideoCore::RasterizerInterface& rasterizer, const VKDevice& device, | ||||
|                              VKMemoryManager& memory_manager, VKScheduler& scheduler, u64 size) | ||||
|     : RasterizerCache{rasterizer}, tegra_memory_manager{tegra_memory_manager} { | ||||
|     const auto usage = vk::BufferUsageFlagBits::eVertexBuffer | | ||||
|                        vk::BufferUsageFlagBits::eIndexBuffer | | ||||
|                        vk::BufferUsageFlagBits::eUniformBuffer; | ||||
|     const auto access = vk::AccessFlagBits::eVertexAttributeRead | vk::AccessFlagBits::eIndexRead | | ||||
|                         vk::AccessFlagBits::eUniformRead; | ||||
|     stream_buffer = | ||||
|         std::make_unique<VKStreamBuffer>(device, memory_manager, scheduler, size, usage, access, | ||||
|                                          vk::PipelineStageFlagBits::eAllCommands); | ||||
|     buffer_handle = stream_buffer->GetBuffer(); | ||||
| } | ||||
| 
 | ||||
| VKBufferCache::~VKBufferCache() = default; | ||||
| 
 | ||||
| u64 VKBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, u64 alignment, | ||||
|                                 bool cache) { | ||||
|     const auto cpu_addr{tegra_memory_manager.GpuToCpuAddress(gpu_addr)}; | ||||
|     ASSERT(cpu_addr); | ||||
| 
 | ||||
|     // Cache management is a big overhead, so only cache entries with a given size.
 | ||||
|     // TODO: Figure out which size is the best for given games.
 | ||||
|     cache &= size >= 2048; | ||||
| 
 | ||||
|     if (cache) { | ||||
|         if (auto entry = TryGet(*cpu_addr); entry) { | ||||
|             if (entry->size >= size && entry->alignment == alignment) { | ||||
|                 return entry->offset; | ||||
|             } | ||||
|             Unregister(entry); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     AlignBuffer(alignment); | ||||
|     const u64 uploaded_offset = buffer_offset; | ||||
| 
 | ||||
|     Memory::ReadBlock(*cpu_addr, buffer_ptr, size); | ||||
| 
 | ||||
|     buffer_ptr += size; | ||||
|     buffer_offset += size; | ||||
| 
 | ||||
|     if (cache) { | ||||
|         auto entry = std::make_shared<CachedBufferEntry>(); | ||||
|         entry->offset = uploaded_offset; | ||||
|         entry->size = size; | ||||
|         entry->alignment = alignment; | ||||
|         entry->addr = *cpu_addr; | ||||
|         Register(entry); | ||||
|     } | ||||
| 
 | ||||
|     return uploaded_offset; | ||||
| } | ||||
| 
 | ||||
| u64 VKBufferCache::UploadHostMemory(const u8* raw_pointer, std::size_t size, u64 alignment) { | ||||
|     AlignBuffer(alignment); | ||||
|     std::memcpy(buffer_ptr, raw_pointer, size); | ||||
|     const u64 uploaded_offset = buffer_offset; | ||||
| 
 | ||||
|     buffer_ptr += size; | ||||
|     buffer_offset += size; | ||||
|     return uploaded_offset; | ||||
| } | ||||
| 
 | ||||
| std::tuple<u8*, u64> VKBufferCache::ReserveMemory(std::size_t size, u64 alignment) { | ||||
|     AlignBuffer(alignment); | ||||
|     u8* const uploaded_ptr = buffer_ptr; | ||||
|     const u64 uploaded_offset = buffer_offset; | ||||
| 
 | ||||
|     buffer_ptr += size; | ||||
|     buffer_offset += size; | ||||
|     return {uploaded_ptr, uploaded_offset}; | ||||
| } | ||||
| 
 | ||||
| void VKBufferCache::Reserve(std::size_t max_size) { | ||||
|     bool invalidate; | ||||
|     std::tie(buffer_ptr, buffer_offset_base, invalidate) = stream_buffer->Reserve(max_size); | ||||
|     buffer_offset = buffer_offset_base; | ||||
| 
 | ||||
|     if (invalidate) { | ||||
|         InvalidateAll(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| VKExecutionContext VKBufferCache::Send(VKExecutionContext exctx) { | ||||
|     return stream_buffer->Send(exctx, buffer_offset - buffer_offset_base); | ||||
| } | ||||
| 
 | ||||
| void VKBufferCache::AlignBuffer(std::size_t alignment) { | ||||
|     // Align the offset, not the mapped pointer
 | ||||
|     const u64 offset_aligned = Common::AlignUp(buffer_offset, alignment); | ||||
|     buffer_ptr += offset_aligned - buffer_offset; | ||||
|     buffer_offset = offset_aligned; | ||||
| } | ||||
| 
 | ||||
| } // namespace Vulkan
 | ||||
							
								
								
									
										87
									
								
								src/video_core/renderer_vulkan/vk_buffer_cache.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/video_core/renderer_vulkan/vk_buffer_cache.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,87 @@ | ||||
| // Copyright 2019 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <tuple> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "video_core/gpu.h" | ||||
| #include "video_core/rasterizer_cache.h" | ||||
| #include "video_core/renderer_vulkan/declarations.h" | ||||
| #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||
| 
 | ||||
| namespace Tegra { | ||||
| class MemoryManager; | ||||
| } | ||||
| 
 | ||||
| namespace Vulkan { | ||||
| 
 | ||||
| class VKDevice; | ||||
| class VKFence; | ||||
| class VKMemoryManager; | ||||
| class VKStreamBuffer; | ||||
| 
 | ||||
| struct CachedBufferEntry final : public RasterizerCacheObject { | ||||
|     VAddr GetAddr() const override { | ||||
|         return addr; | ||||
|     } | ||||
| 
 | ||||
|     std::size_t GetSizeInBytes() const override { | ||||
|         return size; | ||||
|     } | ||||
| 
 | ||||
|     // We do not have to flush this cache as things in it are never modified by us.
 | ||||
|     void Flush() override {} | ||||
| 
 | ||||
|     VAddr addr; | ||||
|     std::size_t size; | ||||
|     u64 offset; | ||||
|     std::size_t alignment; | ||||
| }; | ||||
| 
 | ||||
| class VKBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> { | ||||
| public: | ||||
|     explicit VKBufferCache(Tegra::MemoryManager& tegra_memory_manager, VideoCore::RasterizerInterface& rasterizer, | ||||
|                            const VKDevice& device, VKMemoryManager& memory_manager, | ||||
|                            VKScheduler& scheduler, u64 size); | ||||
|     ~VKBufferCache(); | ||||
| 
 | ||||
|     /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been
 | ||||
|     /// allocated.
 | ||||
|     u64 UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, u64 alignment = 4, | ||||
|                      bool cache = true); | ||||
| 
 | ||||
|     /// Uploads from a host memory. Returns host's buffer offset where it's been allocated.
 | ||||
|     u64 UploadHostMemory(const u8* raw_pointer, std::size_t size, u64 alignment = 4); | ||||
| 
 | ||||
|     /// Reserves memory to be used by host's CPU. Returns mapped address and offset.
 | ||||
|     std::tuple<u8*, u64> ReserveMemory(std::size_t size, u64 alignment = 4); | ||||
| 
 | ||||
|     /// Reserves a region of memory to be used in subsequent upload/reserve operations.
 | ||||
|     void Reserve(std::size_t max_size); | ||||
| 
 | ||||
|     /// Ensures that the set data is sent to the device.
 | ||||
|     [[nodiscard]] VKExecutionContext Send(VKExecutionContext exctx); | ||||
| 
 | ||||
|     /// Returns the buffer cache handle.
 | ||||
|     vk::Buffer GetBuffer() const { | ||||
|         return buffer_handle; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     void AlignBuffer(std::size_t alignment); | ||||
| 
 | ||||
|     Tegra::MemoryManager& tegra_memory_manager; | ||||
| 
 | ||||
|     std::unique_ptr<VKStreamBuffer> stream_buffer; | ||||
|     vk::Buffer buffer_handle; | ||||
| 
 | ||||
|     u8* buffer_ptr = nullptr; | ||||
|     u64 buffer_offset = 0; | ||||
|     u64 buffer_offset_base = 0; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Vulkan
 | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 bunnei
						bunnei