diff --git a/Ghost.Engine/EngineCore.cs b/Ghost.Engine/EngineCore.cs
index 58a69c0..9bbaf75 100644
--- a/Ghost.Engine/EngineCore.cs
+++ b/Ghost.Engine/EngineCore.cs
@@ -2,7 +2,6 @@
using Ghost.Engine.Services;
using Ghost.Graphics;
using Ghost.Graphics.Data;
-using Misaki.HighPerformance.Unsafe.Buffer;
namespace Ghost.Engine;
@@ -12,8 +11,6 @@ internal class EngineCore
{
ActivationHandler.Handle(args);
- AllocationManager.Initialize();
-
GraphicsPipeline.Initialize(GraphicsAPI.D3D12);
GraphicsPipeline.Start();
@@ -29,6 +26,5 @@ internal class EngineCore
{
GraphicsPipeline.SignalCPUReady();
GraphicsPipeline.Shutdown();
- AllocationManager.Dispose();
}
}
\ No newline at end of file
diff --git a/Ghost.Engine/Ghost.Engine.csproj b/Ghost.Engine/Ghost.Engine.csproj
index 96716b3..0f98a92 100644
--- a/Ghost.Engine/Ghost.Engine.csproj
+++ b/Ghost.Engine/Ghost.Engine.csproj
@@ -23,7 +23,7 @@
- ..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll
+ ..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.LowLevel\bin\Release\net9.0\Misaki.HighPerformance.LowLevel.dll
diff --git a/Ghost.Entities/Components/ComponentStorage.cs b/Ghost.Entities/Components/ComponentStorage.cs
index 9742979..d5969e0 100644
--- a/Ghost.Entities/Components/ComponentStorage.cs
+++ b/Ghost.Entities/Components/ComponentStorage.cs
@@ -1,5 +1,5 @@
using Ghost.Core;
-using Misaki.HighPerformance.Unsafe.Collections;
+using Misaki.HighPerformance.LowLevel.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
diff --git a/Ghost.Entities/Ghost.Entities.csproj b/Ghost.Entities/Ghost.Entities.csproj
index 6999cd3..5bb2131 100644
--- a/Ghost.Entities/Ghost.Entities.csproj
+++ b/Ghost.Entities/Ghost.Entities.csproj
@@ -27,7 +27,7 @@
- ..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll
+ ..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.LowLevel\bin\Release\net9.0\Misaki.HighPerformance.LowLevel.dll
diff --git a/Ghost.Entities/Query/QueryFilter.cs b/Ghost.Entities/Query/QueryFilter.cs
index c2aaae7..71032e2 100644
--- a/Ghost.Entities/Query/QueryFilter.cs
+++ b/Ghost.Entities/Query/QueryFilter.cs
@@ -1,5 +1,5 @@
using Ghost.Core;
-using Misaki.HighPerformance.Unsafe.Collections;
+using Misaki.HighPerformance.LowLevel.Collections;
namespace Ghost.Entities.Query;
diff --git a/Ghost.Entities/Template/QueryEnumerable.cs b/Ghost.Entities/Template/QueryEnumerable.cs
index 56b947b..062fbc9 100644
--- a/Ghost.Entities/Template/QueryEnumerable.cs
+++ b/Ghost.Entities/Template/QueryEnumerable.cs
@@ -3,7 +3,7 @@
using Ghost.Core;
using Ghost.Entities.Components;
using Ghost.Entities.Query;
-using Misaki.HighPerformance.Unsafe.Collections;
+using Misaki.HighPerformance.LowLevel.Collections;
namespace Ghost.Entities;
diff --git a/Ghost.Entities/Template/QueryEnumerable.tt b/Ghost.Entities/Template/QueryEnumerable.tt
index 6c45780..876b0fe 100644
--- a/Ghost.Entities/Template/QueryEnumerable.tt
+++ b/Ghost.Entities/Template/QueryEnumerable.tt
@@ -8,7 +8,7 @@
using Ghost.Core;
using Ghost.Entities.Components;
using Ghost.Entities.Query;
-using Misaki.HighPerformance.Unsafe.Collections;
+using Misaki.HighPerformance.LowLevel.Collections;
namespace Ghost.Entities;
diff --git a/Ghost.Graphics/Contracts/ICommandBuffer.cs b/Ghost.Graphics/Contracts/ICommandBuffer.cs
index dca49bf..6757fed 100644
--- a/Ghost.Graphics/Contracts/ICommandBuffer.cs
+++ b/Ghost.Graphics/Contracts/ICommandBuffer.cs
@@ -5,7 +5,10 @@ namespace Ghost.Graphics.Contracts;
public interface ICommandBuffer
{
- public void DrawMesh(Mesh mesh);
- public void CopyResource(IResource dstResource, uint dstOffset, IResource srcResource, uint srcOffset, uint size);
+ // TODO: They should be internal, maybe an interface ICommandBufferInternal?
public void BarrierTransition(IResource resource, ResourceStates beforeState, ResourceStates afterState);
+ public void SetGraphicsRootConstantBufferView(uint slot, ulong gpuAddress);
+
+ public void DrawMesh(Mesh mesh, Material material);
+ public void CopyResource(IResource dstResource, uint dstOffset, IResource srcResource, uint srcOffset, uint size);
}
\ No newline at end of file
diff --git a/Ghost.Graphics/Contracts/IResource.cs b/Ghost.Graphics/Contracts/IResource.cs
index ce8aea5..c062e34 100644
--- a/Ghost.Graphics/Contracts/IResource.cs
+++ b/Ghost.Graphics/Contracts/IResource.cs
@@ -1,6 +1,8 @@
-namespace Ghost.Graphics.Contracts;
+using Misaki.HighPerformance.LowLevel.Collections;
-public interface IResource : IDisposable
+namespace Ghost.Graphics.Contracts;
+
+public unsafe interface IResource : IDisposable
{
public ulong GPUAddress
{
@@ -20,4 +22,17 @@ public interface IResource : IDisposable
public void SetData(Span data)
where T : unmanaged;
+
+ public void SetData(T* data, uint length)
+ where T : unmanaged;
+
+ public void SetData(void* data, uint size);
+
+ public UnsafeArray ReadData(Allocator allocator)
+ where T : unmanaged;
+
+ public void ReadData(T* ppData, uint* size)
+ where T : unmanaged;
+
+ public void ReadData(void* ppData, uint* size);
}
\ No newline at end of file
diff --git a/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs b/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs
index 5003517..4ee6a9c 100644
--- a/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs
+++ b/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs
@@ -17,8 +17,24 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_commandList = commandList;
}
- public void DrawMesh(Mesh mesh)
+ public void BarrierTransition(IResource resource, ResourceStates beforeState, ResourceStates afterState)
{
+ var dxResource = (D3D12Resource)resource;
+ _commandList.Ptr->ResourceBarrierTransition(dxResource.NativeResource.Ptr, beforeState, afterState);
+ }
+
+ public void SetGraphicsRootConstantBufferView(uint slot, ulong gpuAddress)
+ {
+ _commandList.Ptr->SetGraphicsRootConstantBufferView(slot, gpuAddress);
+ }
+
+ public void DrawMesh(Mesh mesh, Material material)
+ {
+ _commandList.Ptr->SetGraphicsRootSignature(material.Shader.RootSignature);
+ _commandList.Ptr->SetPipelineState(material.Shader.PipelineState);
+
+ material.UploadAndBind(this);
+
_commandList.Ptr->IASetPrimitiveTopology(PrimitiveTopology.TriangleList);
_commandList.Ptr->IASetVertexBuffers(0, 1, mesh.VertexBufferView);
_commandList.Ptr->IASetIndexBuffer(mesh.IndexBufferView);
@@ -33,10 +49,4 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_commandList.Ptr->CopyBufferRegion(dstDXResource.NativeResource, dstOffset, srcDXResource.NativeResource, srcOffset, size);
}
-
- public void BarrierTransition(IResource resource, ResourceStates beforeState, ResourceStates afterState)
- {
- var dxResource = (D3D12Resource)resource;
- _commandList.Ptr->ResourceBarrierTransition(dxResource.NativeResource.Ptr, beforeState, afterState);
- }
}
\ No newline at end of file
diff --git a/Ghost.Graphics/D3D12/D3D12Renderer.cs b/Ghost.Graphics/D3D12/D3D12Renderer.cs
index 33dbb53..c0c404b 100644
--- a/Ghost.Graphics/D3D12/D3D12Renderer.cs
+++ b/Ghost.Graphics/D3D12/D3D12Renderer.cs
@@ -224,7 +224,7 @@ internal unsafe class D3D12Renderer : IRenderer
ref var frameResource = ref _frameResources[i];
if (frameResource.backBuffer.Get() is not null)
{
- frameResource.backBuffer.Dispose();
+ var c = frameResource.backBuffer.Reset();
_rtvHeap.ReleaseDescriptor(frameResource.backBufferDescriptorIndexes);
}
diff --git a/Ghost.Graphics/D3D12/D3D12Resource.cs b/Ghost.Graphics/D3D12/D3D12Resource.cs
index fa3f292..17e7f46 100644
--- a/Ghost.Graphics/D3D12/D3D12Resource.cs
+++ b/Ghost.Graphics/D3D12/D3D12Resource.cs
@@ -1,5 +1,6 @@
using Ghost.Core;
using Ghost.Graphics.Contracts;
+using Misaki.HighPerformance.LowLevel.Collections;
using System.Runtime.CompilerServices;
using Win32;
using Win32.Graphics.Direct3D12;
@@ -8,13 +9,11 @@ namespace Ghost.Graphics.D3D12;
public unsafe class D3D12Resource : IResource
{
- private ComPtr _nativeResource
- {
- get;
- set;
- }
+ private ComPtr _nativeResource;
private string _name = string.Empty;
+ private bool _disposed;
+
internal ConstPtr NativeResource => new(_nativeResource.Get());
public ulong GPUAddress => _nativeResource.Get()->GetGPUVirtualAddress();
@@ -40,30 +39,101 @@ public unsafe class D3D12Resource : IResource
TempResource = temp;
}
+ ~D3D12Resource()
+ {
+ DisposeInternal();
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetData(Span data)
where T : unmanaged
{
- var size = (uint)(data.Length * sizeof(T));
- var range = new Win32.Graphics.Direct3D12.Range(0, size);
-
fixed (T* ptr = data)
{
- void* mappedPtr;
- var hr = _nativeResource.Get()->Map(0, &range, &mappedPtr);
- if (hr.Failure)
- {
- var message = hr.ToString();
- throw new InvalidOperationException($"Failed to map resource: {message}");
- }
- Unsafe.CopyBlock(mappedPtr, ptr, size);
- _nativeResource.Get()->Unmap(0, &range);
+ SetData(ptr, (uint)data.Length);
+ }
+ }
+
+ public unsafe void SetData(T* data, uint length)
+ where T : unmanaged
+ {
+ var size = (uint)(length * sizeof(T));
+ SetData((void*)data, size);
+ }
+
+ public unsafe void SetData(void* data, uint size)
+ {
+ ObjectDisposedException.ThrowIf(_disposed, this);
+
+ var range = new Win32.Graphics.Direct3D12.Range(0, size);
+
+ void* mappedPtr;
+ var hr = _nativeResource.Get()->Map(0, &range, &mappedPtr);
+ if (hr.Failure)
+ {
+ var message = hr.ToString();
+ throw new InvalidOperationException($"Failed to map resource: {message}");
+ }
+
+ Unsafe.CopyBlock(mappedPtr, data, size);
+ _nativeResource.Get()->Unmap(0, &range);
+ }
+
+ public UnsafeArray ReadData(Allocator allocator)
+ where T : unmanaged
+ {
+ var size = (uint)_nativeResource.Get()->GetDesc().Width;
+ var data = new UnsafeArray((int)(size / (uint)sizeof(T)), allocator);
+ try
+ {
+ ReadData(data.GetUnsafePtr(), &size);
+ return data;
+ }
+ catch (Exception)
+ {
+ data.Dispose();
+ throw;
+ }
+ }
+
+ public void ReadData(T* pData, uint* size)
+ where T : unmanaged
+ {
+ ReadData(pData, size);
+ }
+
+ public void ReadData(void* pData, uint* size)
+ {
+ ObjectDisposedException.ThrowIf(_disposed, this);
+
+ var range = new Win32.Graphics.Direct3D12.Range(0, (uint)_nativeResource.Get()->GetDesc().Width);
+
+ void* mappedPtr;
+ var hr = _nativeResource.Get()->Map(0, &range, &mappedPtr);
+ if (hr.Failure)
+ {
+ var message = hr.ToString();
+ throw new InvalidOperationException($"Failed to map resource: {message}");
+ }
+
+ Unsafe.CopyBlock(pData, mappedPtr, (uint)(range.End - range.Begin));
+ _nativeResource.Get()->Unmap(0, &range);
+ if (size != null)
+ {
+ *size = (uint)(range.End - range.Begin);
}
}
internal void DisposeInternal()
{
- var c = _nativeResource.Reset();
+ if (_disposed)
+ {
+ return;
+ }
+
+ _nativeResource.Dispose();
+
+ _disposed = true;
}
public void Dispose()
@@ -71,6 +141,7 @@ public unsafe class D3D12Resource : IResource
if (!TempResource)
{
DisposeInternal();
+ GC.SuppressFinalize(this);
}
}
}
\ No newline at end of file
diff --git a/Ghost.Graphics/Data/Material.cs b/Ghost.Graphics/Data/Material.cs
index 8462f94..883f9f1 100644
--- a/Ghost.Graphics/Data/Material.cs
+++ b/Ghost.Graphics/Data/Material.cs
@@ -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 _pipelineState;
+ private readonly Dictionary _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(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;
}
}
\ No newline at end of file
diff --git a/Ghost.Graphics/Data/Mesh.cs b/Ghost.Graphics/Data/Mesh.cs
index 93e565b..c08f4d1 100644
--- a/Ghost.Graphics/Data/Mesh.cs
+++ b/Ghost.Graphics/Data/Mesh.cs
@@ -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;
diff --git a/Ghost.Graphics/Data/Shader.cs b/Ghost.Graphics/Data/Shader.cs
deleted file mode 100644
index 74a1bfe..0000000
--- a/Ghost.Graphics/Data/Shader.cs
+++ /dev/null
@@ -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 _rootSignature;
-
- public ConstPtr RootSignature => new(_rootSignature.Get());
-
- public Shader(string shaderPath)
- {
- }
-
- ///
- /// Compiles HLSL source code from a string into shader bytecode.
- ///
- /// The string containing the HLSL code.
- /// The name of the shader entry point function (e.g., "VSMain").
- /// The shader model to target (e.g., "vs_5_0", "ps_5_0").
- /// A byte array containing the compiled shader bytecode.
- /// Thrown if shader compilation fails.
- public static unsafe byte[] CompileShader(string sourceCode, string entryPoint, string shaderProfile)
- {
- ComPtr bytecodeBlob = default;
- ComPtr 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 byteCode)
- {
- using ComPtr reflector = default;
- fixed (void* codePtr = byteCode)
- {
- D3DReflect(codePtr, (nuint)byteCode.Length, __uuidof(), reflector.GetVoidAddressOf());
- }
-
- ShaderDescription shaderDesc;
- reflector.Get()->GetDesc(&shaderDesc);
-
- var rootParameters = new List();
- var staticSamplers = new List();
-
- 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 signature = default;
- using ComPtr 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().NativeDevice.Ptr->CreateRootSignature(0, signature.Get()->GetBufferPointer(), signature.Get()->GetBufferSize(), __uuidof(), _rootSignature.GetVoidAddressOf());
- }
-}
\ No newline at end of file
diff --git a/Ghost.Graphics/Data/ShaderProperty.cs b/Ghost.Graphics/Data/ShaderProperty.cs
deleted file mode 100644
index ce8252c..0000000
--- a/Ghost.Graphics/Data/ShaderProperty.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using Win32.Graphics.Direct3D;
-
-namespace Ghost.Graphics.Data;
-
-public class ShaderProperty
-{
- public string Name
- {
- get;
- }
-
- public ShaderInputType Type
- {
- get;
- }
-}
\ No newline at end of file
diff --git a/Ghost.Graphics/Ghost.Graphics.csproj b/Ghost.Graphics/Ghost.Graphics.csproj
index f8eefe5..33f5a54 100644
--- a/Ghost.Graphics/Ghost.Graphics.csproj
+++ b/Ghost.Graphics/Ghost.Graphics.csproj
@@ -27,8 +27,11 @@
-
- ..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll
+
+ ..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Image\bin\Release\net9.0\Misaki.HighPerformance.Image.dll
+
+
+ ..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.LowLevel\bin\Release\net9.0\Misaki.HighPerformance.LowLevel.dll
diff --git a/Ghost.Graphics/RenderPasses/MeshRenderPass.cs b/Ghost.Graphics/RenderPasses/MeshRenderPass.cs
index 9517b7a..dd2cc73 100644
--- a/Ghost.Graphics/RenderPasses/MeshRenderPass.cs
+++ b/Ghost.Graphics/RenderPasses/MeshRenderPass.cs
@@ -1,24 +1,18 @@
using Ghost.Graphics.Contracts;
-using Ghost.Graphics.D3D12;
-using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.Data;
+using Ghost.Graphics.Shading;
using Ghost.Graphics.Utilities;
using System.Drawing;
-using System.Runtime.CompilerServices;
-using Win32;
-using Win32.Graphics.Direct3D;
-using Win32.Graphics.Direct3D12;
-using Win32.Graphics.Dxgi.Common;
+using System.Numerics;
namespace Ghost.Graphics.RenderPasses;
internal unsafe class MeshRenderPass : IRenderPass
{
private const string _HLSL_SOURCE = @"
-
cbuffer ConstantBuffer : register(b0)
{
- float4x4 WVP_Matrix;
+ float4 _Color;
};
struct VertexInput
@@ -43,105 +37,35 @@ PixelInput VSMain(VertexInput input)
float4 PSMain(PixelInput input) : SV_TARGET
{
- return float4(1.0, 1.0, 1.0, 1.0);
+ return float4(_Color.xyz, 1.0);
}
";
private Mesh? _mesh;
-
- private ComPtr _rootSignature;
- private ComPtr _pipelineState;
+ private Shader? _shader;
+ private Material? _material;
public void Initialize(ICommandBuffer cmb)
{
- _mesh = MeshBuilder.CreateCube(0.25f, new(Color.AliceBlue));
+ _mesh = MeshBuilder.CreateCube(0.25f);
_mesh.UploadMeshData(cmb);
- CreateRootSignature();
- CreatePipelineStateObject();
- }
+ _shader = new(_HLSL_SOURCE);
+ _material = new(_shader);
- private void CreateRootSignature()
- {
- var rootParameters = new RootParameter[]
- {
- new ()
- {
- ParameterType = RootParameterType.Cbv,
- ShaderVisibility = ShaderVisibility.Vertex,
- Descriptor = new RootDescriptor(0, 0)
- }
- };
-
- var rootSignatureDesc = new RootSignatureDescription(0u, (RootParameter*)Unsafe.AsPointer(ref rootParameters[0]))
- {
- Flags = RootSignatureFlags.AllowInputAssemblerInputLayout
- };
-
- using ComPtr signature = default;
- using ComPtr error = default;
-
- var hr = D3D12SerializeRootSignature(&rootSignatureDesc, RootSignatureVersion.V1_0, signature.GetAddressOf(), error.GetAddressOf());
- if (hr.Failure)
- {
- var errorMessage = System.Text.Encoding.ASCII.GetString((byte*)error.Get()->GetBufferPointer(), (int)error.Get()->GetBufferSize());
- throw new InvalidOperationException($"Failed to serialize root signature: {errorMessage}");
- }
-
- GraphicsPipeline.GetGraphicsDevice().NativeDevice.Ptr->CreateRootSignature(0, signature.Get()->GetBufferPointer(), signature.Get()->GetBufferSize(), __uuidof(), _rootSignature.GetVoidAddressOf());
- }
-
- private void CreatePipelineStateObject()
- {
- try
- {
- var vertexShaderBytecode = Shader.CompileShader(_HLSL_SOURCE, "VSMain", "vs_5_0");
- var pixelShaderBytecode = Shader.CompileShader(_HLSL_SOURCE, "PSMain", "ps_5_0");
-
- fixed (byte* vsPtr = vertexShaderBytecode)
- fixed (byte* psPtr = pixelShaderBytecode)
- {
- var psoDesc = new GraphicsPipelineStateDescription
- {
- pRootSignature = _rootSignature.Get(),
- VS = new ShaderBytecode(vsPtr, (nuint)vertexShaderBytecode.Length),
- PS = new ShaderBytecode(psPtr, (nuint)pixelShaderBytecode.Length),
- InputLayout = D3D12PipelineResource.InputLayoutDescription,
- RasterizerState = RasterizerDescription.CullNone,
- BlendState = BlendDescription.Opaque,
- DepthStencilState = DepthStencilDescription.Default,
- SampleMask = uint.MaxValue,
- PrimitiveTopologyType = PrimitiveTopologyType.Triangle,
- NumRenderTargets = 1,
- SampleDesc = new SampleDescription(1, 0),
- DSVFormat = Format.Unknown,
- };
-
- psoDesc.RTVFormats[0] = D3D12PipelineResource.SWAP_CHAIN_BACK_BUFFER_FORMAT;
-
- // Create the PSO
- GraphicsPipeline.GetGraphicsDevice().NativeDevice.Ptr->CreateGraphicsPipelineState(&psoDesc, __uuidof(), _pipelineState.GetVoidAddressOf());
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.ToString());
- }
+ var color = new Vector4(Color.Brown.R / 255f, Color.Brown.G / 255f, Color.Brown.B / 255f, 1.0f);
+ _material.SetVector("_Color", ref color);
}
public void Execute(ICommandBuffer cmb)
{
- var dx12Cmb = (D3D12CommandBuffer)cmb;
- dx12Cmb.CommandList.Ptr->SetGraphicsRootSignature(_rootSignature.Get());
- dx12Cmb.CommandList.Ptr->SetPipelineState(_pipelineState.Get());
-
- cmb.DrawMesh(_mesh!);
+ cmb.DrawMesh(_mesh!, _material!);
}
public void Dispose()
{
_mesh?.Dispose();
- _rootSignature.Dispose();
- _pipelineState.Dispose();
+ _shader?.Dispose();
+ _material?.Dispose();
}
}
diff --git a/Ghost.Graphics/Shading/CBufferCache.cs b/Ghost.Graphics/Shading/CBufferCache.cs
new file mode 100644
index 0000000..0d8c895
--- /dev/null
+++ b/Ghost.Graphics/Shading/CBufferCache.cs
@@ -0,0 +1,43 @@
+using Ghost.Graphics.Contracts;
+using Misaki.HighPerformance.LowLevel.Collections;
+using Misaki.HighPerformance.LowLevel.Helpers;
+using System.Runtime.CompilerServices;
+
+namespace Ghost.Graphics.Shading;
+
+internal struct CBufferCache : IDisposable
+{
+ public UnsafeArray CpuData
+ {
+ get;
+ }
+
+ public IResource GpuResource
+ {
+ get;
+ }
+
+ private readonly uint _alignedSize;
+
+ public unsafe CBufferCache(uint bufferSize)
+ {
+ CpuData = new((int)bufferSize, Allocator.Persistent);
+
+ _alignedSize = (bufferSize + 255u) & ~255u;
+ GpuResource = GraphicsPipeline.ResourceAllocator.CreateUploadBuffer(_alignedSize);
+ GpuResource.Name = "Material_CBufferCache";
+
+ UploadToGpu();
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public readonly void UploadToGpu()
+ {
+ GpuResource.SetData(CpuData.AsSpan());
+ }
+
+ public readonly void Dispose()
+ {
+ GpuResource.Dispose();
+ }
+}
\ No newline at end of file
diff --git a/Ghost.Graphics/Shading/Shader.cs b/Ghost.Graphics/Shading/Shader.cs
new file mode 100644
index 0000000..4b799bf
--- /dev/null
+++ b/Ghost.Graphics/Shading/Shader.cs
@@ -0,0 +1,291 @@
+using Ghost.Core;
+using Ghost.Graphics.D3D12;
+using Ghost.Graphics.D3D12.Utilities;
+using Misaki.HighPerformance.LowLevel.Helpers;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+using Win32;
+using Win32.Graphics.Direct3D;
+using Win32.Graphics.Direct3D.Fxc;
+using Win32.Graphics.Direct3D12;
+using Win32.Graphics.Dxgi.Common;
+
+namespace Ghost.Graphics.Shading;
+
+internal readonly struct PropertyInfo
+{
+ public required string Name
+ {
+ get; init;
+ }
+
+ public required string CBufferName
+ {
+ get; init;
+ }
+
+ public uint ByteOffset
+ {
+ get; init;
+ }
+
+ public uint Size
+ {
+ get; init;
+ }
+}
+
+internal readonly struct CBufferInfo
+{
+ public required string Name
+ {
+ get; init;
+ }
+
+ public uint Size
+ {
+ get; init;
+ }
+
+ public uint RegisterSlot
+ {
+ get; init;
+ }
+}
+
+public unsafe class Shader : IDisposable
+{
+ private ComPtr _pipelineState;
+ private ComPtr _rootSignature;
+
+ private readonly byte[] _vertexShaderBytecode;
+ private readonly byte[] _pixelShaderBytecode;
+
+ private readonly Dictionary _constantBuffers = new();
+ private readonly Dictionary _properties = new();
+
+ private bool _disposed;
+
+ internal ConstPtr PipelineState => new(_pipelineState.Get());
+ internal ConstPtr RootSignature => new(_rootSignature.Get());
+
+ internal IReadOnlyDictionary ConstantBuffers => _constantBuffers;
+ internal IReadOnlyDictionary Properties => _properties;
+
+ //public Shader(string shaderPath)
+ //{
+
+ //}
+
+ public Shader(string shaderCode)
+ {
+ _vertexShaderBytecode = CompileShader(Encoding.UTF8.GetBytes(shaderCode), "VSMain", "vs_5_0");
+ _pixelShaderBytecode = CompileShader(Encoding.UTF8.GetBytes(shaderCode), "PSMain", "ps_5_0");
+
+ PerformReflection(_vertexShaderBytecode);
+ PerformReflection(_pixelShaderBytecode);
+
+ CreateRootSignature();
+ CreatePipelineStateObject();
+ }
+
+ ~Shader()
+ {
+ Dispose();
+ }
+
+ ///
+ /// Compiles HLSL source code from a string into shader bytecode.
+ ///
+ /// The string containing the HLSL code.
+ /// The name of the shader entry point function (e.g., "VSMain").
+ /// The shader model to target (e.g., "vs_5_0", "ps_5_0").
+ /// A byte array containing the compiled shader bytecode.
+ /// Thrown if shader compilation fails.
+ public static unsafe byte[] CompileShader(byte[] sourceCodeBytes, string entryPoint, string shaderProfile)
+ {
+ using ComPtr bytecodeBlob = default;
+ using ComPtr errorBlob = default;
+
+ 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)
+ {
+ var errorMessage = "Shader compilation failed.";
+ if (errorBlob.Get() is not null)
+ {
+ errorMessage += "\n" + Encoding.ASCII.GetString(
+ (byte*)errorBlob.Get()->GetBufferPointer(),
+ (int)errorBlob.Get()->GetBufferSize()
+ );
+ }
+
+ throw new Exception(errorMessage);
+ }
+
+ var bytecode = new byte[bytecodeBlob.Get()->GetBufferSize()];
+ Unsafe.CopyBlock(bytecode.AsSpan().GetPointer(), bytecodeBlob.Get()->GetBufferPointer(), (uint)bytecode.Length);
+
+ return bytecode;
+ }
+
+ private void CreateRootSignature()
+ {
+ var rootParameters = new RootParameter1[_constantBuffers.Values.Count];
+
+ var i = 0;
+ foreach (var cbufferInfo in _constantBuffers.Values)
+ {
+ var rootParameter = new RootParameter1
+ {
+ ParameterType = RootParameterType.Cbv,
+ ShaderVisibility = ShaderVisibility.All,
+ Descriptor = new RootDescriptor1(cbufferInfo.RegisterSlot, 0),
+ };
+
+ rootParameters[i++] = rootParameter;
+ }
+
+ var rootSignatureDesc = new RootSignatureDescription((uint)rootParameters.Length, (RootParameter*)Unsafe.AsPointer(ref rootParameters[0]))
+ {
+ Flags = RootSignatureFlags.AllowInputAssemblerInputLayout
+ };
+
+ using ComPtr signature = default;
+ using ComPtr error = default;
+
+ var hr = D3D12SerializeRootSignature(&rootSignatureDesc, RootSignatureVersion.V1_0, signature.GetAddressOf(), error.GetAddressOf());
+ if (hr.Failure)
+ {
+ var errorMessage = System.Text.Encoding.ASCII.GetString((byte*)error.Get()->GetBufferPointer(), (int)error.Get()->GetBufferSize());
+ throw new InvalidOperationException($"Failed to serialize root signature: {errorMessage}");
+ }
+
+ GraphicsPipeline.GetGraphicsDevice().NativeDevice.Ptr->CreateRootSignature(0, signature.Get()->GetBufferPointer(), signature.Get()->GetBufferSize(), __uuidof(), _rootSignature.GetVoidAddressOf());
+ }
+
+ private void CreatePipelineStateObject()
+ {
+ try
+ {
+ fixed (byte* vsPtr = _vertexShaderBytecode)
+ fixed (byte* psPtr = _pixelShaderBytecode)
+ {
+ var psoDesc = new GraphicsPipelineStateDescription
+ {
+ pRootSignature = _rootSignature.Get(),
+ VS = new ShaderBytecode(vsPtr, (nuint)_vertexShaderBytecode.Length),
+ PS = new ShaderBytecode(psPtr, (nuint)_pixelShaderBytecode.Length),
+ InputLayout = D3D12PipelineResource.InputLayoutDescription,
+ RasterizerState = RasterizerDescription.CullNone,
+ BlendState = BlendDescription.Opaque,
+ DepthStencilState = DepthStencilDescription.Default,
+ SampleMask = uint.MaxValue,
+ PrimitiveTopologyType = PrimitiveTopologyType.Triangle,
+ NumRenderTargets = 1,
+ SampleDesc = new SampleDescription(1, 0),
+ DSVFormat = Format.Unknown,
+ };
+
+ psoDesc.RTVFormats[0] = D3D12PipelineResource.SWAP_CHAIN_BACK_BUFFER_FORMAT;
+
+ // Create the PSO
+ GraphicsPipeline.GetGraphicsDevice().NativeDevice.Ptr->CreateGraphicsPipelineState(&psoDesc, __uuidof(), _pipelineState.GetVoidAddressOf());
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex.ToString());
+ }
+ }
+
+ private unsafe void PerformReflection(Span byteCode)
+ {
+ using ComPtr reflection = default;
+ fixed (void* codePtr = byteCode)
+ {
+ D3DReflect(codePtr, (nuint)byteCode.Length, __uuidof(), reflection.GetVoidAddressOf());
+ }
+
+ ShaderDescription shaderDesc;
+ reflection.Get()->GetDesc(&shaderDesc);
+
+ for (uint i = 0; i < shaderDesc.BoundResources; i++)
+ {
+ ShaderInputBindDescription bindDesc;
+ reflection.Get()->GetResourceBindingDesc(i, &bindDesc);
+
+ if (bindDesc.Type == ShaderInputType.ConstantBuffer)
+ {
+ var cbufferName = Marshal.PtrToStringAnsi((IntPtr)bindDesc.Name);
+ if (cbufferName == null || _constantBuffers.ContainsKey(cbufferName))
+ {
+ continue;
+ }
+
+ var cbuffer = reflection.Get()->GetConstantBufferByName(bindDesc.Name);
+ ShaderBufferDescription cbufferDesc;
+ cbuffer->GetDesc(&cbufferDesc);
+
+ _constantBuffers.Add(cbufferName, new CBufferInfo
+ {
+ Name = cbufferName,
+ Size = cbufferDesc.Size,
+ RegisterSlot = bindDesc.BindPoint
+ });
+
+ for (uint j = 0; j < cbufferDesc.Variables; j++)
+ {
+ var variable = cbuffer->GetVariableByIndex(j);
+ ShaderVariableDescription varDesc;
+ variable->GetDesc(&varDesc);
+
+ var variableName = Marshal.PtrToStringAnsi((IntPtr)varDesc.Name);
+ if (variableName == null || _properties.ContainsKey(variableName))
+ {
+ continue;
+ }
+
+ _properties.Add(variableName, new PropertyInfo
+ {
+ Name = variableName,
+ CBufferName = cbufferName,
+ ByteOffset = varDesc.StartOffset,
+ Size = varDesc.Size
+ });
+ }
+ }
+ }
+ reflection.Dispose();
+ }
+
+ public void Dispose()
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ _pipelineState.Dispose();
+ _rootSignature.Dispose();
+
+ _constantBuffers.Clear();
+ _properties.Clear();
+
+ GC.SuppressFinalize(this);
+
+ _disposed = true;
+ }
+}
\ No newline at end of file
diff --git a/Ghost.Graphics/Shading/ShaderProperty.cs b/Ghost.Graphics/Shading/ShaderProperty.cs
new file mode 100644
index 0000000..c82b512
--- /dev/null
+++ b/Ghost.Graphics/Shading/ShaderProperty.cs
@@ -0,0 +1,48 @@
+using Misaki.HighPerformance.LowLevel.Buffer;
+using Misaki.HighPerformance.LowLevel.Collections;
+using Misaki.HighPerformance.LowLevel.Helpers;
+
+namespace Ghost.Graphics.Shading;
+
+public enum ShaderPropertyType
+{
+ Float,
+ Float2,
+ Float3,
+ Float4,
+ Color,
+ Matrix,
+ Texture2D,
+ Texture3D
+}
+
+public struct ShaderProperty : IDisposable
+{
+ private UnsafeArray _value;
+ private FixedString128 _name;
+ private readonly uint _valueOffset;
+
+ internal readonly uint Offset => _valueOffset;
+
+ public readonly string Name => _name.Value;
+ public readonly ReadOnlySpan Value => _value.AsSpan();
+
+ public ShaderPropertyType PropertyType
+ {
+ get;
+ }
+
+ public ShaderProperty(Span value, uint offset, string name, ShaderPropertyType type)
+ {
+ _value = new(value.Length, Allocator.Persistent);
+ _valueOffset = offset;
+ _name = new(name);
+ PropertyType = type;
+ }
+
+ public void Dispose()
+ {
+ _value.Dispose();
+ _name.Dispose();
+ }
+}
\ No newline at end of file
diff --git a/Ghost.UnitTest/Ghost.UnitTest.csproj b/Ghost.UnitTest/Ghost.UnitTest.csproj
index 4992dfe..20d0e22 100644
--- a/Ghost.UnitTest/Ghost.UnitTest.csproj
+++ b/Ghost.UnitTest/Ghost.UnitTest.csproj
@@ -56,7 +56,7 @@
- ..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll
+ ..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.LowLevel\bin\Release\net9.0\Misaki.HighPerformance.LowLevel.dll
diff --git a/Ghost.UnitTest/UnitTestApp.xaml b/Ghost.UnitTest/UnitTestApp.xaml
index 2b53ec9..638fd34 100644
--- a/Ghost.UnitTest/UnitTestApp.xaml
+++ b/Ghost.UnitTest/UnitTestApp.xaml
@@ -1,4 +1,4 @@
-
+
-
+
-
+
diff --git a/Ghost.UnitTest/UnitTestAppWindow.xaml.cs b/Ghost.UnitTest/UnitTestAppWindow.xaml.cs
index e792496..cf3aa1f 100644
--- a/Ghost.UnitTest/UnitTestAppWindow.xaml.cs
+++ b/Ghost.UnitTest/UnitTestAppWindow.xaml.cs
@@ -2,7 +2,6 @@ using Ghost.Graphics;
using Ghost.Graphics.Contracts;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
-using Misaki.HighPerformance.Unsafe.Buffer;
using WinRT;
namespace Ghost.UnitTest;
@@ -32,7 +31,6 @@ public sealed partial class UnitTestAppWindow : Window
private void UnitTestAppWindow_Activated(object sender, WindowActivatedEventArgs args)
{
- AllocationManager.Initialize();
GraphicsPipeline.Initialize(Graphics.Data.GraphicsAPI.D3D12);
GraphicsPipeline.Start();
@@ -49,7 +47,6 @@ public sealed partial class UnitTestAppWindow : Window
{
GraphicsPipeline.SignalCPUReady();
GraphicsPipeline.Shutdown();
- AllocationManager.Dispose();
CompositionTarget.Rendering -= OnRendering;
_swapChainPanelNative.Dispose();
_renderView?.Dispose();