Merge pull request #2152 from ReinUsesLisp/vk-stream-buffer
vk_stream_buffer: Implement a stream buffer
This commit is contained in:
commit
1b13859af8
|
@ -111,7 +111,9 @@ if (ENABLE_VULKAN)
|
||||||
renderer_vulkan/vk_resource_manager.cpp
|
renderer_vulkan/vk_resource_manager.cpp
|
||||||
renderer_vulkan/vk_resource_manager.h
|
renderer_vulkan/vk_resource_manager.h
|
||||||
renderer_vulkan/vk_scheduler.cpp
|
renderer_vulkan/vk_scheduler.cpp
|
||||||
renderer_vulkan/vk_scheduler.h)
|
renderer_vulkan/vk_scheduler.h
|
||||||
|
renderer_vulkan/vk_stream_buffer.cpp
|
||||||
|
renderer_vulkan/vk_stream_buffer.h)
|
||||||
|
|
||||||
target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include)
|
target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include)
|
||||||
target_compile_definitions(video_core PRIVATE HAS_VULKAN)
|
target_compile_definitions(video_core PRIVATE HAS_VULKAN)
|
||||||
|
|
|
@ -125,12 +125,13 @@ void VKFence::Protect(VKResource* resource) {
|
||||||
protected_resources.push_back(resource);
|
protected_resources.push_back(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKFence::Unprotect(const VKResource* resource) {
|
void VKFence::Unprotect(VKResource* resource) {
|
||||||
const auto it = std::find(protected_resources.begin(), protected_resources.end(), resource);
|
const auto it = std::find(protected_resources.begin(), protected_resources.end(), resource);
|
||||||
if (it != protected_resources.end()) {
|
ASSERT(it != protected_resources.end());
|
||||||
|
|
||||||
|
resource->OnFenceRemoval(this);
|
||||||
protected_resources.erase(it);
|
protected_resources.erase(it);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
VKFenceWatch::VKFenceWatch() = default;
|
VKFenceWatch::VKFenceWatch() = default;
|
||||||
|
|
||||||
|
@ -141,12 +142,11 @@ VKFenceWatch::~VKFenceWatch() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKFenceWatch::Wait() {
|
void VKFenceWatch::Wait() {
|
||||||
if (!fence) {
|
if (fence == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fence->Wait();
|
fence->Wait();
|
||||||
fence->Unprotect(this);
|
fence->Unprotect(this);
|
||||||
fence = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKFenceWatch::Watch(VKFence& new_fence) {
|
void VKFenceWatch::Watch(VKFence& new_fence) {
|
||||||
|
|
|
@ -63,7 +63,7 @@ public:
|
||||||
void Protect(VKResource* resource);
|
void Protect(VKResource* resource);
|
||||||
|
|
||||||
/// Removes protection for a resource.
|
/// Removes protection for a resource.
|
||||||
void Unprotect(const VKResource* resource);
|
void Unprotect(VKResource* resource);
|
||||||
|
|
||||||
/// Retreives the fence.
|
/// Retreives the fence.
|
||||||
operator vk::Fence() const {
|
operator vk::Fence() const {
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright 2019 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "video_core/renderer_vulkan/declarations.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_device.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_memory_manager.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_resource_manager.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
|
||||||
|
|
||||||
|
namespace Vulkan {
|
||||||
|
|
||||||
|
constexpr u64 WATCHES_INITIAL_RESERVE = 0x4000;
|
||||||
|
constexpr u64 WATCHES_RESERVE_CHUNK = 0x1000;
|
||||||
|
|
||||||
|
VKStreamBuffer::VKStreamBuffer(const VKDevice& device, VKMemoryManager& memory_manager,
|
||||||
|
VKScheduler& scheduler, u64 size, vk::BufferUsageFlags usage,
|
||||||
|
vk::AccessFlags access, vk::PipelineStageFlags pipeline_stage)
|
||||||
|
: device{device}, scheduler{scheduler}, buffer_size{size}, access{access}, pipeline_stage{
|
||||||
|
pipeline_stage} {
|
||||||
|
CreateBuffers(memory_manager, usage);
|
||||||
|
ReserveWatches(WATCHES_INITIAL_RESERVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
VKStreamBuffer::~VKStreamBuffer() = default;
|
||||||
|
|
||||||
|
std::tuple<u8*, u64, bool> VKStreamBuffer::Reserve(u64 size) {
|
||||||
|
ASSERT(size <= buffer_size);
|
||||||
|
mapped_size = size;
|
||||||
|
|
||||||
|
if (offset + size > buffer_size) {
|
||||||
|
// The buffer would overflow, save the amount of used buffers, signal an invalidation and
|
||||||
|
// reset the state.
|
||||||
|
invalidation_mark = used_watches;
|
||||||
|
used_watches = 0;
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {mapped_pointer + offset, offset, invalidation_mark.has_value()};
|
||||||
|
}
|
||||||
|
|
||||||
|
VKExecutionContext VKStreamBuffer::Send(VKExecutionContext exctx, u64 size) {
|
||||||
|
ASSERT_MSG(size <= mapped_size, "Reserved size is too small");
|
||||||
|
|
||||||
|
if (invalidation_mark) {
|
||||||
|
// TODO(Rodrigo): Find a better way to invalidate than waiting for all watches to finish.
|
||||||
|
exctx = scheduler.Flush();
|
||||||
|
std::for_each(watches.begin(), watches.begin() + *invalidation_mark,
|
||||||
|
[&](auto& resource) { resource->Wait(); });
|
||||||
|
invalidation_mark = std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (used_watches + 1 >= watches.size()) {
|
||||||
|
// Ensure that there are enough watches.
|
||||||
|
ReserveWatches(WATCHES_RESERVE_CHUNK);
|
||||||
|
}
|
||||||
|
// Add a watch for this allocation.
|
||||||
|
watches[used_watches++]->Watch(exctx.GetFence());
|
||||||
|
|
||||||
|
offset += size;
|
||||||
|
|
||||||
|
return exctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VKStreamBuffer::CreateBuffers(VKMemoryManager& memory_manager, vk::BufferUsageFlags usage) {
|
||||||
|
const vk::BufferCreateInfo buffer_ci({}, buffer_size, usage, vk::SharingMode::eExclusive, 0,
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
const auto dev = device.GetLogical();
|
||||||
|
const auto& dld = device.GetDispatchLoader();
|
||||||
|
buffer = dev.createBufferUnique(buffer_ci, nullptr, dld);
|
||||||
|
commit = memory_manager.Commit(*buffer, true);
|
||||||
|
mapped_pointer = commit->GetData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VKStreamBuffer::ReserveWatches(std::size_t grow_size) {
|
||||||
|
const std::size_t previous_size = watches.size();
|
||||||
|
watches.resize(previous_size + grow_size);
|
||||||
|
std::generate(watches.begin() + previous_size, watches.end(),
|
||||||
|
[]() { return std::make_unique<VKFenceWatch>(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Vulkan
|
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright 2019 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "video_core/renderer_vulkan/declarations.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_memory_manager.h"
|
||||||
|
|
||||||
|
namespace Vulkan {
|
||||||
|
|
||||||
|
class VKDevice;
|
||||||
|
class VKFence;
|
||||||
|
class VKFenceWatch;
|
||||||
|
class VKResourceManager;
|
||||||
|
class VKScheduler;
|
||||||
|
|
||||||
|
class VKStreamBuffer {
|
||||||
|
public:
|
||||||
|
explicit VKStreamBuffer(const VKDevice& device, VKMemoryManager& memory_manager,
|
||||||
|
VKScheduler& scheduler, u64 size, vk::BufferUsageFlags usage,
|
||||||
|
vk::AccessFlags access, vk::PipelineStageFlags pipeline_stage);
|
||||||
|
~VKStreamBuffer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reserves a region of memory from the stream buffer.
|
||||||
|
* @param size Size to reserve.
|
||||||
|
* @returns A tuple in the following order: Raw memory pointer (with offset added), buffer
|
||||||
|
* offset and a boolean that's true when buffer has been invalidated.
|
||||||
|
*/
|
||||||
|
std::tuple<u8*, u64, bool> Reserve(u64 size);
|
||||||
|
|
||||||
|
/// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy.
|
||||||
|
[[nodiscard]] VKExecutionContext Send(VKExecutionContext exctx, u64 size);
|
||||||
|
|
||||||
|
vk::Buffer GetBuffer() const {
|
||||||
|
return *buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Creates Vulkan buffer handles committing the required the required memory.
|
||||||
|
void CreateBuffers(VKMemoryManager& memory_manager, vk::BufferUsageFlags usage);
|
||||||
|
|
||||||
|
/// Increases the amount of watches available.
|
||||||
|
void ReserveWatches(std::size_t grow_size);
|
||||||
|
|
||||||
|
const VKDevice& device; ///< Vulkan device manager.
|
||||||
|
VKScheduler& scheduler; ///< Command scheduler.
|
||||||
|
const u64 buffer_size; ///< Total size of the stream buffer.
|
||||||
|
const vk::AccessFlags access; ///< Access usage of this stream buffer.
|
||||||
|
const vk::PipelineStageFlags pipeline_stage; ///< Pipeline usage of this stream buffer.
|
||||||
|
|
||||||
|
UniqueBuffer buffer; ///< Mapped buffer.
|
||||||
|
VKMemoryCommit commit; ///< Memory commit.
|
||||||
|
u8* mapped_pointer{}; ///< Pointer to the host visible commit
|
||||||
|
|
||||||
|
u64 offset{}; ///< Buffer iterator.
|
||||||
|
u64 mapped_size{}; ///< Size reserved for the current copy.
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<VKFenceWatch>> watches; ///< Total watches
|
||||||
|
std::size_t used_watches{}; ///< Count of watches, reset on invalidation.
|
||||||
|
std::optional<std::size_t>
|
||||||
|
invalidation_mark{}; ///< Number of watches used in the current invalidation.
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Vulkan
|
Reference in New Issue