diff --git a/Source/Core/VideoBackends/D3D/CMakeLists.txt b/Source/Core/VideoBackends/D3D/CMakeLists.txt
index a90f274eb2..1e3af890fb 100644
--- a/Source/Core/VideoBackends/D3D/CMakeLists.txt
+++ b/Source/Core/VideoBackends/D3D/CMakeLists.txt
@@ -13,6 +13,10 @@ set(SRCS
D3DTexture.h
D3DUtil.cpp
D3DUtil.h
+ DXPipeline.cpp
+ DXPipeline.h
+ DXShader.cpp
+ DXShader.h
DXTexture.cpp
DXTexture.h
FramebufferManager.cpp
diff --git a/Source/Core/VideoBackends/D3D/D3D.vcxproj b/Source/Core/VideoBackends/D3D/D3D.vcxproj
index 50c8ea0fcd..40a4da122f 100644
--- a/Source/Core/VideoBackends/D3D/D3D.vcxproj
+++ b/Source/Core/VideoBackends/D3D/D3D.vcxproj
@@ -43,6 +43,8 @@
+
+
@@ -64,6 +66,8 @@
+
+
diff --git a/Source/Core/VideoBackends/D3D/D3D.vcxproj.filters b/Source/Core/VideoBackends/D3D/D3D.vcxproj.filters
index f544ae67db..d3f7f7b01d 100644
--- a/Source/Core/VideoBackends/D3D/D3D.vcxproj.filters
+++ b/Source/Core/VideoBackends/D3D/D3D.vcxproj.filters
@@ -64,6 +64,12 @@
Render
+
+ Render
+
+
+ Render
+
@@ -118,5 +124,11 @@
Render
+
+ Render
+
+
+ Render
+
\ No newline at end of file
diff --git a/Source/Core/VideoBackends/D3D/DXPipeline.cpp b/Source/Core/VideoBackends/D3D/DXPipeline.cpp
new file mode 100644
index 0000000000..278030a66b
--- /dev/null
+++ b/Source/Core/VideoBackends/D3D/DXPipeline.cpp
@@ -0,0 +1,96 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include
+#include
+
+#include "Common/Assert.h"
+#include "Common/Logging/Log.h"
+
+#include "VideoBackends/D3D/D3DBase.h"
+#include "VideoBackends/D3D/D3DState.h"
+#include "VideoBackends/D3D/DXPipeline.h"
+#include "VideoBackends/D3D/DXShader.h"
+#include "VideoBackends/D3D/DXTexture.h"
+#include "VideoBackends/D3D/Render.h"
+#include "VideoBackends/D3D/VertexManager.h"
+
+namespace DX11
+{
+DXPipeline::DXPipeline(ID3D11InputLayout* input_layout, ID3D11VertexShader* vertex_shader,
+ ID3D11GeometryShader* geometry_shader, ID3D11PixelShader* pixel_shader,
+ ID3D11RasterizerState* rasterizer_state,
+ ID3D11DepthStencilState* depth_state, ID3D11BlendState* blend_state,
+ D3D11_PRIMITIVE_TOPOLOGY primitive_topology)
+ : m_input_layout(input_layout), m_vertex_shader(vertex_shader),
+ m_geometry_shader(geometry_shader), m_pixel_shader(pixel_shader),
+ m_rasterizer_state(rasterizer_state), m_depth_state(depth_state), m_blend_state(blend_state),
+ m_primitive_topology(primitive_topology)
+{
+ if (m_input_layout)
+ m_input_layout->AddRef();
+ if (m_vertex_shader)
+ m_vertex_shader->AddRef();
+ if (m_geometry_shader)
+ m_geometry_shader->AddRef();
+ if (m_pixel_shader)
+ m_pixel_shader->AddRef();
+ if (m_rasterizer_state)
+ m_rasterizer_state->AddRef();
+ if (m_depth_state)
+ m_depth_state->AddRef();
+ if (m_blend_state)
+ m_blend_state->AddRef();
+}
+
+DXPipeline::~DXPipeline()
+{
+ if (m_input_layout)
+ m_input_layout->Release();
+ if (m_vertex_shader)
+ m_vertex_shader->Release();
+ if (m_geometry_shader)
+ m_geometry_shader->Release();
+ if (m_pixel_shader)
+ m_pixel_shader->Release();
+ if (m_rasterizer_state)
+ m_rasterizer_state->Release();
+ if (m_depth_state)
+ m_depth_state->Release();
+ if (m_blend_state)
+ m_blend_state->Release();
+}
+
+std::unique_ptr DXPipeline::Create(const AbstractPipelineConfig& config)
+{
+ StateCache& state_cache = static_cast(g_renderer.get())->GetStateCache();
+ ID3D11RasterizerState* rasterizer_state = state_cache.Get(config.rasterization_state);
+ ID3D11DepthStencilState* depth_state = state_cache.Get(config.depth_state);
+ ID3D11BlendState* blend_state = state_cache.Get(config.blending_state);
+ D3D11_PRIMITIVE_TOPOLOGY primitive_topology =
+ StateCache::GetPrimitiveTopology(config.rasterization_state.primitive);
+ if (!rasterizer_state || !depth_state || !blend_state)
+ {
+ SAFE_RELEASE(rasterizer_state);
+ SAFE_RELEASE(depth_state);
+ SAFE_RELEASE(blend_state);
+ return nullptr;
+ }
+
+ const DXShader* vertex_shader = static_cast(config.vertex_shader);
+ const DXShader* geometry_shader = static_cast(config.geometry_shader);
+ const DXShader* pixel_shader = static_cast(config.pixel_shader);
+ _assert_(vertex_shader != nullptr && pixel_shader != nullptr);
+
+ ID3D11InputLayout* input_layout =
+ const_cast(static_cast(config.vertex_format))
+ ->GetInputLayout(vertex_shader->GetByteCode());
+
+ return std::make_unique(input_layout, vertex_shader->GetD3DVertexShader(),
+ geometry_shader ? geometry_shader->GetD3DGeometryShader() :
+ nullptr,
+ pixel_shader->GetD3DPixelShader(), rasterizer_state,
+ depth_state, blend_state, primitive_topology);
+}
+} // namespace DX11
diff --git a/Source/Core/VideoBackends/D3D/DXPipeline.h b/Source/Core/VideoBackends/D3D/DXPipeline.h
new file mode 100644
index 0000000000..f7b02a7d47
--- /dev/null
+++ b/Source/Core/VideoBackends/D3D/DXPipeline.h
@@ -0,0 +1,43 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+#include
+#include "VideoCommon/AbstractPipeline.h"
+
+namespace DX11
+{
+class DXPipeline final : public AbstractPipeline
+{
+public:
+ DXPipeline(ID3D11InputLayout* input_layout, ID3D11VertexShader* vertex_shader,
+ ID3D11GeometryShader* geometry_shader, ID3D11PixelShader* pixel_shader,
+ ID3D11RasterizerState* rasterizer_state, ID3D11DepthStencilState* depth_state,
+ ID3D11BlendState* blend_state, D3D11_PRIMITIVE_TOPOLOGY primitive_topology);
+ ~DXPipeline() override;
+
+ ID3D11InputLayout* GetInputLayout() const { return m_input_layout; }
+ ID3D11VertexShader* GetVertexShader() const { return m_vertex_shader; }
+ ID3D11GeometryShader* GetGeometryShader() const { return m_geometry_shader; }
+ ID3D11PixelShader* GetPixelShader() const { return m_pixel_shader; }
+ ID3D11RasterizerState* GetRasterizerState() const { return m_rasterizer_state; }
+ ID3D11DepthStencilState* GetDepthState() const { return m_depth_state; }
+ ID3D11BlendState* GetBlendState() const { return m_blend_state; }
+ D3D11_PRIMITIVE_TOPOLOGY GetPrimitiveTopology() const { return m_primitive_topology; }
+ bool HasGeometryShader() const { return m_geometry_shader != nullptr; }
+ static std::unique_ptr Create(const AbstractPipelineConfig& config);
+
+private:
+ ID3D11InputLayout* m_input_layout;
+ ID3D11VertexShader* m_vertex_shader;
+ ID3D11GeometryShader* m_geometry_shader;
+ ID3D11PixelShader* m_pixel_shader;
+ ID3D11RasterizerState* m_rasterizer_state;
+ ID3D11DepthStencilState* m_depth_state;
+ ID3D11BlendState* m_blend_state;
+ D3D11_PRIMITIVE_TOPOLOGY m_primitive_topology;
+};
+} // namespace DX11
diff --git a/Source/Core/VideoBackends/D3D/DXShader.cpp b/Source/Core/VideoBackends/D3D/DXShader.cpp
new file mode 100644
index 0000000000..8ceda15e2b
--- /dev/null
+++ b/Source/Core/VideoBackends/D3D/DXShader.cpp
@@ -0,0 +1,183 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "Common/Assert.h"
+
+#include "VideoBackends/D3D/D3DBase.h"
+#include "VideoBackends/D3D/D3DShader.h"
+#include "VideoBackends/D3D/DXShader.h"
+
+namespace DX11
+{
+DXShader::DXShader(D3DBlob* bytecode, ID3D11VertexShader* vs)
+ : AbstractShader(ShaderStage::Vertex), m_bytecode(bytecode), m_shader(vs)
+{
+}
+
+DXShader::DXShader(D3DBlob* bytecode, ID3D11GeometryShader* gs)
+ : AbstractShader(ShaderStage::Geometry), m_bytecode(bytecode), m_shader(gs)
+{
+}
+
+DXShader::DXShader(D3DBlob* bytecode, ID3D11PixelShader* ps)
+ : AbstractShader(ShaderStage::Pixel), m_bytecode(bytecode), m_shader(ps)
+{
+}
+
+DXShader::DXShader(D3DBlob* bytecode, ID3D11ComputeShader* cs)
+ : AbstractShader(ShaderStage::Compute), m_bytecode(bytecode), m_shader(cs)
+{
+}
+
+DXShader::~DXShader()
+{
+ m_shader->Release();
+ m_bytecode->Release();
+}
+
+D3DBlob* DXShader::GetByteCode() const
+{
+ return m_bytecode;
+}
+
+ID3D11VertexShader* DXShader::GetD3DVertexShader() const
+{
+ _dbg_assert_(VIDEO, m_stage == ShaderStage::Vertex);
+ return static_cast(m_shader);
+}
+
+ID3D11GeometryShader* DXShader::GetD3DGeometryShader() const
+{
+ _dbg_assert_(VIDEO, m_stage == ShaderStage::Geometry);
+ return static_cast(m_shader);
+}
+
+ID3D11PixelShader* DXShader::GetD3DPixelShader() const
+{
+ _dbg_assert_(VIDEO, m_stage == ShaderStage::Pixel);
+ return static_cast(m_shader);
+}
+
+ID3D11ComputeShader* DXShader::GetD3DComputeShader() const
+{
+ _dbg_assert_(VIDEO, m_stage == ShaderStage::Compute);
+ return static_cast(m_shader);
+}
+
+bool DXShader::HasBinary() const
+{
+ _assert_(m_bytecode);
+ return true;
+}
+
+AbstractShader::BinaryData DXShader::GetBinary() const
+{
+ return BinaryData(m_bytecode->Data(), m_bytecode->Data() + m_bytecode->Size());
+}
+
+std::unique_ptr DXShader::CreateFromBlob(ShaderStage stage, D3DBlob* bytecode)
+{
+ switch (stage)
+ {
+ case ShaderStage::Vertex:
+ {
+ ID3D11VertexShader* vs = D3D::CreateVertexShaderFromByteCode(bytecode);
+ if (vs)
+ return std::make_unique(bytecode, vs);
+ }
+ break;
+
+ case ShaderStage::Geometry:
+ {
+ ID3D11GeometryShader* gs = D3D::CreateGeometryShaderFromByteCode(bytecode);
+ if (gs)
+ return std::make_unique(bytecode, gs);
+ }
+ break;
+
+ case ShaderStage::Pixel:
+ {
+ ID3D11PixelShader* ps = D3D::CreatePixelShaderFromByteCode(bytecode);
+ if (ps)
+ return std::make_unique(bytecode, ps);
+ }
+ break;
+
+ case ShaderStage::Compute:
+ {
+ ID3D11ComputeShader* cs = D3D::CreateComputeShaderFromByteCode(bytecode);
+ if (cs)
+ return std::make_unique(bytecode, cs);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return nullptr;
+}
+
+std::unique_ptr DXShader::CreateFromSource(ShaderStage stage, const char* source,
+ size_t length)
+{
+ D3DBlob* bytecode;
+ switch (stage)
+ {
+ case ShaderStage::Vertex:
+ {
+ if (!D3D::CompileVertexShader(std::string(source, length), &bytecode))
+ return nullptr;
+ }
+ break;
+
+ case ShaderStage::Geometry:
+ {
+ if (!D3D::CompileGeometryShader(std::string(source, length), &bytecode))
+ return nullptr;
+ }
+ break;
+
+ case ShaderStage::Pixel:
+ {
+ if (!D3D::CompilePixelShader(std::string(source, length), &bytecode))
+ return nullptr;
+ }
+ break;
+
+ case ShaderStage::Compute:
+ {
+ if (!D3D::CompileComputeShader(std::string(source, length), &bytecode))
+ return nullptr;
+ }
+
+ default:
+ return nullptr;
+ }
+
+ std::unique_ptr shader = CreateFromBlob(stage, bytecode);
+ if (!shader)
+ {
+ bytecode->Release();
+ return nullptr;
+ }
+
+ return shader;
+}
+
+std::unique_ptr DXShader::CreateFromBinary(ShaderStage stage, const void* data,
+ size_t length)
+{
+ D3DBlob* bytecode = new D3DBlob(static_cast(length), static_cast(data));
+ std::unique_ptr shader = CreateFromBlob(stage, bytecode);
+ if (!shader)
+ {
+ bytecode->Release();
+ return nullptr;
+ }
+
+ return shader;
+}
+
+} // namespace DX11
diff --git a/Source/Core/VideoBackends/D3D/DXShader.h b/Source/Core/VideoBackends/D3D/DXShader.h
new file mode 100644
index 0000000000..d39e638dac
--- /dev/null
+++ b/Source/Core/VideoBackends/D3D/DXShader.h
@@ -0,0 +1,48 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+#include
+#include
+#include "Common/CommonTypes.h"
+
+#include "VideoBackends/D3D/D3DBlob.h"
+#include "VideoCommon/AbstractShader.h"
+
+namespace DX11
+{
+class DXShader final : public AbstractShader
+{
+public:
+ // Note: vs/gs/ps/cs references are transferred.
+ DXShader(D3DBlob* bytecode, ID3D11VertexShader* vs);
+ DXShader(D3DBlob* bytecode, ID3D11GeometryShader* gs);
+ DXShader(D3DBlob* bytecode, ID3D11PixelShader* ps);
+ DXShader(D3DBlob* bytecode, ID3D11ComputeShader* cs);
+ ~DXShader() override;
+
+ D3DBlob* GetByteCode() const;
+ ID3D11VertexShader* GetD3DVertexShader() const;
+ ID3D11GeometryShader* GetD3DGeometryShader() const;
+ ID3D11PixelShader* GetD3DPixelShader() const;
+ ID3D11ComputeShader* GetD3DComputeShader() const;
+
+ bool HasBinary() const override;
+ BinaryData GetBinary() const override;
+
+ // Creates a new shader object. The reference to bytecode is not transfered upon failure.
+ static std::unique_ptr CreateFromBlob(ShaderStage stage, D3DBlob* bytecode);
+ static std::unique_ptr CreateFromBinary(ShaderStage stage, const void* data,
+ size_t length);
+ static std::unique_ptr CreateFromSource(ShaderStage stage, const char* source,
+ size_t length);
+
+private:
+ ID3D11DeviceChild* m_shader;
+ D3DBlob* m_bytecode;
+};
+
+} // namespace DX11
diff --git a/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp b/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp
index 94ef4ba516..525e2b33cb 100644
--- a/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp
+++ b/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp
@@ -119,20 +119,20 @@ D3DVertexFormat::~D3DVertexFormat()
SAFE_RELEASE(m_layout);
}
-void D3DVertexFormat::SetInputLayout(D3DBlob* vs_bytecode)
+ID3D11InputLayout* D3DVertexFormat::GetInputLayout(D3DBlob* vs_bytecode)
{
- if (!m_layout)
- {
- // CreateInputLayout requires a shader input, but it only looks at the
- // signature of the shader, so we don't need to recompute it if the shader
- // changes.
- HRESULT hr = DX11::D3D::device->CreateInputLayout(
- m_elems.data(), m_num_elems, vs_bytecode->Data(), vs_bytecode->Size(), &m_layout);
- if (FAILED(hr))
- PanicAlert("Failed to create input layout, %s %d\n", __FILE__, __LINE__);
- DX11::D3D::SetDebugObjectName(m_layout, "input layout used to emulate the GX pipeline");
- }
- DX11::D3D::stateman->SetInputLayout(m_layout);
+ if (m_layout)
+ return m_layout;
+
+ // CreateInputLayout requires a shader input, but it only looks at the
+ // signature of the shader, so we don't need to recompute it if the shader
+ // changes.
+ HRESULT hr = DX11::D3D::device->CreateInputLayout(
+ m_elems.data(), m_num_elems, vs_bytecode->Data(), vs_bytecode->Size(), &m_layout);
+ if (FAILED(hr))
+ PanicAlert("Failed to create input layout, %s %d\n", __FILE__, __LINE__);
+ DX11::D3D::SetDebugObjectName(m_layout, "input layout used to emulate the GX pipeline");
+ return m_layout;
}
} // namespace DX11
diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp
index 77281ffa37..f1751e44f2 100644
--- a/Source/Core/VideoBackends/D3D/Render.cpp
+++ b/Source/Core/VideoBackends/D3D/Render.cpp
@@ -13,6 +13,7 @@
#include
#include
+#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Common/MathUtil.h"
@@ -23,6 +24,8 @@
#include "VideoBackends/D3D/D3DBase.h"
#include "VideoBackends/D3D/D3DState.h"
#include "VideoBackends/D3D/D3DUtil.h"
+#include "VideoBackends/D3D/DXPipeline.h"
+#include "VideoBackends/D3D/DXShader.h"
#include "VideoBackends/D3D/DXTexture.h"
#include "VideoBackends/D3D/FramebufferManager.h"
#include "VideoBackends/D3D/GeometryShaderCache.h"
@@ -41,6 +44,12 @@
namespace DX11
{
+// Reserve 512KB for vertices, and 64KB for uniforms.
+// This should be sufficient for our usages, and if more is required,
+// we split it into multiple draws.
+constexpr u32 UTILITY_VBO_SIZE = 512 * 1024;
+constexpr u32 UTILITY_UBO_SIZE = 64 * 1024;
+
// Nvidia stereo blitting struct defined in "nvstereo.h" from the Nvidia SDK
typedef struct _Nv_Stereo_Image_Header
{
@@ -165,6 +174,16 @@ void Renderer::SetupDeviceObjects()
D3D::SetDebugObjectName(m_reset_rast_state, "rasterizer state for Renderer::ResetAPIState");
m_screenshot_texture = nullptr;
+
+ CD3D11_BUFFER_DESC vbo_desc(UTILITY_VBO_SIZE, D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_DYNAMIC,
+ D3D11_CPU_ACCESS_WRITE);
+ hr = D3D::device->CreateBuffer(&vbo_desc, nullptr, &m_utility_vertex_buffer);
+ CHECK(SUCCEEDED(hr), "Create utility VBO");
+
+ CD3D11_BUFFER_DESC ubo_desc(UTILITY_UBO_SIZE, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC,
+ D3D11_CPU_ACCESS_WRITE);
+ hr = D3D::device->CreateBuffer(&ubo_desc, nullptr, &m_utility_uniform_buffer);
+ CHECK(SUCCEEDED(hr), "Create utility UBO");
}
// Kill off all device objects
@@ -184,6 +203,8 @@ void Renderer::TeardownDeviceObjects()
SAFE_RELEASE(m_reset_rast_state);
SAFE_RELEASE(m_screenshot_texture);
SAFE_RELEASE(m_3d_vision_texture);
+ SAFE_RELEASE(m_utility_vertex_buffer);
+ SAFE_RELEASE(m_utility_uniform_buffer);
}
void Renderer::Create3DVisionTexture(int width, int height)
@@ -229,6 +250,109 @@ void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
D3D::DrawTextScaled(static_cast(left), static_cast(top), 20.f, 0.0f, color, text);
}
+std::unique_ptr Renderer::CreateShaderFromSource(ShaderStage stage,
+ const char* source, size_t length)
+{
+ return DXShader::CreateFromSource(stage, source, length);
+}
+
+std::unique_ptr Renderer::CreateShaderFromBinary(ShaderStage stage,
+ const void* data, size_t length)
+{
+ return DXShader::CreateFromBinary(stage, data, length);
+}
+
+std::unique_ptr Renderer::CreatePipeline(const AbstractPipelineConfig& config)
+{
+ return DXPipeline::Create(config);
+}
+
+void Renderer::UpdateUtilityUniformBuffer(const void* uniforms, u32 uniforms_size)
+{
+ _dbg_assert_(VIDEO, uniforms_size > 0 && uniforms_size < UTILITY_UBO_SIZE);
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ HRESULT hr = D3D::context->Map(m_utility_uniform_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
+ CHECK(SUCCEEDED(hr), "Map utility UBO");
+ std::memcpy(mapped.pData, uniforms, uniforms_size);
+ D3D::context->Unmap(m_utility_uniform_buffer, 0);
+}
+
+void Renderer::UpdateUtilityVertexBuffer(const void* vertices, u32 vertex_stride, u32 num_vertices)
+{
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ HRESULT hr = D3D::context->Map(m_utility_vertex_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
+ CHECK(SUCCEEDED(hr), "Map utility VBO");
+ std::memcpy(mapped.pData, vertices, num_vertices * vertex_stride);
+ D3D::context->Unmap(m_utility_vertex_buffer, 0);
+}
+
+void Renderer::SetPipeline(const AbstractPipeline* pipeline)
+{
+ const DXPipeline* dx_pipeline = static_cast(pipeline);
+
+ D3D::stateman->SetRasterizerState(dx_pipeline->GetRasterizerState());
+ D3D::stateman->SetDepthState(dx_pipeline->GetDepthState());
+ D3D::stateman->SetBlendState(dx_pipeline->GetBlendState());
+ D3D::stateman->SetPrimitiveTopology(dx_pipeline->GetPrimitiveTopology());
+ D3D::stateman->SetInputLayout(dx_pipeline->GetInputLayout());
+ D3D::stateman->SetVertexShader(dx_pipeline->GetVertexShader());
+ D3D::stateman->SetGeometryShader(dx_pipeline->GetGeometryShader());
+ D3D::stateman->SetPixelShader(dx_pipeline->GetPixelShader());
+}
+
+void Renderer::DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, const void* vertices,
+ u32 vertex_stride, u32 num_vertices)
+{
+ // Textures are fine, they're set directly via SetTexture.
+ // Since samplers are set via gx_state, we need to fix this up here.
+ for (size_t stage = 0; stage < m_gx_state.samplers.size(); stage++)
+ D3D::stateman->SetSampler(stage, m_state_cache.Get(m_gx_state.samplers[stage]));
+
+ // Copy in uniforms.
+ if (uniforms_size > 0)
+ {
+ UpdateUtilityUniformBuffer(uniforms, uniforms_size);
+ D3D::stateman->SetVertexConstants(m_utility_uniform_buffer);
+ D3D::stateman->SetPixelConstants(m_utility_uniform_buffer);
+ D3D::stateman->SetGeometryConstants(m_utility_uniform_buffer);
+ }
+
+ // If the vertices are larger than our buffer, we need to break it up into multiple draws.
+ const char* vertices_ptr = static_cast(vertices);
+ while (num_vertices > 0)
+ {
+ u32 vertices_this_draw = num_vertices;
+ if (vertices_ptr)
+ {
+ vertices_this_draw = std::min(vertices_this_draw, UTILITY_VBO_SIZE / vertex_stride);
+ _dbg_assert_(VIDEO, vertices_this_draw > 0);
+ UpdateUtilityVertexBuffer(vertices_ptr, vertex_stride, vertices_this_draw);
+ D3D::stateman->SetVertexBuffer(m_utility_vertex_buffer, vertex_stride, 0);
+ }
+
+ // Apply pending state and draw.
+ D3D::stateman->Apply();
+ D3D::context->Draw(vertices_this_draw, 0);
+ vertices_ptr += vertex_stride * vertices_this_draw;
+ num_vertices -= vertices_this_draw;
+ }
+}
+
+void Renderer::DispatchComputeShader(const AbstractShader* shader, const void* uniforms,
+ u32 uniforms_size, u32 groups_x, u32 groups_y, u32 groups_z)
+{
+ D3D::stateman->SetComputeShader(static_cast(shader)->GetD3DComputeShader());
+
+ if (uniforms_size > 0)
+ {
+ UpdateUtilityUniformBuffer(uniforms, uniforms_size);
+ D3D::stateman->SetComputeConstants(m_utility_uniform_buffer);
+ }
+
+ D3D::stateman->Apply();
+ D3D::context->Dispatch(groups_x, groups_y, groups_z);
+}
+
TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc)
{
TargetRectangle result;
diff --git a/Source/Core/VideoBackends/D3D/Render.h b/Source/Core/VideoBackends/D3D/Render.h
index 4a01225c2d..e06a6c30e2 100644
--- a/Source/Core/VideoBackends/D3D/Render.h
+++ b/Source/Core/VideoBackends/D3D/Render.h
@@ -5,6 +5,7 @@
#pragma once
#include
+#include
#include
#include "VideoBackends/D3D/D3DState.h"
#include "VideoCommon/RenderBase.h"
@@ -25,7 +26,13 @@ public:
std::unique_ptr CreateTexture(const TextureConfig& config) override;
std::unique_ptr
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override;
+ std::unique_ptr CreateShaderFromSource(ShaderStage stage, const char* source,
+ size_t length) override;
+ std::unique_ptr CreateShaderFromBinary(ShaderStage stage, const void* data,
+ size_t length) override;
+ std::unique_ptr CreatePipeline(const AbstractPipelineConfig& config) override;
+ void SetPipeline(const AbstractPipeline* pipeline) override;
void SetBlendingState(const BlendingState& state) override;
void SetScissorRect(const MathUtil::Rectangle& rc) override;
void SetRasterizationState(const RasterizationState& state) override;
@@ -63,6 +70,11 @@ public:
void ReinterpretPixelData(unsigned int convtype) override;
+ void DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, const void* vertices,
+ u32 vertex_stride, u32 num_vertices) override;
+ void DispatchComputeShader(const AbstractShader* shader, const void* uniforms, u32 uniforms_size,
+ u32 groups_x, u32 groups_y, u32 groups_z) override;
+
private:
struct GXPipelineState
{
@@ -82,6 +94,9 @@ private:
void BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D* src_texture,
u32 src_width, u32 src_height, float Gamma);
+ void UpdateUtilityUniformBuffer(const void* uniforms, u32 uniforms_size);
+ void UpdateUtilityVertexBuffer(const void* vertices, u32 vertex_stride, u32 num_vertices);
+
StateCache m_state_cache;
GXPipelineState m_gx_state;
@@ -94,6 +109,9 @@ private:
ID3D11Texture2D* m_screenshot_texture = nullptr;
D3DTexture2D* m_3d_vision_texture = nullptr;
+ ID3D11Buffer* m_utility_vertex_buffer = nullptr;
+ ID3D11Buffer* m_utility_uniform_buffer = nullptr;
+
u32 m_last_multisamples = 1;
bool m_last_stereo_mode = false;
bool m_last_fullscreen_state = false;
diff --git a/Source/Core/VideoBackends/D3D/VertexManager.h b/Source/Core/VideoBackends/D3D/VertexManager.h
index c3de8c314a..5b20cbabaa 100644
--- a/Source/Core/VideoBackends/D3D/VertexManager.h
+++ b/Source/Core/VideoBackends/D3D/VertexManager.h
@@ -23,7 +23,7 @@ class D3DVertexFormat : public NativeVertexFormat
public:
D3DVertexFormat(const PortableVertexDeclaration& vtx_decl);
~D3DVertexFormat();
- void SetInputLayout(D3DBlob* vs_bytecode);
+ ID3D11InputLayout* GetInputLayout(D3DBlob* vs_bytecode);
private:
std::array m_elems{};
diff --git a/Source/Core/VideoBackends/D3D/VertexShaderCache.cpp b/Source/Core/VideoBackends/D3D/VertexShaderCache.cpp
index e36f8b709a..ef40f55a10 100644
--- a/Source/Core/VideoBackends/D3D/VertexShaderCache.cpp
+++ b/Source/Core/VideoBackends/D3D/VertexShaderCache.cpp
@@ -260,7 +260,7 @@ bool VertexShaderCache::SetShader(D3DVertexFormat* vertex_format)
if (!last_entry->shader)
return false;
- vertex_format->SetInputLayout(last_entry->bytecode);
+ D3D::stateman->SetInputLayout(vertex_format->GetInputLayout(last_entry->bytecode));
D3D::stateman->SetVertexShader(last_entry->shader);
return true;
}
@@ -279,7 +279,7 @@ bool VertexShaderCache::SetShader(D3DVertexFormat* vertex_format)
if (!last_entry->shader)
return false;
- vertex_format->SetInputLayout(last_entry->bytecode);
+ D3D::stateman->SetInputLayout(vertex_format->GetInputLayout(last_entry->bytecode));
D3D::stateman->SetVertexShader(last_entry->shader);
return true;
}
@@ -324,7 +324,7 @@ bool VertexShaderCache::SetUberShader(D3DVertexFormat* vertex_format)
if (!last_uber_entry->shader)
return false;
- uber_vertex_format->SetInputLayout(last_uber_entry->bytecode);
+ D3D::stateman->SetInputLayout(uber_vertex_format->GetInputLayout(last_uber_entry->bytecode));
D3D::stateman->SetVertexShader(last_uber_entry->shader);
return true;
}
@@ -340,7 +340,7 @@ bool VertexShaderCache::SetUberShader(D3DVertexFormat* vertex_format)
if (!last_uber_entry->shader)
return false;
- uber_vertex_format->SetInputLayout(last_uber_entry->bytecode);
+ D3D::stateman->SetInputLayout(uber_vertex_format->GetInputLayout(last_uber_entry->bytecode));
D3D::stateman->SetVertexShader(last_uber_entry->shader);
return true;
}
diff --git a/Source/Core/VideoBackends/Null/Render.cpp b/Source/Core/VideoBackends/Null/Render.cpp
index c589c8278f..6c804bd346 100644
--- a/Source/Core/VideoBackends/Null/Render.cpp
+++ b/Source/Core/VideoBackends/Null/Render.cpp
@@ -7,6 +7,8 @@
#include "VideoBackends/Null/NullTexture.h"
#include "VideoBackends/Null/Render.h"
+#include "VideoCommon/AbstractPipeline.h"
+#include "VideoCommon/AbstractShader.h"
#include "VideoCommon/VideoConfig.h"
namespace Null
@@ -33,6 +35,40 @@ std::unique_ptr Renderer::CreateStagingTexture(StagingTe
return std::make_unique(type, config);
}
+class NullShader final : public AbstractShader
+{
+public:
+ explicit NullShader(ShaderStage stage) : AbstractShader(stage) {}
+ ~NullShader() = default;
+
+ bool HasBinary() const override { return false; }
+ BinaryData GetBinary() const override { return {}; }
+};
+
+std::unique_ptr Renderer::CreateShaderFromSource(ShaderStage stage,
+ const char* source, size_t length)
+{
+ return std::make_unique(stage);
+}
+
+std::unique_ptr Renderer::CreateShaderFromBinary(ShaderStage stage,
+ const void* data, size_t length)
+{
+ return std::make_unique(stage);
+}
+
+class NullPipeline final : public AbstractPipeline
+{
+public:
+ NullPipeline() : AbstractPipeline() {}
+ ~NullPipeline() override = default;
+};
+
+std::unique_ptr Renderer::CreatePipeline(const AbstractPipelineConfig& config)
+{
+ return std::make_unique();
+}
+
void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
{
NOTICE_LOG(VIDEO, "RenderText: %s", text.c_str());
diff --git a/Source/Core/VideoBackends/Null/Render.h b/Source/Core/VideoBackends/Null/Render.h
index 32b1d560e5..f7c05ed8c8 100644
--- a/Source/Core/VideoBackends/Null/Render.h
+++ b/Source/Core/VideoBackends/Null/Render.h
@@ -18,6 +18,12 @@ public:
std::unique_ptr
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override;
+ std::unique_ptr CreateShaderFromSource(ShaderStage stage, const char* source,
+ size_t length) override;
+ std::unique_ptr CreateShaderFromBinary(ShaderStage stage, const void* data,
+ size_t length) override;
+ std::unique_ptr CreatePipeline(const AbstractPipelineConfig& config) override;
+
void RenderText(const std::string& pstr, int left, int top, u32 color) override;
u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override { return 0; }
void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override {}
diff --git a/Source/Core/VideoBackends/OGL/CMakeLists.txt b/Source/Core/VideoBackends/OGL/CMakeLists.txt
index 8cd570c3c5..5ecc1a4fb2 100644
--- a/Source/Core/VideoBackends/OGL/CMakeLists.txt
+++ b/Source/Core/VideoBackends/OGL/CMakeLists.txt
@@ -3,6 +3,8 @@ set(SRCS
FramebufferManager.cpp
main.cpp
NativeVertexFormat.cpp
+ OGLPipeline.cpp
+ OGLShader.cpp
OGLTexture.cpp
PerfQuery.cpp
PostProcessing.cpp
diff --git a/Source/Core/VideoBackends/OGL/OGL.vcxproj b/Source/Core/VideoBackends/OGL/OGL.vcxproj
index c8ee325158..4ca44ce563 100644
--- a/Source/Core/VideoBackends/OGL/OGL.vcxproj
+++ b/Source/Core/VideoBackends/OGL/OGL.vcxproj
@@ -36,6 +36,8 @@
+
+
@@ -53,6 +55,8 @@
+
+
diff --git a/Source/Core/VideoBackends/OGL/OGL.vcxproj.filters b/Source/Core/VideoBackends/OGL/OGL.vcxproj.filters
index 63e3c6918e..ea5967fb1e 100644
--- a/Source/Core/VideoBackends/OGL/OGL.vcxproj.filters
+++ b/Source/Core/VideoBackends/OGL/OGL.vcxproj.filters
@@ -56,6 +56,12 @@
Render
+
+ Render
+
+
+ Render
+
@@ -99,6 +105,12 @@
Render
+
+ Render
+
+
+ Render
+
diff --git a/Source/Core/VideoBackends/OGL/OGLPipeline.cpp b/Source/Core/VideoBackends/OGL/OGLPipeline.cpp
new file mode 100644
index 0000000000..9d93381d46
--- /dev/null
+++ b/Source/Core/VideoBackends/OGL/OGLPipeline.cpp
@@ -0,0 +1,62 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "Common/Assert.h"
+
+#include "VideoBackends/OGL/OGLPipeline.h"
+#include "VideoBackends/OGL/OGLShader.h"
+#include "VideoBackends/OGL/ProgramShaderCache.h"
+#include "VideoBackends/OGL/Render.h"
+#include "VideoBackends/OGL/VertexManager.h"
+
+namespace OGL
+{
+static GLenum MapToGLPrimitive(PrimitiveType primitive_type)
+{
+ switch (primitive_type)
+ {
+ case PrimitiveType::Points:
+ return GL_POINTS;
+ case PrimitiveType::Lines:
+ return GL_LINES;
+ case PrimitiveType::Triangles:
+ return GL_TRIANGLES;
+ case PrimitiveType::TriangleStrip:
+ return GL_TRIANGLE_STRIP;
+ default:
+ return 0;
+ }
+}
+OGLPipeline::OGLPipeline(const GLVertexFormat* vertex_format,
+ const RasterizationState& rasterization_state,
+ const DepthState& depth_state, const BlendingState& blending_state,
+ const PipelineProgram* program, GLuint gl_primitive)
+ : m_vertex_format(vertex_format), m_rasterization_state(rasterization_state),
+ m_depth_state(depth_state), m_blending_state(blending_state), m_program(program),
+ m_gl_primitive(gl_primitive)
+{
+}
+
+OGLPipeline::~OGLPipeline()
+{
+ // We don't want to destroy the shaders.
+ ProgramShaderCache::ReleasePipelineProgram(m_program);
+}
+
+std::unique_ptr OGLPipeline::Create(const AbstractPipelineConfig& config)
+{
+ const PipelineProgram* program =
+ ProgramShaderCache::GetPipelineProgram(static_cast(config.vertex_shader),
+ static_cast(config.geometry_shader),
+ static_cast(config.pixel_shader));
+ if (!program)
+ return nullptr;
+
+ const GLVertexFormat* vertex_format = static_cast(config.vertex_format);
+ GLenum gl_primitive = MapToGLPrimitive(config.rasterization_state.primitive);
+ return std::make_unique(vertex_format, config.rasterization_state,
+ config.depth_state, config.blending_state, program,
+ gl_primitive);
+}
+} // namespace OGL
diff --git a/Source/Core/VideoBackends/OGL/OGLPipeline.h b/Source/Core/VideoBackends/OGL/OGLPipeline.h
new file mode 100644
index 0000000000..42371ad927
--- /dev/null
+++ b/Source/Core/VideoBackends/OGL/OGLPipeline.h
@@ -0,0 +1,43 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+
+#include "Common/GL/GLUtil.h"
+#include "VideoBackends/OGL/ProgramShaderCache.h"
+#include "VideoCommon/AbstractPipeline.h"
+#include "VideoCommon/RenderState.h"
+
+namespace OGL
+{
+class OGLPipeline final : public AbstractPipeline
+{
+public:
+ explicit OGLPipeline(const GLVertexFormat* vertex_format,
+ const RasterizationState& rasterization_state, const DepthState& depth_state,
+ const BlendingState& blending_state, const PipelineProgram* program,
+ GLenum gl_primitive);
+ ~OGLPipeline() override;
+
+ const GLVertexFormat* GetVertexFormat() const { return m_vertex_format; }
+ const RasterizationState& GetRasterizationState() const { return m_rasterization_state; }
+ const DepthState& GetDepthState() const { return m_depth_state; }
+ const BlendingState& GetBlendingState() const { return m_blending_state; }
+ const PipelineProgram* GetProgram() const { return m_program; }
+ bool HasVertexInput() const { return m_vertex_format != nullptr; }
+ GLenum GetGLPrimitive() const { return m_gl_primitive; }
+ static std::unique_ptr Create(const AbstractPipelineConfig& config);
+
+private:
+ const GLVertexFormat* m_vertex_format;
+ RasterizationState m_rasterization_state;
+ DepthState m_depth_state;
+ BlendingState m_blending_state;
+ const PipelineProgram* m_program;
+ GLenum m_gl_primitive;
+};
+
+} // namespace OGL
diff --git a/Source/Core/VideoBackends/OGL/OGLShader.cpp b/Source/Core/VideoBackends/OGL/OGLShader.cpp
new file mode 100644
index 0000000000..62c17752f8
--- /dev/null
+++ b/Source/Core/VideoBackends/OGL/OGLShader.cpp
@@ -0,0 +1,77 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "VideoBackends/OGL/OGLShader.h"
+#include "VideoBackends/OGL/ProgramShaderCache.h"
+
+namespace OGL
+{
+static GLenum GetGLShaderTypeForStage(ShaderStage stage)
+{
+ switch (stage)
+ {
+ case ShaderStage::Vertex:
+ return GL_VERTEX_SHADER;
+ case ShaderStage::Geometry:
+ return GL_GEOMETRY_SHADER;
+ case ShaderStage::Pixel:
+ return GL_FRAGMENT_SHADER;
+ case ShaderStage::Compute:
+ return GL_COMPUTE_SHADER;
+ default:
+ return 0;
+ }
+}
+
+OGLShader::OGLShader(ShaderStage stage, GLenum gl_type, GLuint shader_id)
+ : AbstractShader(stage), m_type(gl_type), m_id(shader_id)
+{
+}
+
+OGLShader::OGLShader(GLuint compute_program_id)
+ : AbstractShader(ShaderStage::Compute), m_type(GL_COMPUTE_SHADER), m_id(compute_program_id)
+{
+}
+
+OGLShader::~OGLShader()
+{
+ if (m_stage != ShaderStage::Compute)
+ glDeleteShader(m_id);
+ else
+ glDeleteProgram(m_compute_program_id);
+}
+
+bool OGLShader::HasBinary() const
+{
+ // NOTE: GL shaders do not have binaries, programs do.
+ return false;
+}
+
+AbstractShader::BinaryData OGLShader::GetBinary() const
+{
+ return {};
+}
+
+std::unique_ptr OGLShader::CreateFromSource(ShaderStage stage, const char* source,
+ size_t length)
+{
+ if (stage != ShaderStage::Compute)
+ {
+ GLenum shader_type = GetGLShaderTypeForStage(stage);
+ GLuint shader_id =
+ ProgramShaderCache::CompileSingleShader(shader_type, std::string(source, length));
+ if (!shader_id)
+ return nullptr;
+
+ return std::make_unique(stage, shader_type, shader_id);
+ }
+
+ // Compute shaders.
+ SHADER prog;
+ if (!ProgramShaderCache::CompileComputeShader(prog, std::string(source, length)))
+ return nullptr;
+ return std::make_unique(prog.glprogid);
+}
+
+} // namespace OGL
diff --git a/Source/Core/VideoBackends/OGL/OGLShader.h b/Source/Core/VideoBackends/OGL/OGLShader.h
new file mode 100644
index 0000000000..981c835cc1
--- /dev/null
+++ b/Source/Core/VideoBackends/OGL/OGLShader.h
@@ -0,0 +1,38 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+#include
+
+#include "Common/CommonTypes.h"
+#include "Common/GL/GLUtil.h"
+#include "VideoCommon/AbstractShader.h"
+
+namespace OGL
+{
+class OGLShader final : public AbstractShader
+{
+public:
+ explicit OGLShader(ShaderStage stage, GLenum gl_type, GLuint shader_id);
+ explicit OGLShader(GLuint compute_program_id);
+ ~OGLShader() override;
+
+ GLenum GetGLShaderType() const { return m_type; }
+ GLuint GetGLShaderID() const { return m_id; }
+ GLuint GetGLComputeProgramID() const { return m_compute_program_id; }
+ bool HasBinary() const override;
+ BinaryData GetBinary() const override;
+
+ static std::unique_ptr CreateFromSource(ShaderStage stage, const char* source,
+ size_t length);
+
+private:
+ GLenum m_type;
+ GLuint m_id;
+ GLuint m_compute_program_id;
+};
+
+} // namespace OGL
diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
index 80c30e1f92..034f94a3b0 100644
--- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
+++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
@@ -9,6 +9,7 @@
#include
#include "Common/Align.h"
+#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Common/FileUtil.h"
#include "Common/GL/GLInterfaceBase.h"
@@ -20,6 +21,7 @@
#include "Core/ConfigManager.h"
#include "Core/Host.h"
+#include "VideoBackends/OGL/OGLShader.h"
#include "VideoBackends/OGL/Render.h"
#include "VideoBackends/OGL/StreamBuffer.h"
#include "VideoBackends/OGL/VertexManager.h"
@@ -57,6 +59,7 @@ static LinearDiskCache s_uber_program_disk_cache;
static GLuint CurrentProgram = 0;
ProgramShaderCache::PCache ProgramShaderCache::pshaders;
ProgramShaderCache::UberPCache ProgramShaderCache::ubershaders;
+ProgramShaderCache::PipelineProgramMap ProgramShaderCache::pipelineprograms;
ProgramShaderCache::PCacheEntry* ProgramShaderCache::last_entry;
ProgramShaderCache::PCacheEntry* ProgramShaderCache::last_uber_entry;
SHADERUID ProgramShaderCache::last_uid;
@@ -188,6 +191,47 @@ void SHADER::DestroyShaders()
}
}
+bool PipelineProgramKey::operator!=(const PipelineProgramKey& rhs) const
+{
+ return !operator==(rhs);
+}
+
+bool PipelineProgramKey::operator==(const PipelineProgramKey& rhs) const
+{
+ return std::tie(vertex_shader, geometry_shader, pixel_shader) ==
+ std::tie(rhs.vertex_shader, rhs.geometry_shader, rhs.pixel_shader);
+}
+
+bool PipelineProgramKey::operator<(const PipelineProgramKey& rhs) const
+{
+ return std::tie(vertex_shader, geometry_shader, pixel_shader) <
+ std::tie(rhs.vertex_shader, rhs.geometry_shader, rhs.pixel_shader);
+}
+
+std::size_t PipelineProgramKeyHash::operator()(const PipelineProgramKey& key) const
+{
+ // We would really want std::hash_combine for this..
+ std::hash hasher;
+ return hasher(key.vertex_shader) + hasher(key.geometry_shader) + hasher(key.pixel_shader);
+}
+
+StreamBuffer* ProgramShaderCache::GetUniformBuffer()
+{
+ return s_buffer.get();
+}
+
+u32 ProgramShaderCache::GetUniformBufferAlignment()
+{
+ return s_ubo_align;
+}
+
+void ProgramShaderCache::InvalidateConstants()
+{
+ VertexShaderManager::dirty = true;
+ GeometryShaderManager::dirty = true;
+ PixelShaderManager::dirty = true;
+}
+
void ProgramShaderCache::UploadConstants()
{
if (PixelShaderManager::dirty || VertexShaderManager::dirty || GeometryShaderManager::dirty)
@@ -697,6 +741,10 @@ void ProgramShaderCache::Shutdown()
s_attributeless_VBO = 0;
s_attributeless_VAO = 0;
s_last_VAO = 0;
+
+ // All pipeline programs should have been released.
+ _dbg_assert_(VIDEO, pipelineprograms.empty());
+ pipelineprograms.clear();
}
void ProgramShaderCache::CreateAttributelessVAO()
@@ -732,6 +780,11 @@ void ProgramShaderCache::InvalidateVertexFormat()
s_last_VAO = 0;
}
+void ProgramShaderCache::InvalidateLastProgram()
+{
+ CurrentProgram = 0;
+}
+
GLuint ProgramShaderCache::CreateProgramFromBinary(const u8* value, u32 value_size)
{
const u8* binary = value + sizeof(GLenum);
@@ -860,6 +913,58 @@ void ProgramShaderCache::DestroyShaders()
ubershaders.clear();
}
+const PipelineProgram* ProgramShaderCache::GetPipelineProgram(const OGLShader* vertex_shader,
+ const OGLShader* geometry_shader,
+ const OGLShader* pixel_shader)
+{
+ PipelineProgramKey key = {vertex_shader, geometry_shader, pixel_shader};
+ auto iter = pipelineprograms.find(key);
+ if (iter != pipelineprograms.end())
+ {
+ iter->second->reference_count++;
+ return iter->second.get();
+ }
+
+ std::unique_ptr prog = std::make_unique();
+ prog->key = key;
+
+ // Attach shaders.
+ _assert_(vertex_shader && vertex_shader->GetStage() == ShaderStage::Vertex);
+ _assert_(pixel_shader && pixel_shader->GetStage() == ShaderStage::Pixel);
+ prog->shader.glprogid = glCreateProgram();
+ glAttachShader(prog->shader.glprogid, vertex_shader->GetGLShaderID());
+ glAttachShader(prog->shader.glprogid, pixel_shader->GetGLShaderID());
+ if (geometry_shader)
+ {
+ _assert_(geometry_shader->GetStage() == ShaderStage::Geometry);
+ glAttachShader(prog->shader.glprogid, geometry_shader->GetGLShaderID());
+ }
+
+ // Link program.
+ prog->shader.SetProgramBindings(false);
+ glLinkProgram(prog->shader.glprogid);
+ if (!ProgramShaderCache::CheckProgramLinkResult(prog->shader.glprogid, {}, {}, {}))
+ {
+ prog->shader.Destroy();
+ return nullptr;
+ }
+
+ auto ip = pipelineprograms.emplace(key, std::move(prog));
+ return ip.first->second.get();
+}
+
+void ProgramShaderCache::ReleasePipelineProgram(const PipelineProgram* prog)
+{
+ auto iter = pipelineprograms.find(prog->key);
+ _assert_(iter != pipelineprograms.end() && prog == iter->second.get());
+
+ if (--iter->second->reference_count == 0)
+ {
+ iter->second->shader.Destroy();
+ pipelineprograms.erase(iter);
+ }
+}
+
void ProgramShaderCache::CreateHeader()
{
GlslVersion v = g_ogl_config.eSupportedGLSLVersion;
@@ -1368,5 +1473,4 @@ void ProgramShaderCache::DrawPrerenderArray(const SHADER& shader, PrimitiveType
glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
glDeleteSync(sync);
}
-
} // namespace OGL
diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.h b/Source/Core/VideoBackends/OGL/ProgramShaderCache.h
index 6d3f95954e..635c1d3097 100644
--- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.h
+++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.h
@@ -4,8 +4,10 @@
#pragma once
+#include
#include
#include
+#include
#include "Common/GL/GLUtil.h"
#include "Common/LinearDiskCache.h"
@@ -21,7 +23,9 @@ class cInterfaceBase;
namespace OGL
{
+class OGLShader;
class GLVertexFormat;
+class StreamBuffer;
class SHADERUID
{
@@ -81,6 +85,29 @@ struct SHADER
void DestroyShaders();
};
+struct PipelineProgramKey
+{
+ const OGLShader* vertex_shader;
+ const OGLShader* geometry_shader;
+ const OGLShader* pixel_shader;
+
+ bool operator==(const PipelineProgramKey& rhs) const;
+ bool operator!=(const PipelineProgramKey& rhs) const;
+ bool operator<(const PipelineProgramKey& rhs) const;
+};
+
+struct PipelineProgramKeyHash
+{
+ std::size_t operator()(const PipelineProgramKey& key) const;
+};
+
+struct PipelineProgram
+{
+ PipelineProgramKey key;
+ SHADER shader;
+ std::atomic_size_t reference_count{1};
+};
+
class ProgramShaderCache
{
public:
@@ -98,6 +125,7 @@ public:
static SHADER* SetUberShader(PrimitiveType primitive_type, const GLVertexFormat* vertex_format);
static void BindVertexFormat(const GLVertexFormat* vertex_format);
static void InvalidateVertexFormat();
+ static void InvalidateLastProgram();
static bool CompileShader(SHADER& shader, const std::string& vcode, const std::string& pcode,
const std::string& gcode = "");
@@ -106,6 +134,9 @@ public:
static bool CheckShaderCompileResult(GLuint id, GLenum type, const std::string& code);
static bool CheckProgramLinkResult(GLuint id, const std::string& vcode, const std::string& pcode,
const std::string& gcode);
+ static StreamBuffer* GetUniformBuffer();
+ static u32 GetUniformBufferAlignment();
+ static void InvalidateConstants();
static void UploadConstants();
static void Init();
@@ -115,6 +146,11 @@ public:
static void RetrieveAsyncShaders();
static void PrecompileUberShaders();
+ static const PipelineProgram* GetPipelineProgram(const OGLShader* vertex_shader,
+ const OGLShader* geometry_shader,
+ const OGLShader* pixel_shader);
+ static void ReleasePipelineProgram(const PipelineProgram* prog);
+
private:
template
class ProgramShaderCacheInserter : public LinearDiskCacheReader
@@ -189,6 +225,9 @@ private:
typedef std::map PCache;
typedef std::map UberPCache;
+ typedef std::unordered_map,
+ PipelineProgramKeyHash>
+ PipelineProgramMap;
static void CreateAttributelessVAO();
static GLuint CreateProgramFromBinary(const u8* value, u32 value_size);
@@ -202,6 +241,7 @@ private:
static PCache pshaders;
static UberPCache ubershaders;
+ static PipelineProgramMap pipelineprograms;
static PCacheEntry* last_entry;
static PCacheEntry* last_uber_entry;
static SHADERUID last_uid;
diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index 7b4c98fd3f..04d7e20487 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -13,6 +13,7 @@
#include
#include
+#include "Common/Assert.h"
#include "Common/Atomic.h"
#include "Common/CommonTypes.h"
#include "Common/GL/GLInterfaceBase.h"
@@ -27,11 +28,14 @@
#include "VideoBackends/OGL/BoundingBox.h"
#include "VideoBackends/OGL/FramebufferManager.h"
+#include "VideoBackends/OGL/OGLPipeline.h"
+#include "VideoBackends/OGL/OGLShader.h"
#include "VideoBackends/OGL/OGLTexture.h"
#include "VideoBackends/OGL/PostProcessing.h"
#include "VideoBackends/OGL/ProgramShaderCache.h"
#include "VideoBackends/OGL/RasterFont.h"
#include "VideoBackends/OGL/SamplerCache.h"
+#include "VideoBackends/OGL/StreamBuffer.h"
#include "VideoBackends/OGL/TextureCache.h"
#include "VideoBackends/OGL/VertexManager.h"
@@ -847,6 +851,23 @@ void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
m_backbuffer_width, m_backbuffer_height, color);
}
+std::unique_ptr Renderer::CreateShaderFromSource(ShaderStage stage,
+ const char* source, size_t length)
+{
+ return OGLShader::CreateFromSource(stage, source, length);
+}
+
+std::unique_ptr Renderer::CreateShaderFromBinary(ShaderStage stage,
+ const void* data, size_t length)
+{
+ return nullptr;
+}
+
+std::unique_ptr Renderer::CreatePipeline(const AbstractPipelineConfig& config)
+{
+ return OGLPipeline::Create(config);
+}
+
TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc)
{
TargetRectangle result;
@@ -1217,7 +1238,7 @@ void Renderer::ReinterpretPixelData(unsigned int convtype)
}
}
-void Renderer::SetBlendingState(const BlendingState& state)
+void Renderer::ApplyBlendingState(const BlendingState& state)
{
bool useDualSource =
state.usedualsrc && g_ActiveConfig.backend_info.bSupportsDualSourceBlend &&
@@ -1491,7 +1512,7 @@ void Renderer::RestoreAPIState()
BPFunctions::SetBlendMode();
}
-void Renderer::SetRasterizationState(const RasterizationState& state)
+void Renderer::ApplyRasterizationState(const RasterizationState& state)
{
// none, ccw, cw, ccw
if (state.cullmode != GenMode::CULL_NONE)
@@ -1506,7 +1527,7 @@ void Renderer::SetRasterizationState(const RasterizationState& state)
}
}
-void Renderer::SetDepthState(const DepthState& state)
+void Renderer::ApplyDepthState(const DepthState& state)
{
const GLenum glCmpFuncs[8] = {GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL,
GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS};
@@ -1527,6 +1548,33 @@ void Renderer::SetDepthState(const DepthState& state)
}
}
+void Renderer::SetRasterizationState(const RasterizationState& state)
+{
+ ApplyRasterizationState(state);
+}
+
+void Renderer::SetDepthState(const DepthState& state)
+{
+ ApplyDepthState(state);
+}
+
+void Renderer::SetBlendingState(const BlendingState& state)
+{
+ ApplyBlendingState(state);
+}
+
+void Renderer::SetPipeline(const AbstractPipeline* pipeline)
+{
+ // Not all shader changes currently go through SetPipeline, so we can't
+ // test if the pipeline hasn't changed and skip these applications. Yet.
+ m_graphics_pipeline = static_cast(pipeline);
+ ApplyRasterizationState(m_graphics_pipeline->GetRasterizationState());
+ ApplyDepthState(m_graphics_pipeline->GetDepthState());
+ ApplyBlendingState(m_graphics_pipeline->GetBlendingState());
+ ProgramShaderCache::BindVertexFormat(m_graphics_pipeline->GetVertexFormat());
+ m_graphics_pipeline->GetProgram()->shader.Bind();
+}
+
void Renderer::SetTexture(u32 index, const AbstractTexture* texture)
{
if (m_bound_textures[index] == texture)
@@ -1559,4 +1607,52 @@ void Renderer::SetInterlacingMode()
{
// TODO
}
+
+void Renderer::DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, const void* vertices,
+ u32 vertex_stride, u32 num_vertices)
+{
+ // Copy in uniforms.
+ if (uniforms_size > 0)
+ UploadUtilityUniforms(uniforms, uniforms_size);
+
+ // Draw from base index if there is vertex data.
+ if (vertices)
+ {
+ StreamBuffer* vbuf = static_cast(g_vertex_manager.get())->GetVertexBuffer();
+ auto buf = vbuf->Map(vertex_stride * num_vertices, vertex_stride);
+ std::memcpy(buf.first, vertices, vertex_stride * num_vertices);
+ vbuf->Unmap(vertex_stride * num_vertices);
+ glDrawArrays(m_graphics_pipeline->GetGLPrimitive(), buf.second / vertex_stride, num_vertices);
+ }
+ else
+ {
+ glDrawArrays(m_graphics_pipeline->GetGLPrimitive(), 0, num_vertices);
+ }
+}
+
+void Renderer::UploadUtilityUniforms(const void* uniforms, u32 uniforms_size)
+{
+ _dbg_assert_(VIDEO, uniforms_size > 0);
+
+ auto buf = ProgramShaderCache::GetUniformBuffer()->Map(
+ uniforms_size, ProgramShaderCache::GetUniformBufferAlignment());
+ std::memcpy(buf.first, uniforms, uniforms_size);
+ ProgramShaderCache::GetUniformBuffer()->Unmap(uniforms_size);
+ glBindBufferRange(GL_UNIFORM_BUFFER, 1, ProgramShaderCache::GetUniformBuffer()->m_buffer,
+ buf.second, uniforms_size);
+
+ // This is rather horrible, but because of how the UBOs are bound, this forces it to rebind.
+ ProgramShaderCache::InvalidateConstants();
+}
+
+void Renderer::DispatchComputeShader(const AbstractShader* shader, const void* uniforms,
+ u32 uniforms_size, u32 groups_x, u32 groups_y, u32 groups_z)
+{
+ glUseProgram(static_cast(shader)->GetGLComputeProgramID());
+ if (uniforms_size > 0)
+ UploadUtilityUniforms(uniforms, uniforms_size);
+
+ glDispatchCompute(groups_x, groups_y, groups_z);
+ ProgramShaderCache::InvalidateLastProgram();
+}
}
diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h
index 9a8207b595..e8e385ed7b 100644
--- a/Source/Core/VideoBackends/OGL/Render.h
+++ b/Source/Core/VideoBackends/OGL/Render.h
@@ -14,6 +14,7 @@ struct XFBSourceBase;
namespace OGL
{
+class OGLPipeline;
void ClearEFBCache();
enum GlslVersion
@@ -89,7 +90,13 @@ public:
std::unique_ptr CreateTexture(const TextureConfig& config) override;
std::unique_ptr
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override;
+ std::unique_ptr CreateShaderFromSource(ShaderStage stage, const char* source,
+ size_t length) override;
+ std::unique_ptr CreateShaderFromBinary(ShaderStage stage, const void* data,
+ size_t length) override;
+ std::unique_ptr CreatePipeline(const AbstractPipelineConfig& config) override;
+ void SetPipeline(const AbstractPipeline* pipeline) override;
void SetBlendingState(const BlendingState& state) override;
void SetScissorRect(const MathUtil::Rectangle& rc) override;
void SetRasterizationState(const RasterizationState& state) override;
@@ -121,6 +128,12 @@ public:
void ReinterpretPixelData(unsigned int convtype) override;
+ void DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, const void* vertices,
+ u32 vertex_stride, u32 num_vertices) override;
+
+ void DispatchComputeShader(const AbstractShader* shader, const void* uniforms, u32 uniforms_size,
+ u32 groups_x, u32 groups_y, u32 groups_z) override;
+
private:
void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc,
const TargetRectangle& targetPixelRc, const void* data);
@@ -134,6 +147,12 @@ private:
void CheckForSurfaceChange();
void CheckForSurfaceResize();
+ void ApplyBlendingState(const BlendingState& state);
+ void ApplyRasterizationState(const RasterizationState& state);
+ void ApplyDepthState(const DepthState& state);
+ void UploadUtilityUniforms(const void* uniforms, u32 uniforms_size);
+
std::array m_bound_textures{};
+ const OGLPipeline* m_graphics_pipeline = nullptr;
};
}
diff --git a/Source/Core/VideoBackends/OGL/VertexManager.cpp b/Source/Core/VideoBackends/OGL/VertexManager.cpp
index d9fce2bc75..f98c72b1ad 100644
--- a/Source/Core/VideoBackends/OGL/VertexManager.cpp
+++ b/Source/Core/VideoBackends/OGL/VertexManager.cpp
@@ -61,6 +61,16 @@ void VertexManager::DestroyDeviceObjects()
s_indexBuffer.reset();
}
+StreamBuffer* VertexManager::GetVertexBuffer() const
+{
+ return s_vertexBuffer.get();
+}
+
+OGL::StreamBuffer* VertexManager::GetIndexBuffer() const
+{
+ return s_indexBuffer.get();
+}
+
GLuint VertexManager::GetVertexBufferHandle() const
{
return m_vertex_buffers;
diff --git a/Source/Core/VideoBackends/OGL/VertexManager.h b/Source/Core/VideoBackends/OGL/VertexManager.h
index de7cf08265..39203c5f4c 100644
--- a/Source/Core/VideoBackends/OGL/VertexManager.h
+++ b/Source/Core/VideoBackends/OGL/VertexManager.h
@@ -14,6 +14,7 @@
namespace OGL
{
+class StreamBuffer;
class GLVertexFormat : public NativeVertexFormat
{
public:
@@ -37,6 +38,8 @@ public:
void CreateDeviceObjects() override;
void DestroyDeviceObjects() override;
+ StreamBuffer* GetVertexBuffer() const;
+ StreamBuffer* GetIndexBuffer() const;
GLuint GetVertexBufferHandle() const;
GLuint GetIndexBufferHandle() const;
diff --git a/Source/Core/VideoBackends/Software/SWRenderer.cpp b/Source/Core/VideoBackends/Software/SWRenderer.cpp
index 28bd57557e..cde52b4449 100644
--- a/Source/Core/VideoBackends/Software/SWRenderer.cpp
+++ b/Source/Core/VideoBackends/Software/SWRenderer.cpp
@@ -16,6 +16,8 @@
#include "VideoBackends/Software/SWOGLWindow.h"
#include "VideoBackends/Software/SWTexture.h"
+#include "VideoCommon/AbstractPipeline.h"
+#include "VideoCommon/AbstractShader.h"
#include "VideoCommon/BoundingBox.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/VideoBackendBase.h"
@@ -42,6 +44,40 @@ void SWRenderer::RenderText(const std::string& pstr, int left, int top, u32 colo
SWOGLWindow::s_instance->PrintText(pstr, left, top, color);
}
+class SWShader final : public AbstractShader
+{
+public:
+ explicit SWShader(ShaderStage stage) : AbstractShader(stage) {}
+ ~SWShader() = default;
+
+ bool HasBinary() const override { return false; }
+ BinaryData GetBinary() const override { return {}; }
+};
+
+std::unique_ptr
+SWRenderer::CreateShaderFromSource(ShaderStage stage, const char* source, size_t length)
+{
+ return std::make_unique(stage);
+}
+
+std::unique_ptr SWRenderer::CreateShaderFromBinary(ShaderStage stage,
+ const void* data, size_t length)
+{
+ return std::make_unique(stage);
+}
+
+class SWPipeline final : public AbstractPipeline
+{
+public:
+ SWPipeline() : AbstractPipeline() {}
+ ~SWPipeline() override = default;
+};
+
+std::unique_ptr SWRenderer::CreatePipeline(const AbstractPipelineConfig& config)
+{
+ return std::make_unique();
+}
+
// Called on the GPU thread
void SWRenderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks,
float Gamma)
diff --git a/Source/Core/VideoBackends/Software/SWRenderer.h b/Source/Core/VideoBackends/Software/SWRenderer.h
index ecab73cccc..0631cbdf00 100644
--- a/Source/Core/VideoBackends/Software/SWRenderer.h
+++ b/Source/Core/VideoBackends/Software/SWRenderer.h
@@ -17,6 +17,12 @@ public:
std::unique_ptr
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override;
+ std::unique_ptr CreateShaderFromSource(ShaderStage stage, const char* source,
+ size_t length) override;
+ std::unique_ptr CreateShaderFromBinary(ShaderStage stage, const void* data,
+ size_t length) override;
+ std::unique_ptr CreatePipeline(const AbstractPipelineConfig& config) override;
+
void RenderText(const std::string& pstr, int left, int top, u32 color) override;
u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override;
void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override {}
diff --git a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt
index 25c12fb12c..fe29abe7b1 100644
--- a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt
+++ b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt
@@ -19,6 +19,8 @@ set(SRCS
Util.cpp
VertexFormat.cpp
VertexManager.cpp
+ VKPipeline.cpp
+ VKShader.cpp
VKTexture.cpp
VulkanContext.cpp
VulkanLoader.cpp
diff --git a/Source/Core/VideoBackends/Vulkan/Constants.h b/Source/Core/VideoBackends/Vulkan/Constants.h
index 3e4012fe17..c6758c6fdc 100644
--- a/Source/Core/VideoBackends/Vulkan/Constants.h
+++ b/Source/Core/VideoBackends/Vulkan/Constants.h
@@ -26,7 +26,8 @@ enum STAGING_BUFFER_TYPE
// Descriptor set layouts
enum DESCRIPTOR_SET_LAYOUT
{
- DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS,
+ DESCRIPTOR_SET_LAYOUT_SINGLE_UNIFORM_BUFFER,
+ DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS,
DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS,
DESCRIPTOR_SET_LAYOUT_SHADER_STORAGE_BUFFERS,
DESCRIPTOR_SET_LAYOUT_TEXEL_BUFFERS,
@@ -69,6 +70,7 @@ enum PIPELINE_LAYOUT
PIPELINE_LAYOUT_BBOX,
PIPELINE_LAYOUT_PUSH_CONSTANT,
PIPELINE_LAYOUT_TEXTURE_CONVERSION,
+ PIPELINE_LAYOUT_UTILITY,
PIPELINE_LAYOUT_COMPUTE,
NUM_PIPELINE_LAYOUTS
};
@@ -128,7 +130,7 @@ constexpr u32 MINIMUM_DRAW_CALLS_PER_COMMAND_BUFFER_FOR_READBACK = 10;
union MultisamplingState
{
BitField<0, 5, u32> samples; // 1-16
- BitField<0, 1, u32> per_sample_shading; // SSAA
+ BitField<5, 1, u32> per_sample_shading; // SSAA
u32 hex;
};
diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
index 7f72efb93b..cbd28b3630 100644
--- a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
+++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
@@ -106,7 +106,10 @@ void ObjectCache::DestroySamplers()
bool ObjectCache::CreateDescriptorSetLayouts()
{
- static const VkDescriptorSetLayoutBinding ubo_set_bindings[] = {
+ static const VkDescriptorSetLayoutBinding single_ubo_set_bindings[] = {
+ 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
+ VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_FRAGMENT_BIT};
+ static const VkDescriptorSetLayoutBinding per_stage_ubo_set_bindings[] = {
{UBO_DESCRIPTOR_SET_BINDING_PS, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
VK_SHADER_STAGE_FRAGMENT_BIT},
{UBO_DESCRIPTOR_SET_BINDING_VS, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
@@ -138,7 +141,9 @@ bool ObjectCache::CreateDescriptorSetLayouts()
static const VkDescriptorSetLayoutCreateInfo create_infos[NUM_DESCRIPTOR_SET_LAYOUTS] = {
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
- static_cast(ArraySize(ubo_set_bindings)), ubo_set_bindings},
+ static_cast(ArraySize(single_ubo_set_bindings)), single_ubo_set_bindings},
+ {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
+ static_cast(ArraySize(per_stage_ubo_set_bindings)), per_stage_ubo_set_bindings},
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast(ArraySize(sampler_set_bindings)), sampler_set_bindings},
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
@@ -177,16 +182,19 @@ bool ObjectCache::CreatePipelineLayouts()
// Descriptor sets for each pipeline layout
VkDescriptorSetLayout standard_sets[] = {
- m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS],
+ m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS],
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS]};
VkDescriptorSetLayout bbox_sets[] = {
- m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS],
+ m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS],
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS],
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_SHADER_STORAGE_BUFFERS]};
VkDescriptorSetLayout texture_conversion_sets[] = {
- m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS],
+ m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS],
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS],
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_TEXEL_BUFFERS]};
+ VkDescriptorSetLayout utility_sets[] = {
+ m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_SINGLE_UNIFORM_BUFFER],
+ m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS]};
VkDescriptorSetLayout compute_sets[] = {m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_COMPUTE]};
VkPushConstantRange push_constant_range = {
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, PUSH_CONSTANT_BUFFER_SIZE};
@@ -212,6 +220,10 @@ bool ObjectCache::CreatePipelineLayouts()
static_cast(ArraySize(texture_conversion_sets)), texture_conversion_sets, 1,
&push_constant_range},
+ // Texture Conversion
+ {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
+ static_cast(ArraySize(utility_sets)), utility_sets, 0, nullptr},
+
// Compute
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast(ArraySize(compute_sets)), compute_sets, 1, &compute_push_constant_range}};
diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp
index 7b65884075..e4a7401c54 100644
--- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp
+++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
+#include
#include
#include
#include
@@ -23,9 +24,12 @@
#include "VideoBackends/Vulkan/RasterFont.h"
#include "VideoBackends/Vulkan/Renderer.h"
#include "VideoBackends/Vulkan/StateTracker.h"
+#include "VideoBackends/Vulkan/StreamBuffer.h"
#include "VideoBackends/Vulkan/SwapChain.h"
#include "VideoBackends/Vulkan/TextureCache.h"
#include "VideoBackends/Vulkan/Util.h"
+#include "VideoBackends/Vulkan/VKPipeline.h"
+#include "VideoBackends/Vulkan/VKShader.h"
#include "VideoBackends/Vulkan/VKTexture.h"
#include "VideoBackends/Vulkan/VulkanContext.h"
@@ -171,6 +175,202 @@ std::unique_ptr Renderer::CreateStagingTexture(StagingTe
return VKStagingTexture::Create(type, config);
}
+std::unique_ptr Renderer::CreateShaderFromSource(ShaderStage stage,
+ const char* source, size_t length)
+{
+ return VKShader::CreateFromSource(stage, source, length);
+}
+
+std::unique_ptr Renderer::CreateShaderFromBinary(ShaderStage stage,
+ const void* data, size_t length)
+{
+ return VKShader::CreateFromBinary(stage, data, length);
+}
+
+std::unique_ptr Renderer::CreatePipeline(const AbstractPipelineConfig& config)
+{
+ return VKPipeline::Create(config);
+}
+
+std::tuple Renderer::UpdateUtilityUniformBuffer(const void* uniforms,
+ u32 uniforms_size)
+{
+ StreamBuffer* ubo_buf = g_object_cache->GetUtilityShaderUniformBuffer();
+ if (!ubo_buf->ReserveMemory(uniforms_size, g_vulkan_context->GetUniformBufferAlignment()))
+ {
+ Util::ExecuteCurrentCommandsAndRestoreState(false, true);
+ if (!ubo_buf->ReserveMemory(uniforms_size, g_vulkan_context->GetUniformBufferAlignment()))
+ {
+ PanicAlert("Failed to reserve uniform buffer space for utility draw.");
+ return {};
+ }
+ }
+
+ VkBuffer ubo = ubo_buf->GetBuffer();
+ u32 ubo_offset = static_cast(ubo_buf->GetCurrentOffset());
+ std::memcpy(ubo_buf->GetCurrentHostPointer(), uniforms, uniforms_size);
+ ubo_buf->CommitMemory(uniforms_size);
+
+ return std::tie(ubo, ubo_offset);
+}
+
+void Renderer::SetPipeline(const AbstractPipeline* pipeline)
+{
+ m_graphics_pipeline = static_cast(pipeline);
+}
+
+void Renderer::DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, const void* vertices,
+ u32 vertex_stride, u32 num_vertices)
+{
+ // Binding the utility pipeline layout breaks the standard layout.
+ StateTracker::GetInstance()->SetPendingRebind();
+
+ // Upload uniforms.
+ VkBuffer uniform_buffer = g_object_cache->GetUtilityShaderUniformBuffer()->GetBuffer();
+ u32 uniform_buffer_offset = 0;
+ if (uniforms_size > 0)
+ std::tie(uniform_buffer, uniform_buffer_offset) =
+ UpdateUtilityUniformBuffer(uniforms, uniforms_size);
+
+ // Upload vertices.
+ VkBuffer vertex_buffer = VK_NULL_HANDLE;
+ VkDeviceSize vertex_buffer_offset = 0;
+ if (vertices)
+ {
+ u32 vertices_size = vertex_stride * num_vertices;
+ StreamBuffer* vbo_buf = g_object_cache->GetUtilityShaderVertexBuffer();
+ if (!vbo_buf->ReserveMemory(vertices_size, vertex_stride))
+ {
+ Util::ExecuteCurrentCommandsAndRestoreState(true);
+ if (!vbo_buf->ReserveMemory(vertices_size, vertex_stride))
+ {
+ PanicAlert("Failed to reserve vertex buffer space for utility draw.");
+ return;
+ }
+ }
+
+ vertex_buffer = vbo_buf->GetBuffer();
+ vertex_buffer_offset = vbo_buf->GetCurrentOffset();
+ std::memcpy(vbo_buf->GetCurrentHostPointer(), vertices, vertices_size);
+ vbo_buf->CommitMemory(vertices_size);
+ }
+
+ // Allocate descriptor sets.
+ std::array dsets;
+ dsets[0] = g_command_buffer_mgr->AllocateDescriptorSet(
+ g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_SINGLE_UNIFORM_BUFFER));
+ dsets[1] = g_command_buffer_mgr->AllocateDescriptorSet(
+ g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS));
+
+ // Flush first if failed.
+ if (dsets[0] == VK_NULL_HANDLE || dsets[1] == VK_NULL_HANDLE)
+ {
+ Util::ExecuteCurrentCommandsAndRestoreState(true);
+ dsets[0] = g_command_buffer_mgr->AllocateDescriptorSet(
+ g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_SINGLE_UNIFORM_BUFFER));
+ dsets[1] = g_command_buffer_mgr->AllocateDescriptorSet(
+ g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS));
+
+ if (dsets[0] == VK_NULL_HANDLE || dsets[1] == VK_NULL_HANDLE)
+ {
+ PanicAlert("Failed to allocate descriptor sets in utility draw.");
+ return;
+ }
+ }
+
+ // Build UBO descriptor set.
+ std::array dswrites;
+ VkDescriptorBufferInfo dsbuffer = {uniform_buffer, 0, std::max(uniforms_size, 4u)};
+ dswrites[0] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, dsets[0], 0, 0, 1,
+ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, nullptr, &dsbuffer, nullptr};
+ dswrites[1] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ nullptr,
+ dsets[1],
+ 0,
+ 0,
+ NUM_PIXEL_SHADER_SAMPLERS,
+ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ StateTracker::GetInstance()->GetPSSamplerBindings().data(),
+ nullptr,
+ nullptr};
+
+ // Build commands.
+ VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
+ vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+ m_graphics_pipeline->GetPipeline());
+ if (vertex_buffer != VK_NULL_HANDLE)
+ vkCmdBindVertexBuffers(command_buffer, 0, 1, &vertex_buffer, &vertex_buffer_offset);
+
+ // Update and bind descriptors.
+ VkPipelineLayout pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_UTILITY);
+ vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), static_cast(dswrites.size()),
+ dswrites.data(), 0, nullptr);
+ vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0,
+ static_cast(dsets.size()), dsets.data(), 1, &uniform_buffer_offset);
+
+ // Ensure we're in a render pass before drawing, just in case we had to flush.
+ StateTracker::GetInstance()->BeginRenderPass();
+ vkCmdDraw(command_buffer, num_vertices, 1, 0, 0);
+}
+
+void Renderer::DispatchComputeShader(const AbstractShader* shader, const void* uniforms,
+ u32 uniforms_size, u32 groups_x, u32 groups_y, u32 groups_z)
+{
+ // Binding the utility pipeline layout breaks the standard layout.
+ StateTracker::GetInstance()->SetPendingRebind();
+ StateTracker::GetInstance()->EndRenderPass();
+
+ // Upload uniforms.
+ VkBuffer uniform_buffer = g_object_cache->GetUtilityShaderUniformBuffer()->GetBuffer();
+ u32 uniform_buffer_offset = 0;
+ if (uniforms_size > 0)
+ std::tie(uniform_buffer, uniform_buffer_offset) =
+ UpdateUtilityUniformBuffer(uniforms, uniforms_size);
+
+ // Flush first if failed.
+ VkDescriptorSet dset = g_command_buffer_mgr->AllocateDescriptorSet(
+ g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_COMPUTE));
+ if (dset == VK_NULL_HANDLE)
+ {
+ Util::ExecuteCurrentCommandsAndRestoreState(true);
+ dset = g_command_buffer_mgr->AllocateDescriptorSet(
+ g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_COMPUTE));
+ if (dset == VK_NULL_HANDLE)
+ {
+ PanicAlert("Failed to allocate descriptor sets in utility dispatch.");
+ return;
+ }
+ }
+
+ std::array dswrites;
+ VkDescriptorBufferInfo dsbuffer = {uniform_buffer, 0, std::max(uniforms_size, 4u)};
+ dswrites[0] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, dset, 0, 0, 1,
+ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, nullptr, &dsbuffer, nullptr};
+ dswrites[1] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ nullptr,
+ dset,
+ 1,
+ 0,
+ NUM_PIXEL_SHADER_SAMPLERS,
+ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ StateTracker::GetInstance()->GetPSSamplerBindings().data(),
+ nullptr,
+ nullptr};
+
+ // TODO: Texel buffers, storage images.
+
+ // Build commands.
+ VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
+ VkPipelineLayout pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_UTILITY);
+ vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE,
+ static_cast(shader)->GetComputePipeline());
+ vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), static_cast(dswrites.size()),
+ dswrites.data(), 0, nullptr);
+ vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, 0, 1,
+ &dset, 1, &uniform_buffer_offset);
+ vkCmdDispatch(command_buffer, groups_x, groups_y, groups_z);
+}
+
void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
{
u32 backbuffer_width = m_swap_chain->GetWidth();
diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h
index b695291c2d..a0bc35536c 100644
--- a/Source/Core/VideoBackends/Vulkan/Renderer.h
+++ b/Source/Core/VideoBackends/Vulkan/Renderer.h
@@ -7,6 +7,7 @@
#include
#include
#include
+#include
#include "Common/CommonTypes.h"
#include "VideoBackends/Vulkan/Constants.h"
@@ -22,6 +23,7 @@ class SwapChain;
class StagingTexture2D;
class Texture2D;
class RasterFont;
+class VKPipeline;
class VKTexture;
class Renderer : public ::Renderer
@@ -36,6 +38,12 @@ public:
std::unique_ptr
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override;
+ std::unique_ptr CreateShaderFromSource(ShaderStage stage, const char* source,
+ size_t length) override;
+ std::unique_ptr CreateShaderFromBinary(ShaderStage stage, const void* data,
+ size_t length) override;
+ std::unique_ptr CreatePipeline(const AbstractPipelineConfig& config) override;
+
SwapChain* GetSwapChain() const { return m_swap_chain.get(); }
BoundingBox* GetBoundingBox() const { return m_bounding_box.get(); }
bool Initialize();
@@ -59,6 +67,7 @@ public:
void ResetAPIState() override;
void RestoreAPIState() override;
+ void SetPipeline(const AbstractPipeline* pipeline) override;
void SetBlendingState(const BlendingState& state) override;
void SetScissorRect(const MathUtil::Rectangle& rc) override;
void SetRasterizationState(const RasterizationState& state) override;
@@ -70,6 +79,11 @@ public:
void SetViewport(float x, float y, float width, float height, float near_depth,
float far_depth) override;
+ void DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, const void* vertices,
+ u32 vertex_stride, u32 num_vertices) override;
+ void DispatchComputeShader(const AbstractShader* shader, const void* uniforms, u32 uniforms_size,
+ u32 groups_x, u32 groups_y, u32 groups_z) override;
+
private:
bool CreateSemaphores();
void DestroySemaphores();
@@ -97,6 +111,8 @@ private:
void BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_rect,
const TargetRectangle& src_rect, const Texture2D* src_tex);
+ std::tuple UpdateUtilityUniformBuffer(const void* uniforms, u32 uniforms_size);
+
VkSemaphore m_image_available_semaphore = VK_NULL_HANDLE;
VkSemaphore m_rendering_finished_semaphore = VK_NULL_HANDLE;
@@ -109,5 +125,6 @@ private:
// Shaders used for clear/blit.
VkShaderModule m_clear_fragment_shader = VK_NULL_HANDLE;
+ const VKPipeline* m_graphics_pipeline = nullptr;
};
}
diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp
index ae76ea51c3..d902c1ea97 100644
--- a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp
+++ b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp
@@ -1043,7 +1043,7 @@ bool StateTracker::UpdateDescriptorSet()
m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS] == VK_NULL_HANDLE)
{
VkDescriptorSetLayout layout =
- g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS);
+ g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS);
VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet(layout);
if (set == VK_NULL_HANDLE)
return false;
diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.h b/Source/Core/VideoBackends/Vulkan/StateTracker.h
index c3dbf5b7e8..06f9f3e94f 100644
--- a/Source/Core/VideoBackends/Vulkan/StateTracker.h
+++ b/Source/Core/VideoBackends/Vulkan/StateTracker.h
@@ -41,6 +41,10 @@ public:
}
const DepthState& GetDepthStencilState() const { return m_pipeline_state.depth_state; }
const BlendingState& GetBlendState() const { return m_pipeline_state.blend_state; }
+ const std::array& GetPSSamplerBindings() const
+ {
+ return m_bindings.ps_samplers;
+ }
void SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset);
void SetIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType type);
diff --git a/Source/Core/VideoBackends/Vulkan/Util.cpp b/Source/Core/VideoBackends/Vulkan/Util.cpp
index e74435e6d6..0bb386a5ea 100644
--- a/Source/Core/VideoBackends/Vulkan/Util.cpp
+++ b/Source/Core/VideoBackends/Vulkan/Util.cpp
@@ -566,7 +566,7 @@ void UtilityShaderDraw::BindDescriptors()
if (m_vs_uniform_buffer.buffer != VK_NULL_HANDLE || m_ps_uniform_buffer.buffer != VK_NULL_HANDLE)
{
VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet(
- g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS));
+ g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS));
if (set == VK_NULL_HANDLE)
PanicAlert("Failed to allocate descriptor set for utility draw");
@@ -595,7 +595,7 @@ void UtilityShaderDraw::BindDescriptors()
&dummy_uniform_buffer,
nullptr};
- bind_descriptor_sets[DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS] = set;
+ bind_descriptor_sets[DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS] = set;
}
// PS samplers
diff --git a/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp b/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp
new file mode 100644
index 0000000000..df245644c4
--- /dev/null
+++ b/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp
@@ -0,0 +1,73 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "Common/Assert.h"
+#include "Common/MsgHandler.h"
+
+#include "VideoBackends/Vulkan/ObjectCache.h"
+#include "VideoBackends/Vulkan/Util.h"
+#include "VideoBackends/Vulkan/VKPipeline.h"
+#include "VideoBackends/Vulkan/VKShader.h"
+#include "VideoBackends/Vulkan/VertexFormat.h"
+#include "VideoBackends/Vulkan/VulkanContext.h"
+
+namespace Vulkan
+{
+VKPipeline::VKPipeline(VkPipeline pipeline) : m_pipeline(pipeline)
+{
+}
+
+VKPipeline::~VKPipeline()
+{
+ vkDestroyPipeline(g_vulkan_context->GetDevice(), m_pipeline, nullptr);
+}
+
+std::unique_ptr VKPipeline::Create(const AbstractPipelineConfig& config)
+{
+ _dbg_assert_(VIDEO, config.vertex_shader && config.pixel_shader);
+
+ // Get render pass for config.
+ VkRenderPass render_pass = g_object_cache->GetRenderPass(
+ Util::GetVkFormatForHostTextureFormat(config.framebuffer_state.color_texture_format),
+ VK_FORMAT_UNDEFINED, config.framebuffer_state.samples, VK_ATTACHMENT_LOAD_OP_LOAD);
+
+ // Get pipeline layout.
+ VkPipelineLayout pipeline_layout;
+ switch (config.usage)
+ {
+ case AbstractPipelineUsage::GX:
+ pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
+ break;
+ case AbstractPipelineUsage::Utility:
+ pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_UTILITY);
+ break;
+ default:
+ PanicAlert("Unknown pipeline layout.");
+ return nullptr;
+ }
+
+ // TODO: Move ShaderCache stuff to here.
+ PipelineInfo pinfo;
+ pinfo.vertex_format = static_cast(config.vertex_format);
+ pinfo.pipeline_layout = pipeline_layout;
+ pinfo.vs = static_cast(config.vertex_shader)->GetShaderModule();
+ pinfo.ps = static_cast(config.pixel_shader)->GetShaderModule();
+ pinfo.gs = config.geometry_shader ?
+ static_cast(config.geometry_shader)->GetShaderModule() :
+ VK_NULL_HANDLE;
+ pinfo.render_pass = render_pass;
+ pinfo.rasterization_state.hex = config.rasterization_state.hex;
+ pinfo.depth_state.hex = config.depth_state.hex;
+ pinfo.blend_state.hex = config.blending_state.hex;
+ pinfo.multisampling_state.hex = 0;
+ pinfo.multisampling_state.samples = config.framebuffer_state.samples;
+ pinfo.multisampling_state.per_sample_shading = config.framebuffer_state.per_sample_shading;
+
+ VkPipeline pipeline = g_shader_cache->CreatePipeline(pinfo);
+ if (pipeline == VK_NULL_HANDLE)
+ return nullptr;
+
+ return std::make_unique(pipeline);
+}
+} // namespace Vulkan
diff --git a/Source/Core/VideoBackends/Vulkan/VKPipeline.h b/Source/Core/VideoBackends/Vulkan/VKPipeline.h
new file mode 100644
index 0000000000..ad66313e4c
--- /dev/null
+++ b/Source/Core/VideoBackends/Vulkan/VKPipeline.h
@@ -0,0 +1,27 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+
+#include "VideoBackends/Vulkan/VulkanLoader.h"
+#include "VideoCommon/AbstractPipeline.h"
+
+namespace Vulkan
+{
+class VKPipeline final : public AbstractPipeline
+{
+public:
+ explicit VKPipeline(VkPipeline pipeline);
+ ~VKPipeline() override;
+
+ VkPipeline GetPipeline() const { return m_pipeline; }
+ static std::unique_ptr Create(const AbstractPipelineConfig& config);
+
+private:
+ VkPipeline m_pipeline;
+};
+
+} // namespace Vulkan
diff --git a/Source/Core/VideoBackends/Vulkan/VKShader.cpp b/Source/Core/VideoBackends/Vulkan/VKShader.cpp
new file mode 100644
index 0000000000..59128de6b2
--- /dev/null
+++ b/Source/Core/VideoBackends/Vulkan/VKShader.cpp
@@ -0,0 +1,124 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "Common/Assert.h"
+
+#include "VideoBackends/Vulkan/ShaderCompiler.h"
+#include "VideoBackends/Vulkan/Util.h"
+#include "VideoBackends/Vulkan/VKShader.h"
+#include "VideoBackends/Vulkan/VulkanContext.h"
+
+namespace Vulkan
+{
+VKShader::VKShader(ShaderStage stage, std::vector spv, VkShaderModule mod)
+ : AbstractShader(stage), m_spv(std::move(spv)), m_module(mod),
+ m_compute_pipeline(VK_NULL_HANDLE)
+{
+}
+
+VKShader::VKShader(std::vector spv, VkPipeline compute_pipeline)
+ : AbstractShader(ShaderStage::Compute), m_spv(std::move(spv)), m_module(VK_NULL_HANDLE),
+ m_compute_pipeline(compute_pipeline)
+{
+}
+
+VKShader::~VKShader()
+{
+ if (m_stage != ShaderStage::Compute)
+ vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_module, nullptr);
+ else
+ vkDestroyPipeline(g_vulkan_context->GetDevice(), m_compute_pipeline, nullptr);
+}
+
+bool VKShader::HasBinary() const
+{
+ _assert_(!m_spv.empty());
+ return true;
+}
+
+AbstractShader::BinaryData VKShader::GetBinary() const
+{
+ BinaryData ret(sizeof(u32) * m_spv.size());
+ std::memcpy(ret.data(), m_spv.data(), sizeof(u32) * m_spv.size());
+ return ret;
+}
+
+static std::unique_ptr CreateShaderObject(ShaderStage stage,
+ ShaderCompiler::SPIRVCodeVector spv)
+{
+ VkShaderModule mod = Util::CreateShaderModule(spv.data(), spv.size());
+ if (mod == VK_NULL_HANDLE)
+ return nullptr;
+
+ // If it's a graphics shader, we defer pipeline creation.
+ if (stage != ShaderStage::Compute)
+ return std::make_unique(stage, std::move(spv), mod);
+
+ // If it's a compute shader, we create the pipeline straight away.
+ ComputePipelineInfo pinfo;
+ pinfo.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_COMPUTE);
+ pinfo.cs = mod;
+ VkPipeline pipeline = g_shader_cache->CreateComputePipeline(pinfo);
+ if (pipeline == VK_NULL_HANDLE)
+ {
+ vkDestroyShaderModule(g_vulkan_context->GetDevice(), mod, nullptr);
+ return nullptr;
+ }
+
+ // Shader module is no longer needed, now it is compiled to a pipeline.
+ return std::make_unique(std::move(spv), pipeline);
+}
+
+std::unique_ptr VKShader::CreateFromSource(ShaderStage stage, const char* source,
+ size_t length)
+{
+ ShaderCompiler::SPIRVCodeVector spv;
+ bool result;
+ switch (stage)
+ {
+ case ShaderStage::Vertex:
+ result = ShaderCompiler::CompileVertexShader(&spv, source, length);
+ break;
+ case ShaderStage::Geometry:
+ result = ShaderCompiler::CompileGeometryShader(&spv, source, length);
+ break;
+ case ShaderStage::Pixel:
+ result = ShaderCompiler::CompileFragmentShader(&spv, source, length);
+ break;
+ case ShaderStage::Compute:
+ result = ShaderCompiler::CompileComputeShader(&spv, source, length);
+ break;
+ default:
+ result = false;
+ break;
+ }
+
+ if (!result)
+ return nullptr;
+
+ return CreateShaderObject(stage, std::move(spv));
+}
+
+std::unique_ptr VKShader::CreateFromBinary(ShaderStage stage, const void* data,
+ size_t length)
+{
+ ShaderCompiler::SPIRVCodeVector spv;
+ const size_t size_in_words = sizeof(length) / sizeof(ShaderCompiler::SPIRVCodeType);
+ if (size_in_words > 0)
+ {
+ spv.resize(length / size_in_words);
+ std::memcpy(spv.data(), data, size_in_words);
+ }
+
+ // Non-aligned code sizes, unlikely (unless using VK_NV_glsl).
+ if ((length % sizeof(ShaderCompiler::SPIRVCodeType)) != 0)
+ {
+ spv.resize(size_in_words + 1);
+ std::memcpy(&spv[size_in_words], data, (length % sizeof(ShaderCompiler::SPIRVCodeType)));
+ }
+
+ return CreateShaderObject(stage, std::move(spv));
+}
+
+} // namespace Vulkan
diff --git a/Source/Core/VideoBackends/Vulkan/VKShader.h b/Source/Core/VideoBackends/Vulkan/VKShader.h
new file mode 100644
index 0000000000..a97007adda
--- /dev/null
+++ b/Source/Core/VideoBackends/Vulkan/VKShader.h
@@ -0,0 +1,40 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+#include
+#include
+
+#include "Common/CommonTypes.h"
+#include "VideoBackends/Vulkan/VulkanLoader.h"
+#include "VideoCommon/AbstractShader.h"
+
+namespace Vulkan
+{
+class VKShader final : public AbstractShader
+{
+public:
+ VKShader(ShaderStage stage, std::vector spv, VkShaderModule mod);
+ VKShader(std::vector spv, VkPipeline compute_pipeline);
+ ~VKShader() override;
+
+ VkShaderModule GetShaderModule() const { return m_module; }
+ VkPipeline GetComputePipeline() const { return m_compute_pipeline; }
+ bool HasBinary() const override;
+ BinaryData GetBinary() const override;
+
+ static std::unique_ptr CreateFromSource(ShaderStage stage, const char* source,
+ size_t length);
+ static std::unique_ptr CreateFromBinary(ShaderStage stage, const void* data,
+ size_t length);
+
+private:
+ std::vector m_spv;
+ VkShaderModule m_module;
+ VkPipeline m_compute_pipeline;
+};
+
+} // namespace Vulkan
diff --git a/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj b/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj
index 1a24ff053b..4b28324bb6 100644
--- a/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj
+++ b/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj
@@ -57,6 +57,8 @@
+
+
@@ -84,6 +86,8 @@
+
+
diff --git a/Source/Core/VideoCommon/AbstractPipeline.h b/Source/Core/VideoCommon/AbstractPipeline.h
new file mode 100644
index 0000000000..c0ae61af28
--- /dev/null
+++ b/Source/Core/VideoCommon/AbstractPipeline.h
@@ -0,0 +1,95 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#include "Common/CommonTypes.h"
+#include "VideoCommon/RenderState.h"
+#include "VideoCommon/TextureConfig.h"
+
+class AbstractShader;
+class NativeVertexFormat;
+
+// We use three pipeline usages:
+// - GX
+// - Per-stage UBO (VS/GS/PS, VS constants accessible from PS)
+// - 8 combined image samplers (accessible from PS)
+// - 1 SSBO, accessible from PS if bounding box is enabled
+// - Utility
+// - Single UBO, accessible from all stages [set=0, binding=1]
+// - 8 combined image samplers (accessible from PS) [set=1, binding=0-7]
+// - 1 texel buffer, accessible from PS [set=2, binding=0]
+// - Compute
+// - 1 uniform buffer [set=0, binding=1]
+// - 8 combined image samplers [set=1, binding=0-7]
+// - 1 texel buffer [set=2, binding=0]
+// - 1 storage image [set=3, binding=0]
+enum class AbstractPipelineUsage
+{
+ GX,
+ Utility
+};
+
+struct AbstractPipelineConfig
+{
+ const NativeVertexFormat* vertex_format;
+ const AbstractShader* vertex_shader;
+ const AbstractShader* geometry_shader;
+ const AbstractShader* pixel_shader;
+ RasterizationState rasterization_state;
+ DepthState depth_state;
+ BlendingState blending_state;
+
+ union FramebufferState
+ {
+ BitField<0, 8, AbstractTextureFormat> color_texture_format;
+ BitField<8, 8, AbstractTextureFormat> depth_texture_format;
+ BitField<16, 8, u32> samples;
+ BitField<24, 1, u32> per_sample_shading;
+
+ bool operator==(const FramebufferState& rhs) const { return hex == rhs.hex; }
+ bool operator!=(const FramebufferState& rhs) const { return hex != rhs.hex; }
+ FramebufferState& operator=(const FramebufferState& rhs)
+ {
+ hex = rhs.hex;
+ return *this;
+ }
+
+ u32 hex;
+ } framebuffer_state;
+
+ AbstractPipelineUsage usage;
+
+ bool operator==(const AbstractPipelineConfig& rhs) const
+ {
+ return std::tie(vertex_format, vertex_shader, geometry_shader, pixel_shader,
+ rasterization_state.hex, depth_state.hex, blending_state.hex,
+ framebuffer_state.hex, usage) ==
+ std::tie(rhs.vertex_format, rhs.vertex_shader, rhs.geometry_shader, rhs.pixel_shader,
+ rhs.rasterization_state.hex, rhs.depth_state.hex, rhs.blending_state.hex,
+ rhs.framebuffer_state.hex, rhs.usage);
+ }
+ bool operator!=(const AbstractPipelineConfig& rhs) const { return !operator==(rhs); }
+ bool operator<(const AbstractPipelineConfig& rhs) const
+ {
+ return std::tie(vertex_format, vertex_shader, geometry_shader, pixel_shader,
+ rasterization_state.hex, depth_state.hex, blending_state.hex,
+ framebuffer_state.hex, usage) <
+ std::tie(rhs.vertex_format, rhs.vertex_shader, rhs.geometry_shader, rhs.pixel_shader,
+ rhs.rasterization_state.hex, rhs.depth_state.hex, rhs.blending_state.hex,
+ rhs.framebuffer_state.hex, rhs.usage);
+ }
+};
+
+class AbstractPipeline
+{
+public:
+ AbstractPipeline() = default;
+ virtual ~AbstractPipeline() = default;
+};
diff --git a/Source/Core/VideoCommon/AbstractShader.h b/Source/Core/VideoCommon/AbstractShader.h
new file mode 100644
index 0000000000..4e765d60d9
--- /dev/null
+++ b/Source/Core/VideoCommon/AbstractShader.h
@@ -0,0 +1,34 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+#include
+#include
+
+#include "Common/CommonTypes.h"
+
+enum class ShaderStage
+{
+ Vertex,
+ Geometry,
+ Pixel,
+ Compute
+};
+
+class AbstractShader
+{
+public:
+ explicit AbstractShader(ShaderStage stage) : m_stage(stage) {}
+ virtual ~AbstractShader() = default;
+
+ ShaderStage GetStage() const { return m_stage; }
+ using BinaryData = std::vector;
+ virtual bool HasBinary() const = 0;
+ virtual BinaryData GetBinary() const = 0;
+
+protected:
+ ShaderStage m_stage;
+};
diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h
index 7b1cba2a44..3ec537e831 100644
--- a/Source/Core/VideoCommon/RenderBase.h
+++ b/Source/Core/VideoCommon/RenderBase.h
@@ -33,10 +33,15 @@
#include "VideoCommon/VideoCommon.h"
class AbstractRawTexture;
+class AbstractPipeline;
+class AbstractShader;
class AbstractTexture;
class AbstractStagingTexture;
class PostProcessingShaderImplementation;
struct TextureConfig;
+struct ComputePipelineConfig;
+struct AbstractPipelineConfig;
+enum class ShaderStage;
enum class EFBAccessType;
enum class StagingTextureType;
@@ -69,6 +74,7 @@ public:
PP_EFB_COPY_CLOCKS
};
+ virtual void SetPipeline(const AbstractPipeline* pipeline) {}
virtual void SetBlendingState(const BlendingState& state) {}
virtual void SetScissorRect(const MathUtil::Rectangle& rc) {}
virtual void SetRasterizationState(const RasterizationState& state) {}
@@ -91,6 +97,14 @@ public:
virtual std::unique_ptr
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) = 0;
+ // Shader modules/objects.
+ virtual std::unique_ptr
+ CreateShaderFromSource(ShaderStage stage, const char* source, size_t length) = 0;
+ virtual std::unique_ptr
+ CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length) = 0;
+ virtual std::unique_ptr
+ CreatePipeline(const AbstractPipelineConfig& config) = 0;
+
// Ideal internal resolution - multiple of the native EFB resolution
int GetTargetWidth() const { return m_target_width; }
int GetTargetHeight() const { return m_target_height; }
@@ -160,6 +174,16 @@ public:
virtual void Shutdown();
+ // Drawing utility shaders.
+ virtual void DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, const void* vertices,
+ u32 vertex_stride, u32 num_vertices)
+ {
+ }
+ virtual void DispatchComputeShader(const AbstractShader* shader, const void* uniforms,
+ u32 uniforms_size, u32 groups_x, u32 groups_y, u32 groups_z)
+ {
+ }
+
protected:
std::tuple CalculateTargetScale(int x, int y) const;
bool CalculateTargetSize();
diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj b/Source/Core/VideoCommon/VideoCommon.vcxproj
index 588701e43c..a90312f270 100644
--- a/Source/Core/VideoCommon/VideoCommon.vcxproj
+++ b/Source/Core/VideoCommon/VideoCommon.vcxproj
@@ -99,6 +99,8 @@
+
+
diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters
index 6f69333edd..2dafb43246 100644
--- a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters
+++ b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters
@@ -374,8 +374,14 @@
Base
+
+ Base
+
+
+ Base
+
-
\ No newline at end of file
+