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:
@@ -80,8 +80,25 @@ add_library(XCEngine STATIC
|
||||
src/Debug/FileLogSink.cpp
|
||||
src/Debug/Logger.cpp
|
||||
src/Debug/Profiler.cpp
|
||||
|
||||
# RHI
|
||||
include/XCEngine/RHI/RHIDefines.h
|
||||
include/XCEngine/RHI/IRHIResources.h
|
||||
include/XCEngine/RHI/ICommandList.h
|
||||
include/XCEngine/RHI/IRHIDevice.h
|
||||
include/XCEngine/RHI/IResourceViews.h
|
||||
include/XCEngine/RHI/RHISystem.h
|
||||
|
||||
# Rendering
|
||||
include/XCEngine/Rendering/Resources.h
|
||||
include/XCEngine/Rendering/RenderTarget.h
|
||||
include/XCEngine/Rendering/Shader.h
|
||||
include/XCEngine/Rendering/RenderContext.h
|
||||
include/XCEngine/Rendering/StaticMeshComponent.h
|
||||
)
|
||||
|
||||
add_subdirectory(src/RHI/D3D12)
|
||||
|
||||
target_include_directories(XCEngine PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine
|
||||
|
||||
44
engine/include/XCEngine/RHI/ICommandList.h
Normal file
44
engine/include/XCEngine/RHI/ICommandList.h
Normal 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
|
||||
161
engine/include/XCEngine/RHI/IRHIDevice.h
Normal file
161
engine/include/XCEngine/RHI/IRHIDevice.h
Normal 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
|
||||
97
engine/include/XCEngine/RHI/IRHIResources.h
Normal file
97
engine/include/XCEngine/RHI/IRHIResources.h
Normal 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
|
||||
27
engine/include/XCEngine/RHI/IResourceViews.h
Normal file
27
engine/include/XCEngine/RHI/IResourceViews.h
Normal 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
|
||||
226
engine/include/XCEngine/RHI/RHIDefines.h
Normal file
226
engine/include/XCEngine/RHI/RHIDefines.h
Normal 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
|
||||
28
engine/include/XCEngine/RHI/RHISystem.h
Normal file
28
engine/include/XCEngine/RHI/RHISystem.h
Normal 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
|
||||
46
engine/include/XCEngine/Rendering/RenderContext.h
Normal file
46
engine/include/XCEngine/Rendering/RenderContext.h
Normal 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
|
||||
26
engine/include/XCEngine/Rendering/RenderTarget.h
Normal file
26
engine/include/XCEngine/Rendering/RenderTarget.h
Normal 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
|
||||
74
engine/include/XCEngine/Rendering/Resources.h
Normal file
74
engine/include/XCEngine/Rendering/Resources.h
Normal 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
|
||||
20
engine/include/XCEngine/Rendering/Shader.h
Normal file
20
engine/include/XCEngine/Rendering/Shader.h
Normal 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
|
||||
41
engine/include/XCEngine/Rendering/StaticMeshComponent.h
Normal file
41
engine/include/XCEngine/Rendering/StaticMeshComponent.h
Normal 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
|
||||
24
engine/src/RHI/D3D12/CMakeLists.txt
Normal file
24
engine/src/RHI/D3D12/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
set(D3D12_SOURCES
|
||||
D3D12Device.cpp
|
||||
D3D12Resources.cpp
|
||||
D3D12RenderTarget.cpp
|
||||
D3D12Shader.cpp
|
||||
D3D12RenderContext.cpp
|
||||
D3D12StaticMeshComponent.cpp
|
||||
)
|
||||
|
||||
add_library(XCEngineD3D12 STATIC ${D3D12_SOURCES})
|
||||
|
||||
target_include_directories(XCEngineD3D12 PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/engine/include/XCEngine
|
||||
${CMAKE_SOURCE_DIR}/engine/src
|
||||
)
|
||||
|
||||
target_link_libraries(XCEngineD3D12 PUBLIC
|
||||
dxguid
|
||||
d3d12
|
||||
d3dcompiler
|
||||
)
|
||||
860
engine/src/RHI/D3D12/D3D12Device.cpp
Normal file
860
engine/src/RHI/D3D12/D3D12Device.cpp
Normal file
@@ -0,0 +1,860 @@
|
||||
#include "D3D12RHI.h"
|
||||
#include <RHI\RHIDefines.h>
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
void EngineLog(const char* msg, ...) {
|
||||
char buffer[1024];
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
vsnprintf(buffer, sizeof(buffer), msg, args);
|
||||
va_end(args);
|
||||
|
||||
FILE* f = nullptr;
|
||||
fopen_s(&f, "D:\\xcengine_debug.log", "a");
|
||||
if (f) { fprintf(f, "%s\n", buffer); fclose(f); }
|
||||
|
||||
printf("%s\n", buffer);
|
||||
OutputDebugStringA(buffer);
|
||||
OutputDebugStringA("\n");
|
||||
}
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
|
||||
D3D12Device::D3D12Device() {
|
||||
}
|
||||
|
||||
D3D12Device::~D3D12Device() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
bool D3D12Device::CreateDXGIDevice(void* windowHandle) {
|
||||
|
||||
UINT dxgiFactoryFlags = 0;
|
||||
#ifdef _DEBUG
|
||||
ID3D12Debug* debugController = nullptr;
|
||||
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) {
|
||||
debugController->EnableDebugLayer();
|
||||
dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
|
||||
}
|
||||
#endif
|
||||
|
||||
HRESULT hr = CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&m_dxgiFactory));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;
|
||||
int adapterIndex = 0;
|
||||
bool adapterFound = false;
|
||||
|
||||
while (m_dxgiFactory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND) {
|
||||
DXGI_ADAPTER_DESC1 desc;
|
||||
adapter->GetDesc1(&desc);
|
||||
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
|
||||
adapterIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
hr = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), nullptr);
|
||||
if (SUCCEEDED(hr)) {
|
||||
adapterFound = true;
|
||||
break;
|
||||
}
|
||||
adapterIndex++;
|
||||
}
|
||||
|
||||
if (!adapterFound) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_adapter = adapter;
|
||||
|
||||
hr = D3D12CreateDevice(m_adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D12Device::Initialize(void* windowHandle, uint32_t width, uint32_t height) {
|
||||
if (!CreateDXGIDevice(windowHandle)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
|
||||
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||
queueDesc.Priority = 0;
|
||||
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D12CommandQueue> commandQueue;
|
||||
HRESULT hr = m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_commandQueue = new D3D12CommandQueue(this, commandQueue.Get());
|
||||
|
||||
SwapChainDesc swapChainDesc = {};
|
||||
swapChainDesc.width = width;
|
||||
swapChainDesc.height = height;
|
||||
swapChainDesc.format = Format::R8G8B8A8_UNorm;
|
||||
swapChainDesc.bufferCount = 2;
|
||||
swapChainDesc.vsync = false;
|
||||
swapChainDesc.fullscreen = false;
|
||||
|
||||
ISwapChain* swapChain = nullptr;
|
||||
if (!CreateSwapChain(&swapChain, swapChainDesc, windowHandle)) {
|
||||
return false;
|
||||
}
|
||||
m_swapChain = static_cast<D3D12SwapChain*>(swapChain);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D12Device::Shutdown() {
|
||||
if (m_commandQueue) {
|
||||
delete m_commandQueue;
|
||||
m_commandQueue = nullptr;
|
||||
}
|
||||
if (m_swapChain) {
|
||||
delete m_swapChain;
|
||||
m_swapChain = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool D3D12Device::CreateCommandQueue(ICommandQueue** queue, const CommandQueueDesc& desc) {
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
|
||||
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||
queueDesc.Priority = desc.priority;
|
||||
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D12CommandQueue> commandQueue;
|
||||
HRESULT hr = m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*queue = new D3D12CommandQueue(this, commandQueue.Get());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D12Device::CreateCommandAllocator(ICommandAllocator** allocator) {
|
||||
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> commandAllocator;
|
||||
HRESULT hr = m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocator));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*allocator = new D3D12CommandAllocator(this, commandAllocator.Get());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D12Device::CreateCommandList(ICommandList** list, ICommandAllocator* allocator) {
|
||||
D3D12CommandAllocator* d3d12Allocator = static_cast<D3D12CommandAllocator*>(allocator);
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> commandList;
|
||||
HRESULT hr = m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||
d3d12Allocator->GetNativeAllocator(), nullptr, IID_PPV_ARGS(&commandList));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*list = new D3D12CommandList(this, commandList.Get());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D12Device::CreateFence(IFence** fence) {
|
||||
Microsoft::WRL::ComPtr<ID3D12Fence> d3dFence;
|
||||
HRESULT hr = m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&d3dFence));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*fence = new D3D12Fence(this, d3dFence.Get());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D12Device::CreateDescriptorHeap(IDescriptorHeap** heap, const DescriptorHeapDesc& desc) {
|
||||
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
|
||||
heapDesc.NumDescriptors = desc.count;
|
||||
heapDesc.Flags = desc.shaderVisible ? D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE : D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
|
||||
|
||||
switch (desc.type) {
|
||||
case DescriptorHeapType::CBV_SRV_UAV:
|
||||
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
|
||||
break;
|
||||
case DescriptorHeapType::Sampler:
|
||||
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
|
||||
break;
|
||||
case DescriptorHeapType::RTV:
|
||||
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
|
||||
break;
|
||||
case DescriptorHeapType::DSV:
|
||||
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
|
||||
break;
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> descriptorHeap;
|
||||
HRESULT hr = m_device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&descriptorHeap));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*heap = new D3D12DescriptorHeap(this, descriptorHeap.Get(), desc.type);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D12Device::CreateRootSignature(IRootSignature** signature, const RootSignatureDesc& desc) {
|
||||
*signature = new D3D12RootSignature(this);
|
||||
return (*signature)->Initialize(desc);
|
||||
}
|
||||
|
||||
bool D3D12Device::CreatePipelineState(IPipelineState** pso, const PipelineDesc& desc) {
|
||||
*pso = new D3D12PipelineState(this);
|
||||
return static_cast<D3D12PipelineState*>(*pso)->Initialize(desc);
|
||||
}
|
||||
|
||||
bool D3D12Device::CreateSwapChain(ISwapChain** swapChain, const SwapChainDesc& desc, void* windowHandle) {
|
||||
*swapChain = new D3D12SwapChain(this);
|
||||
bool result = (*swapChain)->Initialize(desc, windowHandle);
|
||||
return result;
|
||||
}
|
||||
|
||||
D3D12CommandQueue::D3D12CommandQueue(D3D12Device* device, ID3D12CommandQueue* queue)
|
||||
: m_device(device), m_commandQueue(queue) {
|
||||
}
|
||||
|
||||
D3D12CommandQueue::~D3D12CommandQueue() {
|
||||
}
|
||||
|
||||
void D3D12CommandQueue::ExecuteCommandLists(void** lists, uint32_t count) {
|
||||
ID3D12CommandList** d3dLists = reinterpret_cast<ID3D12CommandList**>(lists);
|
||||
m_commandQueue->ExecuteCommandLists(count, d3dLists);
|
||||
}
|
||||
|
||||
void D3D12CommandQueue::Signal(void* fence, uint64_t value) {
|
||||
ID3D12Fence* d3dFence = static_cast<ID3D12Fence*>(fence);
|
||||
m_commandQueue->Signal(d3dFence, value);
|
||||
}
|
||||
|
||||
void D3D12CommandQueue::Wait(void* fence, uint64_t value) {
|
||||
ID3D12Fence* d3dFence = static_cast<ID3D12Fence*>(fence);
|
||||
m_commandQueue->Wait(d3dFence, value);
|
||||
}
|
||||
|
||||
uint64_t D3D12CommandQueue::GetTimestampFrequency() const {
|
||||
uint64_t frequency = 0;
|
||||
m_commandQueue->GetTimestampFrequency(&frequency);
|
||||
return frequency;
|
||||
}
|
||||
|
||||
D3D12CommandAllocator::D3D12CommandAllocator(D3D12Device* device, ID3D12CommandAllocator* allocator)
|
||||
: m_device(device), m_commandAllocator(allocator) {
|
||||
}
|
||||
|
||||
D3D12CommandAllocator::~D3D12CommandAllocator() {
|
||||
}
|
||||
|
||||
void D3D12CommandAllocator::Reset() {
|
||||
m_commandAllocator->Reset();
|
||||
}
|
||||
|
||||
D3D12Fence::D3D12Fence(D3D12Device* device, ID3D12Fence* fence)
|
||||
: m_device(device), m_fence(fence) {
|
||||
m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
}
|
||||
|
||||
D3D12Fence::~D3D12Fence() {
|
||||
if (m_fenceEvent) {
|
||||
CloseHandle(m_fenceEvent);
|
||||
m_fenceEvent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t D3D12Fence::GetCompletedValue() const {
|
||||
return m_fence->GetCompletedValue();
|
||||
}
|
||||
|
||||
void D3D12Fence::Signal(uint64_t value) {
|
||||
m_fence->Signal(value);
|
||||
}
|
||||
|
||||
void D3D12Fence::Wait(uint64_t value) {
|
||||
if (m_fence->GetCompletedValue() < value) {
|
||||
m_fence->SetEventOnCompletion(value, m_fenceEvent);
|
||||
WaitForSingleObject(m_fenceEvent, INFINITE);
|
||||
}
|
||||
}
|
||||
|
||||
void D3D12Fence::Wait(uint64_t value, uint64_t timeoutMs) {
|
||||
if (m_fence->GetCompletedValue() < value) {
|
||||
m_fence->SetEventOnCompletion(value, m_fenceEvent);
|
||||
WaitForSingleObject(m_fenceEvent, (DWORD)timeoutMs);
|
||||
}
|
||||
}
|
||||
|
||||
D3D12DescriptorHeap::D3D12DescriptorHeap(D3D12Device* device, ID3D12DescriptorHeap* heap, DescriptorHeapType type)
|
||||
: m_device(device), m_descriptorHeap(heap), m_type(type) {
|
||||
m_descriptorCount = heap->GetDesc().NumDescriptors;
|
||||
m_descriptorSize = device->GetD3D12Device()->GetDescriptorHandleIncrementSize(
|
||||
type == DescriptorHeapType::CBV_SRV_UAV ? D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV :
|
||||
type == DescriptorHeapType::Sampler ? D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER :
|
||||
type == DescriptorHeapType::RTV ? D3D12_DESCRIPTOR_HEAP_TYPE_RTV : D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
|
||||
}
|
||||
|
||||
D3D12DescriptorHeap::~D3D12DescriptorHeap() {
|
||||
}
|
||||
|
||||
void* D3D12DescriptorHeap::GetCPUDescriptorHandle(uint32_t index) const {
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE handle = m_descriptorHeap->GetCPUDescriptorHandleForHeapStart();
|
||||
handle.ptr += index * m_descriptorSize;
|
||||
return (void*)handle.ptr;
|
||||
}
|
||||
|
||||
uint64_t D3D12DescriptorHeap::GetGPUDescriptorHandle(uint32_t index) const {
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE handle = m_descriptorHeap->GetGPUDescriptorHandleForHeapStart();
|
||||
handle.ptr += index * m_descriptorSize;
|
||||
return handle.ptr;
|
||||
}
|
||||
|
||||
void D3D12DescriptorHeap::SetName(const char* name) {
|
||||
if (name) {
|
||||
m_descriptorHeap->SetName(std::wstring(name, name + strlen(name)).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
D3D12SwapChain::D3D12SwapChain(D3D12Device* device)
|
||||
: m_device(device) {
|
||||
}
|
||||
|
||||
D3D12SwapChain::~D3D12SwapChain() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
bool D3D12SwapChain::Initialize(const SwapChainDesc& desc, void* windowHandle) {
|
||||
m_bufferCount = desc.bufferCount;
|
||||
m_vsync = desc.vsync;
|
||||
m_fullscreen = desc.fullscreen;
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
|
||||
swapChainDesc.BufferCount = desc.bufferCount;
|
||||
swapChainDesc.BufferDesc.Width = desc.width;
|
||||
swapChainDesc.BufferDesc.Height = desc.height;
|
||||
swapChainDesc.BufferDesc.Format = FormatToDXGIFormat(desc.format);
|
||||
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
swapChainDesc.OutputWindow = (HWND)windowHandle;
|
||||
swapChainDesc.SampleDesc.Count = 1;
|
||||
swapChainDesc.Windowed = !desc.fullscreen;
|
||||
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||
|
||||
Microsoft::WRL::ComPtr<IDXGISwapChain> swapChain;
|
||||
ID3D12CommandQueue* cmdQueue = (ID3D12CommandQueue*)m_device->GetCommandQueue()->GetNativeQueue();
|
||||
HRESULT hr = m_device->GetDXGIFactory()->CreateSwapChain(
|
||||
cmdQueue, &swapChainDesc, &swapChain);
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = swapChain.As(&m_swapChain);
|
||||
|
||||
DescriptorHeapDesc rtvDesc = {};
|
||||
rtvDesc.type = DescriptorHeapType::RTV;
|
||||
rtvDesc.count = desc.bufferCount;
|
||||
rtvDesc.shaderVisible = false;
|
||||
|
||||
if (!m_device->CreateDescriptorHeap((IDescriptorHeap**)&m_rtvHeap, rtvDesc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE rtvHeapStart;
|
||||
rtvHeapStart.ptr = (UINT64)m_rtvHeap->GetCPUDescriptorHandle(0);
|
||||
|
||||
UINT rtvDescriptorSize = m_device->GetD3D12Device()->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
|
||||
|
||||
|
||||
for (uint32_t i = 0; i < desc.bufferCount; i++) {
|
||||
hr = m_swapChain->GetBuffer(i, IID_PPV_ARGS(&m_buffers[i]));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE rtvPointer;
|
||||
rtvPointer.ptr = rtvHeapStart.ptr + i * rtvDescriptorSize;
|
||||
m_device->GetD3D12Device()->CreateRenderTargetView(m_buffers[i].Get(), nullptr, rtvPointer);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D12SwapChain::Shutdown() {
|
||||
m_buffers[0].Reset();
|
||||
if (m_rtvHeap) {
|
||||
delete m_rtvHeap;
|
||||
m_rtvHeap = nullptr;
|
||||
}
|
||||
m_swapChain.Reset();
|
||||
}
|
||||
|
||||
bool D3D12SwapChain::Present() {
|
||||
HRESULT hr = m_swapChain->Present(m_vsync ? 1 : 0, 0);
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
bool D3D12SwapChain::Resize(uint32_t width, uint32_t height) {
|
||||
for (uint32_t i = 0; i < m_bufferCount; i++) {
|
||||
m_buffers[i].Reset();
|
||||
}
|
||||
|
||||
HRESULT hr = m_swapChain->ResizeBuffers(m_bufferCount, width, height, DXGI_FORMAT_UNKNOWN, 0);
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = *(D3D12_CPU_DESCRIPTOR_HANDLE*)m_rtvHeap->GetCPUDescriptorHandle(0);
|
||||
for (uint32_t i = 0; i < m_bufferCount; i++) {
|
||||
hr = m_swapChain->GetBuffer(i, IID_PPV_ARGS(&m_buffers[i]));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_device->GetD3D12Device()->CreateRenderTargetView(m_buffers[i].Get(), nullptr, rtvHandle);
|
||||
rtvHandle.ptr += m_rtvHeap->GetDescriptorSize();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t D3D12SwapChain::GetCurrentBufferIndex() const {
|
||||
return m_swapChain->GetCurrentBackBufferIndex();
|
||||
}
|
||||
|
||||
void* D3D12SwapChain::GetBuffer(uint32_t index) {
|
||||
return GetBufferResource(index);
|
||||
}
|
||||
|
||||
void D3D12SwapChain::SetFullscreen(bool fullscreen) {
|
||||
m_fullscreen = fullscreen;
|
||||
m_swapChain->SetFullscreenState(fullscreen, nullptr);
|
||||
}
|
||||
|
||||
bool D3D12SwapChain::IsFullscreen() const {
|
||||
return m_fullscreen;
|
||||
}
|
||||
|
||||
D3D12RootSignature::D3D12RootSignature(D3D12Device* device)
|
||||
: m_device(device) {
|
||||
}
|
||||
|
||||
D3D12RootSignature::~D3D12RootSignature() {
|
||||
}
|
||||
|
||||
bool D3D12RootSignature::Initialize(const RootSignatureDesc& desc) {
|
||||
EngineLog("RootSignature: Start init, paramCount=%u", desc.parameterCount);
|
||||
D3D12_ROOT_PARAMETER rootParameters[16] = {};
|
||||
D3D12_DESCRIPTOR_RANGE descriptorRanges[16] = {};
|
||||
|
||||
for (uint32_t i = 0; i < desc.parameterCount && i < 16; i++) {
|
||||
const RootParameter& param = desc.parameters[i];
|
||||
|
||||
switch (param.type) {
|
||||
case RootParameterType::CBV:
|
||||
rootParameters[i].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
|
||||
rootParameters[i].ShaderVisibility = (D3D12_SHADER_VISIBILITY)param.visibility;
|
||||
rootParameters[i].Descriptor.RegisterSpace = param.registerSpace;
|
||||
rootParameters[i].Descriptor.ShaderRegister = param.shaderRegister;
|
||||
break;
|
||||
case RootParameterType::SRV:
|
||||
rootParameters[i].ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
|
||||
rootParameters[i].ShaderVisibility = (D3D12_SHADER_VISIBILITY)param.visibility;
|
||||
rootParameters[i].Descriptor.RegisterSpace = param.registerSpace;
|
||||
rootParameters[i].Descriptor.ShaderRegister = param.shaderRegister;
|
||||
break;
|
||||
case RootParameterType::Constants:
|
||||
rootParameters[i].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
|
||||
rootParameters[i].ShaderVisibility = (D3D12_SHADER_VISIBILITY)param.visibility;
|
||||
rootParameters[i].Constants.RegisterSpace = param.registerSpace;
|
||||
rootParameters[i].Constants.ShaderRegister = param.shaderRegister;
|
||||
rootParameters[i].Constants.Num32BitValues = param.num32BitValues;
|
||||
break;
|
||||
case RootParameterType::DescriptorTable:
|
||||
rootParameters[i].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
||||
rootParameters[i].ShaderVisibility = (D3D12_SHADER_VISIBILITY)param.visibility;
|
||||
rootParameters[i].DescriptorTable.pDescriptorRanges = &descriptorRanges[i];
|
||||
rootParameters[i].DescriptorTable.NumDescriptorRanges = 1;
|
||||
|
||||
descriptorRanges[i].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
||||
descriptorRanges[i].RegisterSpace = param.registerSpace;
|
||||
descriptorRanges[i].BaseShaderRegister = 0;
|
||||
descriptorRanges[i].NumDescriptors = 1;
|
||||
descriptorRanges[i].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
break;
|
||||
default:
|
||||
rootParameters[i].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
break;
|
||||
}
|
||||
EngineLog("RootParam[%d]: type=%d, visibility=%d, reg=%d, space=%d",
|
||||
i, (int)param.type, (int)param.visibility, param.shaderRegister, param.registerSpace);
|
||||
}
|
||||
|
||||
D3D12_STATIC_SAMPLER_DESC samplerDesc = {};
|
||||
samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
samplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
samplerDesc.MaxLOD = D3D12_FLOAT32_MAX;
|
||||
samplerDesc.RegisterSpace = 0;
|
||||
samplerDesc.ShaderRegister = 0;
|
||||
samplerDesc.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
||||
|
||||
D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {};
|
||||
rootSignatureDesc.NumParameters = desc.parameterCount;
|
||||
rootSignatureDesc.pParameters = rootParameters;
|
||||
rootSignatureDesc.NumStaticSamplers = 1;
|
||||
rootSignatureDesc.pStaticSamplers = &samplerDesc;
|
||||
rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3DBlob> signature;
|
||||
Microsoft::WRL::ComPtr<ID3DBlob> errorBlob;
|
||||
HRESULT hr = D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &errorBlob);
|
||||
if (FAILED(hr)) {
|
||||
if (errorBlob) {
|
||||
OutputDebugStringA((const char*)errorBlob->GetBufferPointer());
|
||||
}
|
||||
EngineLog("RootSignature: Serialize FAILED");
|
||||
return false;
|
||||
}
|
||||
|
||||
EngineLog("RootSignature: Serialize SUCCESS, size=%llu", (unsigned long long)signature->GetBufferSize());
|
||||
|
||||
hr = m_device->GetD3D12Device()->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(),
|
||||
IID_PPV_ARGS(&m_rootSignature));
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
EngineLog("RootSignature: SUCCESS");
|
||||
} else {
|
||||
EngineLog("RootSignature: FAILED");
|
||||
}
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
void D3D12RootSignature::SetName(const char* name) {
|
||||
if (name && m_rootSignature) {
|
||||
m_rootSignature->SetName(std::wstring(name, name + strlen(name)).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void* D3D12RootSignature::GetNativeRootSignature() const {
|
||||
return m_rootSignature.Get();
|
||||
}
|
||||
|
||||
D3D12PipelineState::D3D12PipelineState(D3D12Device* device)
|
||||
: m_device(device) {
|
||||
}
|
||||
|
||||
D3D12PipelineState::~D3D12PipelineState() {
|
||||
}
|
||||
|
||||
bool D3D12PipelineState::Initialize(const PipelineDesc& desc) {
|
||||
EngineLog("PipelineState: Start init");
|
||||
|
||||
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
|
||||
|
||||
if (desc.rootSignature) {
|
||||
EngineLog("PipelineState: has rootSignature");
|
||||
D3D12RootSignature* rs = static_cast<D3D12RootSignature*>(desc.rootSignature);
|
||||
psoDesc.pRootSignature = rs->GetNative();
|
||||
EngineLog("PipelineState: rootSignature ptr=%p", psoDesc.pRootSignature);
|
||||
} else {
|
||||
EngineLog("PipelineState: NO rootSignature!");
|
||||
}
|
||||
|
||||
if (desc.vertexShader.size > 0 && desc.vertexShader.bytecode != nullptr) {
|
||||
psoDesc.VS.pShaderBytecode = desc.vertexShader.bytecode;
|
||||
psoDesc.VS.BytecodeLength = desc.vertexShader.size;
|
||||
}
|
||||
if (desc.pixelShader.size > 0 && desc.pixelShader.bytecode != nullptr) {
|
||||
psoDesc.PS.pShaderBytecode = desc.pixelShader.bytecode;
|
||||
psoDesc.PS.BytecodeLength = desc.pixelShader.size;
|
||||
}
|
||||
if (desc.geometryShader.size > 0 && desc.geometryShader.bytecode != nullptr) {
|
||||
psoDesc.GS.pShaderBytecode = desc.geometryShader.bytecode;
|
||||
psoDesc.GS.BytecodeLength = desc.geometryShader.size;
|
||||
}
|
||||
|
||||
D3D12_INPUT_ELEMENT_DESC d3d12Elements[16] = {};
|
||||
for (uint32_t i = 0; i < desc.inputLayout.elementCount && i < 16; i++) {
|
||||
const InputElementDesc& src = desc.inputLayout.elements[i];
|
||||
d3d12Elements[i].SemanticName = src.semanticName;
|
||||
d3d12Elements[i].SemanticIndex = src.semanticIndex;
|
||||
d3d12Elements[i].Format = FormatToDXGIFormat(src.format);
|
||||
d3d12Elements[i].InputSlot = src.inputSlot;
|
||||
d3d12Elements[i].AlignedByteOffset = src.alignedByteOffset;
|
||||
d3d12Elements[i].InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
|
||||
d3d12Elements[i].InstanceDataStepRate = 0;
|
||||
}
|
||||
psoDesc.InputLayout.NumElements = desc.inputLayout.elementCount;
|
||||
psoDesc.InputLayout.pInputElementDescs = d3d12Elements;
|
||||
|
||||
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
|
||||
psoDesc.NumRenderTargets = 1;
|
||||
psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
|
||||
psoDesc.SampleDesc.Count = 1;
|
||||
psoDesc.SampleDesc.Quality = 0;
|
||||
psoDesc.SampleMask = 0xffffffff;
|
||||
|
||||
psoDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
|
||||
psoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_BACK;
|
||||
psoDesc.RasterizerState.DepthClipEnable = TRUE;
|
||||
|
||||
psoDesc.DepthStencilState.DepthEnable = TRUE;
|
||||
psoDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
|
||||
psoDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
|
||||
|
||||
psoDesc.BlendState = { 0 };
|
||||
D3D12_RENDER_TARGET_BLEND_DESC rtBlendDesc = {
|
||||
FALSE,FALSE,
|
||||
D3D12_BLEND_SRC_ALPHA,D3D12_BLEND_INV_SRC_ALPHA,D3D12_BLEND_OP_ADD,
|
||||
D3D12_BLEND_SRC_ALPHA,D3D12_BLEND_INV_SRC_ALPHA,D3D12_BLEND_OP_ADD,
|
||||
D3D12_LOGIC_OP_NOOP,
|
||||
D3D12_COLOR_WRITE_ENABLE_ALL,
|
||||
};
|
||||
for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
|
||||
psoDesc.BlendState.RenderTarget[i] = rtBlendDesc;
|
||||
|
||||
EngineLog("PSO: VS=%p(%u), PS=%p(%u), RTV=%d, DSV=%d",
|
||||
psoDesc.VS.pShaderBytecode, psoDesc.VS.BytecodeLength,
|
||||
psoDesc.PS.pShaderBytecode, psoDesc.PS.BytecodeLength,
|
||||
psoDesc.RTVFormats[0], psoDesc.DSVFormat);
|
||||
|
||||
HRESULT hr = m_device->GetD3D12Device()->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pipelineState));
|
||||
if (FAILED(hr)) {
|
||||
EngineLog("PipelineState: FAILED with HRESULT=%08lx", (unsigned long)hr);
|
||||
return false;
|
||||
}
|
||||
EngineLog("PipelineState: SUCCESS");
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D12PipelineState::SetName(const char* name) {
|
||||
if (name && m_pipelineState) {
|
||||
m_pipelineState->SetName(std::wstring(name, name + strlen(name)).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void* D3D12PipelineState::GetNativePipelineState() const {
|
||||
return m_pipelineState.Get();
|
||||
}
|
||||
|
||||
D3D12CommandList::D3D12CommandList(D3D12Device* device, ID3D12GraphicsCommandList* list)
|
||||
: m_device(device), m_commandList(list) {
|
||||
}
|
||||
|
||||
D3D12CommandList::~D3D12CommandList() {
|
||||
}
|
||||
|
||||
void D3D12CommandList::Reset(void* allocator) {
|
||||
ID3D12CommandAllocator* d3dAllocator = static_cast<ID3D12CommandAllocator*>(allocator);
|
||||
m_commandList->Reset(d3dAllocator, nullptr);
|
||||
}
|
||||
|
||||
void D3D12CommandList::Close() {
|
||||
m_commandList->Close();
|
||||
}
|
||||
|
||||
void D3D12CommandList::SetPipelineState(IPipelineState* pso) {
|
||||
if (pso) {
|
||||
D3D12PipelineState* d3dPso = static_cast<D3D12PipelineState*>(pso);
|
||||
m_commandList->SetPipelineState(d3dPso->GetNative());
|
||||
}
|
||||
}
|
||||
|
||||
void D3D12CommandList::SetRootSignature(IRootSignature* signature) {
|
||||
if (signature) {
|
||||
D3D12RootSignature* d3dRs = static_cast<D3D12RootSignature*>(signature);
|
||||
m_commandList->SetGraphicsRootSignature(d3dRs->GetNative());
|
||||
}
|
||||
}
|
||||
|
||||
void D3D12CommandList::SetPrimitiveTopology(PrimitiveTopology topology) {
|
||||
D3D12_PRIMITIVE_TOPOLOGY d3dTopology;
|
||||
switch (topology) {
|
||||
case PrimitiveTopology::TriangleList:
|
||||
d3dTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
|
||||
break;
|
||||
case PrimitiveTopology::TriangleStrip:
|
||||
d3dTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
|
||||
break;
|
||||
case PrimitiveTopology::LineList:
|
||||
d3dTopology = D3D_PRIMITIVE_TOPOLOGY_LINELIST;
|
||||
break;
|
||||
default:
|
||||
d3dTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
|
||||
break;
|
||||
}
|
||||
m_commandList->IASetPrimitiveTopology(d3dTopology);
|
||||
}
|
||||
|
||||
void D3D12CommandList::SetVertexBuffer(uint32_t slot, IResource* buffer, uint32_t offset, uint32_t stride) {
|
||||
if (buffer) {
|
||||
D3D12Resource* d3dBuffer = static_cast<D3D12Resource*>(buffer);
|
||||
D3D12_VERTEX_BUFFER_VIEW vbView = {};
|
||||
vbView.BufferLocation = d3dBuffer->GetNative()->GetGPUVirtualAddress() + offset;
|
||||
vbView.SizeInBytes = (UINT)stride;
|
||||
vbView.StrideInBytes = stride;
|
||||
m_commandList->IASetVertexBuffers(slot, 1, &vbView);
|
||||
}
|
||||
}
|
||||
|
||||
void D3D12CommandList::SetIndexBuffer(IResource* buffer, uint32_t offset) {
|
||||
if (buffer) {
|
||||
D3D12Resource* d3dBuffer = static_cast<D3D12Resource*>(buffer);
|
||||
D3D12_INDEX_BUFFER_VIEW ibView = {};
|
||||
ibView.BufferLocation = d3dBuffer->GetNative()->GetGPUVirtualAddress() + offset;
|
||||
ibView.Format = DXGI_FORMAT_R32_UINT;
|
||||
ibView.SizeInBytes = 0;
|
||||
m_commandList->IASetIndexBuffer(&ibView);
|
||||
}
|
||||
}
|
||||
|
||||
void D3D12CommandList::SetDescriptorHeap(IDescriptorHeap* heap) {
|
||||
if (heap) {
|
||||
D3D12DescriptorHeap* d3dHeap = static_cast<D3D12DescriptorHeap*>(heap);
|
||||
ID3D12DescriptorHeap* heaps[] = { d3dHeap->GetNativeHeap() };
|
||||
m_commandList->SetDescriptorHeaps(1, heaps);
|
||||
}
|
||||
}
|
||||
|
||||
void D3D12CommandList::SetGraphicsDescriptorTable(uint32_t rootParameterIndex, uint64_t baseDescriptor) {
|
||||
m_commandList->SetGraphicsRootDescriptorTable(rootParameterIndex, D3D12_GPU_DESCRIPTOR_HANDLE{ baseDescriptor });
|
||||
}
|
||||
|
||||
void D3D12CommandList::SetComputeDescriptorTable(uint32_t rootParameterIndex, uint64_t baseDescriptor) {
|
||||
m_commandList->SetComputeRootDescriptorTable(rootParameterIndex, D3D12_GPU_DESCRIPTOR_HANDLE{ baseDescriptor });
|
||||
}
|
||||
|
||||
void D3D12CommandList::DrawInstanced(uint32_t vertexCountPerInstance, uint32_t instanceCount, uint32_t startVertex, uint32_t startInstance) {
|
||||
m_commandList->DrawInstanced(vertexCountPerInstance, instanceCount, startVertex, startInstance);
|
||||
}
|
||||
|
||||
void D3D12CommandList::DrawIndexedInstanced(uint32_t indexCountPerInstance, uint32_t instanceCount, uint32_t startIndex, int32_t baseVertex, uint32_t startInstance) {
|
||||
m_commandList->DrawIndexedInstanced(indexCountPerInstance, instanceCount, startIndex, baseVertex, startInstance);
|
||||
}
|
||||
|
||||
void D3D12CommandList::Dispatch(uint32_t x, uint32_t y, uint32_t z) {
|
||||
m_commandList->Dispatch(x, y, z);
|
||||
}
|
||||
|
||||
void D3D12CommandList::SetViewports(const Viewport* viewports, uint32_t count) {
|
||||
D3D12_VIEWPORT d3dViewports[16] = {};
|
||||
for (uint32_t i = 0; i < count && i < 16; i++) {
|
||||
d3dViewports[i].TopLeftX = viewports[i].topLeftX;
|
||||
d3dViewports[i].TopLeftY = viewports[i].topLeftY;
|
||||
d3dViewports[i].Width = viewports[i].width;
|
||||
d3dViewports[i].Height = viewports[i].height;
|
||||
d3dViewports[i].MinDepth = viewports[i].minDepth;
|
||||
d3dViewports[i].MaxDepth = viewports[i].maxDepth;
|
||||
}
|
||||
m_commandList->RSSetViewports(count, d3dViewports);
|
||||
}
|
||||
|
||||
void D3D12CommandList::SetScissorRects(const Rect* rects, uint32_t count) {
|
||||
D3D12_RECT d3dRects[16] = {};
|
||||
for (uint32_t i = 0; i < count && i < 16; i++) {
|
||||
d3dRects[i].left = rects[i].left;
|
||||
d3dRects[i].top = rects[i].top;
|
||||
d3dRects[i].right = rects[i].right;
|
||||
d3dRects[i].bottom = rects[i].bottom;
|
||||
}
|
||||
m_commandList->RSSetScissorRects(count, d3dRects);
|
||||
}
|
||||
|
||||
void D3D12CommandList::SetRenderTargets(void** targets, uint32_t count, void* depthStencil) {
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandles[8] = {};
|
||||
for (uint32_t i = 0; i < count && i < 8; i++) {
|
||||
rtvHandles[i].ptr = (UINT64)targets[i];
|
||||
}
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = {};
|
||||
if (depthStencil) {
|
||||
dsvHandle.ptr = (UINT64)depthStencil;
|
||||
}
|
||||
|
||||
m_commandList->OMSetRenderTargets(count, rtvHandles, FALSE, depthStencil ? &dsvHandle : nullptr);
|
||||
}
|
||||
|
||||
void D3D12CommandList::ClearRenderTargetView(void* target, const float color[4]) {
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = { (UINT64)target };
|
||||
m_commandList->ClearRenderTargetView(rtvHandle, color, 0, nullptr);
|
||||
}
|
||||
|
||||
void D3D12CommandList::ClearDepthStencilView(void* depth, float depthValue, uint8_t stencil) {
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = { (UINT64)depth };
|
||||
m_commandList->ClearDepthStencilView(dsvHandle, D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, depthValue, stencil, 0, nullptr);
|
||||
}
|
||||
|
||||
void D3D12CommandList::CopyResource(IResource* dst, const IResource* src) {
|
||||
D3D12Resource* d3dDst = static_cast<D3D12Resource*>(dst);
|
||||
D3D12Resource* d3dSrc = static_cast<D3D12Resource*>(const_cast<IResource*>(src));
|
||||
m_commandList->CopyResource(d3dDst->GetNative(), d3dSrc->GetNative());
|
||||
}
|
||||
|
||||
void D3D12CommandList::CopyBuffer(IResource* dst, uint64_t dstOffset, const IResource* src, uint64_t srcOffset, uint64_t size) {
|
||||
D3D12Resource* d3dDst = static_cast<D3D12Resource*>(dst);
|
||||
D3D12Resource* d3dSrc = static_cast<D3D12Resource*>(const_cast<IResource*>(src));
|
||||
m_commandList->CopyBufferRegion(d3dDst->GetNative(), dstOffset, d3dSrc->GetNative(), srcOffset, size);
|
||||
}
|
||||
|
||||
void D3D12CommandList::ResourceBarrier(IResource* resource, ResourceStateFlag before, ResourceStateFlag after) {
|
||||
D3D12Resource* d3dResource = static_cast<D3D12Resource*>(resource);
|
||||
|
||||
D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_COMMON;
|
||||
D3D12_RESOURCE_STATES afterState = D3D12_RESOURCE_STATE_COMMON;
|
||||
|
||||
if (static_cast<uint32_t>(before) & static_cast<uint32_t>(ResourceStateFlag::RenderTarget)) beforeState = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
else if (static_cast<uint32_t>(before) & static_cast<uint32_t>(ResourceStateFlag::DepthWrite)) beforeState = D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
||||
else if (static_cast<uint32_t>(before) & static_cast<uint32_t>(ResourceStateFlag::DepthRead)) beforeState = D3D12_RESOURCE_STATE_DEPTH_READ;
|
||||
else if (static_cast<uint32_t>(before) & static_cast<uint32_t>(ResourceStateFlag::ShaderResource)) beforeState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
else if (static_cast<uint32_t>(before) & static_cast<uint32_t>(ResourceStateFlag::CopyDest)) beforeState = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
else if (static_cast<uint32_t>(before) & static_cast<uint32_t>(ResourceStateFlag::CopySource)) beforeState = D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||||
else if (static_cast<uint32_t>(before) & static_cast<uint32_t>(ResourceStateFlag::Present)) beforeState = D3D12_RESOURCE_STATE_PRESENT;
|
||||
|
||||
if (static_cast<uint32_t>(after) & static_cast<uint32_t>(ResourceStateFlag::RenderTarget)) afterState = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
else if (static_cast<uint32_t>(after) & static_cast<uint32_t>(ResourceStateFlag::DepthWrite)) afterState = D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
||||
else if (static_cast<uint32_t>(after) & static_cast<uint32_t>(ResourceStateFlag::DepthRead)) afterState = D3D12_RESOURCE_STATE_DEPTH_READ;
|
||||
else if (static_cast<uint32_t>(after) & static_cast<uint32_t>(ResourceStateFlag::ShaderResource)) afterState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
else if (static_cast<uint32_t>(after) & static_cast<uint32_t>(ResourceStateFlag::CopyDest)) afterState = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
else if (static_cast<uint32_t>(after) & static_cast<uint32_t>(ResourceStateFlag::CopySource)) afterState = D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||||
else if (static_cast<uint32_t>(after) & static_cast<uint32_t>(ResourceStateFlag::Present)) afterState = D3D12_RESOURCE_STATE_PRESENT;
|
||||
|
||||
D3D12_RESOURCE_BARRIER barrier = {};
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = d3dResource->GetNative();
|
||||
barrier.Transition.StateBefore = beforeState;
|
||||
barrier.Transition.StateAfter = afterState;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
|
||||
m_commandList->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
|
||||
void* D3D12CommandList::GetNativeCommandList() const {
|
||||
return m_commandList.Get();
|
||||
}
|
||||
|
||||
D3D12Resource::D3D12Resource(D3D12Device* device, ID3D12Resource* resource)
|
||||
: m_device(device), m_resource(resource) {
|
||||
}
|
||||
|
||||
D3D12Resource::~D3D12Resource() {
|
||||
m_resource.Reset();
|
||||
}
|
||||
|
||||
} // namespace RHI
|
||||
} // namespace XCEngine
|
||||
237
engine/src/RHI/D3D12/D3D12RHI.h
Normal file
237
engine/src/RHI/D3D12/D3D12RHI.h
Normal file
@@ -0,0 +1,237 @@
|
||||
#pragma once
|
||||
|
||||
#include <RHI\IRHIDevice.h>
|
||||
#include <RHI\IRHIResources.h>
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_4.h>
|
||||
#include <wrl\client.h>
|
||||
#include <windows.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
|
||||
class D3D12Device;
|
||||
|
||||
class D3D12CommandQueue : public ICommandQueue {
|
||||
public:
|
||||
D3D12CommandQueue(D3D12Device* device, ID3D12CommandQueue* queue);
|
||||
~D3D12CommandQueue() override;
|
||||
|
||||
void ExecuteCommandLists(void** lists, uint32_t count) override;
|
||||
void Signal(void* fence, uint64_t value) override;
|
||||
void Wait(void* fence, uint64_t value) override;
|
||||
uint64_t GetTimestampFrequency() const override;
|
||||
|
||||
ID3D12CommandQueue* GetNativeQueue() const { return m_commandQueue.Get(); }
|
||||
|
||||
private:
|
||||
D3D12Device* m_device = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D12CommandQueue> m_commandQueue;
|
||||
};
|
||||
|
||||
class D3D12CommandAllocator : public ICommandAllocator {
|
||||
public:
|
||||
D3D12CommandAllocator(D3D12Device* device, ID3D12CommandAllocator* allocator);
|
||||
~D3D12CommandAllocator() override;
|
||||
|
||||
void Reset() override;
|
||||
|
||||
ID3D12CommandAllocator* GetNativeAllocator() const { return m_commandAllocator.Get(); }
|
||||
|
||||
private:
|
||||
D3D12Device* m_device = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> m_commandAllocator;
|
||||
};
|
||||
|
||||
class D3D12Fence : public IFence {
|
||||
public:
|
||||
D3D12Fence(D3D12Device* device, ID3D12Fence* fence);
|
||||
~D3D12Fence() override;
|
||||
|
||||
uint64_t GetCompletedValue() const override;
|
||||
void Signal(uint64_t value) override;
|
||||
void Wait(uint64_t value) override;
|
||||
void Wait(uint64_t value, uint64_t timeoutMs) override;
|
||||
|
||||
ID3D12Fence* GetNativeFence() const { return m_fence.Get(); }
|
||||
|
||||
private:
|
||||
D3D12Device* m_device = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D12Fence> m_fence;
|
||||
HANDLE m_fenceEvent = nullptr;
|
||||
};
|
||||
|
||||
class D3D12DescriptorHeap : public IDescriptorHeap {
|
||||
public:
|
||||
D3D12DescriptorHeap(D3D12Device* device, ID3D12DescriptorHeap* heap, DescriptorHeapType type);
|
||||
~D3D12DescriptorHeap() override;
|
||||
|
||||
DescriptorHeapType GetType() const override { return m_type; }
|
||||
uint32_t GetDescriptorCount() const override { return m_descriptorCount; }
|
||||
void* GetCPUDescriptorHandle(uint32_t index) const override;
|
||||
uint64_t GetGPUDescriptorHandle(uint32_t index) const override;
|
||||
void SetName(const char* name) override;
|
||||
|
||||
ID3D12DescriptorHeap* GetNativeHeap() const { return m_descriptorHeap.Get(); }
|
||||
uint32_t GetDescriptorSize() const { return m_descriptorSize; }
|
||||
|
||||
private:
|
||||
D3D12Device* m_device = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> m_descriptorHeap;
|
||||
DescriptorHeapType m_type;
|
||||
uint32_t m_descriptorCount = 0;
|
||||
uint32_t m_descriptorSize = 0;
|
||||
};
|
||||
|
||||
class D3D12SwapChain : public ISwapChain {
|
||||
public:
|
||||
D3D12SwapChain(D3D12Device* device);
|
||||
~D3D12SwapChain() override;
|
||||
|
||||
bool Initialize(const SwapChainDesc& desc, void* windowHandle) override;
|
||||
void Shutdown() override;
|
||||
bool Present() override;
|
||||
bool Resize(uint32_t width, uint32_t height) override;
|
||||
uint32_t GetCurrentBufferIndex() const override;
|
||||
void* GetBuffer(uint32_t index) override;
|
||||
void SetFullscreen(bool fullscreen) override;
|
||||
bool IsFullscreen() const override;
|
||||
|
||||
IDXGISwapChain3* GetNativeSwapChain() const { return m_swapChain.Get(); }
|
||||
ID3D12Resource* GetBufferResource(uint32_t index) const { return m_buffers[index].Get(); }
|
||||
D3D12DescriptorHeap* GetRTVHeap() const { return m_rtvHeap; }
|
||||
|
||||
private:
|
||||
D3D12Device* m_device = nullptr;
|
||||
Microsoft::WRL::ComPtr<IDXGISwapChain3> m_swapChain;
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> m_buffers[16];
|
||||
D3D12DescriptorHeap* m_rtvHeap = nullptr;
|
||||
uint32_t m_bufferCount = 0;
|
||||
bool m_vsync = false;
|
||||
bool m_fullscreen = false;
|
||||
};
|
||||
|
||||
class D3D12RootSignature : public IRootSignature {
|
||||
public:
|
||||
D3D12RootSignature(D3D12Device* device);
|
||||
~D3D12RootSignature() override;
|
||||
|
||||
bool Initialize(const RootSignatureDesc& desc) override;
|
||||
void SetName(const char* name) override;
|
||||
void* GetNativeRootSignature() const override;
|
||||
|
||||
ID3D12RootSignature* GetNative() const { return m_rootSignature.Get(); }
|
||||
|
||||
private:
|
||||
D3D12Device* m_device = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D12RootSignature> m_rootSignature;
|
||||
};
|
||||
|
||||
class D3D12PipelineState : public IPipelineState {
|
||||
public:
|
||||
D3D12PipelineState(D3D12Device* device);
|
||||
~D3D12PipelineState() override;
|
||||
|
||||
bool Initialize(const PipelineDesc& desc);
|
||||
void SetName(const char* name) override;
|
||||
void* GetNativePipelineState() const override;
|
||||
|
||||
ID3D12PipelineState* GetNative() const { return m_pipelineState.Get(); }
|
||||
|
||||
private:
|
||||
D3D12Device* m_device = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D12PipelineState> m_pipelineState;
|
||||
};
|
||||
|
||||
class D3D12CommandList : public ICommandList {
|
||||
public:
|
||||
D3D12CommandList(D3D12Device* device, ID3D12GraphicsCommandList* list);
|
||||
~D3D12CommandList() override;
|
||||
|
||||
void Reset(void* allocator) override;
|
||||
void Close() override;
|
||||
void SetPipelineState(IPipelineState* pso) override;
|
||||
void SetRootSignature(IRootSignature* signature) override;
|
||||
void SetPrimitiveTopology(PrimitiveTopology topology) override;
|
||||
void SetVertexBuffer(uint32_t slot, IResource* buffer, uint32_t offset, uint32_t stride) override;
|
||||
void SetIndexBuffer(IResource* buffer, uint32_t offset) override;
|
||||
void SetDescriptorHeap(IDescriptorHeap* heap) override;
|
||||
void SetGraphicsDescriptorTable(uint32_t rootParameterIndex, uint64_t baseDescriptor) override;
|
||||
void SetComputeDescriptorTable(uint32_t rootParameterIndex, uint64_t baseDescriptor) override;
|
||||
void DrawInstanced(uint32_t vertexCountPerInstance, uint32_t instanceCount, uint32_t startVertex, uint32_t startInstance) override;
|
||||
void DrawIndexedInstanced(uint32_t indexCountPerInstance, uint32_t instanceCount, uint32_t startIndex, int32_t baseVertex, uint32_t startInstance) override;
|
||||
void Dispatch(uint32_t x, uint32_t y, uint32_t z) override;
|
||||
void SetViewports(const Viewport* viewports, uint32_t count) override;
|
||||
void SetScissorRects(const Rect* rects, uint32_t count) override;
|
||||
void SetRenderTargets(void** targets, uint32_t count, void* depthStencil) override;
|
||||
void ClearRenderTargetView(void* target, const float color[4]) override;
|
||||
void ClearDepthStencilView(void* depth, float depthValue, uint8_t stencil) override;
|
||||
void CopyResource(IResource* dst, const IResource* src) override;
|
||||
void CopyBuffer(IResource* dst, uint64_t dstOffset, const IResource* src, uint64_t srcOffset, uint64_t size) override;
|
||||
void ResourceBarrier(IResource* resource, ResourceStateFlag before, ResourceStateFlag after) override;
|
||||
void* GetNativeCommandList() const override;
|
||||
|
||||
ID3D12GraphicsCommandList* GetNative() const { return m_commandList.Get(); }
|
||||
|
||||
private:
|
||||
D3D12Device* m_device = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> m_commandList;
|
||||
};
|
||||
|
||||
class D3D12Resource : public IResource {
|
||||
public:
|
||||
D3D12Resource(D3D12Device* device, ID3D12Resource* resource);
|
||||
~D3D12Resource() override;
|
||||
|
||||
void* GetNativeResource() const override { return m_resource.Get(); }
|
||||
ID3D12Resource* GetNative() const { return m_resource.Get(); }
|
||||
|
||||
private:
|
||||
D3D12Device* m_device = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> m_resource;
|
||||
};
|
||||
|
||||
class D3D12Device : public IRHIDevice {
|
||||
public:
|
||||
D3D12Device();
|
||||
~D3D12Device() override;
|
||||
|
||||
GraphicsAPI GetAPI() const override { return GraphicsAPI::Direct3D12; }
|
||||
const char* GetAPIName() const override { return "Direct3D 12"; }
|
||||
|
||||
bool Initialize(void* windowHandle, uint32_t width, uint32_t height) override;
|
||||
void Shutdown() override;
|
||||
|
||||
bool CreateCommandQueue(ICommandQueue** queue, const CommandQueueDesc& desc) override;
|
||||
bool CreateCommandAllocator(ICommandAllocator** allocator) override;
|
||||
bool CreateCommandList(ICommandList** list, ICommandAllocator* allocator) override;
|
||||
bool CreateFence(IFence** fence) override;
|
||||
|
||||
bool CreateDescriptorHeap(IDescriptorHeap** heap, const DescriptorHeapDesc& desc) override;
|
||||
|
||||
bool CreateRootSignature(IRootSignature** signature, const RootSignatureDesc& desc) override;
|
||||
bool CreatePipelineState(IPipelineState** pso, const PipelineDesc& desc) override;
|
||||
|
||||
bool CreateSwapChain(ISwapChain** swapChain, const SwapChainDesc& desc, void* windowHandle) override;
|
||||
|
||||
ICommandQueue* GetCommandQueue() override { return m_commandQueue; }
|
||||
ISwapChain* GetSwapChain() { return m_swapChain; }
|
||||
void* GetNativeDevice() const override { return m_device.Get(); }
|
||||
void* GetNativeAdapter() const override { return m_adapter.Get(); }
|
||||
|
||||
ID3D12Device* GetD3D12Device() const { return m_device.Get(); }
|
||||
IDXGIFactory4* GetDXGIFactory() const { return m_dxgiFactory.Get(); }
|
||||
|
||||
private:
|
||||
bool CreateDXGIDevice(void* windowHandle);
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D12Device> m_device;
|
||||
Microsoft::WRL::ComPtr<IDXGIAdapter1> m_adapter;
|
||||
Microsoft::WRL::ComPtr<IDXGIFactory4> m_dxgiFactory;
|
||||
|
||||
D3D12CommandQueue* m_commandQueue = nullptr;
|
||||
D3D12SwapChain* m_swapChain = nullptr;
|
||||
};
|
||||
|
||||
} // namespace RHI
|
||||
} // namespace XCEngine
|
||||
136
engine/src/RHI/D3D12/D3D12RenderContext.cpp
Normal file
136
engine/src/RHI/D3D12/D3D12RenderContext.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include <Rendering\RenderContext.h>
|
||||
#include <RHI\D3D12\D3D12RHI.h>
|
||||
#include <RHI\D3D12\D3D12Resources.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
|
||||
RenderContext::RenderContext(D3D12Device* device) : m_device(device) {
|
||||
}
|
||||
|
||||
RenderContext::~RenderContext() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
bool RenderContext::Initialize(uint32_t width, uint32_t height) {
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
|
||||
if (!m_device->CreateCommandAllocator(&m_commandAllocator)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_device->CreateCommandList(&m_commandList, m_commandAllocator)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_device->CreateFence(&m_fence)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_swapChain = m_device->GetSwapChain();
|
||||
if (!m_swapChain) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CreateDepthStencil(m_device, width, height, Format::D24_UNorm_S8_UInt, &m_depthStencil)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderContext::Shutdown() {
|
||||
if (m_fence) {
|
||||
m_fence->Wait(m_fenceValue);
|
||||
}
|
||||
|
||||
if (m_commandList) delete m_commandList;
|
||||
if (m_commandAllocator) delete m_commandAllocator;
|
||||
if (m_fence) delete m_fence;
|
||||
if (m_swapChain) delete m_swapChain;
|
||||
if (m_currentRenderTarget) delete m_currentRenderTarget;
|
||||
if (m_depthStencil) delete m_depthStencil;
|
||||
}
|
||||
|
||||
void RenderContext::BeginFrame() {
|
||||
m_commandAllocator->Reset();
|
||||
m_commandList->Reset(m_commandAllocator);
|
||||
|
||||
uint32_t bufferIndex = m_swapChain->GetCurrentBufferIndex();
|
||||
|
||||
if (m_currentRenderTarget) delete m_currentRenderTarget;
|
||||
CreateRenderTargetFromSwapChain(m_device, (D3D12SwapChain*)m_swapChain, bufferIndex, &m_currentRenderTarget);
|
||||
|
||||
D3D12CommandList* cmdList = (D3D12CommandList*)m_commandList;
|
||||
ID3D12GraphicsCommandList* d3dCmdList = cmdList->GetNative();
|
||||
|
||||
D3D12_RESOURCE_BARRIER barrier = {};
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Transition.pResource = ((D3D12RenderTarget*)m_currentRenderTarget)->GetNative();
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
d3dCmdList->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
|
||||
void RenderContext::EndFrame() {
|
||||
D3D12CommandList* cmdList = (D3D12CommandList*)m_commandList;
|
||||
ID3D12GraphicsCommandList* d3dCmdList = cmdList->GetNative();
|
||||
|
||||
D3D12_RESOURCE_BARRIER barrier = {};
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Transition.pResource = ((D3D12RenderTarget*)m_currentRenderTarget)->GetNative();
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
d3dCmdList->ResourceBarrier(1, &barrier);
|
||||
|
||||
m_commandList->Close();
|
||||
|
||||
ICommandQueue* queue = m_device->GetCommandQueue();
|
||||
void* cmdListPtr = m_commandList->GetNativeCommandList();
|
||||
queue->ExecuteCommandLists(&cmdListPtr, 1);
|
||||
|
||||
m_fenceValue++;
|
||||
queue->Signal(m_fence, m_fenceValue);
|
||||
m_fence->Wait(m_fenceValue);
|
||||
}
|
||||
|
||||
void RenderContext::SetViewport(float width, float height) {
|
||||
Viewport viewport = {};
|
||||
viewport.topLeftX = 0;
|
||||
viewport.topLeftY = 0;
|
||||
viewport.width = width;
|
||||
viewport.height = height;
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
m_commandList->SetViewports(&viewport, 1);
|
||||
}
|
||||
|
||||
void RenderContext::SetScissor(int32_t width, int32_t height) {
|
||||
Rect scissor = {};
|
||||
scissor.left = 0;
|
||||
scissor.top = 0;
|
||||
scissor.right = width;
|
||||
scissor.bottom = height;
|
||||
m_commandList->SetScissorRects(&scissor, 1);
|
||||
}
|
||||
|
||||
void RenderContext::ClearRenderTarget(const float color[4]) {
|
||||
D3D12RenderTarget* rt = (D3D12RenderTarget*)m_currentRenderTarget;
|
||||
rt->CreateRTV();
|
||||
m_commandList->ClearRenderTargetView(rt->GetRTV(), color);
|
||||
}
|
||||
|
||||
void RenderContext::ClearDepthStencil() {
|
||||
m_depthStencil->CreateDSV();
|
||||
m_commandList->ClearDepthStencilView(m_depthStencil->GetDSV(), 1.0f, 0);
|
||||
}
|
||||
|
||||
void RenderContext::Present() {
|
||||
m_swapChain->Present();
|
||||
}
|
||||
|
||||
} // namespace RHI
|
||||
} // namespace XCEngine
|
||||
73
engine/src/RHI/D3D12/D3D12RenderTarget.cpp
Normal file
73
engine/src/RHI/D3D12/D3D12RenderTarget.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "D3D12Resources.h"
|
||||
#include "Rendering\RenderTarget.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
|
||||
void D3D12DepthStencil::CreateDSV() {
|
||||
if (m_dsvHandle.ptr != 0) return;
|
||||
D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
|
||||
dsvDesc.Format = FormatToDXGIFormat(m_desc.format);
|
||||
dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
|
||||
m_device->GetD3D12Device()->CreateDepthStencilView(m_resource.Get(), &dsvDesc, m_dsvHandle);
|
||||
}
|
||||
|
||||
void D3D12RenderTarget::CreateRTV() {
|
||||
if (m_rtvHandle.ptr != 0) return;
|
||||
D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
|
||||
rtvDesc.Format = FormatToDXGIFormat(m_desc.format);
|
||||
rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
|
||||
m_device->GetD3D12Device()->CreateRenderTargetView(m_resource.Get(), &rtvDesc, m_rtvHandle);
|
||||
}
|
||||
|
||||
bool CreateDepthStencil(D3D12Device* device, uint32_t width, uint32_t height, Format format, IDepthStencil** outDepthStencil) {
|
||||
D3D12_HEAP_PROPERTIES heapProps = {};
|
||||
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
|
||||
D3D12_RESOURCE_DESC resourceDesc = {};
|
||||
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
resourceDesc.Width = width;
|
||||
resourceDesc.Height = height;
|
||||
resourceDesc.DepthOrArraySize = 1;
|
||||
resourceDesc.MipLevels = 1;
|
||||
resourceDesc.Format = FormatToDXGIFormat(format);
|
||||
resourceDesc.SampleDesc.Count = 1;
|
||||
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||
resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
|
||||
|
||||
D3D12_CLEAR_VALUE clearValue = {};
|
||||
clearValue.Format = FormatToDXGIFormat(format);
|
||||
clearValue.DepthStencil.Depth = 1.0f;
|
||||
clearValue.DepthStencil.Stencil = 0;
|
||||
|
||||
ID3D12Resource* resource = nullptr;
|
||||
HRESULT hr = device->GetD3D12Device()->CreateCommittedResource(
|
||||
&heapProps, D3D12_HEAP_FLAG_NONE, &resourceDesc,
|
||||
D3D12_RESOURCE_STATE_DEPTH_WRITE, &clearValue, IID_PPV_ARGS(&resource));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
TextureDesc desc = {};
|
||||
desc.width = width;
|
||||
desc.height = height;
|
||||
desc.format = format;
|
||||
desc.depthStencil = true;
|
||||
|
||||
*outDepthStencil = new D3D12DepthStencil(device, resource, desc);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CreateRenderTargetFromSwapChain(D3D12Device* device, D3D12SwapChain* swapChain, uint32_t bufferIndex, IRenderTarget** outRenderTarget) {
|
||||
ID3D12Resource* resource = swapChain->GetBufferResource(bufferIndex);
|
||||
|
||||
TextureDesc desc = {};
|
||||
desc.width = 0;
|
||||
desc.height = 0;
|
||||
desc.format = Format::R8G8B8A8_UNorm;
|
||||
desc.renderTarget = true;
|
||||
|
||||
*outRenderTarget = new D3D12RenderTarget(device, resource, desc);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace RHI
|
||||
} // namespace XCEngine
|
||||
378
engine/src/RHI/D3D12/D3D12Resources.cpp
Normal file
378
engine/src/RHI/D3D12/D3D12Resources.cpp
Normal file
@@ -0,0 +1,378 @@
|
||||
#include "D3D12Resources.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
|
||||
D3D12Buffer::D3D12Buffer(D3D12Device* device, ID3D12Resource* resource, const BufferDesc& desc)
|
||||
: m_device(device), m_resource(resource), m_desc(desc) {
|
||||
}
|
||||
|
||||
D3D12Buffer::~D3D12Buffer() {
|
||||
m_resource.Reset();
|
||||
}
|
||||
|
||||
void* D3D12Buffer::Map() {
|
||||
void* data = nullptr;
|
||||
D3D12_RANGE range = { 0, (SIZE_T)m_desc.size };
|
||||
m_resource->Map(0, &range, &data);
|
||||
return data;
|
||||
}
|
||||
|
||||
void D3D12Buffer::Unmap() {
|
||||
m_resource->Unmap(0, nullptr);
|
||||
}
|
||||
|
||||
D3D12VertexBuffer::D3D12VertexBuffer(D3D12Buffer* buffer) : m_buffer(buffer) {
|
||||
}
|
||||
|
||||
D3D12VertexBuffer::~D3D12VertexBuffer() {
|
||||
delete m_buffer;
|
||||
}
|
||||
|
||||
D3D12IndexBuffer::D3D12IndexBuffer(D3D12Buffer* buffer) : m_buffer(buffer) {
|
||||
}
|
||||
|
||||
D3D12IndexBuffer::~D3D12IndexBuffer() {
|
||||
delete m_buffer;
|
||||
}
|
||||
|
||||
D3D12ConstantBuffer::D3D12ConstantBuffer(D3D12Buffer* buffer) : m_buffer(buffer) {
|
||||
}
|
||||
|
||||
D3D12ConstantBuffer::~D3D12ConstantBuffer() {
|
||||
delete m_buffer;
|
||||
}
|
||||
|
||||
D3D12Texture2D::D3D12Texture2D(D3D12Device* device, ID3D12Resource* resource, const TextureDesc& desc)
|
||||
: m_device(device), m_resource(resource), m_desc(desc) {
|
||||
}
|
||||
|
||||
D3D12Texture2D::~D3D12Texture2D() {
|
||||
m_resource.Reset();
|
||||
}
|
||||
|
||||
void D3D12Texture2D::CreateSRV() {
|
||||
if (m_hasSRV) return;
|
||||
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
|
||||
srvDesc.Format = FormatToDXGIFormat(m_desc.format);
|
||||
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Texture2D.MipLevels = m_desc.mipLevels;
|
||||
|
||||
m_device->GetD3D12Device()->CreateShaderResourceView(m_resource.Get(), &srvDesc, m_srvHandle);
|
||||
m_hasSRV = true;
|
||||
}
|
||||
|
||||
void* D3D12Texture2D::GetSRV() {
|
||||
return (void*)m_srvHandle.ptr;
|
||||
}
|
||||
|
||||
void D3D12Texture2D::CreateRTV() {
|
||||
if (m_hasRTV) return;
|
||||
|
||||
D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
|
||||
rtvDesc.Format = FormatToDXGIFormat(m_desc.format);
|
||||
rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
|
||||
|
||||
m_device->GetD3D12Device()->CreateRenderTargetView(m_resource.Get(), &rtvDesc, m_rtvHandle);
|
||||
m_hasRTV = true;
|
||||
}
|
||||
|
||||
void* D3D12Texture2D::GetRTV() {
|
||||
return (void*)m_rtvHandle.ptr;
|
||||
}
|
||||
|
||||
void D3D12Texture2D::CreateDSV() {
|
||||
if (m_hasDSV) return;
|
||||
|
||||
D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
|
||||
dsvDesc.Format = FormatToDXGIFormat(m_desc.format);
|
||||
dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
|
||||
|
||||
m_device->GetD3D12Device()->CreateDepthStencilView(m_resource.Get(), &dsvDesc, m_dsvHandle);
|
||||
m_hasDSV = true;
|
||||
}
|
||||
|
||||
void* D3D12Texture2D::GetDSV() {
|
||||
return (void*)m_dsvHandle.ptr;
|
||||
}
|
||||
|
||||
bool CreateVertexBuffer(D3D12Device* device, ICommandList* commandList,
|
||||
const void* data, uint64_t size, uint32_t stride, IVertexBuffer** outBuffer) {
|
||||
|
||||
D3D12_HEAP_PROPERTIES heapProps = {};
|
||||
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
|
||||
D3D12_RESOURCE_DESC resourceDesc = {};
|
||||
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
resourceDesc.Width = size;
|
||||
resourceDesc.Height = 1;
|
||||
resourceDesc.DepthOrArraySize = 1;
|
||||
resourceDesc.MipLevels = 1;
|
||||
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
resourceDesc.SampleDesc.Count = 1;
|
||||
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
|
||||
ID3D12Resource* bufferResource = nullptr;
|
||||
HRESULT hr = device->GetD3D12Device()->CreateCommittedResource(
|
||||
&heapProps, D3D12_HEAP_FLAG_NONE, &resourceDesc,
|
||||
D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&bufferResource));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
D3D12_RESOURCE_DESC bufferDesc = bufferResource->GetDesc();
|
||||
UINT64 memorySize = 0;
|
||||
UINT64 rowSize = 0;
|
||||
UINT rowCount = 0;
|
||||
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint;
|
||||
device->GetD3D12Device()->GetCopyableFootprints(&bufferDesc, 0, 1, 0, &footprint, &rowCount, &rowSize, &memorySize);
|
||||
|
||||
D3D12_HEAP_PROPERTIES uploadHeapProps = {};
|
||||
uploadHeapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
|
||||
ID3D12Resource* uploadResource = nullptr;
|
||||
hr = device->GetD3D12Device()->CreateCommittedResource(
|
||||
&uploadHeapProps, D3D12_HEAP_FLAG_NONE, &resourceDesc,
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uploadResource));
|
||||
if (FAILED(hr)) {
|
||||
bufferResource->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
UINT8* uploadData = nullptr;
|
||||
uploadResource->Map(0, nullptr, (void**)&uploadData);
|
||||
|
||||
UINT8* dst = uploadData + footprint.Offset;
|
||||
const UINT8* src = (const UINT8*)data;
|
||||
for (UINT i = 0; i < rowCount; i++) {
|
||||
memcpy(dst + footprint.Footprint.RowPitch * i, src + rowSize * i, (SIZE_T)rowSize);
|
||||
}
|
||||
uploadResource->Unmap(0, nullptr);
|
||||
|
||||
D3D12CommandList* d3dCmdList = static_cast<D3D12CommandList*>(commandList);
|
||||
d3dCmdList->GetNative()->CopyBufferRegion(bufferResource, 0, uploadResource, 0, size);
|
||||
|
||||
D3D12_RESOURCE_BARRIER barrier = {};
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Transition.pResource = bufferResource;
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
d3dCmdList->GetNative()->ResourceBarrier(1, &barrier);
|
||||
|
||||
uploadResource->Release();
|
||||
|
||||
BufferDesc bufferDesc2 = {};
|
||||
bufferDesc2.size = size;
|
||||
bufferDesc2.stride = stride;
|
||||
|
||||
D3D12Buffer* buffer = new D3D12Buffer(device, bufferResource, bufferDesc2);
|
||||
*outBuffer = new D3D12VertexBuffer(buffer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CreateIndexBuffer(D3D12Device* device, ICommandList* commandList,
|
||||
const void* data, uint64_t size, IIndexBuffer** outBuffer) {
|
||||
|
||||
D3D12_HEAP_PROPERTIES heapProps = {};
|
||||
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
|
||||
D3D12_RESOURCE_DESC resourceDesc = {};
|
||||
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
resourceDesc.Width = size;
|
||||
resourceDesc.Height = 1;
|
||||
resourceDesc.DepthOrArraySize = 1;
|
||||
resourceDesc.MipLevels = 1;
|
||||
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
resourceDesc.SampleDesc.Count = 1;
|
||||
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
|
||||
ID3D12Resource* bufferResource = nullptr;
|
||||
HRESULT hr = device->GetD3D12Device()->CreateCommittedResource(
|
||||
&heapProps, D3D12_HEAP_FLAG_NONE, &resourceDesc,
|
||||
D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&bufferResource));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
D3D12_RESOURCE_DESC bufferDesc = bufferResource->GetDesc();
|
||||
UINT64 memorySize = 0;
|
||||
UINT64 rowSize = 0;
|
||||
UINT rowCount = 0;
|
||||
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint;
|
||||
device->GetD3D12Device()->GetCopyableFootprints(&bufferDesc, 0, 1, 0, &footprint, &rowCount, &rowSize, &memorySize);
|
||||
|
||||
D3D12_HEAP_PROPERTIES uploadHeapProps = {};
|
||||
uploadHeapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
|
||||
ID3D12Resource* uploadResource = nullptr;
|
||||
hr = device->GetD3D12Device()->CreateCommittedResource(
|
||||
&uploadHeapProps, D3D12_HEAP_FLAG_NONE, &resourceDesc,
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uploadResource));
|
||||
if (FAILED(hr)) {
|
||||
bufferResource->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
UINT8* uploadData = nullptr;
|
||||
uploadResource->Map(0, nullptr, (void**)&uploadData);
|
||||
|
||||
memcpy(uploadData + footprint.Offset, data, (SIZE_T)size);
|
||||
uploadResource->Unmap(0, nullptr);
|
||||
|
||||
D3D12CommandList* d3dCmdList = static_cast<D3D12CommandList*>(commandList);
|
||||
d3dCmdList->GetNative()->CopyBufferRegion(bufferResource, 0, uploadResource, 0, size);
|
||||
|
||||
D3D12_RESOURCE_BARRIER barrier = {};
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Transition.pResource = bufferResource;
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_INDEX_BUFFER;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
d3dCmdList->GetNative()->ResourceBarrier(1, &barrier);
|
||||
|
||||
uploadResource->Release();
|
||||
|
||||
BufferDesc bufferDesc2 = {};
|
||||
bufferDesc2.size = size;
|
||||
bufferDesc2.stride = 4;
|
||||
|
||||
D3D12Buffer* buffer = new D3D12Buffer(device, bufferResource, bufferDesc2);
|
||||
*outBuffer = new D3D12IndexBuffer(buffer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CreateConstantBuffer(D3D12Device* device, uint64_t size, IConstantBuffer** outBuffer) {
|
||||
D3D12_HEAP_PROPERTIES heapProps = {};
|
||||
heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
|
||||
D3D12_RESOURCE_DESC resourceDesc = {};
|
||||
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
resourceDesc.Width = size;
|
||||
resourceDesc.Height = 1;
|
||||
resourceDesc.DepthOrArraySize = 1;
|
||||
resourceDesc.MipLevels = 1;
|
||||
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
resourceDesc.SampleDesc.Count = 1;
|
||||
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
|
||||
ID3D12Resource* bufferResource = nullptr;
|
||||
HRESULT hr = device->GetD3D12Device()->CreateCommittedResource(
|
||||
&heapProps, D3D12_HEAP_FLAG_NONE, &resourceDesc,
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&bufferResource));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
BufferDesc bufferDesc = {};
|
||||
bufferDesc.size = size;
|
||||
bufferDesc.stride = (uint32_t)size;
|
||||
bufferDesc.cpuAccessible = true;
|
||||
|
||||
D3D12Buffer* buffer = new D3D12Buffer(device, bufferResource, bufferDesc);
|
||||
*outBuffer = new D3D12ConstantBuffer(buffer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CreateTexture2D(D3D12Device* device, ICommandList* commandList,
|
||||
const void* pixels, uint32_t width, uint32_t height, Format format, ITexture2D** outTexture) {
|
||||
|
||||
D3D12_HEAP_PROPERTIES heapProps = {};
|
||||
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
|
||||
D3D12_RESOURCE_DESC resourceDesc = {};
|
||||
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
resourceDesc.Width = width;
|
||||
resourceDesc.Height = height;
|
||||
resourceDesc.DepthOrArraySize = 1;
|
||||
resourceDesc.MipLevels = 1;
|
||||
resourceDesc.Format = FormatToDXGIFormat(format);
|
||||
resourceDesc.SampleDesc.Count = 1;
|
||||
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||
|
||||
ID3D12Resource* textureResource = nullptr;
|
||||
HRESULT hr = device->GetD3D12Device()->CreateCommittedResource(
|
||||
&heapProps, D3D12_HEAP_FLAG_NONE, &resourceDesc,
|
||||
D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&textureResource));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
D3D12_RESOURCE_DESC texDesc = textureResource->GetDesc();
|
||||
UINT64 memorySize = 0;
|
||||
UINT64 rowSize = 0;
|
||||
UINT rowCount = 0;
|
||||
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint;
|
||||
device->GetD3D12Device()->GetCopyableFootprints(&texDesc, 0, 1, 0, &footprint, &rowCount, &rowSize, &memorySize);
|
||||
|
||||
D3D12_HEAP_PROPERTIES uploadHeapProps = {};
|
||||
uploadHeapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
|
||||
D3D12_RESOURCE_DESC uploadDesc = {};
|
||||
uploadDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
uploadDesc.Width = memorySize;
|
||||
uploadDesc.Height = 1;
|
||||
uploadDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
|
||||
ID3D12Resource* uploadResource = nullptr;
|
||||
hr = device->GetD3D12Device()->CreateCommittedResource(
|
||||
&uploadHeapProps, D3D12_HEAP_FLAG_NONE, &uploadDesc,
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uploadResource));
|
||||
if (FAILED(hr)) {
|
||||
textureResource->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
UINT8* uploadData = nullptr;
|
||||
uploadResource->Map(0, nullptr, (void**)&uploadData);
|
||||
|
||||
UINT8* dst = uploadData + footprint.Offset;
|
||||
const UINT8* src = (const UINT8*)pixels;
|
||||
for (UINT i = 0; i < rowCount; i++) {
|
||||
memcpy(dst + footprint.Footprint.RowPitch * i, src + rowSize * i, (SIZE_T)rowSize);
|
||||
}
|
||||
uploadResource->Unmap(0, nullptr);
|
||||
|
||||
D3D12CommandList* d3dCmdList = static_cast<D3D12CommandList*>(commandList);
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION dstLoc = {};
|
||||
dstLoc.pResource = textureResource;
|
||||
dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||
dstLoc.SubresourceIndex = 0;
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION srcLoc = {};
|
||||
srcLoc.pResource = uploadResource;
|
||||
srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||
srcLoc.PlacedFootprint = footprint;
|
||||
|
||||
d3dCmdList->GetNative()->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, nullptr);
|
||||
|
||||
D3D12_RESOURCE_BARRIER barrier = {};
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Transition.pResource = textureResource;
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
d3dCmdList->GetNative()->ResourceBarrier(1, &barrier);
|
||||
|
||||
uploadResource->Release();
|
||||
|
||||
TextureDesc texDesc2 = {};
|
||||
texDesc2.width = width;
|
||||
texDesc2.height = height;
|
||||
texDesc2.format = format;
|
||||
|
||||
D3D12Texture2D* texture = new D3D12Texture2D(device, textureResource, texDesc2);
|
||||
*outTexture = texture;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateConstantBuffer(IConstantBuffer* buffer, const void* data, uint64_t size) {
|
||||
D3D12ConstantBuffer* d3dBuffer = static_cast<D3D12ConstantBuffer*>(buffer);
|
||||
void* mapped = d3dBuffer->Map();
|
||||
if (mapped) {
|
||||
memcpy(mapped, data, (SIZE_T)size);
|
||||
d3dBuffer->Unmap();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace RHI
|
||||
} // namespace XCEngine
|
||||
170
engine/src/RHI/D3D12/D3D12Resources.h
Normal file
170
engine/src/RHI/D3D12/D3D12Resources.h
Normal file
@@ -0,0 +1,170 @@
|
||||
#pragma once
|
||||
|
||||
#include <Rendering\Resources.h>
|
||||
#include <Rendering\RenderTarget.h>
|
||||
#include <RHI\D3D12\D3D12RHI.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
|
||||
class D3D12Buffer : public IBuffer {
|
||||
public:
|
||||
D3D12Buffer(D3D12Device* device, ID3D12Resource* resource, const BufferDesc& desc);
|
||||
~D3D12Buffer() override;
|
||||
|
||||
const BufferDesc& GetDesc() const override { return m_desc; }
|
||||
void* Map() override;
|
||||
void Unmap() override;
|
||||
void* GetNativeResource() const override { return m_resource.Get(); }
|
||||
ID3D12Resource* GetNative() const { return m_resource.Get(); }
|
||||
|
||||
private:
|
||||
D3D12Device* m_device = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> m_resource;
|
||||
BufferDesc m_desc;
|
||||
};
|
||||
|
||||
class D3D12VertexBuffer : public IVertexBuffer {
|
||||
public:
|
||||
D3D12VertexBuffer(D3D12Buffer* buffer);
|
||||
~D3D12VertexBuffer() override;
|
||||
|
||||
const BufferDesc& GetDesc() const override { return m_buffer->GetDesc(); }
|
||||
void* Map() override { return m_buffer->Map(); }
|
||||
void Unmap() override { m_buffer->Unmap(); }
|
||||
void* GetNativeResource() const override { return m_buffer->GetNativeResource(); }
|
||||
ID3D12Resource* GetNative() const { return m_buffer->GetNative(); }
|
||||
|
||||
private:
|
||||
D3D12Buffer* m_buffer = nullptr;
|
||||
};
|
||||
|
||||
class D3D12IndexBuffer : public IIndexBuffer {
|
||||
public:
|
||||
D3D12IndexBuffer(D3D12Buffer* buffer);
|
||||
~D3D12IndexBuffer() override;
|
||||
|
||||
const BufferDesc& GetDesc() const override { return m_buffer->GetDesc(); }
|
||||
void* Map() override { return m_buffer->Map(); }
|
||||
void Unmap() override { m_buffer->Unmap(); }
|
||||
void* GetNativeResource() const override { return m_buffer->GetNativeResource(); }
|
||||
ID3D12Resource* GetNative() const { return m_buffer->GetNative(); }
|
||||
|
||||
private:
|
||||
D3D12Buffer* m_buffer = nullptr;
|
||||
};
|
||||
|
||||
class D3D12ConstantBuffer : public IConstantBuffer {
|
||||
public:
|
||||
D3D12ConstantBuffer(D3D12Buffer* buffer);
|
||||
~D3D12ConstantBuffer() override;
|
||||
|
||||
const BufferDesc& GetDesc() const override { return m_buffer->GetDesc(); }
|
||||
void* Map() override { return m_buffer->Map(); }
|
||||
void Unmap() override { return m_buffer->Unmap(); }
|
||||
void* GetNativeResource() const override { return m_buffer->GetNativeResource(); }
|
||||
ID3D12Resource* GetNative() const { return m_buffer->GetNative(); }
|
||||
|
||||
private:
|
||||
D3D12Buffer* m_buffer = nullptr;
|
||||
};
|
||||
|
||||
class D3D12Texture2D : public ITexture2D {
|
||||
public:
|
||||
D3D12Texture2D(D3D12Device* device, ID3D12Resource* resource, const TextureDesc& desc);
|
||||
~D3D12Texture2D() override;
|
||||
|
||||
const TextureDesc& GetDesc() const override { return m_desc; }
|
||||
void CreateSRV() override;
|
||||
void* GetSRV() override;
|
||||
void CreateRTV() override;
|
||||
void* GetRTV() override;
|
||||
void CreateDSV() override;
|
||||
void* GetDSV() override;
|
||||
void* GetNativeResource() const override { return m_resource.Get(); }
|
||||
ID3D12Resource* GetNative() const { return m_resource.Get(); }
|
||||
|
||||
private:
|
||||
D3D12Device* m_device = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> m_resource;
|
||||
TextureDesc m_desc;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE m_srvHandle = {};
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE m_rtvHandle = {};
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE m_dsvHandle = {};
|
||||
bool m_hasSRV = false;
|
||||
bool m_hasRTV = false;
|
||||
bool m_hasDSV = false;
|
||||
};
|
||||
|
||||
bool CreateVertexBuffer(D3D12Device* device, ICommandList* commandList,
|
||||
const void* data, uint64_t size, uint32_t stride, IVertexBuffer** outBuffer);
|
||||
|
||||
bool CreateIndexBuffer(D3D12Device* device, ICommandList* commandList,
|
||||
const void* data, uint64_t size, IIndexBuffer** outBuffer);
|
||||
|
||||
bool CreateConstantBuffer(D3D12Device* device, uint64_t size, IConstantBuffer** outBuffer);
|
||||
|
||||
bool CreateTexture2D(D3D12Device* device, ICommandList* commandList,
|
||||
const void* pixels, uint32_t width, uint32_t height, Format format, ITexture2D** outTexture);
|
||||
|
||||
void UpdateConstantBuffer(IConstantBuffer* buffer, const void* data, uint64_t size);
|
||||
|
||||
class D3D12DepthStencil : public IDepthStencil {
|
||||
public:
|
||||
D3D12DepthStencil(D3D12Device* device, ID3D12Resource* resource, const TextureDesc& desc)
|
||||
: m_device(device), m_resource(resource), m_desc(desc) {}
|
||||
|
||||
~D3D12DepthStencil() override {
|
||||
m_resource.Reset();
|
||||
}
|
||||
|
||||
const TextureDesc& GetDesc() const override { return m_desc; }
|
||||
void* GetNativeResource() const override { return m_resource.Get(); }
|
||||
ID3D12Resource* GetNative() const { return m_resource.Get(); }
|
||||
|
||||
void CreateSRV() override {}
|
||||
void* GetSRV() override { return nullptr; }
|
||||
void CreateRTV() override {}
|
||||
void* GetRTV() override { return nullptr; }
|
||||
void CreateDSV() override;
|
||||
void* GetDSV() override { return (void*)m_dsvHandle.ptr; }
|
||||
|
||||
private:
|
||||
D3D12Device* m_device = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> m_resource;
|
||||
TextureDesc m_desc;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE m_dsvHandle = {};
|
||||
};
|
||||
|
||||
class D3D12RenderTarget : public IRenderTarget {
|
||||
public:
|
||||
D3D12RenderTarget(D3D12Device* device, ID3D12Resource* resource, const TextureDesc& desc)
|
||||
: m_device(device), m_resource(resource), m_desc(desc) {}
|
||||
|
||||
~D3D12RenderTarget() override {
|
||||
m_resource.Reset();
|
||||
}
|
||||
|
||||
const TextureDesc& GetDesc() const override { return m_desc; }
|
||||
void* GetNativeResource() const override { return m_resource.Get(); }
|
||||
ID3D12Resource* GetNative() const { return m_resource.Get(); }
|
||||
|
||||
void CreateSRV() override {}
|
||||
void* GetSRV() override { return nullptr; }
|
||||
void CreateRTV() override;
|
||||
void* GetRTV() override { return (void*)m_rtvHandle.ptr; }
|
||||
void CreateDSV() override {}
|
||||
void* GetDSV() override { return nullptr; }
|
||||
|
||||
private:
|
||||
D3D12Device* m_device = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> m_resource;
|
||||
TextureDesc m_desc;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE m_rtvHandle = {};
|
||||
};
|
||||
|
||||
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
|
||||
40
engine/src/RHI/D3D12/D3D12Shader.cpp
Normal file
40
engine/src/RHI/D3D12/D3D12Shader.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <Rendering\Shader.h>
|
||||
#include <d3dcompiler.h>
|
||||
#include <wrl/client.h>
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
|
||||
bool CompileShader(const char* filePath, const char* entryPoint, const char* target, ShaderBytecode& outBytecode) {
|
||||
UINT flags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
|
||||
|
||||
ID3DBlob* shaderBuffer = nullptr;
|
||||
ID3DBlob* errorBuffer = nullptr;
|
||||
|
||||
HRESULT hr = D3DCompileFromFile(
|
||||
std::wstring(filePath, filePath + strlen(filePath)).c_str(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
entryPoint,
|
||||
target,
|
||||
flags,
|
||||
0,
|
||||
&shaderBuffer,
|
||||
&errorBuffer);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
if (errorBuffer) {
|
||||
OutputDebugStringA((const char*)errorBuffer->GetBufferPointer());
|
||||
errorBuffer->Release();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
outBytecode.bytecode = shaderBuffer->GetBufferPointer();
|
||||
outBytecode.size = shaderBuffer->GetBufferSize();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace RHI
|
||||
} // namespace XCEngine
|
||||
86
engine/src/RHI/D3D12/D3D12StaticMeshComponent.cpp
Normal file
86
engine/src/RHI/D3D12/D3D12StaticMeshComponent.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include <Rendering\StaticMeshComponent.h>
|
||||
#include <RHI\D3D12\D3D12Resources.h>
|
||||
#include <cstdio>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
|
||||
StaticMeshComponent::StaticMeshComponent() {
|
||||
}
|
||||
|
||||
StaticMeshComponent::~StaticMeshComponent() {
|
||||
if (m_vertexBuffer) {
|
||||
delete m_vertexBuffer;
|
||||
}
|
||||
for (auto& pair : m_subMeshes) {
|
||||
delete pair.second;
|
||||
}
|
||||
}
|
||||
|
||||
bool StaticMeshComponent::Initialize(ICommandList* commandList, const char* filePath) {
|
||||
FILE* file = nullptr;
|
||||
if (fopen_s(&file, filePath, "rb") != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int vertexCount = 0;
|
||||
fread(&vertexCount, 4, 1, file);
|
||||
m_vertexCount = vertexCount;
|
||||
|
||||
MeshVertex* vertices = new MeshVertex[vertexCount];
|
||||
fread(vertices, sizeof(MeshVertex), vertexCount, file);
|
||||
|
||||
D3D12Device* device = nullptr;
|
||||
|
||||
CreateVertexBuffer(device, commandList, vertices, sizeof(MeshVertex) * vertexCount, sizeof(MeshVertex), &m_vertexBuffer);
|
||||
|
||||
delete[] vertices;
|
||||
|
||||
while (!feof(file)) {
|
||||
int nameLength = 0;
|
||||
fread(&nameLength, 4, 1, file);
|
||||
if (feof(file)) break;
|
||||
|
||||
char name[256] = {0};
|
||||
fread(name, 1, nameLength, file);
|
||||
|
||||
int indexCount = 0;
|
||||
fread(&indexCount, 4, 1, file);
|
||||
|
||||
uint32_t* indices = new uint32_t[indexCount];
|
||||
fread(indices, sizeof(uint32_t), indexCount, file);
|
||||
|
||||
IIndexBuffer* indexBuffer = nullptr;
|
||||
CreateIndexBuffer(device, commandList, indices, sizeof(uint32_t) * indexCount, &indexBuffer);
|
||||
|
||||
delete[] indices;
|
||||
|
||||
SubMesh* submesh = new SubMesh();
|
||||
submesh->indexBuffer = indexBuffer;
|
||||
submesh->indexCount = indexCount;
|
||||
m_subMeshes[std::string(name)] = submesh;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
void StaticMeshComponent::Render(ICommandList* commandList) {
|
||||
if (!m_vertexBuffer) return;
|
||||
|
||||
commandList->SetVertexBuffer(0, m_vertexBuffer, 0, sizeof(MeshVertex));
|
||||
|
||||
if (m_subMeshes.empty()) {
|
||||
commandList->DrawInstanced(m_vertexCount, 1, 0, 0);
|
||||
}
|
||||
else {
|
||||
for (auto& pair : m_subMeshes) {
|
||||
SubMesh* submesh = pair.second;
|
||||
commandList->SetIndexBuffer(submesh->indexBuffer, 0);
|
||||
commandList->DrawIndexedInstanced(submesh->indexCount, 1, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace RHI
|
||||
} // namespace XCEngine
|
||||
Reference in New Issue
Block a user