forked from Misaki/GhostEngine
Refactor graphics engine dependencies and structure
Removed references to `Misaki.HighPerformance.Unsafe` and replaced them with `Misaki.HighPerformance.LowLevel` in multiple files. Removed calls to `AllocationManager.Initialize()` and `AllocationManager.Dispose()` in `EngineCore.cs`. Added new methods to the `ICommandBuffer` interface for enhanced rendering capabilities. Updated the `D3D12CommandBuffer` class to implement new graphics command handling methods. Added the `Material` class to manage shader properties and caching for improved performance. Encapsulated shader compilation and reflection processes within the `Shader` class for better organization. Added the `CBufferCache` struct to optimize GPU resource management for constant buffer data. Updated the `MeshRenderPass` class to utilize the new `Material` class for dynamic mesh rendering. Updated various project files to reflect the restructuring of dependencies. Modified XAML files and code-behind for improved readability and maintainability.
This commit is contained in:
@@ -1,21 +1,103 @@
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.Shading;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Graphics.Data;
|
||||
|
||||
public class Material : IDisposable
|
||||
{
|
||||
// TODO: Pipeline state should be abstracted that can support multiple graphics APIs.
|
||||
private ComPtr<ID3D12PipelineState> _pipelineState;
|
||||
private readonly Dictionary<string, CBufferCache> _cbufferCaches;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public Shader Shader
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = Shader.Empty;
|
||||
}
|
||||
|
||||
public Material(Shader shader)
|
||||
{
|
||||
Shader = shader;
|
||||
|
||||
_cbufferCaches = new();
|
||||
foreach (var cbufferInfo in shader.ConstantBuffers.Values)
|
||||
{
|
||||
_cbufferCaches.Add(cbufferInfo.Name, new CBufferCache(cbufferInfo.Size));
|
||||
}
|
||||
}
|
||||
|
||||
~Material()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetFloat(string propertyName, in float value)
|
||||
{
|
||||
WriteToCache(propertyName, in value);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetVector(string propertyName, in Vector4 value)
|
||||
{
|
||||
WriteToCache(propertyName, in value);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetMatrix(string propertyName, in Matrix4x4 value)
|
||||
{
|
||||
WriteToCache(propertyName, in value);
|
||||
}
|
||||
|
||||
private unsafe void WriteToCache<T>(string propertyName, in T value) where T : unmanaged
|
||||
{
|
||||
if (!Shader.Properties.TryGetValue(propertyName, out var propInfo))
|
||||
{
|
||||
throw new ArgumentException($"Property '{propertyName}' does not exist in the shader.", nameof(propertyName));
|
||||
}
|
||||
|
||||
if (propInfo.Size != sizeof(T))
|
||||
{
|
||||
throw new ArgumentException($"Property '{propertyName}' has a size mismatch. Expected {sizeof(T)} bytes, but got {propInfo.Size} bytes.", nameof(propertyName));
|
||||
}
|
||||
|
||||
var cache = _cbufferCaches[propInfo.CBufferName];
|
||||
|
||||
Unsafe.WriteUnaligned(ref cache.CpuData[(int)propInfo.ByteOffset], value);
|
||||
}
|
||||
|
||||
public void UploadAndBind(ICommandBuffer cmb)
|
||||
{
|
||||
foreach (var cache in _cbufferCaches.Values)
|
||||
{
|
||||
cache.UploadToGpu();
|
||||
}
|
||||
|
||||
foreach (var (name, cache) in _cbufferCaches)
|
||||
{
|
||||
var cbufferInfo = Shader.ConstantBuffers[name];
|
||||
cmb.SetGraphicsRootConstantBufferView(cbufferInfo.RegisterSlot, cache.GpuResource.GPUAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_pipelineState.Dispose();
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var cache in _cbufferCaches.Values)
|
||||
{
|
||||
cache.Dispose();
|
||||
}
|
||||
|
||||
_cbufferCaches.Clear();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Misaki.HighPerformance.Unsafe.Collections;
|
||||
using Misaki.HighPerformance.Unsafe.Helpers;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Helpers;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.D3D12;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D;
|
||||
using Win32.Graphics.Direct3D.Fxc;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.Data;
|
||||
|
||||
public unsafe class Shader
|
||||
{
|
||||
private static readonly Shader s_empty = new("ErrorShader");
|
||||
public static Shader Empty => s_empty;
|
||||
|
||||
private ComPtr<ID3D12RootSignature> _rootSignature;
|
||||
|
||||
public ConstPtr<ID3D12RootSignature> RootSignature => new(_rootSignature.Get());
|
||||
|
||||
public Shader(string shaderPath)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compiles HLSL source code from a string into shader bytecode.
|
||||
/// </summary>
|
||||
/// <param name="sourceCode">The string containing the HLSL code.</param>
|
||||
/// <param name="entryPoint">The name of the shader entry point function (e.g., "VSMain").</param>
|
||||
/// <param name="shaderProfile">The shader model to target (e.g., "vs_5_0", "ps_5_0").</param>
|
||||
/// <returns>A byte array containing the compiled shader bytecode.</returns>
|
||||
/// <exception cref="Exception">Thrown if shader compilation fails.</exception>
|
||||
public static unsafe byte[] CompileShader(string sourceCode, string entryPoint, string shaderProfile)
|
||||
{
|
||||
ComPtr<ID3DBlob> bytecodeBlob = default;
|
||||
ComPtr<ID3DBlob> errorBlob = default;
|
||||
|
||||
// Convert strings to null-terminated ASCII for the native function
|
||||
var sourceCodeBytes = Encoding.UTF8.GetBytes(sourceCode);
|
||||
var entryPointBytes = Encoding.UTF8.GetBytes(entryPoint);
|
||||
var shaderProfileBytes = Encoding.UTF8.GetBytes(shaderProfile);
|
||||
|
||||
// Call the D3DCompile function
|
||||
var hr = D3DCompile(
|
||||
sourceCodeBytes.AsSpan(),
|
||||
entryPointBytes.AsSpan(),
|
||||
shaderProfileBytes.AsSpan(),
|
||||
CompileFlags.EnableStrictness | CompileFlags.Debug,
|
||||
bytecodeBlob.GetAddressOf(),
|
||||
errorBlob.GetAddressOf()
|
||||
);
|
||||
|
||||
if (hr.Failure)
|
||||
{
|
||||
// If compilation fails, get the error message from the error blob
|
||||
var errorMessage = "Shader compilation failed.";
|
||||
if (errorBlob.Get() is not null)
|
||||
{
|
||||
errorMessage += "\n" + Encoding.ASCII.GetString(
|
||||
(byte*)errorBlob.Get()->GetBufferPointer(),
|
||||
(int)errorBlob.Get()->GetBufferSize()
|
||||
);
|
||||
}
|
||||
errorBlob.Dispose();
|
||||
throw new Exception(errorMessage);
|
||||
}
|
||||
|
||||
// Copy the compiled bytecode from the blob into a managed byte array
|
||||
var bytecode = new byte[bytecodeBlob.Get()->GetBufferSize()];
|
||||
Marshal.Copy((IntPtr)bytecodeBlob.Get()->GetBufferPointer(), bytecode, 0, bytecode.Length);
|
||||
|
||||
// Clean up the COM blobs
|
||||
bytecodeBlob.Dispose();
|
||||
errorBlob.Dispose();
|
||||
|
||||
return bytecode;
|
||||
}
|
||||
|
||||
private void LoadShader(Span<byte> byteCode)
|
||||
{
|
||||
using ComPtr<ID3D12ShaderReflection> reflector = default;
|
||||
fixed (void* codePtr = byteCode)
|
||||
{
|
||||
D3DReflect(codePtr, (nuint)byteCode.Length, __uuidof<ID3D12ShaderReflection>(), reflector.GetVoidAddressOf());
|
||||
}
|
||||
|
||||
ShaderDescription shaderDesc;
|
||||
reflector.Get()->GetDesc(&shaderDesc);
|
||||
|
||||
var rootParameters = new List<RootParameter>();
|
||||
var staticSamplers = new List<StaticSamplerDescription>();
|
||||
|
||||
for (uint i = 0; i < shaderDesc.BoundResources; i++)
|
||||
{
|
||||
ShaderInputBindDescription bindDesc;
|
||||
reflector.Get()->GetResourceBindingDesc(i, &bindDesc);
|
||||
|
||||
switch (bindDesc.Type)
|
||||
{
|
||||
case ShaderInputType.ConstantBuffer:
|
||||
var cbufferParam = new RootParameter();
|
||||
cbufferParam.ParameterType = RootParameterType.Cbv;
|
||||
cbufferParam.ShaderVisibility = ShaderVisibility.All;
|
||||
cbufferParam.Descriptor.RegisterSpace = bindDesc.Space;
|
||||
cbufferParam.Descriptor.ShaderRegister = bindDesc.BindPoint;
|
||||
|
||||
rootParameters.Add(cbufferParam);
|
||||
|
||||
var cbuffer = reflector.Get()->GetConstantBufferByName(bindDesc.Name);
|
||||
ShaderBufferDescription cbufferDesc;
|
||||
cbuffer->GetDesc(&cbufferDesc);
|
||||
|
||||
for (var j = 0u; j < cbufferDesc.Variables; j++)
|
||||
{
|
||||
var variable = cbuffer->GetVariableByIndex(j);
|
||||
ShaderVariableDescription varDesc;
|
||||
variable->GetDesc(&varDesc);
|
||||
}
|
||||
|
||||
break;
|
||||
case ShaderInputType.TextureBuffer:
|
||||
break;
|
||||
case ShaderInputType.Texture:
|
||||
break;
|
||||
case ShaderInputType.Sampler:
|
||||
var samplerDesc = new StaticSamplerDescription
|
||||
{
|
||||
Filter = Filter.MinMagMipLinear,
|
||||
AddressU = TextureAddressMode.Wrap,
|
||||
AddressV = TextureAddressMode.Wrap,
|
||||
AddressW = TextureAddressMode.Wrap,
|
||||
ShaderVisibility = ShaderVisibility.All,
|
||||
ShaderRegister = bindDesc.BindPoint,
|
||||
RegisterSpace = bindDesc.Space,
|
||||
};
|
||||
staticSamplers.Add(samplerDesc);
|
||||
break;
|
||||
|
||||
case ShaderInputType.UavRwTyped:
|
||||
break;
|
||||
case ShaderInputType.Structured:
|
||||
break;
|
||||
case ShaderInputType.UavRwStructured:
|
||||
break;
|
||||
case ShaderInputType.ByteAddress:
|
||||
break;
|
||||
case ShaderInputType.UavRwByteAddress:
|
||||
break;
|
||||
case ShaderInputType.UavAppendStructured:
|
||||
break;
|
||||
case ShaderInputType.UavConsumeStructured:
|
||||
break;
|
||||
case ShaderInputType.UavRwStructuredWithCounter:
|
||||
break;
|
||||
case ShaderInputType.RtAccelerationStructure:
|
||||
break;
|
||||
case ShaderInputType.UavFeedbackTexture:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateRootSignature()
|
||||
{
|
||||
var rootSignatureDesc = new RootSignatureDescription();
|
||||
|
||||
using ComPtr<ID3DBlob> signature = default;
|
||||
using ComPtr<ID3DBlob> error = default;
|
||||
|
||||
var hr = D3D12SerializeRootSignature(&rootSignatureDesc, RootSignatureVersion.V1_2, signature.GetAddressOf(), error.GetAddressOf());
|
||||
if (hr.Failure)
|
||||
{
|
||||
var errorMessage = System.Text.Encoding.ASCII.GetString((byte*)error.Get()->GetBufferPointer(), (int)error.Get()->GetBufferSize());
|
||||
throw new Exception($"Failed to serialize root signature: {errorMessage}");
|
||||
}
|
||||
|
||||
GraphicsPipeline.GetGraphicsDevice<D3D12GraphicsDevice>().NativeDevice.Ptr->CreateRootSignature(0, signature.Get()->GetBufferPointer(), signature.Get()->GetBufferSize(), __uuidof<ID3D12RootSignature>(), _rootSignature.GetVoidAddressOf());
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Win32.Graphics.Direct3D;
|
||||
|
||||
namespace Ghost.Graphics.Data;
|
||||
|
||||
public class ShaderProperty
|
||||
{
|
||||
public string Name
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public ShaderInputType Type
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user