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)
 | 
			
		||||
    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
 | 
			
		||||
		Reference in New Issue
	
	Block a user