summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2020-01-06 17:59:20 -0300
committerGravatar ReinUsesLisp2020-01-06 18:13:41 -0300
commit5b01f80a12b84f05bc4e34fc384f85e1a7860117 (patch)
tree35df5af1a7d6118e8cadd8ece49fd5d270bc0d30 /src
parentvk_memory_manager: Misc changes (diff)
downloadyuzu-5b01f80a12b84f05bc4e34fc384f85e1a7860117.tar.gz
yuzu-5b01f80a12b84f05bc4e34fc384f85e1a7860117.tar.xz
yuzu-5b01f80a12b84f05bc4e34fc384f85e1a7860117.zip
vk_stream_buffer/vk_buffer_cache: Avoid halting and use generic cache
The stream buffer before this commit once it was full (no more bytes to write before looping) waiting for all previous operations to finish. This was a temporary solution and had a noticeable performance penalty in performance (from what a profiler showed). To avoid this mark with fences usages of the stream buffer and once it loops wait for them to be signaled. On average this will never wait. Each fence knows where its usage finishes, resulting in a non-paged stream buffer. On the other side, the buffer cache is reimplemented using the generic buffer cache. It makes use of the staging buffer pool and the new stream buffer.
Diffstat (limited to 'src')
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp143
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h73
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.cpp142
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.h44
4 files changed, 340 insertions, 62 deletions
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index c36ede898..1ba544943 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -1,3 +1,146 @@
1// Copyright 2019 yuzu Emulator Project 1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <cstring>
7#include <memory>
8#include <optional>
9#include <tuple>
10
11#include "common/assert.h"
12#include "common/bit_util.h"
13#include "core/core.h"
14#include "video_core/renderer_vulkan/declarations.h"
15#include "video_core/renderer_vulkan/vk_buffer_cache.h"
16#include "video_core/renderer_vulkan/vk_device.h"
17#include "video_core/renderer_vulkan/vk_scheduler.h"
18#include "video_core/renderer_vulkan/vk_stream_buffer.h"
19
20namespace Vulkan {
21
22namespace {
23
24const auto BufferUsage =
25 vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eIndexBuffer |
26 vk::BufferUsageFlagBits::eUniformBuffer | vk::BufferUsageFlagBits::eStorageBuffer;
27
28const auto UploadPipelineStage =
29 vk::PipelineStageFlagBits::eTransfer | vk::PipelineStageFlagBits::eVertexInput |
30 vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eFragmentShader |
31 vk::PipelineStageFlagBits::eComputeShader;
32
33const auto UploadAccessBarriers =
34 vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eShaderRead |
35 vk::AccessFlagBits::eUniformRead | vk::AccessFlagBits::eVertexAttributeRead |
36 vk::AccessFlagBits::eIndexRead;
37
38auto CreateStreamBuffer(const VKDevice& device, VKScheduler& scheduler) {
39 return std::make_unique<VKStreamBuffer>(device, scheduler, BufferUsage);
40}
41
42} // Anonymous namespace
43
44CachedBufferBlock::CachedBufferBlock(const VKDevice& device, VKMemoryManager& memory_manager,
45 CacheAddr cache_addr, std::size_t size)
46 : VideoCommon::BufferBlock{cache_addr, size} {
47 const vk::BufferCreateInfo buffer_ci({}, static_cast<vk::DeviceSize>(size),
48 BufferUsage | vk::BufferUsageFlagBits::eTransferSrc |
49 vk::BufferUsageFlagBits::eTransferDst,
50 vk::SharingMode::eExclusive, 0, nullptr);
51
52 const auto& dld{device.GetDispatchLoader()};
53 const auto dev{device.GetLogical()};
54 buffer.handle = dev.createBufferUnique(buffer_ci, nullptr, dld);
55 buffer.commit = memory_manager.Commit(*buffer.handle, false);
56}
57
58CachedBufferBlock::~CachedBufferBlock() = default;
59
60VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system,
61 const VKDevice& device, VKMemoryManager& memory_manager,
62 VKScheduler& scheduler, VKStagingBufferPool& staging_pool)
63 : VideoCommon::BufferCache<Buffer, vk::Buffer, VKStreamBuffer>{rasterizer, system,
64 CreateStreamBuffer(device,
65 scheduler)},
66 device{device}, memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{
67 staging_pool} {}
68
69VKBufferCache::~VKBufferCache() = default;
70
71Buffer VKBufferCache::CreateBlock(CacheAddr cache_addr, std::size_t size) {
72 return std::make_shared<CachedBufferBlock>(device, memory_manager, cache_addr, size);
73}
74
75const vk::Buffer* VKBufferCache::ToHandle(const Buffer& buffer) {
76 return buffer->GetHandle();
77}
78
79const vk::Buffer* VKBufferCache::GetEmptyBuffer(std::size_t size) {
80 size = std::max(size, std::size_t(4));
81 const auto& empty = staging_pool.GetUnusedBuffer(size, false);
82 scheduler.RequestOutsideRenderPassOperationContext();
83 scheduler.Record([size, buffer = *empty.handle](vk::CommandBuffer cmdbuf, auto& dld) {
84 cmdbuf.fillBuffer(buffer, 0, size, 0, dld);
85 });
86 return &*empty.handle;
87}
88
89void VKBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size,
90 const u8* data) {
91 const auto& staging = staging_pool.GetUnusedBuffer(size, true);
92 std::memcpy(staging.commit->Map(size), data, size);
93
94 scheduler.RequestOutsideRenderPassOperationContext();
95 scheduler.Record([staging = *staging.handle, buffer = *buffer->GetHandle(), offset,
96 size](auto cmdbuf, auto& dld) {
97 cmdbuf.copyBuffer(staging, buffer, {{0, offset, size}}, dld);
98 cmdbuf.pipelineBarrier(
99 vk::PipelineStageFlagBits::eTransfer, UploadPipelineStage, {}, {},
100 {vk::BufferMemoryBarrier(vk::AccessFlagBits::eTransferWrite, UploadAccessBarriers,
101 VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, buffer,
102 offset, size)},
103 {}, dld);
104 });
105}
106
107void VKBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size,
108 u8* data) {
109 const auto& staging = staging_pool.GetUnusedBuffer(size, true);
110 scheduler.RequestOutsideRenderPassOperationContext();
111 scheduler.Record([staging = *staging.handle, buffer = *buffer->GetHandle(), offset,
112 size](auto cmdbuf, auto& dld) {
113 cmdbuf.pipelineBarrier(
114 vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eFragmentShader |
115 vk::PipelineStageFlagBits::eComputeShader,
116 vk::PipelineStageFlagBits::eTransfer, {}, {},
117 {vk::BufferMemoryBarrier(vk::AccessFlagBits::eShaderWrite,
118 vk::AccessFlagBits::eTransferRead, VK_QUEUE_FAMILY_IGNORED,
119 VK_QUEUE_FAMILY_IGNORED, buffer, offset, size)},
120 {}, dld);
121 cmdbuf.copyBuffer(buffer, staging, {{offset, 0, size}}, dld);
122 });
123 scheduler.Finish();
124
125 std::memcpy(data, staging.commit->Map(size), size);
126}
127
128void VKBufferCache::CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset,
129 std::size_t dst_offset, std::size_t size) {
130 scheduler.RequestOutsideRenderPassOperationContext();
131 scheduler.Record([src_buffer = *src->GetHandle(), dst_buffer = *dst->GetHandle(), src_offset,
132 dst_offset, size](auto cmdbuf, auto& dld) {
133 cmdbuf.copyBuffer(src_buffer, dst_buffer, {{src_offset, dst_offset, size}}, dld);
134 cmdbuf.pipelineBarrier(
135 vk::PipelineStageFlagBits::eTransfer, UploadPipelineStage, {}, {},
136 {vk::BufferMemoryBarrier(vk::AccessFlagBits::eTransferRead,
137 vk::AccessFlagBits::eShaderWrite, VK_QUEUE_FAMILY_IGNORED,
138 VK_QUEUE_FAMILY_IGNORED, src_buffer, src_offset, size),
139 vk::BufferMemoryBarrier(vk::AccessFlagBits::eTransferWrite, UploadAccessBarriers,
140 VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, dst_buffer,
141 dst_offset, size)},
142 {}, dld);
143 });
144}
145
146} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index bc6e584cf..3f38eed0c 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -3,3 +3,76 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6
7#include <memory>
8#include <unordered_map>
9#include <vector>
10
11#include "common/common_types.h"
12#include "video_core/buffer_cache/buffer_cache.h"
13#include "video_core/rasterizer_cache.h"
14#include "video_core/renderer_vulkan/declarations.h"
15#include "video_core/renderer_vulkan/vk_memory_manager.h"
16#include "video_core/renderer_vulkan/vk_resource_manager.h"
17#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
18#include "video_core/renderer_vulkan/vk_stream_buffer.h"
19
20namespace Core {
21class System;
22}
23
24namespace Vulkan {
25
26class VKDevice;
27class VKMemoryManager;
28class VKScheduler;
29
30class CachedBufferBlock final : public VideoCommon::BufferBlock {
31public:
32 explicit CachedBufferBlock(const VKDevice& device, VKMemoryManager& memory_manager,
33 CacheAddr cache_addr, std::size_t size);
34 ~CachedBufferBlock();
35
36 const vk::Buffer* GetHandle() const {
37 return &*buffer.handle;
38 }
39
40private:
41 VKBuffer buffer;
42};
43
44using Buffer = std::shared_ptr<CachedBufferBlock>;
45
46class VKBufferCache final : public VideoCommon::BufferCache<Buffer, vk::Buffer, VKStreamBuffer> {
47public:
48 explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system,
49 const VKDevice& device, VKMemoryManager& memory_manager,
50 VKScheduler& scheduler, VKStagingBufferPool& staging_pool);
51 ~VKBufferCache();
52
53 const vk::Buffer* GetEmptyBuffer(std::size_t size) override;
54
55protected:
56 void WriteBarrier() override {}
57
58 Buffer CreateBlock(CacheAddr cache_addr, std::size_t size) override;
59
60 const vk::Buffer* ToHandle(const Buffer& buffer) override;
61
62 void UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size,
63 const u8* data) override;
64
65 void DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size,
66 u8* data) override;
67
68 void CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset,
69 std::size_t dst_offset, std::size_t size) override;
70
71private:
72 const VKDevice& device;
73 VKMemoryManager& memory_manager;
74 VKScheduler& scheduler;
75 VKStagingBufferPool& staging_pool;
76};
77
78} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
index 62f1427f5..d48d3b44c 100644
--- a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
+++ b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
@@ -3,86 +3,144 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <memory>
7#include <optional> 6#include <optional>
7#include <tuple>
8#include <vector> 8#include <vector>
9 9
10#include "common/alignment.h"
10#include "common/assert.h" 11#include "common/assert.h"
11#include "video_core/renderer_vulkan/declarations.h" 12#include "video_core/renderer_vulkan/declarations.h"
12#include "video_core/renderer_vulkan/vk_device.h" 13#include "video_core/renderer_vulkan/vk_device.h"
13#include "video_core/renderer_vulkan/vk_memory_manager.h"
14#include "video_core/renderer_vulkan/vk_resource_manager.h" 14#include "video_core/renderer_vulkan/vk_resource_manager.h"
15#include "video_core/renderer_vulkan/vk_scheduler.h" 15#include "video_core/renderer_vulkan/vk_scheduler.h"
16#include "video_core/renderer_vulkan/vk_stream_buffer.h" 16#include "video_core/renderer_vulkan/vk_stream_buffer.h"
17 17
18namespace Vulkan { 18namespace Vulkan {
19 19
20namespace {
21
20constexpr u64 WATCHES_INITIAL_RESERVE = 0x4000; 22constexpr u64 WATCHES_INITIAL_RESERVE = 0x4000;
21constexpr u64 WATCHES_RESERVE_CHUNK = 0x1000; 23constexpr u64 WATCHES_RESERVE_CHUNK = 0x1000;
22 24
23VKStreamBuffer::VKStreamBuffer(const VKDevice& device, VKMemoryManager& memory_manager, 25constexpr u64 STREAM_BUFFER_SIZE = 256 * 1024 * 1024;
24 VKScheduler& scheduler, u64 size, vk::BufferUsageFlags usage, 26
25 vk::AccessFlags access, vk::PipelineStageFlags pipeline_stage) 27std::optional<u32> FindMemoryType(const VKDevice& device, u32 filter,
26 : device{device}, scheduler{scheduler}, buffer_size{size}, access{access}, pipeline_stage{ 28 vk::MemoryPropertyFlags wanted) {
27 pipeline_stage} { 29 const auto properties = device.GetPhysical().getMemoryProperties(device.GetDispatchLoader());
28 CreateBuffers(memory_manager, usage); 30 for (u32 i = 0; i < properties.memoryTypeCount; i++) {
29 ReserveWatches(WATCHES_INITIAL_RESERVE); 31 if (!(filter & (1 << i))) {
32 continue;
33 }
34 if ((properties.memoryTypes[i].propertyFlags & wanted) == wanted) {
35 return i;
36 }
37 }
38 return {};
39}
40
41} // Anonymous namespace
42
43VKStreamBuffer::VKStreamBuffer(const VKDevice& device, VKScheduler& scheduler,
44 vk::BufferUsageFlags usage)
45 : device{device}, scheduler{scheduler} {
46 CreateBuffers(usage);
47 ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE);
48 ReserveWatches(previous_watches, WATCHES_INITIAL_RESERVE);
30} 49}
31 50
32VKStreamBuffer::~VKStreamBuffer() = default; 51VKStreamBuffer::~VKStreamBuffer() = default;
33 52
34std::tuple<u8*, u64, bool> VKStreamBuffer::Reserve(u64 size) { 53std::tuple<u8*, u64, bool> VKStreamBuffer::Map(u64 size, u64 alignment) {
35 ASSERT(size <= buffer_size); 54 ASSERT(size <= STREAM_BUFFER_SIZE);
36 mapped_size = size; 55 mapped_size = size;
37 56
38 if (offset + size > buffer_size) { 57 if (alignment > 0) {
39 // The buffer would overflow, save the amount of used buffers, signal an invalidation and 58 offset = Common::AlignUp(offset, alignment);
40 // reset the state. 59 }
41 invalidation_mark = used_watches; 60
42 used_watches = 0; 61 WaitPendingOperations(offset);
62
63 bool invalidated = false;
64 if (offset + size > STREAM_BUFFER_SIZE) {
65 // The buffer would overflow, save the amount of used watches and reset the state.
66 invalidation_mark = current_watch_cursor;
67 current_watch_cursor = 0;
43 offset = 0; 68 offset = 0;
69
70 // Swap watches and reset waiting cursors.
71 std::swap(previous_watches, current_watches);
72 wait_cursor = 0;
73 wait_bound = 0;
74
75 // Ensure that we don't wait for uncommitted fences.
76 scheduler.Flush();
77
78 invalidated = true;
44 } 79 }
45 80
46 return {mapped_pointer + offset, offset, invalidation_mark.has_value()}; 81 const auto dev = device.GetLogical();
82 const auto& dld = device.GetDispatchLoader();
83 const auto pointer = reinterpret_cast<u8*>(dev.mapMemory(*memory, offset, size, {}, dld));
84 return {pointer, offset, invalidated};
47} 85}
48 86
49void VKStreamBuffer::Send(u64 size) { 87void VKStreamBuffer::Unmap(u64 size) {
50 ASSERT_MSG(size <= mapped_size, "Reserved size is too small"); 88 ASSERT_MSG(size <= mapped_size, "Reserved size is too small");
51 89
52 if (invalidation_mark) { 90 const auto dev = device.GetLogical();
53 // TODO(Rodrigo): Find a better way to invalidate than waiting for all watches to finish. 91 dev.unmapMemory(*memory, device.GetDispatchLoader());
54 scheduler.Flush(); 92
55 std::for_each(watches.begin(), watches.begin() + *invalidation_mark, 93 offset += size;
56 [&](auto& resource) { resource->Wait(); });
57 invalidation_mark = std::nullopt;
58 }
59 94
60 if (used_watches + 1 >= watches.size()) { 95 if (current_watch_cursor + 1 >= current_watches.size()) {
61 // Ensure that there are enough watches. 96 // Ensure that there are enough watches.
62 ReserveWatches(WATCHES_RESERVE_CHUNK); 97 ReserveWatches(current_watches, WATCHES_RESERVE_CHUNK);
63 } 98 }
64 // Add a watch for this allocation. 99 auto& watch = current_watches[current_watch_cursor++];
65 watches[used_watches++]->Watch(scheduler.GetFence()); 100 watch.upper_bound = offset;
66 101 watch.fence.Watch(scheduler.GetFence());
67 offset += size;
68} 102}
69 103
70void VKStreamBuffer::CreateBuffers(VKMemoryManager& memory_manager, vk::BufferUsageFlags usage) { 104void VKStreamBuffer::CreateBuffers(vk::BufferUsageFlags usage) {
71 const vk::BufferCreateInfo buffer_ci({}, buffer_size, usage, vk::SharingMode::eExclusive, 0, 105 const vk::BufferCreateInfo buffer_ci({}, STREAM_BUFFER_SIZE, usage, vk::SharingMode::eExclusive,
72 nullptr); 106 0, nullptr);
73
74 const auto dev = device.GetLogical(); 107 const auto dev = device.GetLogical();
75 const auto& dld = device.GetDispatchLoader(); 108 const auto& dld = device.GetDispatchLoader();
76 buffer = dev.createBufferUnique(buffer_ci, nullptr, dld); 109 buffer = dev.createBufferUnique(buffer_ci, nullptr, dld);
77 commit = memory_manager.Commit(*buffer, true); 110
78 mapped_pointer = commit->GetData(); 111 const auto requirements = dev.getBufferMemoryRequirements(*buffer, dld);
112 // Prefer device local host visible allocations (this should hit AMD's pinned memory).
113 auto type = FindMemoryType(device, requirements.memoryTypeBits,
114 vk::MemoryPropertyFlagBits::eHostVisible |
115 vk::MemoryPropertyFlagBits::eHostCoherent |
116 vk::MemoryPropertyFlagBits::eDeviceLocal);
117 if (!type) {
118 // Otherwise search for a host visible allocation.
119 type = FindMemoryType(device, requirements.memoryTypeBits,
120 vk::MemoryPropertyFlagBits::eHostVisible |
121 vk::MemoryPropertyFlagBits::eHostCoherent);
122 ASSERT_MSG(type, "No host visible and coherent memory type found");
123 }
124 const vk::MemoryAllocateInfo alloc_ci(requirements.size, *type);
125 memory = dev.allocateMemoryUnique(alloc_ci, nullptr, dld);
126
127 dev.bindBufferMemory(*buffer, *memory, 0, dld);
79} 128}
80 129
81void VKStreamBuffer::ReserveWatches(std::size_t grow_size) { 130void VKStreamBuffer::ReserveWatches(std::vector<Watch>& watches, std::size_t grow_size) {
82 const std::size_t previous_size = watches.size(); 131 watches.resize(watches.size() + grow_size);
83 watches.resize(previous_size + grow_size); 132}
84 std::generate(watches.begin() + previous_size, watches.end(), 133
85 []() { return std::make_unique<VKFenceWatch>(); }); 134void VKStreamBuffer::WaitPendingOperations(u64 requested_upper_bound) {
135 if (!invalidation_mark) {
136 return;
137 }
138 while (requested_upper_bound < wait_bound && wait_cursor < *invalidation_mark) {
139 auto& watch = previous_watches[wait_cursor];
140 wait_bound = watch.upper_bound;
141 watch.fence.Wait();
142 ++wait_cursor;
143 }
86} 144}
87 145
88} // namespace Vulkan 146} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.h b/src/video_core/renderer_vulkan/vk_stream_buffer.h
index 842e54162..187c0c612 100644
--- a/src/video_core/renderer_vulkan/vk_stream_buffer.h
+++ b/src/video_core/renderer_vulkan/vk_stream_buffer.h
@@ -4,28 +4,24 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
8#include <optional> 7#include <optional>
9#include <tuple> 8#include <tuple>
10#include <vector> 9#include <vector>
11 10
12#include "common/common_types.h" 11#include "common/common_types.h"
13#include "video_core/renderer_vulkan/declarations.h" 12#include "video_core/renderer_vulkan/declarations.h"
14#include "video_core/renderer_vulkan/vk_memory_manager.h"
15 13
16namespace Vulkan { 14namespace Vulkan {
17 15
18class VKDevice; 16class VKDevice;
19class VKFence; 17class VKFence;
20class VKFenceWatch; 18class VKFenceWatch;
21class VKResourceManager;
22class VKScheduler; 19class VKScheduler;
23 20
24class VKStreamBuffer { 21class VKStreamBuffer final {
25public: 22public:
26 explicit VKStreamBuffer(const VKDevice& device, VKMemoryManager& memory_manager, 23 explicit VKStreamBuffer(const VKDevice& device, VKScheduler& scheduler,
27 VKScheduler& scheduler, u64 size, vk::BufferUsageFlags usage, 24 vk::BufferUsageFlags usage);
28 vk::AccessFlags access, vk::PipelineStageFlags pipeline_stage);
29 ~VKStreamBuffer(); 25 ~VKStreamBuffer();
30 26
31 /** 27 /**
@@ -34,39 +30,47 @@ public:
34 * @returns A tuple in the following order: Raw memory pointer (with offset added), buffer 30 * @returns A tuple in the following order: Raw memory pointer (with offset added), buffer
35 * offset and a boolean that's true when buffer has been invalidated. 31 * offset and a boolean that's true when buffer has been invalidated.
36 */ 32 */
37 std::tuple<u8*, u64, bool> Reserve(u64 size); 33 std::tuple<u8*, u64, bool> Map(u64 size, u64 alignment);
38 34
39 /// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy. 35 /// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy.
40 void Send(u64 size); 36 void Unmap(u64 size);
41 37
42 vk::Buffer GetBuffer() const { 38 vk::Buffer GetHandle() const {
43 return *buffer; 39 return *buffer;
44 } 40 }
45 41
46private: 42private:
43 struct Watch final {
44 VKFenceWatch fence;
45 u64 upper_bound{};
46 };
47
47 /// Creates Vulkan buffer handles committing the required the required memory. 48 /// Creates Vulkan buffer handles committing the required the required memory.
48 void CreateBuffers(VKMemoryManager& memory_manager, vk::BufferUsageFlags usage); 49 void CreateBuffers(vk::BufferUsageFlags usage);
49 50
50 /// Increases the amount of watches available. 51 /// Increases the amount of watches available.
51 void ReserveWatches(std::size_t grow_size); 52 void ReserveWatches(std::vector<Watch>& watches, std::size_t grow_size);
53
54 void WaitPendingOperations(u64 requested_upper_bound);
52 55
53 const VKDevice& device; ///< Vulkan device manager. 56 const VKDevice& device; ///< Vulkan device manager.
54 VKScheduler& scheduler; ///< Command scheduler. 57 VKScheduler& scheduler; ///< Command scheduler.
55 const u64 buffer_size; ///< Total size of the stream buffer.
56 const vk::AccessFlags access; ///< Access usage of this stream buffer. 58 const vk::AccessFlags access; ///< Access usage of this stream buffer.
57 const vk::PipelineStageFlags pipeline_stage; ///< Pipeline usage of this stream buffer. 59 const vk::PipelineStageFlags pipeline_stage; ///< Pipeline usage of this stream buffer.
58 60
59 UniqueBuffer buffer; ///< Mapped buffer. 61 UniqueBuffer buffer; ///< Mapped buffer.
60 VKMemoryCommit commit; ///< Memory commit. 62 UniqueDeviceMemory memory; ///< Memory allocation.
61 u8* mapped_pointer{}; ///< Pointer to the host visible commit
62 63
63 u64 offset{}; ///< Buffer iterator. 64 u64 offset{}; ///< Buffer iterator.
64 u64 mapped_size{}; ///< Size reserved for the current copy. 65 u64 mapped_size{}; ///< Size reserved for the current copy.
65 66
66 std::vector<std::unique_ptr<VKFenceWatch>> watches; ///< Total watches 67 std::vector<Watch> current_watches; ///< Watches recorded in the current iteration.
67 std::size_t used_watches{}; ///< Count of watches, reset on invalidation. 68 std::size_t current_watch_cursor{}; ///< Count of watches, reset on invalidation.
68 std::optional<std::size_t> 69 std::optional<std::size_t> invalidation_mark; ///< Number of watches used in the previous cycle.
69 invalidation_mark{}; ///< Number of watches used in the current invalidation. 70
71 std::vector<Watch> previous_watches; ///< Watches used in the previous iteration.
72 std::size_t wait_cursor{}; ///< Last watch being waited for completion.
73 u64 wait_bound{}; ///< Highest offset being watched for completion.
70}; 74};
71 75
72} // namespace Vulkan 76} // namespace Vulkan