feat: 实现D3D12 RHI抽象层,修复PSO创建问题

- 添加RHI接口定义(IRHIDevice, ICommandList, IResource等)
- 实现D3D12Device, D3D12CommandList, D3D12PipelineState等
- 修复RootSignature参数数量(3->4)与HelloEarth一致
- 修复DSV格式设置(Unknown->D24_UNorm_S8_UInt)
- 添加Geometry Shader编译
- 创建XCEngineDemo项目验证RHI功能
This commit is contained in:
2026-03-14 02:42:59 +08:00
parent 6a0dfb150d
commit 5f12393424
76 changed files with 3112 additions and 15048 deletions

View File

@@ -0,0 +1,44 @@
#pragma once
#include "RHIDefines.h"
#include "IRHIResources.h"
namespace XCEngine {
namespace RHI {
class IResource {
public:
virtual ~IResource() = default;
virtual void* GetNativeResource() const = 0;
};
class ICommandList {
public:
virtual ~ICommandList() = default;
virtual void Reset(void* allocator) = 0;
virtual void Close() = 0;
virtual void SetPipelineState(IPipelineState* pso) = 0;
virtual void SetRootSignature(IRootSignature* signature) = 0;
virtual void SetPrimitiveTopology(PrimitiveTopology topology) = 0;
virtual void SetVertexBuffer(uint32_t slot, IResource* buffer, uint32_t offset = 0, uint32_t stride = 0) = 0;
virtual void SetIndexBuffer(IResource* buffer, uint32_t offset = 0) = 0;
virtual void SetDescriptorHeap(IDescriptorHeap* heap) = 0;
virtual void SetGraphicsDescriptorTable(uint32_t rootParameterIndex, uint64_t baseDescriptor) = 0;
virtual void SetComputeDescriptorTable(uint32_t rootParameterIndex, uint64_t baseDescriptor) = 0;
virtual void DrawInstanced(uint32_t vertexCountPerInstance, uint32_t instanceCount, uint32_t startVertex, uint32_t startInstance) = 0;
virtual void DrawIndexedInstanced(uint32_t indexCountPerInstance, uint32_t instanceCount, uint32_t startIndex, int32_t baseVertex, uint32_t startInstance) = 0;
virtual void Dispatch(uint32_t x, uint32_t y, uint32_t z) = 0;
virtual void SetViewports(const Viewport* viewports, uint32_t count) = 0;
virtual void SetScissorRects(const Rect* rects, uint32_t count) = 0;
virtual void SetRenderTargets(void** targets, uint32_t count, void* depthStencil) = 0;
virtual void ClearRenderTargetView(void* target, const float color[4]) = 0;
virtual void ClearDepthStencilView(void* depth, float depthValue, uint8_t stencil) = 0;
virtual void CopyResource(IResource* dst, const IResource* src) = 0;
virtual void CopyBuffer(IResource* dst, uint64_t dstOffset, const IResource* src, uint64_t srcOffset, uint64_t size) = 0;
virtual void ResourceBarrier(IResource* resource, ResourceStateFlag before, ResourceStateFlag after) = 0;
virtual void* GetNativeCommandList() const = 0;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,161 @@
#pragma once
#include "RHIDefines.h"
#include "ICommandList.h"
namespace XCEngine {
namespace RHI {
struct ShaderBytecode {
const void* bytecode = nullptr;
uint64_t size = 0;
};
struct InputElementDesc {
const char* semanticName = nullptr;
uint32_t semanticIndex = 0;
Format format = Format::Unknown;
uint32_t inputSlot = 0;
uint32_t alignedByteOffset = 0;
uint32_t inputSlotClass = 0; // 0 = per-vertex
uint32_t instanceDataStepRate = 0;
};
struct InputLayoutDesc {
const InputElementDesc* elements = nullptr;
uint32_t elementCount = 0;
};
enum class PrimitiveTopologyType {
Undefined,
Point,
Line,
Triangle,
Patch
};
enum class BlendFactor {
Zero,
One,
SrcColor,
InvSrcColor,
SrcAlpha,
InvSrcAlpha,
DestAlpha,
InvDestAlpha,
DestColor,
InvDestColor
};
enum class BlendOp {
Add,
Subtract,
ReverseSubtract,
Min,
Max
};
enum class ComparisonFunc {
Never,
Less,
Equal,
LessEqual,
Greater,
NotEqual,
GreaterEqual,
Always
};
enum class CullMode {
None,
Front,
Back
};
enum class FillMode {
Wireframe,
Solid
};
struct BlendState {
bool enable = false;
BlendFactor srcBlend = BlendFactor::SrcAlpha;
BlendFactor destBlend = BlendFactor::InvSrcAlpha;
BlendOp blendOp = BlendOp::Add;
BlendFactor srcBlendAlpha = BlendFactor::One;
BlendFactor destBlendAlpha = BlendFactor::Zero;
BlendOp blendOpAlpha = BlendOp::Add;
uint8_t renderTargetWriteMask = 0xF;
};
struct DepthStencilState {
bool depthEnable = true;
bool stencilEnable = false;
ComparisonFunc depthFunc = ComparisonFunc::Less;
uint8_t depthWriteMask = 0xFF;
uint8_t stencilReadMask = 0xFF;
uint8_t stencilWriteMask = 0xFF;
};
struct RasterizerState {
FillMode fillMode = FillMode::Solid;
CullMode cullMode = CullMode::Back;
bool frontCounterClockwise = true;
int32_t depthBias = 0;
float depthBiasClamp = 0.0f;
float slopeScaledDepthBias = 0.0f;
bool depthClipEnable = true;
bool scissorEnable = false;
bool multisampleEnable = false;
};
struct RenderState {
BlendState blendState;
DepthStencilState depthStencilState;
RasterizerState rasterizerState;
};
struct PipelineDesc {
IRootSignature* rootSignature = nullptr;
ShaderBytecode vertexShader;
ShaderBytecode pixelShader;
ShaderBytecode geometryShader;
ShaderBytecode computeShader;
RenderState renderState;
InputLayoutDesc inputLayout;
PrimitiveTopologyType topologyType = PrimitiveTopologyType::Triangle;
uint32_t numRenderTargets = 1;
Format rtvFormats[8] = { Format::Unknown };
Format dsvFormat = Format::Unknown;
SampleCount sampleCount = SampleCount::Count1;
};
class IRHIDevice {
public:
virtual ~IRHIDevice() = default;
virtual GraphicsAPI GetAPI() const = 0;
virtual const char* GetAPIName() const = 0;
virtual bool Initialize(void* windowHandle, uint32_t width, uint32_t height) = 0;
virtual void Shutdown() = 0;
virtual bool CreateCommandQueue(ICommandQueue** queue, const CommandQueueDesc& desc) = 0;
virtual bool CreateCommandAllocator(ICommandAllocator** allocator) = 0;
virtual bool CreateCommandList(ICommandList** list, ICommandAllocator* allocator) = 0;
virtual bool CreateFence(IFence** fence) = 0;
virtual bool CreateDescriptorHeap(IDescriptorHeap** heap, const DescriptorHeapDesc& desc) = 0;
virtual bool CreateRootSignature(IRootSignature** signature, const RootSignatureDesc& desc) = 0;
virtual bool CreatePipelineState(IPipelineState** pso, const PipelineDesc& desc) = 0;
virtual bool CreateSwapChain(ISwapChain** swapChain, const SwapChainDesc& desc, void* windowHandle) = 0;
virtual ICommandQueue* GetCommandQueue() = 0;
virtual void* GetNativeDevice() const = 0;
virtual void* GetNativeAdapter() const = 0;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,97 @@
#pragma once
#include "RHIDefines.h"
#include <unknwn.h>
namespace XCEngine {
namespace RHI {
class ICommandQueue {
public:
virtual ~ICommandQueue() = default;
virtual void ExecuteCommandLists(void** lists, uint32_t count) = 0;
virtual void Signal(void* fence, uint64_t value) = 0;
virtual void Wait(void* fence, uint64_t value) = 0;
virtual uint64_t GetTimestampFrequency() const = 0;
virtual IUnknown* GetNativeQueue() const = 0;
};
class ICommandAllocator {
public:
virtual ~ICommandAllocator() = default;
virtual void Reset() = 0;
};
class IFence {
public:
virtual ~IFence() = default;
virtual uint64_t GetCompletedValue() const = 0;
virtual void Signal(uint64_t value) = 0;
virtual void Wait(uint64_t value) = 0;
virtual void Wait(uint64_t value, uint64_t timeoutMs) = 0;
};
class IDescriptorHeap {
public:
virtual ~IDescriptorHeap() = default;
virtual DescriptorHeapType GetType() const = 0;
virtual uint32_t GetDescriptorCount() const = 0;
virtual void* GetCPUDescriptorHandle(uint32_t index) const = 0;
virtual uint64_t GetGPUDescriptorHandle(uint32_t index) const = 0;
virtual void SetName(const char* name) = 0;
};
class ISwapChain {
public:
virtual ~ISwapChain() = default;
virtual bool Initialize(const SwapChainDesc& desc, void* windowHandle) = 0;
virtual void Shutdown() = 0;
virtual bool Present() = 0;
virtual bool Resize(uint32_t width, uint32_t height) = 0;
virtual uint32_t GetCurrentBufferIndex() const = 0;
virtual void* GetBuffer(uint32_t index) = 0;
virtual void SetFullscreen(bool fullscreen) = 0;
virtual bool IsFullscreen() const = 0;
};
enum class RootParameterType {
DescriptorTable,
Constants,
CBV,
SRV,
UAV,
Sampler
};
struct RootParameter {
RootParameterType type;
uint32_t shaderRegister = 0;
uint32_t registerSpace = 0;
uint32_t num32BitValues = 4;
ShaderVisibility visibility = ShaderVisibility::All;
};
struct RootSignatureDesc {
RootParameter* parameters = nullptr;
uint32_t parameterCount = 0;
uint32_t flags = 0;
};
class IRootSignature {
public:
virtual ~IRootSignature() = default;
virtual bool Initialize(const RootSignatureDesc& desc) = 0;
virtual void SetName(const char* name) = 0;
virtual void* GetNativeRootSignature() const = 0;
};
class IPipelineState {
public:
virtual ~IPipelineState() = default;
virtual void SetName(const char* name) = 0;
virtual void* GetNativePipelineState() const = 0;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,27 @@
#pragma once
#include "RHIDefines.h"
namespace XCEngine {
namespace RHI {
class IShaderResourceView {
public:
virtual ~IShaderResourceView() = default;
virtual void* GetNativeSRV() const = 0;
};
class IRenderTargetView {
public:
virtual ~IRenderTargetView() = default;
virtual void* GetNativeRTV() const = 0;
};
class IDepthStencilView {
public:
virtual ~IDepthStencilView() = default;
virtual void* GetNativeDSV() const = 0;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,226 @@
#pragma once
#include <cstdint>
#include <dxgi1_4.h>
#include <d3d12.h>
namespace XCEngine {
namespace RHI {
enum class Format : uint32_t {
Unknown = 0,
R8_UNorm = 61,
R8G8_UNorm = 49,
R8G8B8A8_UNorm = 28,
R16G16B16A16_Float = 10,
R32G32B32A32_Float = 2,
R16_Float = 54,
R32_Float = 41,
D16_UNorm = 55,
D24_UNorm_S8_UInt = 45,
D32_Float = 40,
BC1_UNorm = 71,
BC2_UNorm = 74,
BC3_UNorm = 77,
BC4_UNorm = 80,
BC5_UNorm = 83,
BC6H_UF16 = 95,
BC7_UNorm = 98
};
enum class PrimitiveTopology : uint8_t {
Undefined = 0,
PointList = 1,
LineList = 2,
LineStrip = 3,
TriangleList = 4,
TriangleStrip = 5,
LineListAdj = 10,
LineStripAdj = 11,
TriangleListAdj = 12,
TriangleStripAdj = 13,
PatchList = 33
};
enum class GraphicsAPI : uint8_t {
Unknown,
Direct3D11,
Direct3D12,
Vulkan,
Metal,
OpenGL
};
enum class CommandListType : uint8_t {
Direct,
Compute,
Copy,
Bundle
};
enum class ShaderVisibility : uint8_t {
All = 0,
Vertex = 1,
Hull = 2,
Domain = 3,
Geometry = 4,
Pixel = 5,
Amplification = 6,
Mesh = 7
};
enum class DescriptorHeapType : uint8_t {
CBV_SRV_UAV,
Sampler,
RTV,
DSV
};
enum class QueryType : uint8_t {
Occlusion,
Timestamp,
PipelineStatistics
};
enum class SampleCount : uint8_t {
Count1 = 1,
Count2 = 2,
Count4 = 4,
Count8 = 8
};
struct CommandQueueDesc {
CommandListType type = CommandListType::Direct;
int32_t priority = 0;
const char* name = nullptr;
};
struct DescriptorHeapDesc {
DescriptorHeapType type;
uint32_t count = 0;
bool shaderVisible = false;
const char* name = nullptr;
};
struct QueryHeapDesc {
QueryType type;
uint32_t count = 0;
const char* name = nullptr;
};
struct SwapChainDesc {
uint32_t width = 0;
uint32_t height = 0;
Format format = Format::Unknown;
uint32_t bufferCount = 2;
bool vsync = false;
bool fullscreen = false;
};
enum class ResourceStateFlag : uint32_t {
Common = 0,
VertexBuffer = 1 << 0,
ConstantBuffer = 1 << 1,
IndexBuffer = 1 << 2,
RenderTarget = 1 << 3,
UnorderedAccess = 1 << 4,
DepthWrite = 1 << 5,
DepthRead = 1 << 6,
ShaderResource = 1 << 7,
IndirectArgument = 1 << 8,
CopyDest = 1 << 9,
CopySource = 1 << 10,
ResolveDest = 1 << 11,
ResolveSource = 1 << 12,
Present = 1 << 13,
RaytracingAccelerationStructure = 1 << 14,
ShadingRateSource = 1 << 15
};
inline ResourceStateFlag operator|(ResourceStateFlag a, ResourceStateFlag b) {
return static_cast<ResourceStateFlag>(static_cast<uint32_t>(a) | static_cast<uint32_t>(b));
}
inline ResourceStateFlag operator&(ResourceStateFlag a, ResourceStateFlag b) {
return static_cast<ResourceStateFlag>(static_cast<uint32_t>(a) & static_cast<uint32_t>(b));
}
struct Viewport {
float topLeftX = 0.0f;
float topLeftY = 0.0f;
float width = 0.0f;
float height = 0.0f;
float minDepth = 0.0f;
float maxDepth = 1.0f;
};
struct Rect {
int32_t left = 0;
int32_t top = 0;
int32_t right = 0;
int32_t bottom = 0;
};
struct Color {
float r = 0.0f;
float g = 0.0f;
float b = 0.0f;
float a = 1.0f;
};
struct ClearValue {
Color color;
float depth = 1.0f;
uint8_t stencil = 0;
};
inline DXGI_FORMAT FormatToDXGIFormat(Format format) {
switch (format) {
case Format::Unknown: return DXGI_FORMAT_UNKNOWN;
case Format::R8_UNorm: return DXGI_FORMAT_R8_UNORM;
case Format::R8G8_UNorm: return DXGI_FORMAT_R8G8_UNORM;
case Format::R8G8B8A8_UNorm: return DXGI_FORMAT_R8G8B8A8_UNORM;
case Format::R16G16B16A16_Float: return DXGI_FORMAT_R16G16B16A16_FLOAT;
case Format::R32G32B32A32_Float: return DXGI_FORMAT_R32G32B32A32_FLOAT;
case Format::R16_Float: return DXGI_FORMAT_R16_FLOAT;
case Format::R32_Float: return DXGI_FORMAT_R32_FLOAT;
case Format::D16_UNorm: return DXGI_FORMAT_D16_UNORM;
case Format::D24_UNorm_S8_UInt: return DXGI_FORMAT_D24_UNORM_S8_UINT;
case Format::D32_Float: return DXGI_FORMAT_D32_FLOAT;
case Format::BC1_UNorm: return DXGI_FORMAT_BC1_UNORM;
case Format::BC2_UNorm: return DXGI_FORMAT_BC2_UNORM;
case Format::BC3_UNorm: return DXGI_FORMAT_BC3_UNORM;
case Format::BC4_UNorm: return DXGI_FORMAT_BC4_UNORM;
case Format::BC5_UNorm: return DXGI_FORMAT_BC5_UNORM;
case Format::BC6H_UF16: return DXGI_FORMAT_BC6H_UF16;
case Format::BC7_UNorm: return DXGI_FORMAT_BC7_UNORM;
default: return DXGI_FORMAT_UNKNOWN;
}
}
inline Format DXGIFormatToFormat(DXGI_FORMAT dxgiFormat) {
switch (dxgiFormat) {
case DXGI_FORMAT_UNKNOWN: return Format::Unknown;
case DXGI_FORMAT_R8_UNORM: return Format::R8_UNorm;
case DXGI_FORMAT_R8G8_UNORM: return Format::R8G8_UNorm;
case DXGI_FORMAT_R8G8B8A8_UNORM: return Format::R8G8B8A8_UNorm;
case DXGI_FORMAT_R16G16B16A16_FLOAT: return Format::R16G16B16A16_Float;
case DXGI_FORMAT_R32G32B32A32_FLOAT: return Format::R32G32B32A32_Float;
case DXGI_FORMAT_R16_FLOAT: return Format::R16_Float;
case DXGI_FORMAT_R32_FLOAT: return Format::R32_Float;
case DXGI_FORMAT_D16_UNORM: return Format::D16_UNorm;
case DXGI_FORMAT_D24_UNORM_S8_UINT: return Format::D24_UNorm_S8_UInt;
case DXGI_FORMAT_D32_FLOAT: return Format::D32_Float;
case DXGI_FORMAT_BC1_UNORM: return Format::BC1_UNorm;
case DXGI_FORMAT_BC2_UNORM: return Format::BC2_UNorm;
case DXGI_FORMAT_BC3_UNORM: return Format::BC3_UNorm;
case DXGI_FORMAT_BC4_UNORM: return Format::BC4_UNorm;
case DXGI_FORMAT_BC5_UNORM: return Format::BC5_UNorm;
case DXGI_FORMAT_BC6H_UF16: return Format::BC6H_UF16;
case DXGI_FORMAT_BC7_UNORM: return Format::BC7_UNorm;
default: return Format::Unknown;
}
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,28 @@
#pragma once
#include "RHIDefines.h"
#include "IRHIDevice.h"
namespace XCEngine {
namespace RHI {
class RHISystem {
public:
static RHISystem& Get();
bool Initialize(GraphicsAPI api, void* windowHandle, uint32_t width, uint32_t height);
void Shutdown();
IRHIDevice* GetDevice() { return m_device; }
GraphicsAPI GetAPI() const { return m_api; }
private:
RHISystem() = default;
~RHISystem() = default;
GraphicsAPI m_api = GraphicsAPI::Unknown;
IRHIDevice* m_device = nullptr;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,46 @@
#pragma once
#include <RHI\IRHIDevice.h>
#include <RHI\IRHIResources.h>
#include <Rendering\RenderTarget.h>
namespace XCEngine {
namespace RHI {
class RenderContext {
public:
RenderContext(D3D12Device* device);
~RenderContext();
bool Initialize(uint32_t width, uint32_t height);
void Shutdown();
void BeginFrame();
void EndFrame();
ICommandList* GetCommandList() { return m_commandList; }
ISwapChain* GetSwapChain() { return m_swapChain; }
IRenderTarget* GetCurrentRenderTarget() { return m_currentRenderTarget; }
IDepthStencil* GetDepthStencil() { return m_depthStencil; }
void SetViewport(float width, float height);
void SetScissor(int32_t width, int32_t height);
void ClearRenderTarget(const float color[4]);
void ClearDepthStencil();
void Present();
private:
D3D12Device* m_device = nullptr;
ICommandAllocator* m_commandAllocator = nullptr;
ICommandList* m_commandList = nullptr;
IFence* m_fence = nullptr;
ISwapChain* m_swapChain = nullptr;
IRenderTarget* m_currentRenderTarget = nullptr;
IDepthStencil* m_depthStencil = nullptr;
uint64_t m_fenceValue = 0;
uint32_t m_width = 0;
uint32_t m_height = 0;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,26 @@
#pragma once
#include <Rendering/Resources.h>
namespace XCEngine {
namespace RHI {
class D3D12Device;
class D3D12SwapChain;
class IDepthStencil : public ITexture2D {
public:
virtual ~IDepthStencil() = default;
};
class IRenderTarget : public ITexture2D {
public:
virtual ~IRenderTarget() = default;
};
bool CreateDepthStencil(D3D12Device* device, uint32_t width, uint32_t height, Format format, IDepthStencil** outDepthStencil);
bool CreateRenderTargetFromSwapChain(D3D12Device* device, D3D12SwapChain* swapChain, uint32_t bufferIndex, IRenderTarget** outRenderTarget);
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,74 @@
#pragma once
#include <RHI\IRHIDevice.h>
#include <RHI\IRHIResources.h>
namespace XCEngine {
namespace RHI {
struct BufferDesc {
uint64_t size = 0;
uint32_t stride = 0;
bool cpuAccessible = false;
};
class IBuffer : public IResource {
public:
virtual ~IBuffer() = default;
virtual const BufferDesc& GetDesc() const = 0;
virtual void* Map() = 0;
virtual void Unmap() = 0;
};
class IVertexBuffer : public IBuffer {
public:
virtual ~IVertexBuffer() = default;
};
class IIndexBuffer : public IBuffer {
public:
virtual ~IIndexBuffer() = default;
};
class IConstantBuffer : public IBuffer {
public:
virtual ~IConstantBuffer() = default;
};
struct TextureDesc {
uint32_t width = 0;
uint32_t height = 0;
uint32_t depth = 1;
uint32_t mipLevels = 1;
uint32_t arraySize = 1;
Format format = Format::Unknown;
bool renderTarget = false;
bool depthStencil = false;
};
class ITexture2D : public IResource {
public:
virtual ~ITexture2D() = default;
virtual const TextureDesc& GetDesc() const = 0;
virtual void CreateSRV() = 0;
virtual void* GetSRV() = 0;
virtual void CreateRTV() = 0;
virtual void* GetRTV() = 0;
virtual void CreateDSV() = 0;
virtual void* GetDSV() = 0;
};
class ITextureCube : public IResource {
public:
virtual ~ITextureCube() = default;
virtual const TextureDesc& GetDesc() const = 0;
};
class ISampler {
public:
virtual ~ISampler() = default;
virtual void* GetNativeSampler() const = 0;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,20 @@
#pragma once
#include <RHI\RHIDefines.h>
#include <RHI\IRHIDevice.h>
namespace XCEngine {
namespace RHI {
class IShader {
public:
virtual ~IShader() = default;
virtual const ShaderBytecode& GetBytecode() const = 0;
virtual const char* GetEntryPoint() const = 0;
virtual const char* GetTarget() const = 0;
};
bool CompileShader(const char* filePath, const char* entryPoint, const char* target, ShaderBytecode& outBytecode);
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,41 @@
#pragma once
#include <cstdint>
#include <string>
#include <unordered_map>
#include <Rendering\Resources.h>
namespace XCEngine {
namespace RHI {
struct MeshVertex {
float position[4];
float texcoord[4];
float normal[4];
float tangent[4];
};
struct SubMesh {
IIndexBuffer* indexBuffer = nullptr;
uint32_t indexCount = 0;
};
class StaticMeshComponent {
public:
StaticMeshComponent();
~StaticMeshComponent();
bool Initialize(ICommandList* commandList, const char* filePath);
void Render(ICommandList* commandList);
IVertexBuffer* GetVertexBuffer() { return m_vertexBuffer; }
uint32_t GetVertexCount() const { return m_vertexCount; }
private:
IVertexBuffer* m_vertexBuffer = nullptr;
uint32_t m_vertexCount = 0;
std::unordered_map<std::string, SubMesh*> m_subMeshes;
};
} // namespace RHI
} // namespace XCEngine