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