diff options
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/VertexManager.cpp')
| -rw-r--r-- | Source/Core/VideoBackends/Vulkan/VertexManager.cpp | 352 |
1 files changed, 283 insertions, 69 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/VertexManager.cpp b/Source/Core/VideoBackends/Vulkan/VertexManager.cpp index bd0ab0b54d..7fcbf84013 100644 --- a/Source/Core/VideoBackends/Vulkan/VertexManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/VertexManager.cpp @@ -4,87 +4,140 @@ #include "VideoBackends/Vulkan/VertexManager.h" +#include "Common/Align.h" #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" -#include "VideoBackends/Vulkan/BoundingBox.h" #include "VideoBackends/Vulkan/CommandBufferManager.h" -#include "VideoBackends/Vulkan/FramebufferManager.h" #include "VideoBackends/Vulkan/Renderer.h" #include "VideoBackends/Vulkan/StateTracker.h" #include "VideoBackends/Vulkan/StreamBuffer.h" -#include "VideoBackends/Vulkan/Util.h" #include "VideoBackends/Vulkan/VertexFormat.h" #include "VideoBackends/Vulkan/VulkanContext.h" -#include "VideoCommon/BoundingBox.h" +#include "VideoCommon/GeometryShaderManager.h" #include "VideoCommon/IndexGenerator.h" +#include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/VertexLoaderManager.h" +#include "VideoCommon/VertexShaderManager.h" #include "VideoCommon/VideoConfig.h" namespace Vulkan { -// TODO: Clean up this mess -constexpr size_t INITIAL_VERTEX_BUFFER_SIZE = VertexManager::MAXVBUFFERSIZE * 2; -constexpr size_t MAX_VERTEX_BUFFER_SIZE = VertexManager::MAXVBUFFERSIZE * 16; -constexpr size_t INITIAL_INDEX_BUFFER_SIZE = VertexManager::MAXIBUFFERSIZE * sizeof(u16) * 2; -constexpr size_t MAX_INDEX_BUFFER_SIZE = VertexManager::MAXIBUFFERSIZE * sizeof(u16) * 16; - -VertexManager::VertexManager() - : m_cpu_vertex_buffer(MAXVBUFFERSIZE), m_cpu_index_buffer(MAXIBUFFERSIZE) +static VkBufferView CreateTexelBufferView(VkBuffer buffer, VkFormat vk_format) { -} + // Create a view of the whole buffer, we'll offset our texel load into it + VkBufferViewCreateInfo view_info = { + VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, // VkStructureType sType + nullptr, // const void* pNext + 0, // VkBufferViewCreateFlags flags + buffer, // VkBuffer buffer + vk_format, // VkFormat format + 0, // VkDeviceSize offset + VK_WHOLE_SIZE // VkDeviceSize range + }; -VertexManager::~VertexManager() -{ + VkBufferView view; + VkResult res = vkCreateBufferView(g_vulkan_context->GetDevice(), &view_info, nullptr, &view); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkCreateBufferView failed: "); + return VK_NULL_HANDLE; + } + + return view; } -VertexManager* VertexManager::GetInstance() +VertexManager::VertexManager() = default; + +VertexManager::~VertexManager() { - return static_cast<VertexManager*>(g_vertex_manager.get()); + DestroyTexelBufferViews(); } bool VertexManager::Initialize() { - m_vertex_stream_buffer = StreamBuffer::Create(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - INITIAL_VERTEX_BUFFER_SIZE, MAX_VERTEX_BUFFER_SIZE); + m_vertex_stream_buffer = + StreamBuffer::Create(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VERTEX_STREAM_BUFFER_SIZE * 4); + m_index_stream_buffer = + StreamBuffer::Create(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, INDEX_STREAM_BUFFER_SIZE * 4); + m_uniform_stream_buffer = + StreamBuffer::Create(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, UNIFORM_STREAM_BUFFER_SIZE * 4); + if (!m_vertex_stream_buffer || !m_index_stream_buffer || !m_uniform_stream_buffer) + { + PanicAlert("Failed to allocate streaming buffers"); + return false; + } - m_index_stream_buffer = StreamBuffer::Create(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, - INITIAL_INDEX_BUFFER_SIZE, MAX_INDEX_BUFFER_SIZE); + // The validation layer complains if max(offsets) + max(ubo_ranges) >= ubo_size. + // To work around this we reserve the maximum buffer size at all times, but only commit + // as many bytes as we use. + m_uniform_buffer_reserve_size = sizeof(PixelShaderConstants); + m_uniform_buffer_reserve_size = Common::AlignUp(m_uniform_buffer_reserve_size, + g_vulkan_context->GetUniformBufferAlignment()) + + sizeof(VertexShaderConstants); + m_uniform_buffer_reserve_size = Common::AlignUp(m_uniform_buffer_reserve_size, + g_vulkan_context->GetUniformBufferAlignment()) + + sizeof(GeometryShaderConstants); - if (!m_vertex_stream_buffer || !m_index_stream_buffer) + // Prefer an 8MB buffer if possible, but use less if the device doesn't support this. + // This buffer is potentially going to be addressed as R8s in the future, so we assume + // that one element is one byte. This doesn't use min() because of a NDK compiler bug.. + const u32 texel_buffer_size = + TEXEL_STREAM_BUFFER_SIZE > g_vulkan_context->GetDeviceLimits().maxTexelBufferElements ? + g_vulkan_context->GetDeviceLimits().maxTexelBufferElements : + TEXEL_STREAM_BUFFER_SIZE; + m_texel_stream_buffer = + StreamBuffer::Create(VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, texel_buffer_size); + if (!m_texel_stream_buffer) { - PanicAlert("Failed to allocate streaming buffers"); + PanicAlert("Failed to allocate streaming texel buffer"); return false; } - return true; -} + static constexpr std::array<std::pair<TexelBufferFormat, VkFormat>, NUM_TEXEL_BUFFER_FORMATS> + format_mapping = {{ + {TEXEL_BUFFER_FORMAT_R8_UINT, VK_FORMAT_R8_UINT}, + {TEXEL_BUFFER_FORMAT_R16_UINT, VK_FORMAT_R16_UINT}, + {TEXEL_BUFFER_FORMAT_RGBA8_UINT, VK_FORMAT_R8G8B8A8_UNORM}, + {TEXEL_BUFFER_FORMAT_R32G32_UINT, VK_FORMAT_R32G32_UINT}, + }}; + for (const auto& it : format_mapping) + { + if ((m_texel_buffer_views[it.first] = CreateTexelBufferView(m_texel_stream_buffer->GetBuffer(), + it.second)) == VK_NULL_HANDLE) + { + PanicAlert("Failed to create texel buffer view"); + return false; + } + } -std::unique_ptr<NativeVertexFormat> -VertexManager::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) -{ - return std::make_unique<VertexFormat>(vtx_decl); -} + // Bind the buffers to all the known spots even if it's not used, to keep the driver happy. + UploadAllConstants(); + StateTracker::GetInstance()->SetUtilityUniformBuffer(m_uniform_stream_buffer->GetBuffer(), 0, + sizeof(VertexShaderConstants)); + for (u32 i = 0; i < NUM_COMPUTE_TEXEL_BUFFERS; i++) + { + StateTracker::GetInstance()->SetTexelBuffer(i, + m_texel_buffer_views[TEXEL_BUFFER_FORMAT_R8_UINT]); + } -void VertexManager::UploadUtilityUniforms(const void* uniforms, u32 uniforms_size) -{ - StateTracker::GetInstance()->UpdateConstants(uniforms, uniforms_size); + return true; } -void VertexManager::ResetBuffer(u32 vertex_stride, bool cull_all) +void VertexManager::DestroyTexelBufferViews() { - if (cull_all) + for (VkBufferView view : m_texel_buffer_views) { - // Not drawing on the gpu, so store in a heap buffer instead - m_cur_buffer_pointer = m_base_buffer_pointer = m_cpu_vertex_buffer.data(); - m_end_buffer_pointer = m_base_buffer_pointer + m_cpu_vertex_buffer.size(); - IndexGenerator::Start(m_cpu_index_buffer.data()); - return; + if (view != VK_NULL_HANDLE) + vkDestroyBufferView(g_vulkan_context->GetDevice(), view, nullptr); } +} +void VertexManager::ResetBuffer(u32 vertex_stride) +{ // Attempt to allocate from buffers bool has_vbuffer_allocation = m_vertex_stream_buffer->ReserveMemory(MAXVBUFFERSIZE, vertex_stride); @@ -94,7 +147,7 @@ void VertexManager::ResetBuffer(u32 vertex_stride, bool cull_all) { // Flush any pending commands first, so that we can wait on the fences WARN_LOG(VIDEO, "Executing command list while waiting for space in vertex/index buffer"); - Util::ExecuteCurrentCommandsAndRestoreState(false); + Renderer::GetInstance()->ExecuteCommandBuffer(false); // Attempt to allocate again, this may cause a fence wait if (!has_vbuffer_allocation) @@ -122,10 +175,8 @@ void VertexManager::CommitBuffer(u32 num_vertices, u32 vertex_stride, u32 num_in const u32 index_data_size = num_indices * sizeof(u16); *out_base_vertex = - vertex_stride > 0 ? - static_cast<u32>(m_vertex_stream_buffer->GetCurrentOffset() / vertex_stride) : - 0; - *out_base_index = static_cast<u32>(m_index_stream_buffer->GetCurrentOffset() / sizeof(u16)); + vertex_stride > 0 ? (m_vertex_stream_buffer->GetCurrentOffset() / vertex_stride) : 0; + *out_base_index = m_index_stream_buffer->GetCurrentOffset() / sizeof(u16); m_vertex_stream_buffer->CommitMemory(vertex_data_size); m_index_stream_buffer->CommitMemory(index_data_size); @@ -138,43 +189,206 @@ void VertexManager::CommitBuffer(u32 num_vertices, u32 vertex_stride, u32 num_in VK_INDEX_TYPE_UINT16); } -void VertexManager::UploadConstants() +void VertexManager::UploadUniforms() +{ + UpdateVertexShaderConstants(); + UpdateGeometryShaderConstants(); + UpdatePixelShaderConstants(); +} + +void VertexManager::UpdateVertexShaderConstants() +{ + if (!VertexShaderManager::dirty || !ReserveConstantStorage()) + return; + + StateTracker::GetInstance()->SetGXUniformBuffer( + UBO_DESCRIPTOR_SET_BINDING_VS, m_uniform_stream_buffer->GetBuffer(), + m_uniform_stream_buffer->GetCurrentOffset(), sizeof(VertexShaderConstants)); + std::memcpy(m_uniform_stream_buffer->GetCurrentHostPointer(), &VertexShaderManager::constants, + sizeof(VertexShaderConstants)); + m_uniform_stream_buffer->CommitMemory(sizeof(VertexShaderConstants)); + ADDSTAT(stats.thisFrame.bytesUniformStreamed, sizeof(VertexShaderConstants)); + VertexShaderManager::dirty = false; +} + +void VertexManager::UpdateGeometryShaderConstants() +{ + if (!GeometryShaderManager::dirty || !ReserveConstantStorage()) + return; + + StateTracker::GetInstance()->SetGXUniformBuffer( + UBO_DESCRIPTOR_SET_BINDING_GS, m_uniform_stream_buffer->GetBuffer(), + m_uniform_stream_buffer->GetCurrentOffset(), sizeof(GeometryShaderConstants)); + std::memcpy(m_uniform_stream_buffer->GetCurrentHostPointer(), &GeometryShaderManager::constants, + sizeof(GeometryShaderConstants)); + m_uniform_stream_buffer->CommitMemory(sizeof(GeometryShaderConstants)); + ADDSTAT(stats.thisFrame.bytesUniformStreamed, sizeof(GeometryShaderConstants)); + GeometryShaderManager::dirty = false; +} + +void VertexManager::UpdatePixelShaderConstants() +{ + if (!PixelShaderManager::dirty || !ReserveConstantStorage()) + return; + + StateTracker::GetInstance()->SetGXUniformBuffer( + UBO_DESCRIPTOR_SET_BINDING_PS, m_uniform_stream_buffer->GetBuffer(), + m_uniform_stream_buffer->GetCurrentOffset(), sizeof(PixelShaderConstants)); + std::memcpy(m_uniform_stream_buffer->GetCurrentHostPointer(), &PixelShaderManager::constants, + sizeof(PixelShaderConstants)); + m_uniform_stream_buffer->CommitMemory(sizeof(PixelShaderConstants)); + ADDSTAT(stats.thisFrame.bytesUniformStreamed, sizeof(PixelShaderConstants)); + PixelShaderManager::dirty = false; +} + +bool VertexManager::ReserveConstantStorage() { - StateTracker::GetInstance()->UpdateVertexShaderConstants(); - StateTracker::GetInstance()->UpdateGeometryShaderConstants(); - StateTracker::GetInstance()->UpdatePixelShaderConstants(); + if (m_uniform_stream_buffer->ReserveMemory(m_uniform_buffer_reserve_size, + g_vulkan_context->GetUniformBufferAlignment())) + { + return true; + } + + // The only places that call constant updates are safe to have state restored. + WARN_LOG(VIDEO, "Executing command buffer while waiting for space in uniform buffer"); + Renderer::GetInstance()->ExecuteCommandBuffer(false); + + // Since we are on a new command buffer, all constants have been invalidated, and we need + // to reupload them. We may as well do this now, since we're issuing a draw anyway. + UploadAllConstants(); + return false; } -void VertexManager::DrawCurrentBatch(u32 base_index, u32 num_indices, u32 base_vertex) +void VertexManager::UploadAllConstants() { - // Flush all EFB pokes and invalidate the peek cache. - FramebufferManager::GetInstance()->InvalidatePeekCache(); - FramebufferManager::GetInstance()->FlushEFBPokes(); + // We are free to re-use parts of the buffer now since we're uploading all constants. + const u32 ub_alignment = static_cast<u32>(g_vulkan_context->GetUniformBufferAlignment()); + const u32 pixel_constants_offset = 0; + const u32 vertex_constants_offset = + Common::AlignUp(pixel_constants_offset + sizeof(PixelShaderConstants), ub_alignment); + const u32 geometry_constants_offset = + Common::AlignUp(vertex_constants_offset + sizeof(VertexShaderConstants), ub_alignment); + const u32 allocation_size = geometry_constants_offset + sizeof(GeometryShaderConstants); - // If bounding box is enabled, we need to flush any changes first, then invalidate what we have. - if (g_vulkan_context->SupportsBoundingBox()) + // Allocate everything at once. + // We should only be here if the buffer was full and a command buffer was submitted anyway. + if (!m_uniform_stream_buffer->ReserveMemory(allocation_size, ub_alignment)) { - BoundingBox* bounding_box = Renderer::GetInstance()->GetBoundingBox(); - bool bounding_box_enabled = (::BoundingBox::active && g_ActiveConfig.bBBoxEnable); - if (bounding_box_enabled) - { - bounding_box->Flush(); - bounding_box->Invalidate(); - } + PanicAlert("Failed to allocate space for constants in streaming buffer"); + return; } - // Bind all pending state to the command buffer - if (StateTracker::GetInstance()->Bind()) + // Update bindings + StateTracker::GetInstance()->SetGXUniformBuffer( + UBO_DESCRIPTOR_SET_BINDING_PS, m_uniform_stream_buffer->GetBuffer(), + m_uniform_stream_buffer->GetCurrentOffset() + pixel_constants_offset, + sizeof(PixelShaderConstants)); + StateTracker::GetInstance()->SetGXUniformBuffer( + UBO_DESCRIPTOR_SET_BINDING_VS, m_uniform_stream_buffer->GetBuffer(), + m_uniform_stream_buffer->GetCurrentOffset() + vertex_constants_offset, + sizeof(VertexShaderConstants)); + StateTracker::GetInstance()->SetGXUniformBuffer( + UBO_DESCRIPTOR_SET_BINDING_GS, m_uniform_stream_buffer->GetBuffer(), + m_uniform_stream_buffer->GetCurrentOffset() + geometry_constants_offset, + sizeof(GeometryShaderConstants)); + + // Copy the actual data in + std::memcpy(m_uniform_stream_buffer->GetCurrentHostPointer() + pixel_constants_offset, + &PixelShaderManager::constants, sizeof(PixelShaderConstants)); + std::memcpy(m_uniform_stream_buffer->GetCurrentHostPointer() + vertex_constants_offset, + &VertexShaderManager::constants, sizeof(VertexShaderConstants)); + std::memcpy(m_uniform_stream_buffer->GetCurrentHostPointer() + geometry_constants_offset, + &GeometryShaderManager::constants, sizeof(GeometryShaderConstants)); + + // Finally, flush buffer memory after copying + m_uniform_stream_buffer->CommitMemory(allocation_size); + ADDSTAT(stats.thisFrame.bytesUniformStreamed, allocation_size); + + // Clear dirty flags + VertexShaderManager::dirty = false; + GeometryShaderManager::dirty = false; + PixelShaderManager::dirty = false; +} + +void VertexManager::UploadUtilityUniforms(const void* data, u32 data_size) +{ + InvalidateConstants(); + if (!m_uniform_stream_buffer->ReserveMemory(data_size, + g_vulkan_context->GetUniformBufferAlignment())) { - vkCmdDrawIndexed(g_command_buffer_mgr->GetCurrentCommandBuffer(), num_indices, 1, base_index, - base_vertex, 0); + WARN_LOG(VIDEO, "Executing command buffer while waiting for ext space in uniform buffer"); + Renderer::GetInstance()->ExecuteCommandBuffer(false); } - else + + StateTracker::GetInstance()->SetUtilityUniformBuffer( + m_uniform_stream_buffer->GetBuffer(), m_uniform_stream_buffer->GetCurrentOffset(), data_size); + std::memcpy(m_uniform_stream_buffer->GetCurrentHostPointer(), data, data_size); + m_uniform_stream_buffer->CommitMemory(data_size); + ADDSTAT(stats.thisFrame.bytesUniformStreamed, data_size); +} + +bool VertexManager::UploadTexelBuffer(const void* data, u32 data_size, TexelBufferFormat format, + u32* out_offset) +{ + if (data_size > m_texel_stream_buffer->GetCurrentSize()) + return false; + + const u32 elem_size = GetTexelBufferElementSize(format); + if (!m_texel_stream_buffer->ReserveMemory(data_size, elem_size)) { - WARN_LOG(VIDEO, "Skipped draw of %u indices", num_indices); + // Try submitting cmdbuffer. + WARN_LOG(VIDEO, "Submitting command buffer while waiting for space in texel buffer"); + Renderer::GetInstance()->ExecuteCommandBuffer(false, false); + if (!m_texel_stream_buffer->ReserveMemory(data_size, elem_size)) + { + PanicAlert("Failed to allocate %u bytes from texel buffer", data_size); + return false; + } } - StateTracker::GetInstance()->OnDraw(); + std::memcpy(m_texel_stream_buffer->GetCurrentHostPointer(), data, data_size); + *out_offset = static_cast<u32>(m_texel_stream_buffer->GetCurrentOffset()) / elem_size; + m_texel_stream_buffer->CommitMemory(data_size); + ADDSTAT(stats.thisFrame.bytesUniformStreamed, data_size); + StateTracker::GetInstance()->SetTexelBuffer(0, m_texel_buffer_views[format]); + return true; } +bool VertexManager::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) +{ + const u32 elem_size = GetTexelBufferElementSize(format); + const u32 palette_elem_size = GetTexelBufferElementSize(palette_format); + const u32 reserve_size = data_size + palette_size + palette_elem_size; + if (reserve_size > m_texel_stream_buffer->GetCurrentSize()) + return false; + + if (!m_texel_stream_buffer->ReserveMemory(reserve_size, elem_size)) + { + // Try submitting cmdbuffer. + WARN_LOG(VIDEO, "Submitting command buffer while waiting for space in texel buffer"); + Renderer::GetInstance()->ExecuteCommandBuffer(false, false); + if (!m_texel_stream_buffer->ReserveMemory(reserve_size, elem_size)) + { + PanicAlert("Failed to allocate %u bytes from texel buffer", reserve_size); + return false; + } + } + + const u32 palette_byte_offset = Common::AlignUp(data_size, palette_elem_size); + std::memcpy(m_texel_stream_buffer->GetCurrentHostPointer(), data, data_size); + std::memcpy(m_texel_stream_buffer->GetCurrentHostPointer() + palette_byte_offset, palette_data, + palette_size); + *out_offset = static_cast<u32>(m_texel_stream_buffer->GetCurrentOffset()) / elem_size; + *out_palette_offset = + (static_cast<u32>(m_texel_stream_buffer->GetCurrentOffset()) + palette_byte_offset) / + palette_elem_size; + + m_texel_stream_buffer->CommitMemory(palette_byte_offset + palette_size); + ADDSTAT(stats.thisFrame.bytesUniformStreamed, palette_byte_offset + palette_size); + StateTracker::GetInstance()->SetTexelBuffer(0, m_texel_buffer_views[format]); + StateTracker::GetInstance()->SetTexelBuffer(1, m_texel_buffer_views[palette_format]); + return true; +} } // namespace Vulkan |
