Merge pull request #2178 from ReinUsesLisp/vk-buffer-cache
vk_buffer_cache: Implement a buffer cache
This commit is contained in:
		@@ -104,6 +104,8 @@ add_library(video_core STATIC
 | 
				
			|||||||
if (ENABLE_VULKAN)
 | 
					if (ENABLE_VULKAN)
 | 
				
			||||||
    target_sources(video_core PRIVATE
 | 
					    target_sources(video_core PRIVATE
 | 
				
			||||||
        renderer_vulkan/declarations.h
 | 
					        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.cpp
 | 
				
			||||||
        renderer_vulkan/vk_device.h
 | 
					        renderer_vulkan/vk_device.h
 | 
				
			||||||
        renderer_vulkan/vk_memory_manager.cpp
 | 
					        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
 | 
				
			||||||
		Reference in New Issue
	
	Block a user