summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/Vulkan/VertexManager.cpp
diff options
context:
space:
mode:
authorStenzek <stenzek@gmail.com>2016-08-13 22:57:50 +1000
committerStenzek <stenzek@gmail.com>2016-10-01 02:40:01 +1000
commit77a128ab8707659a531029a21a2e874108eedf38 (patch)
tree7b4a52ee61208eec2bf44a7745f3dac3bed1bafb /Source/Core/VideoBackends/Vulkan/VertexManager.cpp
parentfdd954e7e757b4601eb6d2636e12623aef63c9b4 (diff)
Implement experimental Vulkan backend
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/VertexManager.cpp')
-rw-r--r--Source/Core/VideoBackends/Vulkan/VertexManager.cpp218
1 files changed, 218 insertions, 0 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/VertexManager.cpp b/Source/Core/VideoBackends/Vulkan/VertexManager.cpp
new file mode 100644
index 0000000000..e95c1d6ade
--- /dev/null
+++ b/Source/Core/VideoBackends/Vulkan/VertexManager.cpp
@@ -0,0 +1,218 @@
+// Copyright 2016 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "VideoBackends/Vulkan/VertexManager.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/IndexGenerator.h"
+#include "VideoCommon/Statistics.h"
+#include "VideoCommon/VertexLoaderManager.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)
+{
+}
+
+VertexManager::~VertexManager()
+{
+}
+
+bool VertexManager::Initialize(StateTracker* state_tracker)
+{
+ m_state_tracker = state_tracker;
+
+ m_vertex_stream_buffer = StreamBuffer::Create(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
+ INITIAL_VERTEX_BUFFER_SIZE, MAX_VERTEX_BUFFER_SIZE);
+
+ m_index_stream_buffer = StreamBuffer::Create(VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
+ INITIAL_INDEX_BUFFER_SIZE, MAX_INDEX_BUFFER_SIZE);
+
+ if (!m_vertex_stream_buffer || !m_index_stream_buffer)
+ {
+ PanicAlert("Failed to allocate streaming buffers");
+ return false;
+ }
+
+ return true;
+}
+
+NativeVertexFormat*
+VertexManager::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl)
+{
+ return new VertexFormat(vtx_decl);
+}
+
+void VertexManager::PrepareDrawBuffers(u32 stride)
+{
+ size_t vertex_data_size = IndexGenerator::GetNumVerts() * stride;
+ size_t index_data_size = IndexGenerator::GetIndexLen() * sizeof(u16);
+
+ m_vertex_stream_buffer->CommitMemory(vertex_data_size);
+ m_index_stream_buffer->CommitMemory(index_data_size);
+
+ ADDSTAT(stats.thisFrame.bytesVertexStreamed, static_cast<int>(vertex_data_size));
+ ADDSTAT(stats.thisFrame.bytesIndexStreamed, static_cast<int>(index_data_size));
+
+ m_state_tracker->SetVertexBuffer(m_vertex_stream_buffer->GetBuffer(), 0);
+ m_state_tracker->SetIndexBuffer(m_index_stream_buffer->GetBuffer(), 0, VK_INDEX_TYPE_UINT16);
+}
+
+void VertexManager::ResetBuffer(u32 stride)
+{
+ if (m_cull_all)
+ {
+ // 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;
+ }
+
+ // Attempt to allocate from buffers
+ bool has_vbuffer_allocation = m_vertex_stream_buffer->ReserveMemory(MAXVBUFFERSIZE, stride);
+ bool has_ibuffer_allocation = m_index_stream_buffer->ReserveMemory(MAXIBUFFERSIZE, sizeof(u16));
+ if (!has_vbuffer_allocation || !has_ibuffer_allocation)
+ {
+ // 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(m_state_tracker, false);
+
+ // Attempt to allocate again, this may cause a fence wait
+ if (!has_vbuffer_allocation)
+ has_vbuffer_allocation = m_vertex_stream_buffer->ReserveMemory(MAXVBUFFERSIZE, stride);
+ if (!has_ibuffer_allocation)
+ has_ibuffer_allocation = m_index_stream_buffer->ReserveMemory(MAXIBUFFERSIZE, sizeof(u16));
+
+ // If we still failed, that means the allocation was too large and will never succeed, so panic
+ if (!has_vbuffer_allocation || !has_ibuffer_allocation)
+ PanicAlert("Failed to allocate space in streaming buffers for pending draw");
+ }
+
+ // Update pointers
+ m_base_buffer_pointer = m_vertex_stream_buffer->GetHostPointer();
+ m_end_buffer_pointer = m_vertex_stream_buffer->GetCurrentHostPointer() + MAXVBUFFERSIZE;
+ m_cur_buffer_pointer = m_vertex_stream_buffer->GetCurrentHostPointer();
+ IndexGenerator::Start(reinterpret_cast<u16*>(m_index_stream_buffer->GetCurrentHostPointer()));
+
+ // Update base indices
+ m_current_draw_base_vertex =
+ static_cast<u32>(m_vertex_stream_buffer->GetCurrentOffset() / stride);
+ m_current_draw_base_index =
+ static_cast<u32>(m_index_stream_buffer->GetCurrentOffset() / sizeof(u16));
+}
+
+void VertexManager::vFlush(bool use_dst_alpha)
+{
+ const VertexFormat* vertex_format =
+ static_cast<VertexFormat*>(VertexLoaderManager::GetCurrentVertexFormat());
+ u32 vertex_stride = vertex_format->GetVertexStride();
+
+ // Commit memory to device
+ PrepareDrawBuffers(vertex_stride);
+
+ // Figure out the number of indices to draw
+ u32 index_count = IndexGenerator::GetIndexLen();
+
+ // Update assembly state
+ m_state_tracker->SetVertexFormat(vertex_format);
+ switch (m_current_primitive_type)
+ {
+ case PRIMITIVE_POINTS:
+ m_state_tracker->SetPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_POINT_LIST);
+ m_state_tracker->DisableBackFaceCulling();
+ break;
+
+ case PRIMITIVE_LINES:
+ m_state_tracker->SetPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_LINE_LIST);
+ m_state_tracker->DisableBackFaceCulling();
+ break;
+
+ case PRIMITIVE_TRIANGLES:
+ m_state_tracker->SetPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
+ g_renderer->SetGenerationMode();
+ break;
+ }
+
+ // Can we do single-pass dst alpha?
+ DSTALPHA_MODE dstalpha_mode = DSTALPHA_NONE;
+ if (use_dst_alpha && g_vulkan_context->SupportsDualSourceBlend())
+ dstalpha_mode = DSTALPHA_DUAL_SOURCE_BLEND;
+
+ // Check for any shader stage changes
+ m_state_tracker->CheckForShaderChanges(m_current_primitive_type, dstalpha_mode);
+
+ // Update any changed constants
+ m_state_tracker->UpdateVertexShaderConstants();
+ m_state_tracker->UpdateGeometryShaderConstants();
+ m_state_tracker->UpdatePixelShaderConstants();
+
+ // Flush all EFB pokes and invalidate the peek cache.
+ // TODO: Cleaner way without the cast.
+ FramebufferManager* framebuffer_mgr =
+ static_cast<FramebufferManager*>(g_framebuffer_manager.get());
+ framebuffer_mgr->InvalidatePeekCache();
+ framebuffer_mgr->FlushEFBPokes(m_state_tracker);
+
+ // If bounding box is enabled, we need to flush any changes first, then invalidate what we have.
+ if (g_vulkan_context->SupportsBoundingBox())
+ {
+ BoundingBox* bounding_box = static_cast<Renderer*>(g_renderer.get())->GetBoundingBox();
+ bool bounding_box_enabled = (::BoundingBox::active && g_ActiveConfig.bBBoxEnable);
+ if (bounding_box_enabled)
+ {
+ bounding_box->Flush(m_state_tracker);
+ bounding_box->Invalidate(m_state_tracker);
+ }
+
+ // Update which descriptor set/pipeline layout to use.
+ m_state_tracker->SetBBoxEnable(bounding_box_enabled);
+ }
+
+ // Bind all pending state to the command buffer
+ if (!m_state_tracker->Bind())
+ {
+ WARN_LOG(VIDEO, "Skipped draw of %u indices", index_count);
+ return;
+ }
+
+ // Execute the draw
+ vkCmdDrawIndexed(g_command_buffer_mgr->GetCurrentCommandBuffer(), index_count, 1,
+ m_current_draw_base_index, m_current_draw_base_vertex, 0);
+
+ // If we can't do single pass dst alpha, we now need to draw the alpha pass.
+ if (use_dst_alpha && !g_vulkan_context->SupportsDualSourceBlend())
+ {
+ m_state_tracker->CheckForShaderChanges(m_current_primitive_type, DSTALPHA_ALPHA_PASS);
+ if (!m_state_tracker->Bind())
+ {
+ WARN_LOG(VIDEO, "Skipped draw of %u indices (alpha pass)", index_count);
+ return;
+ }
+
+ vkCmdDrawIndexed(g_command_buffer_mgr->GetCurrentCommandBuffer(), index_count, 1,
+ m_current_draw_base_index, m_current_draw_base_vertex, 0);
+ }
+
+ m_state_tracker->OnDraw();
+}
+
+} // namespace Vulkan