1 // Copyright 2019 yuzu Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4
5 #include <algorithm>
6 #include <cstring>
7 #include <memory>
8
9 #include "core/core.h"
10 #include "video_core/buffer_cache/buffer_cache.h"
11 #include "video_core/renderer_vulkan/vk_buffer_cache.h"
12 #include "video_core/renderer_vulkan/vk_device.h"
13 #include "video_core/renderer_vulkan/vk_scheduler.h"
14 #include "video_core/renderer_vulkan/vk_stream_buffer.h"
15 #include "video_core/renderer_vulkan/wrapper.h"
16
17 namespace Vulkan {
18
19 namespace {
20
21 constexpr VkBufferUsageFlags BUFFER_USAGE =
22 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
23 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
24
25 constexpr VkPipelineStageFlags UPLOAD_PIPELINE_STAGE =
26 VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT |
27 VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
28 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
29
30 constexpr VkAccessFlags UPLOAD_ACCESS_BARRIERS =
31 VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_UNIFORM_READ_BIT |
32 VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_INDEX_READ_BIT;
33
CreateStreamBuffer(const VKDevice & device,VKScheduler & scheduler)34 std::unique_ptr<VKStreamBuffer> CreateStreamBuffer(const VKDevice& device, VKScheduler& scheduler) {
35 return std::make_unique<VKStreamBuffer>(device, scheduler, BUFFER_USAGE);
36 }
37
38 } // Anonymous namespace
39
Buffer(const VKDevice & device,VKMemoryManager & memory_manager,VKScheduler & scheduler_,VKStagingBufferPool & staging_pool_,VAddr cpu_addr_,std::size_t size_)40 Buffer::Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VKScheduler& scheduler_,
41 VKStagingBufferPool& staging_pool_, VAddr cpu_addr_, std::size_t size_)
42 : BufferBlock{cpu_addr_, size_}, scheduler{scheduler_}, staging_pool{staging_pool_} {
43 const VkBufferCreateInfo ci{
44 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
45 .pNext = nullptr,
46 .flags = 0,
47 .size = static_cast<VkDeviceSize>(size_),
48 .usage = BUFFER_USAGE | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
49 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
50 .queueFamilyIndexCount = 0,
51 .pQueueFamilyIndices = nullptr,
52 };
53
54 buffer.handle = device.GetLogical().CreateBuffer(ci);
55 buffer.commit = memory_manager.Commit(buffer.handle, false);
56 }
57
58 Buffer::~Buffer() = default;
59
Upload(std::size_t offset,std::size_t data_size,const u8 * data)60 void Buffer::Upload(std::size_t offset, std::size_t data_size, const u8* data) {
61 const auto& staging = staging_pool.GetUnusedBuffer(data_size, true);
62 std::memcpy(staging.commit->Map(data_size), data, data_size);
63
64 scheduler.RequestOutsideRenderPassOperationContext();
65
66 const VkBuffer handle = Handle();
67 scheduler.Record(
68 [staging = *staging.handle, handle, offset, data_size](vk::CommandBuffer cmdbuf) {
69 cmdbuf.CopyBuffer(staging, handle, VkBufferCopy{0, offset, data_size});
70
71 const VkBufferMemoryBarrier barrier{
72 .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
73 .pNext = nullptr,
74 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
75 .dstAccessMask = UPLOAD_ACCESS_BARRIERS,
76 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
77 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
78 .buffer = handle,
79 .offset = offset,
80 .size = data_size,
81 };
82 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, UPLOAD_PIPELINE_STAGE, 0, {},
83 barrier, {});
84 });
85 }
86
Download(std::size_t offset,std::size_t data_size,u8 * data)87 void Buffer::Download(std::size_t offset, std::size_t data_size, u8* data) {
88 const auto& staging = staging_pool.GetUnusedBuffer(data_size, true);
89 scheduler.RequestOutsideRenderPassOperationContext();
90
91 const VkBuffer handle = Handle();
92 scheduler.Record(
93 [staging = *staging.handle, handle, offset, data_size](vk::CommandBuffer cmdbuf) {
94 const VkBufferMemoryBarrier barrier{
95 .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
96 .pNext = nullptr,
97 .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
98 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
99 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
100 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
101 .buffer = handle,
102 .offset = offset,
103 .size = data_size,
104 };
105
106 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
107 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
108 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
109 VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, barrier, {});
110 cmdbuf.CopyBuffer(handle, staging, VkBufferCopy{offset, 0, data_size});
111 });
112 scheduler.Finish();
113
114 std::memcpy(data, staging.commit->Map(data_size), data_size);
115 }
116
CopyFrom(const Buffer & src,std::size_t src_offset,std::size_t dst_offset,std::size_t copy_size)117 void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset,
118 std::size_t copy_size) {
119 scheduler.RequestOutsideRenderPassOperationContext();
120
121 const VkBuffer dst_buffer = Handle();
122 scheduler.Record([src_buffer = src.Handle(), dst_buffer, src_offset, dst_offset,
123 copy_size](vk::CommandBuffer cmdbuf) {
124 cmdbuf.CopyBuffer(src_buffer, dst_buffer, VkBufferCopy{src_offset, dst_offset, copy_size});
125
126 std::array<VkBufferMemoryBarrier, 2> barriers;
127 barriers[0].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
128 barriers[0].pNext = nullptr;
129 barriers[0].srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
130 barriers[0].dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
131 barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
132 barriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
133 barriers[0].buffer = src_buffer;
134 barriers[0].offset = src_offset;
135 barriers[0].size = copy_size;
136 barriers[1].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
137 barriers[1].pNext = nullptr;
138 barriers[1].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
139 barriers[1].dstAccessMask = UPLOAD_ACCESS_BARRIERS;
140 barriers[1].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
141 barriers[1].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
142 barriers[1].buffer = dst_buffer;
143 barriers[1].offset = dst_offset;
144 barriers[1].size = copy_size;
145 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, UPLOAD_PIPELINE_STAGE, 0, {},
146 barriers, {});
147 });
148 }
149
VKBufferCache(VideoCore::RasterizerInterface & rasterizer_,Tegra::MemoryManager & gpu_memory_,Core::Memory::Memory & cpu_memory_,const VKDevice & device_,VKMemoryManager & memory_manager_,VKScheduler & scheduler_,VKStagingBufferPool & staging_pool_)150 VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer_,
151 Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_,
152 const VKDevice& device_, VKMemoryManager& memory_manager_,
153 VKScheduler& scheduler_, VKStagingBufferPool& staging_pool_)
154 : BufferCache{rasterizer_, gpu_memory_, cpu_memory_, CreateStreamBuffer(device_, scheduler_)},
155 device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_}, staging_pool{
156 staging_pool_} {}
157
158 VKBufferCache::~VKBufferCache() = default;
159
CreateBlock(VAddr cpu_addr,std::size_t size)160 std::shared_ptr<Buffer> VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) {
161 return std::make_shared<Buffer>(device, memory_manager, scheduler, staging_pool, cpu_addr,
162 size);
163 }
164
GetEmptyBuffer(std::size_t size)165 VKBufferCache::BufferInfo VKBufferCache::GetEmptyBuffer(std::size_t size) {
166 size = std::max(size, std::size_t(4));
167 const auto& empty = staging_pool.GetUnusedBuffer(size, false);
168 scheduler.RequestOutsideRenderPassOperationContext();
169 scheduler.Record([size, buffer = *empty.handle](vk::CommandBuffer cmdbuf) {
170 cmdbuf.FillBuffer(buffer, 0, size, 0);
171 });
172 return {*empty.handle, 0, 0};
173 }
174
175 } // namespace Vulkan
176