diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index 22fe45004..19f685eb4 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -37,6 +37,8 @@ + + @@ -161,6 +163,8 @@ + + @@ -197,6 +201,8 @@ true + + diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index 3109cb210..d4e9fd66a 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -188,6 +188,12 @@ gpu + + gpu + + + gpu + @@ -395,6 +401,18 @@ gpu + + gpu + + + gpu + + + gpu + + + gpu + diff --git a/src/core/gpu/d3d11_device.h b/src/core/gpu/d3d11_device.h index 94098f57d..c6bb1b8aa 100644 --- a/src/core/gpu/d3d11_device.h +++ b/src/core/gpu/d3d11_device.h @@ -8,15 +8,20 @@ #include "d3d11/stream_buffer.h" #include "d3d11_texture.h" #include "gpu_device.h" +#include "gpu_pipeline.h" #include "postprocessing_chain.h" #include #include #include #include #include +#include #include #include +class D3D11Pipeline; +class D3D11Shader; + class D3D11Device final : public GPUDevice { public: @@ -65,6 +70,11 @@ public: void ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src, u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width, u32 height) override; + std::unique_ptr CreateShaderFromBinary(GPUShader::Stage stage, gsl::span data) override; + std::unique_ptr CreateShaderFromSource(GPUShader::Stage stage, const std::string_view& source, + std::vector* out_binary = nullptr) override; + std::unique_ptr CreatePipeline(const GPUPipeline::GraphicsConfig& config) override; + bool GetHostRefreshRate(float* refresh_rate) override; bool SetGPUTimingEnabled(bool enabled) override; @@ -78,7 +88,12 @@ public: static AdapterAndModeList StaticGetAdapterAndModeList(); -protected: +private: + using RasterizationStateMap = std::unordered_map>; + using DepthStateMap = std::unordered_map>; + using BlendStateMap = std::unordered_map>; + using InputLayoutMap = std::unordered_map, GPUPipeline::InputLayoutHash>; + static constexpr u32 DISPLAY_UNIFORM_BUFFER_SIZE = 64; static constexpr u32 IMGUI_VERTEX_BUFFER_SIZE = 4 * 1024 * 1024; static constexpr u32 IMGUI_INDEX_BUFFER_SIZE = 2 * 1024 * 1024; @@ -98,6 +113,11 @@ protected: bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode); bool CreateSwapChainRTV(); + ComPtr GetRasterizationState(const GPUPipeline::RasterizationState& rs); + ComPtr GetDepthState(const GPUPipeline::DepthState& ds); + ComPtr GetBlendState(const GPUPipeline::BlendState& bs); + ComPtr GetInputLayout(const GPUPipeline::InputLayout& il, const D3D11Shader* vs); + void RenderDisplay(); void RenderSoftwareCursor(); void RenderImGui(); @@ -142,6 +162,11 @@ protected: ComPtr m_linear_sampler; ComPtr m_border_sampler; + RasterizationStateMap m_rasterization_states; + DepthStateMap m_depth_states; + BlendStateMap m_blend_states; + InputLayoutMap m_input_layouts; + D3D11::StreamBuffer m_display_uniform_buffer; ComPtr m_readback_staging_texture; DXGI_FORMAT m_readback_staging_texture_format = DXGI_FORMAT_UNKNOWN; diff --git a/src/core/gpu/d3d11_pipeline.cpp b/src/core/gpu/d3d11_pipeline.cpp new file mode 100644 index 000000000..dade73170 --- /dev/null +++ b/src/core/gpu/d3d11_pipeline.cpp @@ -0,0 +1,242 @@ +// SPDX-FileCopyrightText: 2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#include "d3d11_pipeline.h" +#include "d3d11_device.h" +#include "d3d11_shader.h" + +#include "common/assert.h" +#include "common/log.h" + +#include +#include + +Log_SetChannel(D3D11Device); + +D3D11Pipeline::D3D11Pipeline(ComPtr rs, ComPtr ds, + ComPtr bs, ComPtr il, ComPtr vs, + ComPtr ps, D3D11_PRIMITIVE_TOPOLOGY topology) + : m_rs(std::move(rs)), m_ds(std::move(ds)), m_bs(std::move(bs)), m_il(std::move(il)), m_vs(std::move(vs)), + m_ps(std::move(ps)), m_topology(topology) +{ +} + +D3D11Pipeline::~D3D11Pipeline() = default; + +void D3D11Pipeline::SetDebugName(const std::string_view& name) +{ + UnreachableCode(); +} + +void D3D11Pipeline::Bind(ID3D11DeviceContext* context) +{ + context->IASetInputLayout(GetInputLayout()); + context->IASetPrimitiveTopology(GetPrimitiveTopology()); + context->RSSetState(GetRasterizerState()); + context->OMSetDepthStencilState(GetDepthStencilState(), 0); + context->OMSetBlendState(GetBlendState(), nullptr, 0xFFFFFFFFu); + context->VSSetShader(GetVertexShader(), nullptr, 0); + context->PSSetShader(GetPixelShader(), nullptr, 0); +} + +D3D11Device::ComPtr D3D11Device::GetRasterizationState(const GPUPipeline::RasterizationState& rs) +{ + ComPtr drs; + + const auto it = m_rasterization_states.find(rs.key); + if (it != m_rasterization_states.end()) + { + drs = it->second; + return drs; + } + + static constexpr std::array(GPUPipeline::CullMode::MaxCount)> cull_mapping = {{ + D3D11_CULL_NONE, // None + D3D11_CULL_FRONT, // Front + D3D11_CULL_BACK, // Back + }}; + + D3D11_RASTERIZER_DESC desc = {}; + desc.FillMode = D3D11_FILL_SOLID; + desc.CullMode = cull_mapping[static_cast(rs.cull_mode.GetValue())]; + desc.ScissorEnable = TRUE; + // desc.MultisampleEnable ??? + + HRESULT hr = m_device->CreateRasterizerState(&desc, drs.GetAddressOf()); + if (FAILED(hr)) + Log_ErrorPrintf("Failed to create depth state with %08X", hr); + + m_rasterization_states.emplace(rs.key, drs); + return drs; +} + +D3D11Device::ComPtr D3D11Device::GetDepthState(const GPUPipeline::DepthState& ds) +{ + ComPtr dds; + + const auto it = m_depth_states.find(ds.key); + if (it != m_depth_states.end()) + { + dds = it->second; + return dds; + } + + static constexpr std::array(GPUPipeline::DepthFunc::MaxCount)> func_mapping = + {{ + D3D11_COMPARISON_NEVER, // Never + D3D11_COMPARISON_ALWAYS, // Always + D3D11_COMPARISON_LESS, // Less + D3D11_COMPARISON_LESS_EQUAL, // LessEqual + D3D11_COMPARISON_GREATER, // Greater + D3D11_COMPARISON_GREATER_EQUAL, // GreaterEqual + D3D11_COMPARISON_EQUAL, // Equal + }}; + + D3D11_DEPTH_STENCIL_DESC desc = {}; + desc.DepthEnable = ds.depth_test != GPUPipeline::DepthFunc::Never; + desc.DepthFunc = func_mapping[static_cast(ds.depth_test.GetValue())]; + desc.DepthWriteMask = ds.depth_write ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + + HRESULT hr = m_device->CreateDepthStencilState(&desc, dds.GetAddressOf()); + if (FAILED(hr)) + Log_ErrorPrintf("Failed to create depth state with %08X", hr); + + m_depth_states.emplace(ds.key, dds); + return dds; +} + +D3D11Device::ComPtr D3D11Device::GetBlendState(const GPUPipeline::BlendState& bs) +{ + ComPtr dbs; + + const auto it = m_blend_states.find(bs.key); + if (it != m_blend_states.end()) + { + dbs = it->second; + return dbs; + } + + static constexpr std::array(GPUPipeline::BlendFunc::MaxCount)> blend_mapping = {{ + D3D11_BLEND_ZERO, // Zero + D3D11_BLEND_ONE, // One + D3D11_BLEND_SRC_COLOR, // SrcColor + D3D11_BLEND_INV_SRC_COLOR, // InvSrcColor + D3D11_BLEND_DEST_COLOR, // DstColor + D3D11_BLEND_INV_DEST_COLOR, // InvDstColor + D3D11_BLEND_SRC_ALPHA, // SrcAlpha + D3D11_BLEND_INV_SRC_ALPHA, // InvSrcAlpha + D3D11_BLEND_SRC1_ALPHA, // SrcAlpha1 + D3D11_BLEND_INV_SRC1_ALPHA, // InvSrcAlpha1 + D3D11_BLEND_DEST_ALPHA, // DstAlpha + D3D11_BLEND_INV_DEST_ALPHA, // InvDstAlpha + }}; + + static constexpr std::array(GPUPipeline::BlendOp::MaxCount)> op_mapping = {{ + D3D11_BLEND_OP_ADD, // Add + D3D11_BLEND_OP_SUBTRACT, // Subtract + D3D11_BLEND_OP_REV_SUBTRACT, // ReverseSubtract + D3D11_BLEND_OP_MIN, // Min + D3D11_BLEND_OP_MAX, // Max + }}; + + D3D11_BLEND_DESC blend_desc = {}; + D3D11_RENDER_TARGET_BLEND_DESC& tgt_desc = blend_desc.RenderTarget[0]; + tgt_desc.BlendEnable = bs.enable; + tgt_desc.RenderTargetWriteMask = bs.write_mask; + if (bs.enable) + { + tgt_desc.SrcBlend = blend_mapping[static_cast(bs.src_blend.GetValue())]; + tgt_desc.DestBlend = blend_mapping[static_cast(bs.dst_blend.GetValue())]; + tgt_desc.BlendOp = op_mapping[static_cast(bs.blend_op.GetValue())]; + tgt_desc.SrcBlendAlpha = blend_mapping[static_cast(bs.src_alpha_blend.GetValue())]; + tgt_desc.DestBlendAlpha = blend_mapping[static_cast(bs.dst_alpha_blend.GetValue())]; + tgt_desc.BlendOpAlpha = op_mapping[static_cast(bs.alpha_blend_op.GetValue())]; + } + + HRESULT hr = m_device->CreateBlendState(&blend_desc, dbs.GetAddressOf()); + if (FAILED(hr)) + Log_ErrorPrintf("Failed to create blend state with %08X", hr); + + m_blend_states.emplace(bs.key, dbs); + return dbs; +} + +D3D11Device::ComPtr D3D11Device::GetInputLayout(const GPUPipeline::InputLayout& il, + const D3D11Shader* vs) +{ + ComPtr dil; + const auto it = m_input_layouts.find(il); + if (it != m_input_layouts.end()) + { + dil = it->second; + return dil; + } + + static constexpr std::array(GPUPipeline::VertexAttribute::Semantic::MaxCount)> + semantics = {{ + "POSITION", // Position + "TEXCOORD", // Texcoord + "COLOR", // Color + }}; + + static constexpr u32 MAX_COMPONENTS = 4; + static constexpr const DXGI_FORMAT + format_mapping[static_cast(GPUPipeline::VertexAttribute::Type::MaxCount)][MAX_COMPONENTS] = { + {DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_R32G32B32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT}, // Float + {DXGI_FORMAT_R8_UINT, DXGI_FORMAT_R8G8_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UINT}, // UInt8 + {DXGI_FORMAT_R8_SINT, DXGI_FORMAT_R8G8_SINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_SINT}, // SInt8 + {DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM}, // UNorm8 + {DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16G16_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_UINT}, // UInt16 + {DXGI_FORMAT_R16_SINT, DXGI_FORMAT_R16G16_SINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_SINT}, // SInt16 + {DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R16G16_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_UNORM}, // UNorm16 + {DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32G32_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32G32B32A32_UINT}, // UInt32 + {DXGI_FORMAT_R32_SINT, DXGI_FORMAT_R32G32_SINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32G32B32A32_SINT}, // SInt32 + }; + + D3D11_INPUT_ELEMENT_DESC* elems = + static_cast(alloca(sizeof(D3D11_INPUT_ELEMENT_DESC) * il.vertex_attributes.size())); + for (size_t i = 0; i < il.vertex_attributes.size(); i++) + { + const GPUPipeline::VertexAttribute& va = il.vertex_attributes[i]; + Assert(va.components > 0 && va.components < MAX_COMPONENTS); + + D3D11_INPUT_ELEMENT_DESC& elem = elems[i]; + elem.SemanticName = semantics[static_cast(va.semantic.GetValue())]; + elem.SemanticIndex = va.semantic_index; + elem.Format = format_mapping[static_cast(va.type.GetValue())][va.components - 1]; + elem.InputSlot = 0; + elem.AlignedByteOffset = va.offset; + elem.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + elem.InstanceDataStepRate = 0; + } + + HRESULT hr = m_device->CreateInputLayout(elems, static_cast(il.vertex_attributes.size()), + vs->GetBytecode().data(), vs->GetBytecode().size(), dil.GetAddressOf()); + if (FAILED(hr)) + Log_ErrorPrintf("Failed to create input layout with %08X", hr); + + m_input_layouts.emplace(il, dil); + return dil; +} + +std::unique_ptr D3D11Device::CreatePipeline(const GPUPipeline::GraphicsConfig& config) +{ + ComPtr rs = GetRasterizationState(config.rasterization); + ComPtr ds = GetDepthState(config.depth); + ComPtr bs = GetBlendState(config.blend); + + static constexpr std::array(GPUPipeline::Primitive::MaxCount)> primitives = + {{ + D3D11_PRIMITIVE_TOPOLOGY_POINTLIST, // Points + D3D11_PRIMITIVE_TOPOLOGY_LINELIST, // Lines + D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, // Triangles + D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, // TriangleStrips + }}; + + return std::unique_ptr( + new D3D11Pipeline(std::move(rs), std::move(ds), std::move(bs), nullptr, + static_cast(config.vertex_shader)->GetD3DVertexShader(), + static_cast(config.pixel_shader)->GetD3DPixelShader(), + primitives[static_cast(config.primitive)])); +} diff --git a/src/core/gpu/d3d11_pipeline.h b/src/core/gpu/d3d11_pipeline.h new file mode 100644 index 000000000..fbffc8e68 --- /dev/null +++ b/src/core/gpu/d3d11_pipeline.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#pragma once + +#include "gpu_pipeline.h" + +#include "common/windows_headers.h" + +#include + +#include +#include + +#include "gsl/span" + +class D3D11Device; + +class D3D11Pipeline final : public GPUPipeline +{ + friend D3D11Device; + + template + using ComPtr = Microsoft::WRL::ComPtr; + +public: + ~D3D11Pipeline() override; + + void SetDebugName(const std::string_view& name) override; + + ALWAYS_INLINE ID3D11RasterizerState* GetRasterizerState() const { return m_rs.Get(); } + ALWAYS_INLINE ID3D11DepthStencilState* GetDepthStencilState() const { return m_ds.Get(); } + ALWAYS_INLINE ID3D11BlendState* GetBlendState() const { return m_bs.Get(); } + ALWAYS_INLINE ID3D11InputLayout* GetInputLayout() const { return m_il.Get(); } + ALWAYS_INLINE ID3D11VertexShader* GetVertexShader() const { return m_vs.Get(); } + ALWAYS_INLINE ID3D11PixelShader* GetPixelShader() const { return m_ps.Get(); } + ALWAYS_INLINE D3D11_PRIMITIVE_TOPOLOGY GetPrimitiveTopology() const { return m_topology; } + + void Bind(ID3D11DeviceContext* context); + +private: + D3D11Pipeline(ComPtr rs, ComPtr ds, ComPtr bs, + ComPtr il, ComPtr vs, ComPtr ps, + D3D11_PRIMITIVE_TOPOLOGY topology); + + ComPtr m_rs; + ComPtr m_ds; + ComPtr m_bs; + ComPtr m_il; + ComPtr m_vs; + ComPtr m_ps; + D3D11_PRIMITIVE_TOPOLOGY m_topology; +}; diff --git a/src/core/gpu/d3d11_shader.cpp b/src/core/gpu/d3d11_shader.cpp new file mode 100644 index 000000000..cc124678b --- /dev/null +++ b/src/core/gpu/d3d11_shader.cpp @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: 2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#include "d3d11_shader.h" +#include "d3d11/shader_compiler.h" +#include "d3d11_device.h" + +#include "common/assert.h" + +D3D11Shader::D3D11Shader(Stage stage, Microsoft::WRL::ComPtr shader) + : GPUShader(stage), m_shader(std::move(shader)) +{ +} + +D3D11Shader::~D3D11Shader() = default; + +ID3D11VertexShader* D3D11Shader::GetD3DVertexShader() const +{ + DebugAssert(m_stage == Stage::Vertex); + return static_cast(m_shader.Get()); +} + +ID3D11PixelShader* D3D11Shader::GetD3DPixelShader() const +{ + DebugAssert(m_stage == Stage::Pixel); + return static_cast(m_shader.Get()); +} + +ID3D11ComputeShader* D3D11Shader::GetD3DComputeShader() const +{ + DebugAssert(m_stage == Stage::Compute); + return static_cast(m_shader.Get()); +} + +void D3D11Shader::SetDebugName(const std::string_view& name) +{ + Panic("Implement me"); +} + +std::unique_ptr D3D11Device::CreateShaderFromBinary(GPUShader::Stage stage, gsl::span data) +{ + ComPtr shader; + std::vector bytecode; + switch (stage) + { + case GPUShader::Stage::Vertex: + shader = D3D11::ShaderCompiler::CreateVertexShader(D3D11Device::GetD3DDevice(), data.data(), data.size()); + bytecode.resize(data.size()); + std::memcpy(bytecode.data(), data.data(), data.size()); + break; + + case GPUShader::Stage::Pixel: + shader = D3D11::ShaderCompiler::CreatePixelShader(D3D11Device::GetD3DDevice(), data.data(), data.size()); + break; + + case GPUShader::Stage::Compute: + shader = D3D11::ShaderCompiler::CreateComputeShader(D3D11Device::GetD3DDevice(), data.data(), data.size()); + break; + + default: + UnreachableCode(); + break; + } + + if (!shader) + return {}; + + return std::unique_ptr(new D3D11Shader(stage, std::move(shader), std::move(bytecode))); +} + +std::unique_ptr D3D11Device::CreateShaderFromSource(GPUShader::Stage stage, const std::string_view& source, + std::vector* out_binary /* = nullptr */) +{ + // TODO: This shouldn't be dependent on build type. +#ifdef _DEBUG + constexpr bool debug = true; +#else + constexpr bool debug = false; +#endif + + ComPtr blob; + switch (stage) + { + case GPUShader::Stage::Vertex: + blob = D3D11::ShaderCompiler::CompileShader(D3D11::ShaderCompiler::Type::Vertex, m_device->GetFeatureLevel(), + source, debug); + break; + + case GPUShader::Stage::Pixel: + blob = D3D11::ShaderCompiler::CompileShader(D3D11::ShaderCompiler::Type::Pixel, m_device->GetFeatureLevel(), + source, debug); + break; + + case GPUShader::Stage::Compute: + blob = D3D11::ShaderCompiler::CompileShader(D3D11::ShaderCompiler::Type::Compute, m_device->GetFeatureLevel(), + source, debug); + break; + + default: + UnreachableCode(); + break; + } + + if (out_binary) + { + const size_t size = blob->GetBufferSize(); + out_binary->resize(size); + std::memcpy(out_binary->data(), blob->GetBufferPointer(), size); + } + + return CreateShaderFromBinary( + stage, gsl::span(static_cast(blob->GetBufferPointer()), blob->GetBufferSize())); +} diff --git a/src/core/gpu/d3d11_shader.h b/src/core/gpu/d3d11_shader.h new file mode 100644 index 000000000..e37406a69 --- /dev/null +++ b/src/core/gpu/d3d11_shader.h @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#pragma once + +#include "gpu_shader.h" + +#include "common/windows_headers.h" + +#include +#include + +#include +#include + +#include "gsl/span" + +class D3D11Device; + +class D3D11Shader final : public GPUShader +{ + friend D3D11Device; + +public: + ~D3D11Shader() override; + + ID3D11VertexShader* GetD3DVertexShader() const; + ID3D11PixelShader* GetD3DPixelShader() const; + ID3D11ComputeShader* GetD3DComputeShader() const; + + ALWAYS_INLINE const std::vector& GetBytecode() const { return m_bytecode; } + + void SetDebugName(const std::string_view& name) override; + +private: + D3D11Shader(Stage stage, Microsoft::WRL::ComPtr shader, std::vector bytecode); + + Microsoft::WRL::ComPtr m_shader; + std::vector m_bytecode; // only for VS +}; diff --git a/src/core/gpu/gpu_device.cpp b/src/core/gpu/gpu_device.cpp index ad1d58fe2..d3894b6f2 100644 --- a/src/core/gpu/gpu_device.cpp +++ b/src/core/gpu/gpu_device.cpp @@ -6,6 +6,7 @@ #include "common/align.h" #include "common/assert.h" #include "common/file_system.h" +#include "common/hash_combine.h" #include "common/log.h" #include "common/string_util.h" #include "common/timer.h" @@ -22,6 +23,75 @@ Log_SetChannel(GPUDevice); std::unique_ptr g_host_display; +size_t GPUPipeline::InputLayoutHash::operator()(const InputLayout& il) const +{ + std::size_t h = 0; + hash_combine(h, il.vertex_attributes.size(), il.vertex_stride); + + for (const VertexAttribute& va : il.vertex_attributes) + hash_combine(h, va.key); + + return h; +} + +bool GPUPipeline::InputLayout::operator==(const InputLayout& rhs) const +{ + return (vertex_stride == rhs.vertex_stride && vertex_attributes.size() == rhs.vertex_attributes.size() && + std::memcmp(vertex_attributes.data(), rhs.vertex_attributes.data(), + sizeof(VertexAttribute) * rhs.vertex_attributes.size()) == 0); +} + +bool GPUPipeline::InputLayout::operator!=(const InputLayout& rhs) const +{ + return (vertex_stride != rhs.vertex_stride || + vertex_attributes.size() != rhs.vertex_attributes.size() && + std::memcmp(vertex_attributes.data(), rhs.vertex_attributes.data(), + sizeof(VertexAttribute) * rhs.vertex_attributes.size()) != 0); +} + +GPUPipeline::RasterizationState GPUPipeline::RasterizationState::GetNoCullState() +{ + RasterizationState ret = {}; + ret.cull_mode = CullMode::None; + return ret; +} + +GPUPipeline::DepthState GPUPipeline::DepthState::GetNoTestsState() +{ + DepthState ret = {}; + ret.depth_test = DepthFunc::Always; + return ret; +} + +GPUPipeline::DepthState GPUPipeline::DepthState::GetAlwaysWriteState() +{ + DepthState ret = {}; + ret.depth_test = DepthFunc::Always; + ret.depth_write = true; + return ret; +} + +GPUPipeline::BlendState GPUPipeline::BlendState::GetNoBlendingState() +{ + BlendState ret = {}; + ret.write_mask = 0xf; + return ret; +} + +GPUPipeline::BlendState GPUPipeline::BlendState::GetAlphaBlendingState() +{ + BlendState ret = {}; + ret.enable = true; + ret.src_blend = BlendFunc::SrcAlpha; + ret.dst_blend = BlendFunc::InvSrcAlpha; + ret.blend_op = BlendOp::Add; + ret.src_alpha_blend = BlendFunc::One; + ret.dst_alpha_blend = BlendFunc::Zero; + ret.alpha_blend_op = BlendOp::Add; + ret.write_mask = 0xf; + return ret; +} + GPUDevice::~GPUDevice() = default; RenderAPI GPUDevice::GetPreferredAPI() @@ -54,6 +124,28 @@ void GPUDevice::ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 UnreachableCode(); } +std::unique_ptr GPUDevice::CreateShaderFromBinary(GPUShader::Stage stage, gsl::span data) +{ + // TODO: REMOVE ME + UnreachableCode(); + return {}; +} + +std::unique_ptr GPUDevice::CreateShaderFromSource(GPUShader::Stage stage, const std::string_view& source, + std::vector* out_binary /* = nullptr */) +{ + // TODO: REMOVE ME + UnreachableCode(); + return {}; +} + +std::unique_ptr GPUDevice::CreatePipeline(const GPUPipeline::GraphicsConfig& config) +{ + // TODO: REMOVE ME + UnreachableCode(); + return {}; +} + bool GPUDevice::ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate) { if (!mode.empty()) diff --git a/src/core/gpu/gpu_device.h b/src/core/gpu/gpu_device.h index 5530927b8..c48f2687c 100644 --- a/src/core/gpu/gpu_device.h +++ b/src/core/gpu/gpu_device.h @@ -2,10 +2,16 @@ // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #pragma once +#include "gpu_pipeline.h" +#include "gpu_shader.h" +#include "gpu_texture.h" + #include "common/rectangle.h" #include "common/types.h" #include "common/window_info.h" -#include "gpu_texture.h" + +#include "gsl/span" + #include #include #include @@ -101,6 +107,12 @@ public: GPUTexture* src, u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width, u32 height); + /// Shader abstraction. + virtual std::unique_ptr CreateShaderFromBinary(GPUShader::Stage stage, gsl::span data); + virtual std::unique_ptr CreateShaderFromSource(GPUShader::Stage stage, const std::string_view& source, + std::vector* out_binary = nullptr); + virtual std::unique_ptr CreatePipeline(const GPUPipeline::GraphicsConfig& config); + /// Returns false if the window was completely occluded. virtual bool Render(bool skip_present) = 0; diff --git a/src/core/gpu/gpu_pipeline.h b/src/core/gpu/gpu_pipeline.h new file mode 100644 index 000000000..abbfb7457 --- /dev/null +++ b/src/core/gpu/gpu_pipeline.h @@ -0,0 +1,199 @@ +// SPDX-FileCopyrightText: 2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#pragma once + +#include "gpu_texture.h" + +#include "common/bitfield.h" +#include "common/types.h" + +#include "gsl/span" + +#include + +class GPUShader; + +class GPUPipeline +{ +public: + enum class Layout : u8 + { + // 128 byte UBO via push constants, 1 texture. + SingleTexture, + + MaxCount + }; + + enum class Primitive : u8 + { + Points, + Lines, + Triangles, + TriangleStrips, + + MaxCount + }; + + union VertexAttribute + { + enum class Semantic : u8 + { + Position, + Texcoord, + Color, + + MaxCount + }; + + enum class Type : u8 + { + Float, + UInt8, + SInt8, + UNorm8, + UInt16, + SInt16, + UNorm16, + UInt32, + SInt32, + + MaxCount + }; + + BitField semantic; + BitField semantic_index; + BitField type; + BitField components; + BitField offset; + u32 key; + }; + + struct InputLayout + { + gsl::span vertex_attributes; + u32 vertex_stride; + + bool operator==(const InputLayout& rhs) const; + bool operator!=(const InputLayout& rhs) const; + }; + + struct InputLayoutHash + { + size_t operator()(const InputLayout& il) const; + }; + + enum class CullMode : u8 + { + None, + Front, + Back, + + MaxCount + }; + + enum class DepthFunc : u8 + { + Never, + Always, + Less, + LessEqual, + Greater, + GreaterEqual, + Equal, + + MaxCount + }; + + enum class BlendFunc : u8 + { + Zero, + One, + SrcColor, + InvSrcColor, + DstColor, + InvDstColor, + SrcAlpha, + InvSrcAlpha, + SrcAlpha1, + InvSrcAlpha1, + DstAlpha, + InvDstAlpha, + + MaxCount + }; + + enum class BlendOp : u8 + { + Add, + Subtract, + ReverseSubtract, + Min, + Max, + + MaxCount + }; + + union RasterizationState + { + BitField cull_mode; + u8 key; + + static RasterizationState GetNoCullState(); + }; + + struct DepthState + { + BitField depth_test; + BitField depth_write; + u8 key; + + static DepthState GetNoTestsState(); + static DepthState GetAlwaysWriteState(); + }; + + struct BlendState + { + BitField enable; + BitField src_blend; + BitField src_alpha_blend; + BitField dst_blend; + BitField dst_alpha_blend; + BitField blend_op; + BitField alpha_blend_op; + BitField write_r; + BitField write_g; + BitField write_b; + BitField write_a; + BitField write_mask; + u32 key; + + static BlendState GetNoBlendingState(); + static BlendState GetAlphaBlendingState(); + }; + + struct GraphicsConfig + { + Layout layout; + + Primitive primitive; + InputLayout input_layout; + + RasterizationState rasterization; + DepthState depth; + BlendState blend; + + const GPUShader* vertex_shader; + const GPUShader* pixel_shader; + + GPUTexture::Format color_format; + GPUTexture::Format depth_format; + u32 samples; + bool per_sample_shading; + }; + + GPUPipeline() = default; + virtual ~GPUPipeline() = default; + + virtual void SetDebugName(const std::string_view& name) = 0; +}; diff --git a/src/core/gpu/gpu_shader.h b/src/core/gpu/gpu_shader.h new file mode 100644 index 000000000..84183edec --- /dev/null +++ b/src/core/gpu/gpu_shader.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#pragma once + +#include + +#include "common/types.h" + +class GPUShader +{ +public: + enum class Stage + { + Vertex, + Pixel, + Compute + }; + + GPUShader(Stage stage) : m_stage(stage) {} + virtual ~GPUShader() = default; + + ALWAYS_INLINE Stage GetStage() const { return m_stage; } + + virtual void SetDebugName(const std::string_view& name) = 0; + +protected: + Stage m_stage; +};