Shader/pipeline abstraction

This commit is contained in:
Stenzek 2023-07-31 00:56:49 +10:00
parent 9b967e3751
commit dbf31ff75d
11 changed files with 831 additions and 2 deletions

View File

@ -37,6 +37,8 @@
<ClCompile Include="gpu\d3d11\shader_compiler.cpp" />
<ClCompile Include="gpu\d3d11\stream_buffer.cpp" />
<ClCompile Include="gpu\d3d11_device.cpp" />
<ClCompile Include="gpu\d3d11_pipeline.cpp" />
<ClCompile Include="gpu\d3d11_shader.cpp" />
<ClCompile Include="gpu\d3d11_texture.cpp" />
<ClCompile Include="gpu\d3d12\context.cpp" />
<ClCompile Include="gpu\d3d12\descriptor_heap_manager.cpp" />
@ -161,6 +163,8 @@
<ClInclude Include="gpu\d3d11\shader_compiler.h" />
<ClInclude Include="gpu\d3d11\stream_buffer.h" />
<ClInclude Include="gpu\d3d11_device.h" />
<ClInclude Include="gpu\d3d11_pipeline.h" />
<ClInclude Include="gpu\d3d11_shader.h" />
<ClInclude Include="gpu\d3d_shaders.h" />
<ClInclude Include="gpu\d3d11_texture.h" />
<ClInclude Include="gpu\d3d12\context.h" />
@ -197,6 +201,8 @@
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="gpu\gpu_device.h" />
<ClInclude Include="gpu\gpu_pipeline.h" />
<ClInclude Include="gpu\gpu_shader.h" />
<ClInclude Include="gpu\gpu_texture.h" />
<ClInclude Include="gpu\imgui_impl_dx12.h" />
<ClInclude Include="gpu\imgui_impl_opengl3.h" />

View File

@ -188,6 +188,12 @@
<ClCompile Include="gpu\d3d11_texture.cpp">
<Filter>gpu</Filter>
</ClCompile>
<ClCompile Include="gpu\d3d11_shader.cpp">
<Filter>gpu</Filter>
</ClCompile>
<ClCompile Include="gpu\d3d11_pipeline.cpp">
<Filter>gpu</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="types.h" />
@ -395,6 +401,18 @@
<ClInclude Include="gpu\d3d_shaders.h">
<Filter>gpu</Filter>
</ClInclude>
<ClInclude Include="gpu\gpu_pipeline.h">
<Filter>gpu</Filter>
</ClInclude>
<ClInclude Include="gpu\gpu_shader.h">
<Filter>gpu</Filter>
</ClInclude>
<ClInclude Include="gpu\d3d11_shader.h">
<Filter>gpu</Filter>
</ClInclude>
<ClInclude Include="gpu\d3d11_pipeline.h">
<Filter>gpu</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="gpu">

View File

@ -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 <d3d11.h>
#include <dxgi.h>
#include <memory>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
#include <wrl/client.h>
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<GPUShader> CreateShaderFromBinary(GPUShader::Stage stage, gsl::span<const u8> data) override;
std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShader::Stage stage, const std::string_view& source,
std::vector<u8>* out_binary = nullptr) override;
std::unique_ptr<GPUPipeline> 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<u8, ComPtr<ID3D11RasterizerState>>;
using DepthStateMap = std::unordered_map<u8, ComPtr<ID3D11DepthStencilState>>;
using BlendStateMap = std::unordered_map<u32, ComPtr<ID3D11BlendState>>;
using InputLayoutMap = std::unordered_map<GPUPipeline::InputLayout, ComPtr<ID3D11InputLayout>, 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<ID3D11RasterizerState> GetRasterizationState(const GPUPipeline::RasterizationState& rs);
ComPtr<ID3D11DepthStencilState> GetDepthState(const GPUPipeline::DepthState& ds);
ComPtr<ID3D11BlendState> GetBlendState(const GPUPipeline::BlendState& bs);
ComPtr<ID3D11InputLayout> GetInputLayout(const GPUPipeline::InputLayout& il, const D3D11Shader* vs);
void RenderDisplay();
void RenderSoftwareCursor();
void RenderImGui();
@ -142,6 +162,11 @@ protected:
ComPtr<ID3D11SamplerState> m_linear_sampler;
ComPtr<ID3D11SamplerState> 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<ID3D11Texture2D> m_readback_staging_texture;
DXGI_FORMAT m_readback_staging_texture_format = DXGI_FORMAT_UNKNOWN;

View File

