1 // Copyright 2016 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4
5 #include "VideoBackends/Vulkan/VertexManager.h"
6
7 #include "Common/Align.h"
8 #include "Common/CommonTypes.h"
9 #include "Common/Logging/Log.h"
10 #include "Common/MsgHandler.h"
11
12 #include "VideoBackends/Vulkan/CommandBufferManager.h"
13 #include "VideoBackends/Vulkan/Renderer.h"
14 #include "VideoBackends/Vulkan/StateTracker.h"
15 #include "VideoBackends/Vulkan/StreamBuffer.h"
16 #include "VideoBackends/Vulkan/VertexFormat.h"
17 #include "VideoBackends/Vulkan/VulkanContext.h"
18
19 #include "VideoCommon/GeometryShaderManager.h"
20 #include "VideoCommon/IndexGenerator.h"
21 #include "VideoCommon/PixelShaderManager.h"
22 #include "VideoCommon/Statistics.h"
23 #include "VideoCommon/VertexLoaderManager.h"
24 #include "VideoCommon/VertexShaderManager.h"
25 #include "VideoCommon/VideoConfig.h"
26
27 namespace Vulkan
28 {
CreateTexelBufferView(VkBuffer buffer,VkFormat vk_format)29 static VkBufferView CreateTexelBufferView(VkBuffer buffer, VkFormat vk_format)
30 {
31 // Create a view of the whole buffer, we'll offset our texel load into it
32 VkBufferViewCreateInfo view_info = {
33 VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, // VkStructureType sType
34 nullptr, // const void* pNext
35 0, // VkBufferViewCreateFlags flags
36 buffer, // VkBuffer buffer
37 vk_format, // VkFormat format
38 0, // VkDeviceSize offset
39 VK_WHOLE_SIZE // VkDeviceSize range
40 };
41
42 VkBufferView view;
43 VkResult res = vkCreateBufferView(g_vulkan_context->GetDevice(), &view_info, nullptr, &view);
44 if (res != VK_SUCCESS)
45 {
46 LOG_VULKAN_ERROR(res, "vkCreateBufferView failed: ");
47 return VK_NULL_HANDLE;
48 }
49
50 return view;
51 }
52
53 VertexManager::VertexManager() = default;
54
~VertexManager()55 VertexManager::~VertexManager()
56 {
57 DestroyTexelBufferViews();
58 }
59
Initialize()60 bool VertexManager::Initialize()
61 {
62 if (!VertexManagerBase::Initialize())
63 return false;
64
65 m_vertex_stream_buffer =
66 StreamBuffer::Create(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VERTEX_STREAM_BUFFER_SIZE);
67 m_index_stream_buffer =
68 StreamBuffer::Create(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, INDEX_STREAM_BUFFER_SIZE);
69 m_uniform_stream_buffer =
70 StreamBuffer::Create(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, UNIFORM_STREAM_BUFFER_SIZE);
71 if (!m_vertex_stream_buffer || !m_index_stream_buffer || !m_uniform_stream_buffer)
72 {
73 PanicAlert("Failed to allocate streaming buffers");
74 return false;
75 }
76
77 // The validation layer complains if max(offsets) + max(ubo_ranges) >= ubo_size.
78 // To work around this we reserve the maximum buffer size at all times, but only commit
79 // as many bytes as we use.
80 m_uniform_buffer_reserve_size = sizeof(PixelShaderConstants);
81 m_uniform_buffer_reserve_size = Common::AlignUp(m_uniform_buffer_reserve_size,
82 g_vulkan_context->GetUniformBufferAlignment()) +
83 sizeof(VertexShaderConstants);
84 m_uniform_buffer_reserve_size = Common::AlignUp(m_uniform_buffer_reserve_size,
85 g_vulkan_context->GetUniformBufferAlignment()) +
86 sizeof(GeometryShaderConstants);
87
88 // Prefer an 8MB buffer if possible, but use less if the device doesn't support this.
89 // This buffer is potentially going to be addressed as R8s in the future, so we assume
90 // that one element is one byte. This doesn't use min() because of a NDK compiler bug..
91 const u32 texel_buffer_size =
92 TEXEL_STREAM_BUFFER_SIZE > g_vulkan_context->GetDeviceLimits().maxTexelBufferElements ?
93 g_vulkan_context->GetDeviceLimits().maxTexelBufferElements :
94 TEXEL_STREAM_BUFFER_SIZE;
95 m_texel_stream_buffer =
96 StreamBuffer::Create(VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, texel_buffer_size);
97 if (!m_texel_stream_buffer)
98 {
99 PanicAlert("Failed to allocate streaming texel buffer");
100 return false;
101 }
102
103 static constexpr std::array<std::pair<TexelBufferFormat, VkFormat>, NUM_TEXEL_BUFFER_FORMATS>
104 format_mapping = {{
105 {TEXEL_BUFFER_FORMAT_R8_UINT, VK_FORMAT_R8_UINT},
106 {TEXEL_BUFFER_FORMAT_R16_UINT, VK_FORMAT_R16_UINT},
107 {TEXEL_BUFFER_FORMAT_RGBA8_UINT, VK_FORMAT_R8G8B8A8_UINT},
108 {TEXEL_BUFFER_FORMAT_R32G32_UINT, VK_FORMAT_R32G32_UINT},
109 }};
110 for (const auto& it : format_mapping)
111 {
112 if ((m_texel_buffer_views[it.first] = CreateTexelBufferView(m_texel_stream_buffer->GetBuffer(),
113 it.second)) == VK_NULL_HANDLE)
114 {
115 PanicAlert("Failed to create texel buffer view");
116 return false;
117 }
118 }
119
120 // Bind the buffers to all the known spots even if it's not used, to keep the driver happy.
121 UploadAllConstants();
122 StateTracker::GetInstance()->SetUtilityUniformBuffer(m_uniform_stream_buffer->GetBuffer(), 0,
123 sizeof(VertexShaderConstants));
124 for (u32 i = 0; i < NUM_COMPUTE_TEXEL_BUFFERS; i++)
125 {
126 StateTracker::GetInstance()->SetTexelBuffer(i,
127 m_texel_buffer_views[TEXEL_BUFFER_FORMAT_R8_UINT]);
128 }
129
130 return true;
131 }
132
DestroyTexelBufferViews()133 void VertexManager::DestroyTexelBufferViews()
134 {
135 for (VkBufferView view : m_texel_buffer_views)
136 {
137 if (view != VK_NULL_HANDLE)
138 vkDestroyBufferView(g_vulkan_context->GetDevice(), view, nullptr);
139 }
140 }
141
ResetBuffer(u32 vertex_stride)142 void VertexManager::ResetBuffer(u32 vertex_stride)
143 {
144 // Attempt to allocate from buffers
145 bool has_vbuffer_allocation =
146 m_vertex_stream_buffer->ReserveMemory(MAXVBUFFERSIZE, vertex_stride);
147 bool has_ibuffer_allocation =
148 m_index_stream_buffer->ReserveMemory(MAXIBUFFERSIZE * sizeof(u16), sizeof(u16));
149 if (!has_vbuffer_allocation || !has_ibuffer_allocation)
150 {
151 // Flush any pending commands first, so that we can wait on the fences
152 WARN_LOG(VIDEO, "Executing command list while waiting for space in vertex/index buffer");
153 Renderer::GetInstance()->ExecuteCommandBuffer(false);
154
155 // Attempt to allocate again, this may cause a fence wait
156 if (!has_vbuffer_allocation)
157 has_vbuffer_allocation = m_vertex_stream_buffer->ReserveMemory(MAXVBUFFERSIZE, vertex_stride);
158 if (!has_ibuffer_allocation)
159 has_ibuffer_allocation =
160 m_index_stream_buffer->ReserveMemory(MAXIBUFFERSIZE * sizeof(u16), sizeof(u16));
161
162 // If we still failed, that means the allocation was too large and will never succeed, so panic
163 if (!has_vbuffer_allocation || !has_ibuffer_allocation)
164 PanicAlert("Failed to allocate space in streaming buffers for pending draw");
165 }
166
167 // Update pointers
168 m_base_buffer_pointer = m_vertex_stream_buffer->GetHostPointer();
169 m_end_buffer_pointer = m_vertex_stream_buffer->GetCurrentHostPointer() + MAXVBUFFERSIZE;
170 m_cur_buffer_pointer = m_vertex_stream_buffer->GetCurrentHostPointer();
171 m_index_generator.Start(reinterpret_cast<u16*>(m_index_stream_buffer->GetCurrentHostPointer()));
172 }
173
CommitBuffer(u32 num_vertices,u32 vertex_stride,u32 num_indices,u32 * out_base_vertex,u32 * out_base_index)174 void VertexManager::CommitBuffer(u32 num_vertices, u32 vertex_stride, u32 num_indices,
175 u32* out_base_vertex, u32* out_base_index)
176 {
177 const u32 vertex_data_size = num_vertices * vertex_stride;
178 const u32 index_data_size = num_indices * sizeof(u16);
179
180 *out_base_vertex =
181 vertex_stride > 0 ? (m_vertex_stream_buffer->GetCurrentOffset() / vertex_stride) : 0;
182 *out_base_index = m_index_stream_buffer->GetCurrentOffset() / sizeof(u16);
183
184 m_vertex_stream_buffer->CommitMemory(vertex_data_size);
185 m_index_stream_buffer->CommitMemory(index_data_size);
186
187 ADDSTAT(g_stats.this_frame.bytes_vertex_streamed, static_cast<int>(vertex_data_size));
188 ADDSTAT(g_stats.this_frame.bytes_index_streamed, static_cast<int>(index_data_size));
189
190 StateTracker::GetInstance()->SetVertexBuffer(m_vertex_stream_buffer->GetBuffer(), 0);
191 StateTracker::GetInstance()->SetIndexBuffer(m_index_stream_buffer->GetBuffer(), 0,
192 VK_INDEX_TYPE_UINT16);
193 }
194
UploadUniforms()195 void VertexManager::UploadUniforms()
196 {
197 UpdateVertexShaderConstants();
198 UpdateGeometryShaderConstants();
199 UpdatePixelShaderConstants();
200 }
201
UpdateVertexShaderConstants()202 void VertexManager::UpdateVertexShaderConstants()
203 {
204 if (!VertexShaderManager::dirty || !ReserveConstantStorage())
205 return;
206
207 StateTracker::GetInstance()->SetGXUniformBuffer(
208 UBO_DESCRIPTOR_SET_BINDING_VS, m_uniform_stream_buffer->GetBuffer(),
209 m_uniform_stream_buffer->GetCurrentOffset(), sizeof(VertexShaderConstants));
210 std::memcpy(m_uniform_stream_buffer->GetCurrentHostPointer(), &VertexShaderManager::constants,
211 sizeof(VertexShaderConstants));
212 m_uniform_stream_buffer->CommitMemory(sizeof(VertexShaderConstants));
213 ADDSTAT(g_stats.this_frame.bytes_uniform_streamed, sizeof(VertexShaderConstants));
214 VertexShaderManager::dirty = false;
215 }
216
UpdateGeometryShaderConstants()217 void VertexManager::UpdateGeometryShaderConstants()
218 {
219 if (!GeometryShaderManager::dirty || !ReserveConstantStorage())
220 return;
221
222 StateTracker::GetInstance()->SetGXUniformBuffer(
223 UBO_DESCRIPTOR_SET_BINDING_GS, m_uniform_stream_buffer->GetBuffer(),
224 m_uniform_stream_buffer->GetCurrentOffset(), sizeof(GeometryShaderConstants));
225 std::memcpy(m_uniform_stream_buffer->GetCurrentHostPointer(), &GeometryShaderManager::constants,
226 sizeof(GeometryShaderConstants));
227 m_uniform_stream_buffer->CommitMemory(sizeof(GeometryShaderConstants));
228 ADDSTAT(g_stats.this_frame.bytes_uniform_streamed, sizeof(GeometryShaderConstants));
229 GeometryShaderManager::dirty = false;
230 }
231
UpdatePixelShaderConstants()232 void VertexManager::UpdatePixelShaderConstants()
233 {
234 if (!PixelShaderManager::dirty || !ReserveConstantStorage())
235 return;
236
237 StateTracker::GetInstance()->SetGXUniformBuffer(
238 UBO_DESCRIPTOR_SET_BINDING_PS, m_uniform_stream_buffer->GetBuffer(),
239 m_uniform_stream_buffer->GetCurrentOffset(), sizeof(PixelShaderConstants));
240 std::memcpy(m_uniform_stream_buffer->GetCurrentHostPointer(), &PixelShaderManager::constants,
241 sizeof(PixelShaderConstants));
242 m_uniform_stream_buffer->CommitMemory(sizeof(PixelShaderConstants));
243 ADDSTAT(g_stats.this_frame.bytes_uniform_streamed, sizeof(PixelShaderConstants));
244 PixelShaderManager::dirty = false;
245 }
246
ReserveConstantStorage()247 bool VertexManager::ReserveConstantStorage()
248 {
249 if (m_uniform_stream_buffer->ReserveMemory(m_uniform_buffer_reserve_size,
250 g_vulkan_context->GetUniformBufferAlignment()))
251 {
252 return true;
253 }
254
255 // The only places that call constant updates are safe to have state restored.
256 WARN_LOG(VIDEO, "Executing command buffer while waiting for space in uniform buffer");
257 Renderer::GetInstance()->ExecuteCommandBuffer(false);
258
259 // Since we are on a new command buffer, all constants have been invalidated, and we need
260 // to reupload them. We may as well do this now, since we're issuing a draw anyway.
261 UploadAllConstants();
262 return false;
263 }
264
UploadAllConstants()265 void VertexManager::UploadAllConstants()
266 {
267 // We are free to re-use parts of the buffer now since we're uploading all constants.
268 const u32 ub_alignment = static_cast<u32>(g_vulkan_context->GetUniformBufferAlignment());
269 const u32 pixel_constants_offset = 0;
270 const u32 vertex_constants_offset =
271 Common::AlignUp(pixel_constants_offset + sizeof(PixelShaderConstants), ub_alignment);
272 const u32 geometry_constants_offset =
273 Common::AlignUp(vertex_constants_offset + sizeof(VertexShaderConstants), ub_alignment);
274 const u32 allocation_size = geometry_constants_offset + sizeof(GeometryShaderConstants);
275
276 // Allocate everything at once.
277 // We should only be here if the buffer was full and a command buffer was submitted anyway.
278 if (!m_uniform_stream_buffer->ReserveMemory(allocation_size, ub_alignment))
279 {
280 PanicAlert("Failed to allocate space for constants in streaming buffer");
281 return;
282 }
283
284 // Update bindings
285 StateTracker::GetInstance()->SetGXUniformBuffer(
286 UBO_DESCRIPTOR_SET_BINDING_PS, m_uniform_stream_buffer->GetBuffer(),
287 m_uniform_stream_buffer->GetCurrentOffset() + pixel_constants_offset,
288 sizeof(PixelShaderConstants));
289 StateTracker::GetInstance()->SetGXUniformBuffer(
290 UBO_DESCRIPTOR_SET_BINDING_VS, m_uniform_stream_buffer->GetBuffer(),
291 m_uniform_stream_buffer->GetCurrentOffset() + vertex_constants_offset,
292 sizeof(VertexShaderConstants));
293 StateTracker::GetInstance()->SetGXUniformBuffer(
294 UBO_DESCRIPTOR_SET_BINDING_GS, m_uniform_stream_buffer->GetBuffer(),
295 m_uniform_stream_buffer->GetCurrentOffset() + geometry_constants_offset,
296 sizeof(GeometryShaderConstants));
297
298 // Copy the actual data in
299 std::memcpy(m_uniform_stream_buffer->GetCurrentHostPointer() + pixel_constants_offset,
300 &PixelShaderManager::constants, sizeof(PixelShaderConstants));
301 std::memcpy(m_uniform_stream_buffer->GetCurrentHostPointer() + vertex_constants_offset,
302 &VertexShaderManager::constants, sizeof(VertexShaderConstants));
303 std::memcpy(m_uniform_stream_buffer->GetCurrentHostPointer() + geometry_constants_offset,
304 &GeometryShaderManager::constants, sizeof(GeometryShaderConstants));
305
306 // Finally, flush buffer memory after copying
307 m_uniform_stream_buffer->CommitMemory(allocation_size);
308 ADDSTAT(g_stats.this_frame.bytes_uniform_streamed, allocation_size);
309
310 // Clear dirty flags
311 VertexShaderManager::dirty = false;
312 GeometryShaderManager::dirty = false;
313 PixelShaderManager::dirty = false;
314 }
315
UploadUtilityUniforms(const void * data,u32 data_size)316 void VertexManager::UploadUtilityUniforms(const void* data, u32 data_size)
317 {
318 InvalidateConstants();
319 if (!m_uniform_stream_buffer->ReserveMemory(data_size,
320 g_vulkan_context->GetUniformBufferAlignment()))
321 {
322 WARN_LOG(VIDEO, "Executing command buffer while waiting for ext space in uniform buffer");
323 Renderer::GetInstance()->ExecuteCommandBuffer(false);
324 }
325
326 StateTracker::GetInstance()->SetUtilityUniformBuffer(
327 m_uniform_stream_buffer->GetBuffer(), m_uniform_stream_buffer->GetCurrentOffset(), data_size);
328 std::memcpy(m_uniform_stream_buffer->GetCurrentHostPointer(), data, data_size);
329 m_uniform_stream_buffer->CommitMemory(data_size);
330 ADDSTAT(g_stats.this_frame.bytes_uniform_streamed, data_size);
331 }
332
UploadTexelBuffer(const void * data,u32 data_size,TexelBufferFormat format,u32 * out_offset)333 bool VertexManager::UploadTexelBuffer(const void* data, u32 data_size, TexelBufferFormat format,
334 u32* out_offset)
335 {
336 if (data_size > m_texel_stream_buffer->GetCurrentSize())
337 return false;
338
339 const u32 elem_size = GetTexelBufferElementSize(format);
340 if (!m_texel_stream_buffer->ReserveMemory(data_size, elem_size))
341 {
342 // Try submitting cmdbuffer.
343 WARN_LOG(VIDEO, "Submitting command buffer while waiting for space in texel buffer");
344 Renderer::GetInstance()->ExecuteCommandBuffer(false, false);
345 if (!m_texel_stream_buffer->ReserveMemory(data_size, elem_size))
346 {
347 PanicAlert("Failed to allocate %u bytes from texel buffer", data_size);
348 return false;
349 }
350 }
351
352 std::memcpy(m_texel_stream_buffer->GetCurrentHostPointer(), data, data_size);
353 *out_offset = static_cast<u32>(m_texel_stream_buffer->GetCurrentOffset()) / elem_size;
354 m_texel_stream_buffer->CommitMemory(data_size);
355 ADDSTAT(g_stats.this_frame.bytes_uniform_streamed, data_size);
356 StateTracker::GetInstance()->SetTexelBuffer(0, m_texel_buffer_views[format]);
357 return true;
358 }
359
UploadTexelBuffer(const void * data,u32 data_size,TexelBufferFormat format,u32 * out_offset,const void * palette_data,u32 palette_size,TexelBufferFormat palette_format,u32 * out_palette_offset)360 bool VertexManager::UploadTexelBuffer(const void* data, u32 data_size, TexelBufferFormat format,
361 u32* out_offset, const void* palette_data, u32 palette_size,
362 TexelBufferFormat palette_format, u32* out_palette_offset)
363 {
364 const u32 elem_size = GetTexelBufferElementSize(format);
365 const u32 palette_elem_size = GetTexelBufferElementSize(palette_format);
366 const u32 reserve_size = data_size + palette_size + palette_elem_size;
367 if (reserve_size > m_texel_stream_buffer->GetCurrentSize())
368 return false;
369
370 if (!m_texel_stream_buffer->ReserveMemory(reserve_size, elem_size))
371 {
372 // Try submitting cmdbuffer.
373 WARN_LOG(VIDEO, "Submitting command buffer while waiting for space in texel buffer");
374 Renderer::GetInstance()->ExecuteCommandBuffer(false, false);
375 if (!m_texel_stream_buffer->ReserveMemory(reserve_size, elem_size))
376 {
377 PanicAlert("Failed to allocate %u bytes from texel buffer", reserve_size);
378 return false;
379 }
380 }
381
382 const u32 palette_byte_offset = Common::AlignUp(data_size, palette_elem_size);
383 std::memcpy(m_texel_stream_buffer->GetCurrentHostPointer(), data, data_size);
384 std::memcpy(m_texel_stream_buffer->GetCurrentHostPointer() + palette_byte_offset, palette_data,
385 palette_size);
386 *out_offset = static_cast<u32>(m_texel_stream_buffer->GetCurrentOffset()) / elem_size;
387 *out_palette_offset =
388 (static_cast<u32>(m_texel_stream_buffer->GetCurrentOffset()) + palette_byte_offset) /
389 palette_elem_size;
390
391 m_texel_stream_buffer->CommitMemory(palette_byte_offset + palette_size);
392 ADDSTAT(g_stats.this_frame.bytes_uniform_streamed, palette_byte_offset + palette_size);
393 StateTracker::GetInstance()->SetTexelBuffer(0, m_texel_buffer_views[format]);
394 StateTracker::GetInstance()->SetTexelBuffer(1, m_texel_buffer_views[palette_format]);
395 return true;
396 }
397 } // namespace Vulkan
398