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:
@@ -2,7 +2,6 @@
|
|||||||
using Ghost.Engine.Services;
|
using Ghost.Engine.Services;
|
||||||
using Ghost.Graphics;
|
using Ghost.Graphics;
|
||||||
using Ghost.Graphics.Data;
|
using Ghost.Graphics.Data;
|
||||||
using Misaki.HighPerformance.Unsafe.Buffer;
|
|
||||||
|
|
||||||
namespace Ghost.Engine;
|
namespace Ghost.Engine;
|
||||||
|
|
||||||
@@ -12,8 +11,6 @@ internal class EngineCore
|
|||||||
{
|
{
|
||||||
ActivationHandler.Handle(args);
|
ActivationHandler.Handle(args);
|
||||||
|
|
||||||
AllocationManager.Initialize();
|
|
||||||
|
|
||||||
GraphicsPipeline.Initialize(GraphicsAPI.D3D12);
|
GraphicsPipeline.Initialize(GraphicsAPI.D3D12);
|
||||||
GraphicsPipeline.Start();
|
GraphicsPipeline.Start();
|
||||||
|
|
||||||
@@ -29,6 +26,5 @@ internal class EngineCore
|
|||||||
{
|
{
|
||||||
GraphicsPipeline.SignalCPUReady();
|
GraphicsPipeline.SignalCPUReady();
|
||||||
GraphicsPipeline.Shutdown();
|
GraphicsPipeline.Shutdown();
|
||||||
AllocationManager.Dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Misaki.HighPerformance.Unsafe">
|
<Reference Include="Misaki.HighPerformance.Unsafe">
|
||||||
<HintPath>..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll</HintPath>
|
<HintPath>..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.LowLevel\bin\Release\net9.0\Misaki.HighPerformance.LowLevel.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Misaki.HighPerformance.Unsafe.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Misaki.HighPerformance.Unsafe">
|
<Reference Include="Misaki.HighPerformance.Unsafe">
|
||||||
<HintPath>..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll</HintPath>
|
<HintPath>..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.LowLevel\bin\Release\net9.0\Misaki.HighPerformance.LowLevel.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Misaki.HighPerformance.Unsafe.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
|
||||||
namespace Ghost.Entities.Query;
|
namespace Ghost.Entities.Query;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Entities.Components;
|
using Ghost.Entities.Components;
|
||||||
using Ghost.Entities.Query;
|
using Ghost.Entities.Query;
|
||||||
using Misaki.HighPerformance.Unsafe.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
|
||||||
namespace Ghost.Entities;
|
namespace Ghost.Entities;
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Entities.Components;
|
using Ghost.Entities.Components;
|
||||||
using Ghost.Entities.Query;
|
using Ghost.Entities.Query;
|
||||||
using Misaki.HighPerformance.Unsafe.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
|
||||||
namespace Ghost.Entities;
|
namespace Ghost.Entities;
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ namespace Ghost.Graphics.Contracts;
|
|||||||
|
|
||||||
public interface ICommandBuffer
|
public interface ICommandBuffer
|
||||||
{
|
{
|
||||||
public void DrawMesh(Mesh mesh);
|
// TODO: They should be internal, maybe an interface ICommandBufferInternal?
|
||||||
public void CopyResource(IResource dstResource, uint dstOffset, IResource srcResource, uint srcOffset, uint size);
|
|
||||||
public void BarrierTransition(IResource resource, ResourceStates beforeState, ResourceStates afterState);
|
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);
|
||||||
}
|
}
|
||||||
@@ -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
|
public ulong GPUAddress
|
||||||
{
|
{
|
||||||
@@ -20,4 +22,17 @@ public interface IResource : IDisposable
|
|||||||
|
|
||||||
public void SetData<T>(Span<T> data)
|
public void SetData<T>(Span<T> data)
|
||||||
where T : unmanaged;
|
where T : unmanaged;
|
||||||
|
|
||||||
|
public void SetData<T>(T* data, uint length)
|
||||||
|
where T : unmanaged;
|
||||||
|
|
||||||
|
public void SetData(void* data, uint size);
|
||||||
|
|
||||||
|
public UnsafeArray<T> ReadData<T>(Allocator allocator)
|
||||||
|
where T : unmanaged;
|
||||||
|
|
||||||
|
public void ReadData<T>(T* ppData, uint* size)
|
||||||
|
where T : unmanaged;
|
||||||
|
|
||||||
|
public void ReadData(void* ppData, uint* size);
|
||||||
}
|
}
|
||||||
@@ -17,8 +17,24 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
_commandList = commandList;
|
_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->IASetPrimitiveTopology(PrimitiveTopology.TriangleList);
|
||||||
_commandList.Ptr->IASetVertexBuffers(0, 1, mesh.VertexBufferView);
|
_commandList.Ptr->IASetVertexBuffers(0, 1, mesh.VertexBufferView);
|
||||||
_commandList.Ptr->IASetIndexBuffer(mesh.IndexBufferView);
|
_commandList.Ptr->IASetIndexBuffer(mesh.IndexBufferView);
|
||||||
@@ -33,10 +49,4 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
|
|
||||||
_commandList.Ptr->CopyBufferRegion(dstDXResource.NativeResource, dstOffset, srcDXResource.NativeResource, srcOffset, size);
|
_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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -224,7 +224,7 @@ internal unsafe class D3D12Renderer : IRenderer
|
|||||||
ref var frameResource = ref _frameResources[i];
|
ref var frameResource = ref _frameResources[i];
|
||||||
if (frameResource.backBuffer.Get() is not null)
|
if (frameResource.backBuffer.Get() is not null)
|
||||||
{
|
{
|
||||||
frameResource.backBuffer.Dispose();
|
var c = frameResource.backBuffer.Reset();
|
||||||
_rtvHeap.ReleaseDescriptor(frameResource.backBufferDescriptorIndexes);
|
_rtvHeap.ReleaseDescriptor(frameResource.backBufferDescriptorIndexes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Graphics.Contracts;
|
using Ghost.Graphics.Contracts;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Win32;
|
using Win32;
|
||||||
using Win32.Graphics.Direct3D12;
|
using Win32.Graphics.Direct3D12;
|
||||||
@@ -8,13 +9,11 @@ namespace Ghost.Graphics.D3D12;
|
|||||||
|
|
||||||
public unsafe class D3D12Resource : IResource
|
public unsafe class D3D12Resource : IResource
|
||||||
{
|
{
|
||||||
private ComPtr<ID3D12Resource> _nativeResource
|
private ComPtr<ID3D12Resource> _nativeResource;
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
private string _name = string.Empty;
|
private string _name = string.Empty;
|
||||||
|
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
internal ConstPtr<ID3D12Resource> NativeResource => new(_nativeResource.Get());
|
internal ConstPtr<ID3D12Resource> NativeResource => new(_nativeResource.Get());
|
||||||
|
|
||||||
public ulong GPUAddress => _nativeResource.Get()->GetGPUVirtualAddress();
|
public ulong GPUAddress => _nativeResource.Get()->GetGPUVirtualAddress();
|
||||||
@@ -40,15 +39,34 @@ public unsafe class D3D12Resource : IResource
|
|||||||
TempResource = temp;
|
TempResource = temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~D3D12Resource()
|
||||||
|
{
|
||||||
|
DisposeInternal();
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void SetData<T>(Span<T> data)
|
public void SetData<T>(Span<T> data)
|
||||||
where T : unmanaged
|
where T : unmanaged
|
||||||
{
|
{
|
||||||
var size = (uint)(data.Length * sizeof(T));
|
|
||||||
var range = new Win32.Graphics.Direct3D12.Range(0, size);
|
|
||||||
|
|
||||||
fixed (T* ptr = data)
|
fixed (T* ptr = data)
|
||||||
{
|
{
|
||||||
|
SetData(ptr, (uint)data.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void SetData<T>(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;
|
void* mappedPtr;
|
||||||
var hr = _nativeResource.Get()->Map(0, &range, &mappedPtr);
|
var hr = _nativeResource.Get()->Map(0, &range, &mappedPtr);
|
||||||
if (hr.Failure)
|
if (hr.Failure)
|
||||||
@@ -56,14 +74,66 @@ public unsafe class D3D12Resource : IResource
|
|||||||
var message = hr.ToString();
|
var message = hr.ToString();
|
||||||
throw new InvalidOperationException($"Failed to map resource: {message}");
|
throw new InvalidOperationException($"Failed to map resource: {message}");
|
||||||
}
|
}
|
||||||
Unsafe.CopyBlock(mappedPtr, ptr, size);
|
|
||||||
|
Unsafe.CopyBlock(mappedPtr, data, size);
|
||||||
_nativeResource.Get()->Unmap(0, &range);
|
_nativeResource.Get()->Unmap(0, &range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UnsafeArray<T> ReadData<T>(Allocator allocator)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
var size = (uint)_nativeResource.Get()->GetDesc().Width;
|
||||||
|
var data = new UnsafeArray<T>((int)(size / (uint)sizeof(T)), allocator);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ReadData(data.GetUnsafePtr(), &size);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
data.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReadData<T>(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()
|
internal void DisposeInternal()
|
||||||
{
|
{
|
||||||
var c = _nativeResource.Reset();
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_nativeResource.Dispose();
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -71,6 +141,7 @@ public unsafe class D3D12Resource : IResource
|
|||||||
if (!TempResource)
|
if (!TempResource)
|
||||||
{
|
{
|
||||||
DisposeInternal();
|
DisposeInternal();
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,103 @@
|
|||||||
using Win32;
|
using Ghost.Graphics.Contracts;
|
||||||
using Win32.Graphics.Direct3D12;
|
using Ghost.Graphics.Shading;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ghost.Graphics.Data;
|
namespace Ghost.Graphics.Data;
|
||||||
|
|
||||||
public class Material : IDisposable
|
public class Material : IDisposable
|
||||||
{
|
{
|
||||||
// TODO: Pipeline state should be abstracted that can support multiple graphics APIs.
|
private readonly Dictionary<string, CBufferCache> _cbufferCaches;
|
||||||
private ComPtr<ID3D12PipelineState> _pipelineState;
|
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
public Shader Shader
|
public Shader Shader
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
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()
|
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.Core;
|
||||||
using Ghost.Graphics.Contracts;
|
using Ghost.Graphics.Contracts;
|
||||||
using Misaki.HighPerformance.Unsafe.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
using Misaki.HighPerformance.Unsafe.Helpers;
|
using Misaki.HighPerformance.LowLevel.Helpers;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Win32.Graphics.Direct3D12;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -27,8 +27,11 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Misaki.HighPerformance.Unsafe">
|
<Reference Include="Misaki.HighPerformance.Image">
|
||||||
<HintPath>..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll</HintPath>
|
<HintPath>..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Image\bin\Release\net9.0\Misaki.HighPerformance.Image.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Misaki.HighPerformance.LowLevel">
|
||||||
|
<HintPath>..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.LowLevel\bin\Release\net9.0\Misaki.HighPerformance.LowLevel.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,18 @@
|
|||||||
using Ghost.Graphics.Contracts;
|
using Ghost.Graphics.Contracts;
|
||||||
using Ghost.Graphics.D3D12;
|
|
||||||
using Ghost.Graphics.D3D12.Utilities;
|
|
||||||
using Ghost.Graphics.Data;
|
using Ghost.Graphics.Data;
|
||||||
|
using Ghost.Graphics.Shading;
|
||||||
using Ghost.Graphics.Utilities;
|
using Ghost.Graphics.Utilities;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Numerics;
|
||||||
using Win32;
|
|
||||||
using Win32.Graphics.Direct3D;
|
|
||||||
using Win32.Graphics.Direct3D12;
|
|
||||||
using Win32.Graphics.Dxgi.Common;
|
|
||||||
|
|
||||||
namespace Ghost.Graphics.RenderPasses;
|
namespace Ghost.Graphics.RenderPasses;
|
||||||
|
|
||||||
internal unsafe class MeshRenderPass : IRenderPass
|
internal unsafe class MeshRenderPass : IRenderPass
|
||||||
{
|
{
|
||||||
private const string _HLSL_SOURCE = @"
|
private const string _HLSL_SOURCE = @"
|
||||||
|
|
||||||
cbuffer ConstantBuffer : register(b0)
|
cbuffer ConstantBuffer : register(b0)
|
||||||
{
|
{
|
||||||
float4x4 WVP_Matrix;
|
float4 _Color;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexInput
|
struct VertexInput
|
||||||
@@ -43,105 +37,35 @@ PixelInput VSMain(VertexInput input)
|
|||||||
|
|
||||||
float4 PSMain(PixelInput input) : SV_TARGET
|
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 Mesh? _mesh;
|
||||||
|
private Shader? _shader;
|
||||||
private ComPtr<ID3D12RootSignature> _rootSignature;
|
private Material? _material;
|
||||||
private ComPtr<ID3D12PipelineState> _pipelineState;
|
|
||||||
|
|
||||||
public void Initialize(ICommandBuffer cmb)
|
public void Initialize(ICommandBuffer cmb)
|
||||||
{
|
{
|
||||||
_mesh = MeshBuilder.CreateCube(0.25f, new(Color.AliceBlue));
|
_mesh = MeshBuilder.CreateCube(0.25f);
|
||||||
_mesh.UploadMeshData(cmb);
|
_mesh.UploadMeshData(cmb);
|
||||||
|
|
||||||
CreateRootSignature();
|
_shader = new(_HLSL_SOURCE);
|
||||||
CreatePipelineStateObject();
|
_material = new(_shader);
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateRootSignature()
|
var color = new Vector4(Color.Brown.R / 255f, Color.Brown.G / 255f, Color.Brown.B / 255f, 1.0f);
|
||||||
{
|
_material.SetVector("_Color", ref color);
|
||||||
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<ID3DBlob> signature = default;
|
|
||||||
using ComPtr<ID3DBlob> 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<D3D12GraphicsDevice>().NativeDevice.Ptr->CreateRootSignature(0, signature.Get()->GetBufferPointer(), signature.Get()->GetBufferSize(), __uuidof<ID3D12RootSignature>(), _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<D3D12GraphicsDevice>().NativeDevice.Ptr->CreateGraphicsPipelineState(&psoDesc, __uuidof<ID3D12PipelineState>(), _pipelineState.GetVoidAddressOf());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex.ToString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Execute(ICommandBuffer cmb)
|
public void Execute(ICommandBuffer cmb)
|
||||||
{
|
{
|
||||||
var dx12Cmb = (D3D12CommandBuffer)cmb;
|
cmb.DrawMesh(_mesh!, _material!);
|
||||||
dx12Cmb.CommandList.Ptr->SetGraphicsRootSignature(_rootSignature.Get());
|
|
||||||
dx12Cmb.CommandList.Ptr->SetPipelineState(_pipelineState.Get());
|
|
||||||
|
|
||||||
cmb.DrawMesh(_mesh!);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_mesh?.Dispose();
|
_mesh?.Dispose();
|
||||||
_rootSignature.Dispose();
|
_shader?.Dispose();
|
||||||
_pipelineState.Dispose();
|
_material?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
43
Ghost.Graphics/Shading/CBufferCache.cs
Normal file
43
Ghost.Graphics/Shading/CBufferCache.cs
Normal file
@@ -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<byte> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
291
Ghost.Graphics/Shading/Shader.cs
Normal file
291
Ghost.Graphics/Shading/Shader.cs
Normal file
@@ -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<ID3D12PipelineState> _pipelineState;
|
||||||
|
private ComPtr<ID3D12RootSignature> _rootSignature;
|
||||||
|
|
||||||
|
private readonly byte[] _vertexShaderBytecode;
|
||||||
|
private readonly byte[] _pixelShaderBytecode;
|
||||||
|
|
||||||
|
private readonly Dictionary<string, CBufferInfo> _constantBuffers = new();
|
||||||
|
private readonly Dictionary<string, PropertyInfo> _properties = new();
|
||||||
|
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
internal ConstPtr<ID3D12PipelineState> PipelineState => new(_pipelineState.Get());
|
||||||
|
internal ConstPtr<ID3D12RootSignature> RootSignature => new(_rootSignature.Get());
|
||||||
|
|
||||||
|
internal IReadOnlyDictionary<string, CBufferInfo> ConstantBuffers => _constantBuffers;
|
||||||
|
internal IReadOnlyDictionary<string, PropertyInfo> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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(byte[] sourceCodeBytes, string entryPoint, string shaderProfile)
|
||||||
|
{
|
||||||
|
using ComPtr<ID3DBlob> bytecodeBlob = default;
|
||||||
|
using ComPtr<ID3DBlob> 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<ID3DBlob> signature = default;
|
||||||
|
using ComPtr<ID3DBlob> 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<D3D12GraphicsDevice>().NativeDevice.Ptr->CreateRootSignature(0, signature.Get()->GetBufferPointer(), signature.Get()->GetBufferSize(), __uuidof<ID3D12RootSignature>(), _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<D3D12GraphicsDevice>().NativeDevice.Ptr->CreateGraphicsPipelineState(&psoDesc, __uuidof<ID3D12PipelineState>(), _pipelineState.GetVoidAddressOf());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void PerformReflection(Span<byte> byteCode)
|
||||||
|
{
|
||||||
|
using ComPtr<ID3D12ShaderReflection> reflection = default;
|
||||||
|
fixed (void* codePtr = byteCode)
|
||||||
|
{
|
||||||
|
D3DReflect(codePtr, (nuint)byteCode.Length, __uuidof<ID3D12ShaderReflection>(), 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
48
Ghost.Graphics/Shading/ShaderProperty.cs
Normal file
48
Ghost.Graphics/Shading/ShaderProperty.cs
Normal file
@@ -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<byte> _value;
|
||||||
|
private FixedString128 _name;
|
||||||
|
private readonly uint _valueOffset;
|
||||||
|
|
||||||
|
internal readonly uint Offset => _valueOffset;
|
||||||
|
|
||||||
|
public readonly string Name => _name.Value;
|
||||||
|
public readonly ReadOnlySpan<byte> Value => _value.AsSpan();
|
||||||
|
|
||||||
|
public ShaderPropertyType PropertyType
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShaderProperty(Span<byte> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Misaki.HighPerformance.Unsafe">
|
<Reference Include="Misaki.HighPerformance.Unsafe">
|
||||||
<HintPath>..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll</HintPath>
|
<HintPath>..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.LowLevel\bin\Release\net9.0\Misaki.HighPerformance.LowLevel.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<Application
|
<Application
|
||||||
x:Class="Ghost.UnitTest.UnitTestApp"
|
x:Class="Ghost.UnitTest.UnitTestApp"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Ghost.Graphics;
|
|||||||
using Ghost.Graphics.Contracts;
|
using Ghost.Graphics.Contracts;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Media;
|
using Microsoft.UI.Xaml.Media;
|
||||||
using Misaki.HighPerformance.Unsafe.Buffer;
|
|
||||||
using WinRT;
|
using WinRT;
|
||||||
|
|
||||||
namespace Ghost.UnitTest;
|
namespace Ghost.UnitTest;
|
||||||
@@ -32,7 +31,6 @@ public sealed partial class UnitTestAppWindow : Window
|
|||||||
|
|
||||||
private void UnitTestAppWindow_Activated(object sender, WindowActivatedEventArgs args)
|
private void UnitTestAppWindow_Activated(object sender, WindowActivatedEventArgs args)
|
||||||
{
|
{
|
||||||
AllocationManager.Initialize();
|
|
||||||
GraphicsPipeline.Initialize(Graphics.Data.GraphicsAPI.D3D12);
|
GraphicsPipeline.Initialize(Graphics.Data.GraphicsAPI.D3D12);
|
||||||
GraphicsPipeline.Start();
|
GraphicsPipeline.Start();
|
||||||
|
|
||||||
@@ -49,7 +47,6 @@ public sealed partial class UnitTestAppWindow : Window
|
|||||||
{
|
{
|
||||||
GraphicsPipeline.SignalCPUReady();
|
GraphicsPipeline.SignalCPUReady();
|
||||||
GraphicsPipeline.Shutdown();
|
GraphicsPipeline.Shutdown();
|
||||||
AllocationManager.Dispose();
|
|
||||||
CompositionTarget.Rendering -= OnRendering;
|
CompositionTarget.Rendering -= OnRendering;
|
||||||
_swapChainPanelNative.Dispose();
|
_swapChainPanelNative.Dispose();
|
||||||
_renderView?.Dispose();
|
_renderView?.Dispose();
|
||||||
|
|||||||
Reference in New Issue
Block a user