@ -0,0 +1,242 @@
// SPDX-FileCopyrightText: 2023 Connor McLaughlin <stenzek@gmail.com>
// 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 <array>
#include <malloc.h>
Log_SetChannel(D3D11Device);
D3D11Pipeline::D3D11Pipeline(ComPtr<ID3D11RasterizerState> rs, ComPtr<ID3D11DepthStencilState> ds,
ComPtr<ID3D11BlendState> bs, ComPtr<ID3D11InputLayout> il, ComPtr<ID3D11VertexShader> vs,
ComPtr<ID3D11PixelShader> 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<ID3D11RasterizerState> D3D11Device::GetRasterizationState(const GPUPipeline::RasterizationState& rs)
{
ComPtr<ID3D11RasterizerState> 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<D3D11_CULL_MODE, static_cast<u32>(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<u8>(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<ID3D11DepthStencilState> D3D11Device::GetDepthState(const GPUPipeline::DepthState& ds)
{
ComPtr<ID3D11DepthStencilState> 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<D3D11_COMPARISON_FUNC, static_cast<u32>(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<u8>(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<ID3D11BlendState> D3D11Device::GetBlendState(const GPUPipeline::BlendState& bs)
{
ComPtr<ID3D11BlendState> 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<D3D11_BLEND, static_cast<u32>(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<D3D11_BLEND_OP, static_cast<u32>(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<u8>(bs.src_blend.GetValue())];
tgt_desc.DestBlend = blend_mapping[static_cast<u8>(bs.dst_blend.GetValue())];
tgt_desc.BlendOp = op_mapping[static_cast<u8>(bs.blend_op.GetValue())];
tgt_desc.SrcBlendAlpha = blend_mapping[static_cast<u8>(bs.src_alpha_blend.GetValue())];
tgt_desc.DestBlendAlpha = blend_mapping[static_cast<u8>(bs.dst_alpha_blend.GetValue())];
tgt_desc.BlendOpAlpha = op_mapping[static_cast<u8>(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<ID3D11InputLayout> D3D11Device::GetInputLayout(const GPUPipeline::InputLayout& il,
const D3D11Shader* vs)
{
ComPtr<ID3D11InputLayout> dil;
const auto it = m_input_layouts.find(il);
if (it != m_input_layouts.end())
{
dil = it->second;
return dil;
}
static constexpr std::array<const char*, static_cast<u32>(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<u8>(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<D3D11_INPUT_ELEMENT_DESC*>(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<u8>(va.semantic.GetValue())];
elem.SemanticIndex = va.semantic_index;
elem.Format = format_mapping[static_cast<u8>(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<UINT>(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<GPUPipeline> D3D11Device::CreatePipeline(const GPUPipeline::GraphicsConfig& config)
{
ComPtr<ID3D11RasterizerState> rs = GetRasterizationState(config.rasterization);
ComPtr<ID3D11DepthStencilState> ds = GetDepthState(config.depth);
ComPtr<ID3D11BlendState> bs = GetBlendState(config.blend);
static constexpr std::array<D3D11_PRIMITIVE_TOPOLOGY, static_cast<u32>(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<GPUPipeline>(
new D3D11Pipeline(std::move(rs), std::move(ds), std::move(bs), nullptr,
static_cast<const D3D11Shader*>(config.vertex_shader)->GetD3DVertexShader(),
static_cast<const D3D11Shader*>(config.pixel_shader)->GetD3DPixelShader(),
primitives[static_cast<u8>(config.primitive)]));
}

View File

@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: 2023 Connor McLaughlin <stenzek@gmail.com>
// 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 <memory>
#include <d3d11.h>
#include <wrl/client.h>
#include "gsl/span"
class D3D11Device;
class D3D11Pipeline final : public GPUPipeline
{
friend D3D11Device;
template<typename T>
using ComPtr = Microsoft::WRL::ComPtr<T>;
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<ID3D11RasterizerState> rs, ComPtr<ID3D11DepthStencilState> ds, ComPtr<ID3D11BlendState> bs,
ComPtr<ID3D11InputLayout> il, ComPtr<ID3D11VertexShader> vs, ComPtr<ID3D11PixelShader> ps,
D3D11_PRIMITIVE_TOPOLOGY topology);
ComPtr<ID3D11RasterizerState> m_rs;
ComPtr<ID3D11DepthStencilState> m_ds;
ComPtr<ID3D11BlendState> m_bs;
ComPtr<ID3D11InputLayout> m_il;
ComPtr<ID3D11VertexShader> m_vs;
ComPtr<ID3D11PixelShader> m_ps;
D3D11_PRIMITIVE_TOPOLOGY m_topology;
};

View File

@ -0,0 +1,113 @@
// SPDX-FileCopyrightText: 2023 Connor McLaughlin <stenzek@gmail.com>
// 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<ID3D11DeviceChild> shader)
: GPUShader(stage), m_shader(std::move(shader))
{
}
D3D11Shader::~D3D11Shader() = default;
ID3D11VertexShader* D3D11Shader::GetD3DVertexShader() const
{
DebugAssert(m_stage == Stage::Vertex);
return static_cast<ID3D11VertexShader*>(m_shader.Get());
}
ID3D11PixelShader* D3D11Shader::GetD3DPixelShader() const
{
DebugAssert(m_stage == Stage::Pixel);
return static_cast<ID3D11PixelShader*>(m_shader.Get());
}
ID3D11ComputeShader* D3D11Shader::GetD3DComputeShader() const
{
DebugAssert(m_stage == Stage::Compute);
return static_cast<ID3D11ComputeShader*>(m_shader.Get());
}
void D3D11Shader::SetDebugName(const std::string_view& name)
{
Panic("Implement me");
}
std::unique_ptr<GPUShader> D3D11Device::CreateShaderFromBinary(GPUShader::Stage stage, gsl::span<const u8> data)
{
ComPtr<ID3D11DeviceChild> shader;
std::vector<u8> 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<GPUShader>(new D3D11Shader(stage, std::move(shader), std::move(bytecode)));
}
std::unique_ptr<GPUShader> D3D11Device::CreateShaderFromSource(GPUShader::Stage stage, const std::string_view& source,
std::vector<u8>* 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<ID3DBlob> 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<const u8>(static_cast<const u8*>(blob->GetBufferPointer()), blob->GetBufferSize()));
}

View File

@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: 2023 Connor McLaughlin <stenzek@gmail.com>
// 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 <memory>
#include <vector>
#include <d3d11.h>
#include <wrl/client.h>
#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<u8>& GetBytecode() const { return m_bytecode; }
void SetDebugName(const std::string_view& name) override;
private:
D3D11Shader(Stage stage, Microsoft::WRL::ComPtr<ID3D11DeviceChild> shader, std::vector<u8> bytecode);
Microsoft::WRL::ComPtr<ID3D11DeviceChild> m_shader;
std::vector<u8> m_bytecode; // only for VS
};

View File

@ -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<GPUDevice> 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<GPUShader> GPUDevice::CreateShaderFromBinary(GPUShader::Stage stage, gsl::span<const u8> data)
{
// TODO: REMOVE ME
UnreachableCode();
return {};
}
std::unique_ptr<GPUShader> GPUDevice::CreateShaderFromSource(GPUShader::Stage stage, const std::string_view& source,
std::vector<u8>* out_binary /* = nullptr */)
{
// TODO: REMOVE ME
UnreachableCode();
return {};
}
std::unique_ptr<GPUPipeline> 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())

View File

@ -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 <memory>
#include <string>
#include <string_view>
@ -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<GPUShader> CreateShaderFromBinary(GPUShader::Stage stage, gsl::span<const u8> data);
virtual std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShader::Stage stage, const std::string_view& source,
std::vector<u8>* out_binary = nullptr);
virtual std::unique_ptr<GPUPipeline> CreatePipeline(const GPUPipeline::GraphicsConfig& config);
/// Returns false if the window was completely occluded.
virtual bool Render(bool skip_present) = 0;

199
src/core/gpu/gpu_pipeline.h Normal file
View File

@ -0,0 +1,199 @@
// SPDX-FileCopyrightText: 2023 Connor McLaughlin <stenzek@gmail.com>
// 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 <string_view>
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<u32, Semantic, 0, 3> semantic;
BitField<u32, u8, 4, 8> semantic_index;
BitField<u32, Type, 12, 4> type;
BitField<u32, u8, 16, 2> components;
BitField<u32, u8, 18, 8> offset;
u32 key;
};
struct InputLayout
{
gsl::span<const VertexAttribute> 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<u8, CullMode, 0, 2> cull_mode;
u8 key;
static RasterizationState GetNoCullState();
};
struct DepthState
{
BitField<u8, DepthFunc, 0, 3> depth_test;
BitField<u8, bool, 4, 1> depth_write;
u8 key;
static DepthState GetNoTestsState();
static DepthState GetAlwaysWriteState();
};
struct BlendState
{
BitField<u32, bool, 0, 1> enable;
BitField<u32, BlendFunc, 1, 4> src_blend;
BitField<u32, BlendFunc, 5, 4> src_alpha_blend;
BitField<u32, BlendFunc, 9, 4> dst_blend;
BitField<u32, BlendFunc, 13, 4> dst_alpha_blend;
BitField<u32, BlendOp, 17, 3> blend_op;
BitField<u32, BlendOp, 20, 3> alpha_blend_op;
BitField<u32, bool, 24, 1> write_r;
BitField<u32, bool, 25, 1> write_g;
BitField<u32, bool, 26, 1> write_b;
BitField<u32, bool, 27, 1> write_a;
BitField<u32, u8, 24, 4> 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;
};

29
src/core/gpu/gpu_shader.h Normal file
View File

@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: 2023 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include <string_view>
#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;
};