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 +