Refactor GPU resource management and rendering pipeline
- Introduced `Handle<T>` and `Identifier<T>` for lightweight, strongly-typed resource identifiers. - Replaced `BitSet` with `UnsafeBitSet` for improved performance and memory safety. - Refactored `Mesh` and `Material` into `MeshClass` and `MaterialClass` for better GPU resource handling. - Added `D3D12ResourceDatabase` to centralize GPU resource tracking and lifecycle management. - Updated `D3D12ShaderCompiler` to load shaders from disk and dynamically populate constant buffers and textures. - Enhanced `ICommandBuffer` with new upload operations for buffers and textures. - Refactored `Vertex` struct for simplified memory layout and better performance. - Updated `MeshBuilder` and rendering logic to align with new resource and shader structures. - Added `BindlessDescriptor` support to `TextureHandle` and `BufferHandle`. - Removed unused classes and performed general cleanup. - Updated unit tests and demos to reflect the new architecture.
This commit is contained in:
88
Ghost.Core/Handle.cs
Normal file
88
Ghost.Core/Handle.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
namespace Ghost.Core;
|
||||
|
||||
public readonly struct Handle<T>
|
||||
{
|
||||
public readonly int id;
|
||||
|
||||
public readonly int generation;
|
||||
|
||||
public Handle(int id, int generation)
|
||||
{
|
||||
this.id = id;
|
||||
this.generation = generation;
|
||||
}
|
||||
|
||||
public static Handle<T> Invalid => new(-1, -1);
|
||||
|
||||
public readonly override int GetHashCode()
|
||||
{
|
||||
return id.GetHashCode();
|
||||
}
|
||||
|
||||
public readonly override bool Equals(object? obj)
|
||||
{
|
||||
return obj is Handle<T> id && Equals(id);
|
||||
}
|
||||
|
||||
public readonly bool Equals(Handle<T> other)
|
||||
{
|
||||
return id == other.id;
|
||||
}
|
||||
|
||||
public readonly int CompareTo(Handle<T> other)
|
||||
{
|
||||
return id.CompareTo(other.id);
|
||||
}
|
||||
|
||||
public static bool operator ==(Handle<T> a, Handle<T> b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(Handle<T> a, Handle<T> b)
|
||||
{
|
||||
return !a.Equals(b);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct Identifier<T>
|
||||
{
|
||||
public readonly int value;
|
||||
|
||||
public Identifier(int value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static Identifier<T> Invalid => new(-1);
|
||||
|
||||
public readonly override int GetHashCode()
|
||||
{
|
||||
return value.GetHashCode();
|
||||
}
|
||||
|
||||
public readonly override bool Equals(object? obj)
|
||||
{
|
||||
return obj is Identifier<T> id && Equals(id);
|
||||
}
|
||||
|
||||
public readonly bool Equals(Identifier<T> other)
|
||||
{
|
||||
return value == other.value;
|
||||
}
|
||||
|
||||
public readonly int CompareTo(Identifier<T> other)
|
||||
{
|
||||
return value.CompareTo(other.value);
|
||||
}
|
||||
|
||||
public static bool operator ==(Identifier<T> a, Identifier<T> b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(Identifier<T> a, Identifier<T> b)
|
||||
{
|
||||
return !a.Equals(b);
|
||||
}
|
||||
}
|
||||
@@ -472,7 +472,7 @@ internal struct ComponentStorage : IDisposable
|
||||
private int _currentCapacity = 16;
|
||||
|
||||
private IComponentPool?[] _componentPools = new IComponentPool[16];
|
||||
private BitSet?[] _componentEntityMasks = new BitSet[16];
|
||||
private UnsafeBitSet?[] _componentEntityMasks = new UnsafeBitSet?[16];
|
||||
|
||||
private readonly Dictionary<TypeHandle, int> _typeIDMap = new(16);
|
||||
private readonly Dictionary<int, TypeHandle> _typeHandleMap = new(16);
|
||||
@@ -487,7 +487,7 @@ internal struct ComponentStorage : IDisposable
|
||||
}
|
||||
|
||||
internal readonly IReadOnlyList<IComponentPool?> ComponentPools => _componentPools;
|
||||
internal readonly IReadOnlyList<BitSet?> ComponentEntityMasks => _componentEntityMasks;
|
||||
internal readonly IReadOnlyList<UnsafeBitSet?> ComponentEntityMasks => _componentEntityMasks;
|
||||
internal readonly ScriptComponentPool ScriptComponentPool => _scriptComponentPool;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -621,7 +621,7 @@ internal struct ComponentStorage : IDisposable
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool TryGetMask(TypeHandle typeHandle, [NotNullWhen(true)] out BitSet? bitSet)
|
||||
public readonly bool TryGetMask(TypeHandle typeHandle, [NotNullWhen(true)] out UnsafeBitSet? bitSet)
|
||||
{
|
||||
if (!_typeIDMap.TryGetValue(typeHandle, out var id)
|
||||
|| id >= _currentCapacity)
|
||||
@@ -631,17 +631,17 @@ internal struct ComponentStorage : IDisposable
|
||||
}
|
||||
|
||||
bitSet = _componentEntityMasks[id];
|
||||
return bitSet != null;
|
||||
return bitSet.HasValue;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool TryGetMask<T>([NotNullWhen(true)] out BitSet? bitSet)
|
||||
public readonly bool TryGetMask<T>([NotNullWhen(true)] out UnsafeBitSet? bitSet)
|
||||
where T : unmanaged, IComponentData
|
||||
{
|
||||
return TryGetMask(TypeHandle.Get<T>(), out bitSet);
|
||||
}
|
||||
|
||||
public BitSet GetOrCreateMask<T>()
|
||||
public UnsafeBitSet GetOrCreateMask<T>()
|
||||
where T : unmanaged, IComponentData
|
||||
{
|
||||
var typeHandle = TypeHandle.Get<T>();
|
||||
@@ -658,12 +658,12 @@ internal struct ComponentStorage : IDisposable
|
||||
}
|
||||
|
||||
ref var set = ref _componentEntityMasks[id];
|
||||
set ??= new BitSet();
|
||||
set ??= new UnsafeBitSet();
|
||||
|
||||
return set;
|
||||
return set.Value;
|
||||
}
|
||||
|
||||
public BitSet GetOrCreateMask(Type type)
|
||||
public UnsafeBitSet GetOrCreateMask(Type type)
|
||||
{
|
||||
var typeHandle = TypeHandle.Get(type);
|
||||
if (!_typeIDMap.TryGetValue(typeHandle, out var id))
|
||||
@@ -679,9 +679,9 @@ internal struct ComponentStorage : IDisposable
|
||||
}
|
||||
|
||||
ref var set = ref _componentEntityMasks[id];
|
||||
set ??= new BitSet();
|
||||
set ??= new UnsafeBitSet();
|
||||
|
||||
return set;
|
||||
return set.Value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -704,7 +704,11 @@ internal struct ComponentStorage : IDisposable
|
||||
pool?.Dispose();
|
||||
}
|
||||
|
||||
Array.Clear(_componentPools);
|
||||
foreach (var bitSet in _componentEntityMasks)
|
||||
{
|
||||
bitSet?.Dispose();
|
||||
}
|
||||
|
||||
_scriptComponentPool.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -186,7 +186,7 @@ public readonly struct EntityManager : IDisposable
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool HasComponent(Entity entity, TypeHandle typeHandle)
|
||||
{
|
||||
return _world.ComponentStorage.TryGetMask(typeHandle, out var bitSet) && bitSet.IsSet(entity.ID);
|
||||
return _world.ComponentStorage.TryGetMask(typeHandle, out var bitSet) && bitSet.Value.IsSet(entity.ID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -25,25 +25,20 @@ internal struct QueryFilter()
|
||||
internal List<TypeHandle> _absent = new(6);
|
||||
internal List<TypeHandle> _disabled = new(6);
|
||||
|
||||
public readonly BitSet ComputeFilterBitMask(World world)
|
||||
public readonly UnsafeBitSet ComputeFilterBitMask(World world)
|
||||
{
|
||||
BitSet? allMask = null;
|
||||
BitSet? anyMask = null;
|
||||
BitSet? absentMask = null;
|
||||
|
||||
var hasAll = false;
|
||||
var hasAny = false;
|
||||
var hasAbsent = false;
|
||||
UnsafeBitSet? allMask = null;
|
||||
UnsafeBitSet? anyMask = null;
|
||||
UnsafeBitSet? absentMask = null;
|
||||
|
||||
foreach (var typeHandle in _all)
|
||||
{
|
||||
var mask = world.ComponentStorage.GetOrCreateMask(typeHandle);
|
||||
|
||||
if (!hasAll)
|
||||
if (!allMask.HasValue)
|
||||
{
|
||||
allMask = new BitSet(mask.Length);
|
||||
allMask.SetAll();
|
||||
hasAll = true;
|
||||
allMask = new UnsafeBitSet(mask.Length);
|
||||
allMask.Value.SetAll();
|
||||
}
|
||||
|
||||
allMask &= mask;
|
||||
@@ -53,10 +48,9 @@ internal struct QueryFilter()
|
||||
{
|
||||
var mask = world.ComponentStorage.GetOrCreateMask(typeHandle);
|
||||
|
||||
if (!hasAny)
|
||||
if (!anyMask.HasValue)
|
||||
{
|
||||
anyMask = new BitSet(mask.Length);
|
||||
hasAny = true;
|
||||
anyMask = new UnsafeBitSet(mask.Length);
|
||||
}
|
||||
|
||||
anyMask |= mask;
|
||||
@@ -66,31 +60,30 @@ internal struct QueryFilter()
|
||||
{
|
||||
var mask = world.ComponentStorage.GetOrCreateMask(typeHandle);
|
||||
|
||||
if (!hasAbsent)
|
||||
if (!absentMask.HasValue)
|
||||
{
|
||||
absentMask = new BitSet(mask.Length);
|
||||
hasAbsent = true;
|
||||
absentMask = new UnsafeBitSet(mask.Length);
|
||||
}
|
||||
|
||||
absentMask |= mask;
|
||||
}
|
||||
|
||||
var result = new BitSet(world.EntityManager.EntityCount);
|
||||
var result = new UnsafeBitSet(world.EntityManager.EntityCount);
|
||||
result.SetAll();
|
||||
|
||||
if (hasAll)
|
||||
if (allMask.HasValue)
|
||||
{
|
||||
result &= allMask!;
|
||||
result &= allMask.Value;
|
||||
}
|
||||
|
||||
if (hasAny)
|
||||
if (anyMask.HasValue)
|
||||
{
|
||||
result &= anyMask!;
|
||||
result &= anyMask.Value;
|
||||
}
|
||||
|
||||
if (hasAbsent)
|
||||
if (absentMask.HasValue)
|
||||
{
|
||||
result &= ~absentMask!;
|
||||
result &= ~absentMask.Value;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -42,7 +42,7 @@ public struct QueryEnumerable<T0>
|
||||
private int _index;
|
||||
private readonly int _count;
|
||||
|
||||
private BitSet _filterMask;
|
||||
private UnsafeBitSet _filterMask;
|
||||
|
||||
public QueryItem<T0> Current
|
||||
{
|
||||
@@ -213,7 +213,7 @@ public struct QueryEnumerable<T0, T1>
|
||||
private int _index;
|
||||
private readonly int _count;
|
||||
|
||||
private BitSet _filterMask;
|
||||
private UnsafeBitSet _filterMask;
|
||||
|
||||
public QueryItem<T0, T1> Current
|
||||
{
|
||||
@@ -389,7 +389,7 @@ public struct QueryEnumerable<T0, T1, T2>
|
||||
private int _index;
|
||||
private readonly int _count;
|
||||
|
||||
private BitSet _filterMask;
|
||||
private UnsafeBitSet _filterMask;
|
||||
|
||||
public QueryItem<T0, T1, T2> Current
|
||||
{
|
||||
@@ -570,7 +570,7 @@ public struct QueryEnumerable<T0, T1, T2, T3>
|
||||
private int _index;
|
||||
private readonly int _count;
|
||||
|
||||
private BitSet _filterMask;
|
||||
private UnsafeBitSet _filterMask;
|
||||
|
||||
public QueryItem<T0, T1, T2, T3> Current
|
||||
{
|
||||
@@ -756,7 +756,7 @@ public struct QueryEnumerable<T0, T1, T2, T3, T4>
|
||||
private int _index;
|
||||
private readonly int _count;
|
||||
|
||||
private BitSet _filterMask;
|
||||
private UnsafeBitSet _filterMask;
|
||||
|
||||
public QueryItem<T0, T1, T2, T3, T4> Current
|
||||
{
|
||||
@@ -947,7 +947,7 @@ public struct QueryEnumerable<T0, T1, T2, T3, T4, T5>
|
||||
private int _index;
|
||||
private readonly int _count;
|
||||
|
||||
private BitSet _filterMask;
|
||||
private UnsafeBitSet _filterMask;
|
||||
|
||||
public QueryItem<T0, T1, T2, T3, T4, T5> Current
|
||||
{
|
||||
@@ -1143,7 +1143,7 @@ public struct QueryEnumerable<T0, T1, T2, T3, T4, T5, T6>
|
||||
private int _index;
|
||||
private readonly int _count;
|
||||
|
||||
private BitSet _filterMask;
|
||||
private UnsafeBitSet _filterMask;
|
||||
|
||||
public QueryItem<T0, T1, T2, T3, T4, T5, T6> Current
|
||||
{
|
||||
@@ -1344,7 +1344,7 @@ public struct QueryEnumerable<T0, T1, T2, T3, T4, T5, T6, T7>
|
||||
private int _index;
|
||||
private readonly int _count;
|
||||
|
||||
private BitSet _filterMask;
|
||||
private UnsafeBitSet _filterMask;
|
||||
|
||||
public QueryItem<T0, T1, T2, T3, T4, T5, T6, T7> Current
|
||||
{
|
||||
|
||||
@@ -66,7 +66,7 @@ public struct QueryEnumerable<<#= generics #>>
|
||||
private int _index;
|
||||
private readonly int _count;
|
||||
|
||||
private BitSet _filterMask;
|
||||
private UnsafeBitSet _filterMask;
|
||||
|
||||
public QueryItem<<#= generics #>> Current
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@ public unsafe class CommandList
|
||||
/// </summary>
|
||||
/// <param name="mesh">The mesh to draw</param>
|
||||
/// <param name="material">The bindless material to use</param>
|
||||
public void DrawMesh(Mesh mesh, Material material)
|
||||
public void DrawMesh(MeshClass mesh, MaterialClass material)
|
||||
{
|
||||
// Bind the bindless material (sets up root signature, pipeline state, and descriptor heaps)
|
||||
material.Bind(this);
|
||||
@@ -62,7 +62,7 @@ public unsafe class CommandList
|
||||
_commandList.Ptr->OMSetRenderTargets(1, pRtvHandle, false, pDsvHandle);
|
||||
}
|
||||
|
||||
public void ClearRenderTarget(RenderTexture renderTarget, Color16 color)
|
||||
public void ClearRenderTarget(RenderTexture renderTarget, Color128 color)
|
||||
{
|
||||
renderTarget.ClearColor(this, color);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
@@ -16,23 +17,35 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
private ComPtr<ID3D12GraphicsCommandList10> _commandList;
|
||||
|
||||
private readonly D3D12PipelineStateController _stateController;
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
private readonly D3D12ResourceAllocator _resourceAllocator;
|
||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||
|
||||
private readonly CommandBufferType _type;
|
||||
private bool _isRecording;
|
||||
private bool _disposed;
|
||||
|
||||
public CommandBufferType Type => _type;
|
||||
|
||||
public ID3D12GraphicsCommandList10* NativeCommandList => _commandList.Get();
|
||||
|
||||
public D3D12CommandBuffer(D3D12RenderDevice device, D3D12PipelineStateController stateController, D3D12DescriptorAllocator descriptorAllocator, CommandBufferType type)
|
||||
public D3D12CommandBuffer(
|
||||
D3D12RenderDevice device,
|
||||
D3D12PipelineStateController stateController,
|
||||
D3D12ResourceDatabase resourceDatabase,
|
||||
D3D12ResourceAllocator resourceAllocator,
|
||||
D3D12DescriptorAllocator descriptorAllocator,
|
||||
CommandBufferType type)
|
||||
{
|
||||
_type = type;
|
||||
var commandListType = ConvertCommandBufferType(type);
|
||||
|
||||
device.NativeDevice->CreateCommandAllocator(commandListType, __uuidof<ID3D12CommandAllocator>(), _allocator.GetVoidAddressOf());
|
||||
device.NativeDevice->CreateCommandList(0u, commandListType, _allocator.Get(), null, __uuidof<ID3D12GraphicsCommandList10>(), _commandList.GetVoidAddressOf());
|
||||
device.NativeDevice->CreateCommandList1(0u, commandListType, CommandListFlags.None, __uuidof<ID3D12GraphicsCommandList10>(), _commandList.GetVoidAddressOf());
|
||||
|
||||
_stateController = stateController;
|
||||
_resourceDatabase = resourceDatabase;
|
||||
_resourceAllocator = resourceAllocator;
|
||||
_descriptorAllocator = descriptorAllocator;
|
||||
|
||||
// Command lists are created in recording state, so close it
|
||||
@@ -40,8 +53,28 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
_isRecording = false;
|
||||
}
|
||||
|
||||
~D3D12CommandBuffer()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
}
|
||||
|
||||
private void ThrowIfNotRecording()
|
||||
{
|
||||
if (!_isRecording)
|
||||
{
|
||||
throw new InvalidOperationException("Command buffer is not recording");
|
||||
}
|
||||
}
|
||||
|
||||
public void Begin()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (_isRecording)
|
||||
{
|
||||
throw new InvalidOperationException("Command buffer is already recording");
|
||||
@@ -54,16 +87,14 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
|
||||
public void End()
|
||||
{
|
||||
if (!_isRecording)
|
||||
{
|
||||
throw new InvalidOperationException("Command buffer is not recording");
|
||||
}
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
|
||||
_commandList.Get()->Close();
|
||||
_isRecording = false;
|
||||
}
|
||||
|
||||
public void BeginRenderPass(IRenderTarget renderTarget, Color16 clearColor)
|
||||
public void BeginRenderPass(IRenderTarget renderTarget, Color128 clearColor)
|
||||
{
|
||||
// TODO: Implement render pass begin
|
||||
throw new NotImplementedException();
|
||||
@@ -77,18 +108,27 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
|
||||
public void SetViewport(ViewportDesc viewport)
|
||||
{
|
||||
var d3d12Viewport = new Viewport(viewport.Width, viewport.Height, viewport.X, viewport.Y, viewport.MinDepth, viewport.MaxDepth);
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
|
||||
var d3d12Viewport = new Viewport(viewport.width, viewport.height, viewport.x, viewport.y, viewport.minDepth, viewport.maxDepth);
|
||||
_commandList.Get()->RSSetViewports(1, &d3d12Viewport);
|
||||
}
|
||||
|
||||
public void SetScissorRect(RectDesc rect)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
|
||||
var d3d12Rect = new Rect(rect.Left, rect.Top, rect.Right, rect.Bottom);
|
||||
_commandList.Get()->RSSetScissorRects(1, &d3d12Rect);
|
||||
}
|
||||
|
||||
public void ResourceBarrier(IResource resource, ResourceState before, ResourceState after)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
|
||||
if (resource is D3D12Texture d3d12Texture)
|
||||
{
|
||||
_commandList.Get()->ResourceBarrierTransition(d3d12Texture.NativeResource,
|
||||
@@ -118,8 +158,11 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
}
|
||||
|
||||
// TODO: Batch draw calls by material to minimize state changes
|
||||
public void DrawMesh(Mesh mesh, Material material)
|
||||
public void DrawMesh(MeshClass mesh, MaterialClass material)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
|
||||
// Bind the bindless material (sets up root signature, pipeline state, and descriptor heaps)
|
||||
var shaderPipeline = _stateController.GetShaderPipeline(material.Shader);
|
||||
if (shaderPipeline is not D3D12ShaderPipeline d3d12Pipeline)
|
||||
@@ -128,8 +171,8 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
}
|
||||
|
||||
// Set root signature and pipeline state
|
||||
_commandList.Get()->SetGraphicsRootSignature(d3d12Pipeline.rootSignature.Get());
|
||||
_commandList.Get()->SetPipelineState(d3d12Pipeline.pipelineState.Get());
|
||||
_commandList.Get()->SetGraphicsRootSignature(d3d12Pipeline.rootSignature.Get());
|
||||
|
||||
// Set descriptor heaps - CRUCIAL: Use the specialized bindless heap for SM 6.6
|
||||
var heaps = stackalloc ID3D12DescriptorHeap*[2];
|
||||
@@ -142,7 +185,8 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
foreach (var cbufferInfo in material.Shader.ConstantBuffers)
|
||||
{
|
||||
var cache = material.CBufferCaches[(int)cbufferInfo.RegisterSlot];
|
||||
_commandList.Get()->SetGraphicsRootConstantBufferView(rootParamIndex++, cache.GpuResource.GPUAddress);
|
||||
var resource = _resourceDatabase.GetResource(cache.GpuResource.ResourceHandle);
|
||||
_commandList.Get()->SetGraphicsRootConstantBufferView(rootParamIndex++, resource->GetGPUVirtualAddress());
|
||||
}
|
||||
|
||||
// Bind sampler descriptor table (last root parameter)
|
||||
@@ -163,9 +207,67 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
|
||||
public void Dispatch(uint threadGroupCountX, uint threadGroupCountY = 1, uint threadGroupCountZ = 1)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
|
||||
_commandList.Get()->Dispatch(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
|
||||
}
|
||||
|
||||
public void Upload<T>(BufferHandle buffer, ReadOnlySpan<T> data)
|
||||
where T : unmanaged
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
|
||||
var sizeInBytes = (uint)(data.Length * sizeof(T));
|
||||
|
||||
var uploadHandle = _resourceAllocator.CreateUploadBuffer(sizeInBytes);
|
||||
var uploadResource = _resourceDatabase.GetResource(uploadHandle.ResourceHandle);
|
||||
|
||||
void* mappedData;
|
||||
uploadResource->Map(0, null, &mappedData);
|
||||
fixed (T* dataPtr = data)
|
||||
{
|
||||
MemoryUtilities.MemCpy(mappedData, dataPtr, sizeInBytes);
|
||||
}
|
||||
uploadResource->Unmap(0, null);
|
||||
|
||||
var resource = _resourceDatabase.GetResource(buffer.ResourceHandle);
|
||||
|
||||
// Copy from upload buffer to destination
|
||||
_commandList.Get()->CopyBufferRegion(resource, 0, uploadResource, 0, sizeInBytes);
|
||||
}
|
||||
|
||||
public void Upload(TextureHandle texture, uint firstSubresource, ref SubResourceData subresources, uint numSubresources)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
|
||||
var textureResource = _resourceDatabase.GetResource(texture.ResourceHandle);
|
||||
|
||||
var resourceDesc = textureResource->GetDesc();
|
||||
var requiredSize = GetRequiredIntermediateSize(textureResource, firstSubresource, numSubresources);
|
||||
|
||||
var uploadHandle = _resourceAllocator.CreateUploadBuffer(requiredSize);
|
||||
var uploadResource = _resourceDatabase.GetResource(uploadHandle.ResourceHandle);
|
||||
|
||||
var d3d12Subresources = new SubresourceData
|
||||
{
|
||||
pData = subresources.pData,
|
||||
RowPitch = subresources.rowPitch,
|
||||
SlicePitch = subresources.slicePitch
|
||||
};
|
||||
|
||||
UpdateSubresources(
|
||||
(ID3D12GraphicsCommandList*)_commandList.Get(),
|
||||
textureResource,
|
||||
uploadResource,
|
||||
0,
|
||||
firstSubresource,
|
||||
numSubresources,
|
||||
&d3d12Subresources);
|
||||
}
|
||||
|
||||
private static CommandListType ConvertCommandBufferType(CommandBufferType type)
|
||||
{
|
||||
return type switch
|
||||
@@ -197,11 +299,20 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isRecording)
|
||||
{
|
||||
throw new InvalidOperationException("Command buffer is still recording");
|
||||
}
|
||||
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_commandList.Dispose();
|
||||
_allocator.Dispose();
|
||||
_disposed = true;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,11 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
pDevice->CreateFence(0, FenceFlags.None, __uuidof<ID3D12Fence1>(), _fence.GetVoidAddressOf());
|
||||
}
|
||||
|
||||
~D3D12CommandQueue()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void Submit(ICommandBuffer commandBuffer)
|
||||
{
|
||||
if (commandBuffer is D3D12CommandBuffer d3d12CommandBuffer)
|
||||
@@ -113,12 +118,16 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_fenceEvent?.Dispose();
|
||||
_fence.Dispose();
|
||||
_queue.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,12 +9,17 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
|
||||
#endif
|
||||
|
||||
private readonly D3D12RenderDevice _device;
|
||||
private readonly D3D12PipelineStateController _stateController;
|
||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
private readonly D3D12ResourceAllocator _resourceAllocator;
|
||||
|
||||
private readonly D3D12PipelineStateController _stateController;
|
||||
private readonly D3D12CommandBuffer _copyCommandBuffer;
|
||||
|
||||
|
||||
public IRenderDevice Device => _device;
|
||||
public IResourceDatabase ResourceDatabase => _resourceDatabase;
|
||||
public IResourceAllocator ResourceAllocator => _resourceAllocator;
|
||||
|
||||
public IPipelineStateController PipelineStateController => _stateController;
|
||||
@@ -24,12 +29,25 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
|
||||
#if DEBUG
|
||||
_debugLayer = new();
|
||||
#endif
|
||||
|
||||
_device = new();
|
||||
_descriptorAllocator = new(_device);
|
||||
_resourceAllocator = new(renderSystem, _device, _descriptorAllocator);
|
||||
|
||||
_stateController = new(_device);
|
||||
_resourceDatabase = new(_descriptorAllocator);
|
||||
_resourceAllocator = new(renderSystem, _device, _descriptorAllocator, _resourceDatabase);
|
||||
|
||||
_stateController = new(_device, _resourceDatabase);
|
||||
_copyCommandBuffer = new(
|
||||
_device,
|
||||
_stateController,
|
||||
_resourceDatabase,
|
||||
_resourceAllocator,
|
||||
_descriptorAllocator,
|
||||
CommandBufferType.Copy);
|
||||
}
|
||||
|
||||
~D3D12GraphicsEngine()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public IRenderer CreateRenderer()
|
||||
@@ -39,7 +57,13 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
|
||||
|
||||
public ICommandBuffer CreateCommandBuffer(CommandBufferType type = CommandBufferType.Graphics)
|
||||
{
|
||||
return new D3D12CommandBuffer(_device, _stateController, _descriptorAllocator, type);
|
||||
return new D3D12CommandBuffer(
|
||||
_device,
|
||||
_stateController,
|
||||
_resourceDatabase,
|
||||
_resourceAllocator,
|
||||
_descriptorAllocator,
|
||||
type);
|
||||
}
|
||||
|
||||
public ISwapChain CreateSwapChain(SwapChainDesc desc)
|
||||
@@ -47,15 +71,30 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
|
||||
return new D3D12SwapChain(_device.DXGIFactory, ((D3D12CommandQueue)_device.ComputeQueue).NativeQueue, desc);
|
||||
}
|
||||
|
||||
public void BeginFrame()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void EndFrame()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_copyCommandBuffer.Dispose();
|
||||
_stateController.Dispose();
|
||||
_descriptorAllocator.Dispose();
|
||||
_resourceAllocator.Dispose();
|
||||
_device.Dispose();
|
||||
|
||||
_resourceAllocator.Dispose();
|
||||
_resourceDatabase.Dispose();
|
||||
|
||||
_descriptorAllocator.Dispose();
|
||||
_device.Dispose();
|
||||
#if DEBUG
|
||||
_debugLayer.Dispose();
|
||||
#endif
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Win32;
|
||||
@@ -8,7 +9,7 @@ using Win32.Graphics.Dxgi.Common;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal class D3D12ShaderPipeline : IShaderPipeline
|
||||
internal class D3D12ShaderPipeline : IShaderPipeline, IDisposable
|
||||
{
|
||||
public ComPtr<ID3D12RootSignature> rootSignature;
|
||||
public ComPtr<ID3D12PipelineState> pipelineState;
|
||||
@@ -21,42 +22,51 @@ internal class D3D12ShaderPipeline : IShaderPipeline
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
rootSignature.Dispose();
|
||||
pipelineState.Dispose();
|
||||
samplerHeap.Dispose();
|
||||
vsResult.Dispose();
|
||||
psResult.Dispose();
|
||||
csResult.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe class D3D12PipelineStateController : IPipelineStateController, IDisposable
|
||||
{
|
||||
private readonly ID3D12Device14* _device;
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
|
||||
private readonly Dictionary<Shader, D3D12ShaderPipeline> _shaderPipelines;
|
||||
private readonly Dictionary<Identifier<Shader>, D3D12ShaderPipeline> _shaderPipelines;
|
||||
|
||||
public D3D12PipelineStateController(D3D12RenderDevice device)
|
||||
public D3D12PipelineStateController(D3D12RenderDevice device, D3D12ResourceDatabase resourceDatabase)
|
||||
{
|
||||
_device = device.NativeDevice;
|
||||
_resourceDatabase = resourceDatabase;
|
||||
|
||||
_shaderPipelines = new();
|
||||
}
|
||||
|
||||
// TODO: Support compute shaders
|
||||
public void ColectionShader(ReadOnlySpan<Shader> shaders)
|
||||
public void CompileShader(Identifier<Shader> id, string shaderPath)
|
||||
{
|
||||
foreach (var shader in shaders)
|
||||
{
|
||||
_shaderPipelines.TryAdd(shader, new()
|
||||
{
|
||||
Type = PipelineType.Graphics
|
||||
});
|
||||
}
|
||||
}
|
||||
var vsResult = D3D12ShaderCompiler.Compile(shaderPath, D3D12ShaderCompiler.ShaderStage.VertexShader, D3D12ShaderCompiler.CompilerVersion.SM_6_6);
|
||||
var psResult = D3D12ShaderCompiler.Compile(shaderPath, D3D12ShaderCompiler.ShaderStage.PixelShader, D3D12ShaderCompiler.CompilerVersion.SM_6_6);
|
||||
|
||||
public void CompileCollected()
|
||||
{
|
||||
foreach (var kvp in _shaderPipelines)
|
||||
{
|
||||
var vsResult = D3D12ShaderCompiler.Compile(kvp.Key, D3D12ShaderCompiler.ShaderStage.VertexShader, D3D12ShaderCompiler.CompilerVersion.SM_6_6);
|
||||
var psResult = D3D12ShaderCompiler.Compile(kvp.Key, D3D12ShaderCompiler.ShaderStage.PixelShader, D3D12ShaderCompiler.CompilerVersion.SM_6_6);
|
||||
var shader = _resourceDatabase.GetShader(id);
|
||||
|
||||
kvp.Value.vsResult = vsResult;
|
||||
kvp.Value.psResult = psResult;
|
||||
}
|
||||
D3D12ShaderCompiler.PerformDXCReflection(shader, vsResult.reflection.Get());
|
||||
D3D12ShaderCompiler.PerformDXCReflection(shader, psResult.reflection.Get());
|
||||
|
||||
var shaderPipeline = new D3D12ShaderPipeline
|
||||
{
|
||||
Type = PipelineType.Graphics,
|
||||
vsResult = vsResult,
|
||||
psResult = psResult
|
||||
};
|
||||
|
||||
_shaderPipelines[id] = shaderPipeline;
|
||||
}
|
||||
|
||||
private void CreateRootSignature(Shader shader, D3D12ShaderPipeline shaderPipeline)
|
||||
@@ -219,31 +229,29 @@ internal unsafe class D3D12PipelineStateController : IPipelineStateController, I
|
||||
{
|
||||
foreach (var kvp in _shaderPipelines)
|
||||
{
|
||||
CreateRootSignature(kvp.Key, kvp.Value);
|
||||
var shader = _resourceDatabase.GetShader(kvp.Key);
|
||||
|
||||
CreateRootSignature(shader, kvp.Value);
|
||||
CreatePipelineStateObject(kvp.Value);
|
||||
CreateSamplerHeap(kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public IShaderPipeline GetShaderPipeline(Shader shader)
|
||||
public IShaderPipeline GetShaderPipeline(Identifier<Shader> id)
|
||||
{
|
||||
if (_shaderPipelines.TryGetValue(shader, out var pipeline))
|
||||
if (_shaderPipelines.TryGetValue(id, out var pipeline))
|
||||
{
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
throw new KeyNotFoundException($"Shader pipeline not found for shader: {shader}");
|
||||
throw new KeyNotFoundException($"Shader pipeline not found for shader ID: {id}");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var kvp in _shaderPipelines)
|
||||
{
|
||||
kvp.Value.rootSignature.Dispose();
|
||||
kvp.Value.pipelineState.Dispose();
|
||||
kvp.Value.samplerHeap.Dispose();
|
||||
kvp.Value.vsResult.Dispose();
|
||||
kvp.Value.psResult.Dispose();
|
||||
kvp.Value.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,7 +169,7 @@ public unsafe class D3D12Renderer : IRenderer
|
||||
|
||||
private void RenderScene(IRenderTarget target, ICommandBuffer cmd)
|
||||
{
|
||||
var clearColor = new Color16 { r = 1.0f, g = 0.0f, b = 1.0f, a = 1.0f };
|
||||
var clearColor = new Color128 { r = 1.0f, g = 0.0f, b = 1.0f, a = 1.0f };
|
||||
|
||||
cmd.BeginRenderPass(target, clearColor);
|
||||
|
||||
@@ -207,7 +207,7 @@ public unsafe class D3D12Renderer : IRenderer
|
||||
// 3. Apply post-processing effects (tone mapping, gamma correction, etc.)
|
||||
|
||||
// For now, just clear the destination (this should be replaced with actual blit)
|
||||
var clearColor = new Color16 { r = 0.0f, g = 0.0f, b = 0.0f, a = 1.0f };
|
||||
var clearColor = new Color128 { r = 0.0f, g = 0.0f, b = 0.0f, a = 1.0f };
|
||||
cmd.BeginRenderPass(destination, clearColor);
|
||||
cmd.EndRenderPass();
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -10,32 +11,8 @@ using static Win32.Graphics.D3D12MemoryAllocator.Apis;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource>, IDisposable
|
||||
internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
|
||||
{
|
||||
private readonly struct AllocationInfo : IDisposable
|
||||
{
|
||||
public readonly Allocation allocation;
|
||||
public readonly uint cpuFenceValue;
|
||||
|
||||
public bool Allocated => allocation.IsNotNull;
|
||||
|
||||
public AllocationInfo(in Allocation allocation, uint cpuFenceValue)
|
||||
{
|
||||
this.allocation = allocation;
|
||||
this.cpuFenceValue = cpuFenceValue;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (allocation.IsNull)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
allocation.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private const uint _MAX_BYTES = D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u;
|
||||
private const uint _MAX_TEXTURE2D_DIMENSION = 16384u;
|
||||
private const uint _MAX_TEXTURE3D_DIMENSION = 2048u;
|
||||
@@ -45,8 +22,8 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource
|
||||
private readonly Allocator _allocator;
|
||||
private readonly RenderSystem _renderSystem;
|
||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
|
||||
private UnsafeSlotMap<AllocationInfo> _allocations = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
||||
private UnsafeQueue<ResourceHandle> _temResources = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
||||
|
||||
private Guid* IID_NULL
|
||||
@@ -60,7 +37,7 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource
|
||||
}
|
||||
}
|
||||
|
||||
public D3D12ResourceAllocator(RenderSystem renderSystem, D3D12RenderDevice device, D3D12DescriptorAllocator descriptorAllocator)
|
||||
public D3D12ResourceAllocator(RenderSystem renderSystem, D3D12RenderDevice device, D3D12DescriptorAllocator descriptorAllocator, D3D12ResourceDatabase resourceDatabase)
|
||||
{
|
||||
var desc = new AllocatorDesc
|
||||
{
|
||||
@@ -74,6 +51,12 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource
|
||||
_device = device.NativeDevice;
|
||||
_renderSystem = renderSystem;
|
||||
_descriptorAllocator = descriptorAllocator;
|
||||
_resourceDatabase = resourceDatabase;
|
||||
}
|
||||
|
||||
~D3D12ResourceAllocator()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -94,11 +77,11 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource
|
||||
}
|
||||
}
|
||||
|
||||
private ResourceHandle TrackResource(ref readonly Allocation allocation, bool isTemp)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private ResourceHandle TrackResource(ref readonly Allocation allocation, ResourceStates state, bool isTemp)
|
||||
{
|
||||
var id = _allocations.Add(new(in allocation, _renderSystem.CPUFenceValue), out var generation);
|
||||
var handle = _resourceDatabase.AddResource(in allocation, _renderSystem.CPUFenceValue, D3D12StatesToRHIState(state));
|
||||
|
||||
var handle = new ResourceHandle(id, generation);
|
||||
if (isTemp)
|
||||
{
|
||||
_temResources.Enqueue(handle);
|
||||
@@ -107,18 +90,40 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource
|
||||
return handle;
|
||||
}
|
||||
|
||||
public TextureHandle CreateTextureHandle(ref readonly TextureDesc desc, bool tempResource = false)
|
||||
public TextureHandle CreateTexture(ref readonly TextureDesc desc, bool tempResource = false)
|
||||
{
|
||||
CheckTexture2DSize(desc.Width, desc.Height);
|
||||
|
||||
var resourceDesc = ResourceDescription.Tex2D(
|
||||
ConvertTextureFormat(desc.Format),
|
||||
var d3d12Format = ConvertTextureFormat(desc.Format);
|
||||
var mipLevels = desc.MipLevels == 0 ? (ushort)(1 + Math.Floor(Math.Log2(Math.Max(desc.Width, desc.Height)))) : (ushort)desc.MipLevels;
|
||||
|
||||
var resourceDesc = desc.Dimension switch
|
||||
{
|
||||
TextureDimension.Texture2D => ResourceDescription.Tex2D(
|
||||
d3d12Format,
|
||||
desc.Width,
|
||||
desc.Height,
|
||||
mipLevels: (ushort)desc.MipLevels,
|
||||
mipLevels: mipLevels,
|
||||
flags: ConvertTextureUsage(desc.Usage)),
|
||||
TextureDimension.Texture3D => ResourceDescription.Tex3D(
|
||||
d3d12Format,
|
||||
desc.Width,
|
||||
desc.Height,
|
||||
(ushort)desc.Slice,
|
||||
flags: ConvertTextureUsage(desc.Usage)),
|
||||
//case TextureDimension.TextureCube:
|
||||
// break;
|
||||
TextureDimension.Texture2DArray => ResourceDescription.Tex2D(
|
||||
d3d12Format,
|
||||
desc.Width,
|
||||
desc.Height,
|
||||
mipLevels: mipLevels,
|
||||
arraySize: (ushort)desc.Slice,
|
||||
flags: ConvertTextureUsage(desc.Usage)
|
||||
);
|
||||
flags: ConvertTextureUsage(desc.Usage)),
|
||||
//case TextureDimension.TextureCubeArray:
|
||||
// break;
|
||||
_ => throw new ArgumentException($"Unsupported texture dimension: {desc.Dimension}"),
|
||||
};
|
||||
|
||||
var allocationDesc = new AllocationDesc
|
||||
{
|
||||
@@ -131,10 +136,58 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource
|
||||
Allocation allocation = default;
|
||||
ThrowIfFailed(_allocator.CreateResource(&allocationDesc, in resourceDesc, initialState, null, &allocation, IID_NULL, null));
|
||||
|
||||
return new(TrackResource(in allocation, tempResource));
|
||||
var handle = TrackResource(in allocation, initialState, tempResource);
|
||||
|
||||
if (desc.CreationFlags.HasFlag(TextureCreationFlags.Bindless))
|
||||
{
|
||||
var descriptorHandle = _descriptorAllocator.AllocateBindless();
|
||||
var srvDesc = new ShaderResourceViewDescription
|
||||
{
|
||||
Format = d3d12Format,
|
||||
Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING
|
||||
};
|
||||
|
||||
switch (desc.Dimension)
|
||||
{
|
||||
case TextureDimension.Texture2D:
|
||||
srvDesc.ViewDimension = SrvDimension.Texture2D;
|
||||
srvDesc.Texture2D = new Texture2DSrv
|
||||
{
|
||||
MipLevels = mipLevels,
|
||||
};
|
||||
break;
|
||||
case TextureDimension.Texture3D:
|
||||
srvDesc.ViewDimension = SrvDimension.Texture3D;
|
||||
srvDesc.Texture3D = new Texture3DSrv
|
||||
{
|
||||
MipLevels = 0,
|
||||
};
|
||||
break;
|
||||
case TextureDimension.Texture2DArray:
|
||||
srvDesc.ViewDimension = SrvDimension.Texture2DArray;
|
||||
srvDesc.Texture2DArray = new Texture2DArraySrv
|
||||
{
|
||||
MipLevels = mipLevels,
|
||||
ArraySize = desc.Slice,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported texture dimension for SRV: {desc.Dimension}");
|
||||
}
|
||||
|
||||
public BufferHandle CreateBufferHandle(ref readonly BufferDesc desc, bool tempResource = false)
|
||||
_device->CreateShaderResourceView(allocation.Resource, &srvDesc, _descriptorAllocator.GetCpuHandle(descriptorHandle));
|
||||
}
|
||||
|
||||
return new(handle);
|
||||
}
|
||||
|
||||
public TextureHandle CreateRenderTarget(ref readonly RenderTargetDesc desc, bool tempResource = false)
|
||||
{
|
||||
var textureDesc = RenderTargetDesc.ToTextureDescriptor(desc);
|
||||
return CreateTexture(ref textureDesc, tempResource);
|
||||
}
|
||||
|
||||
public BufferHandle CreateBuffer(ref readonly BufferDesc desc, bool tempResource = false)
|
||||
{
|
||||
CheckBufferSize((uint)desc.Size);
|
||||
|
||||
@@ -150,12 +203,11 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource
|
||||
Allocation allocation = default;
|
||||
ThrowIfFailed(_allocator.CreateResource(&allocationDesc, in resourceDescription, initialState, null, &allocation, IID_NULL, null));
|
||||
|
||||
var handle = TrackResource(in allocation, tempResource);
|
||||
var handle = TrackResource(in allocation, initialState, tempResource);
|
||||
|
||||
if (desc.Usage.HasFlag(BufferUsage.ShaderResource) && desc.CreationFlags.HasFlag(BufferCreationFlags.Bindless))
|
||||
{
|
||||
var isRaw = desc.Usage.HasFlag(BufferUsage.Raw);
|
||||
|
||||
var descriptorHandle = _descriptorAllocator.AllocateBindless();
|
||||
|
||||
var srvDesc = new ShaderResourceViewDescription
|
||||
@@ -189,23 +241,75 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource
|
||||
return new(handle);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public IRenderTarget CreateRenderTarget(ref readonly RenderTargetDesc desc, bool tempResource = false)
|
||||
public BufferHandle CreateUploadBuffer(ulong size, bool temp = true)
|
||||
{
|
||||
var textureDesc = RenderTargetDesc.ToTextureDescriptor(desc);
|
||||
return D3D12RenderTarget.Create(CreateTextureHandle(in textureDesc), in desc);
|
||||
var desc = new BufferDesc
|
||||
{
|
||||
Size = size,
|
||||
Usage = BufferUsage.Upload,
|
||||
MemoryType = MemoryType.Upload,
|
||||
};
|
||||
|
||||
return CreateBuffer(in desc, temp);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ITexture CreateTexture(ref readonly TextureDesc desc, bool tempResource = false)
|
||||
public Identifier<Mesh> CreateMesh(ReadOnlySpan<Vertex> vertices, ReadOnlySpan<uint> indices)
|
||||
{
|
||||
return new D3D12Texture(CreateTextureHandle(in desc, tempResource), in desc);
|
||||
var vertexBufferDesc = new BufferDesc
|
||||
{
|
||||
Size = (ulong)(vertices.Length * Unsafe.SizeOf<Vertex>()),
|
||||
Stride = (uint)Unsafe.SizeOf<Vertex>(),
|
||||
Usage = BufferUsage.Vertex | BufferUsage.ShaderResource,
|
||||
MemoryType = MemoryType.Default,
|
||||
CreationFlags = BufferCreationFlags.Bindless
|
||||
};
|
||||
|
||||
var indexBufferDesc = new BufferDesc
|
||||
{
|
||||
Size = (ulong)(indices.Length * sizeof(uint)),
|
||||
Stride = sizeof(uint),
|
||||
Usage = BufferUsage.Index | BufferUsage.ShaderResource,
|
||||
MemoryType = MemoryType.Default,
|
||||
CreationFlags = BufferCreationFlags.Bindless
|
||||
};
|
||||
|
||||
var vertexBuffer = CreateBuffer(ref vertexBufferDesc, true);
|
||||
var indexBuffer = CreateBuffer(ref indexBufferDesc, true);
|
||||
|
||||
var data = new Mesh(vertices, indices, vertexBuffer, indexBuffer);
|
||||
return _resourceDatabase.AddMesh(in data);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public IBuffer CreateBuffer(ref readonly BufferDesc desc, bool tempResource = false)
|
||||
public Identifier<Material> CreateMaterial(Identifier<Shader> shader)
|
||||
{
|
||||
return new D3D12Buffer(CreateBufferHandle(in desc, tempResource), in desc, this);
|
||||
var materialData = new Material
|
||||
{
|
||||
Shader = shader,
|
||||
};
|
||||
|
||||
var shaderResource = _resourceDatabase.GetShader(shader);
|
||||
|
||||
if (shaderResource.ConstantBuffers.Count > 0)
|
||||
{
|
||||
var maxSlot = shaderResource.ConstantBuffers.Max(cb => cb.RegisterSlot);
|
||||
materialData._cBufferCaches = new UnsafeArray<CBufferCache>((int)maxSlot + 1, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
||||
|
||||
foreach (var cbufferInfo in shaderResource.ConstantBuffers)
|
||||
{
|
||||
var desc = new BufferDesc
|
||||
{
|
||||
Size = cbufferInfo.Size,
|
||||
Usage = BufferUsage.Constant,
|
||||
MemoryType = MemoryType.Default,
|
||||
};
|
||||
|
||||
var buffer = CreateBuffer(in desc);
|
||||
|
||||
materialData._cBufferCaches[cbufferInfo.RegisterSlot] = new CBufferCache(buffer, cbufferInfo.Size);
|
||||
}
|
||||
}
|
||||
|
||||
return _resourceDatabase.AddMaterial(in materialData);
|
||||
}
|
||||
|
||||
#region Conversion Methods
|
||||
@@ -319,6 +423,42 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource
|
||||
return ResourceStates.Common;
|
||||
}
|
||||
|
||||
private static ResourceState D3D12StatesToRHIState(ResourceStates states)
|
||||
{
|
||||
switch (states)
|
||||
{
|
||||
//case ResourceStates.None:
|
||||
//case ResourceStates.Present:
|
||||
case ResourceStates.Common:
|
||||
return ResourceState.Common;
|
||||
case ResourceStates.VertexAndConstantBuffer:
|
||||
return ResourceState.VertexAndConstantBuffer;
|
||||
case ResourceStates.IndexBuffer:
|
||||
return ResourceState.IndexBuffer;
|
||||
case ResourceStates.RenderTarget:
|
||||
return ResourceState.RenderTarget;
|
||||
case ResourceStates.UnorderedAccess:
|
||||
return ResourceState.UnorderedAccess;
|
||||
case ResourceStates.DepthWrite:
|
||||
return ResourceState.DepthWrite;
|
||||
case ResourceStates.DepthRead:
|
||||
return ResourceState.DepthRead;
|
||||
case ResourceStates.PixelShaderResource:
|
||||
return ResourceState.PixelShaderResource;
|
||||
//case ResourceStates.Predication:
|
||||
case ResourceStates.IndirectArgument:
|
||||
return ResourceState.IndirectArgument;
|
||||
case ResourceStates.CopyDest:
|
||||
return ResourceState.CopyDest;
|
||||
case ResourceStates.CopySource:
|
||||
return ResourceState.CopySource;
|
||||
case ResourceStates.GenericRead:
|
||||
return ResourceState.GenericRead;
|
||||
default:
|
||||
return ResourceState.Common;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void ReleaseTempResource()
|
||||
@@ -326,9 +466,8 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource
|
||||
while (_temResources.Count > 0)
|
||||
{
|
||||
var handle = _temResources.Peek();
|
||||
|
||||
if (_allocations.TryGetElementAt(handle.id, handle.generation, out var info)
|
||||
&& info.Allocated)
|
||||
ref var info = ref _resourceDatabase.GetResourceInfo(handle, out var exist);
|
||||
if (exist && info.Allocated && info.cpuFenceValue > _renderSystem.GPUFenceValue)
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -338,22 +477,6 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource
|
||||
}
|
||||
}
|
||||
|
||||
public ID3D12Resource* GetResource(ResourceHandle handle)
|
||||
{
|
||||
if (!handle.IsValid)
|
||||
{
|
||||
throw new InvalidOperationException("Invalid resource handle.");
|
||||
}
|
||||
|
||||
var info = _allocations.GetElementAt(handle.id, handle.generation);
|
||||
if (!info.Allocated)
|
||||
{
|
||||
throw new InvalidOperationException($"Resource with ID {handle.id} and generation {handle.generation} is not allocated or has been released.");
|
||||
}
|
||||
|
||||
return info.allocation.Resource;
|
||||
}
|
||||
|
||||
public void ReleaseResource(ResourceHandle handle)
|
||||
{
|
||||
if (!handle.IsValid)
|
||||
@@ -361,7 +484,7 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource
|
||||
return;
|
||||
}
|
||||
|
||||
ref var info = ref _allocations.GetElementReferenceAt(handle.id, handle.generation, out var exist);
|
||||
ref var info = ref _resourceDatabase.GetResourceInfo(handle, out var exist);
|
||||
|
||||
if (!exist || !info.Allocated)
|
||||
{
|
||||
@@ -369,24 +492,26 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource
|
||||
}
|
||||
|
||||
info.Dispose();
|
||||
_allocations.Remove(handle.id, handle.generation);
|
||||
_resourceDatabase.RemoveResource(handle);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
#if DEBUG
|
||||
if (_allocations.Count > 0)
|
||||
if (_temResources.Count > 0)
|
||||
{
|
||||
throw new InvalidOperationException($"ResourceAllocator is being disposed with {_allocations.Count} allocations still registered. Ensure all resources are released before disposing.");
|
||||
throw new InvalidOperationException($"ResourceAllocator is being disposed with {_temResources.Count} temp allocations still registered. Ensure all resources are released before disposing.");
|
||||
}
|
||||
#endif
|
||||
foreach (var info in _allocations)
|
||||
|
||||
foreach (var handle in _temResources)
|
||||
{
|
||||
info.Dispose();
|
||||
ReleaseResource(handle);
|
||||
}
|
||||
|
||||
_allocations.Dispose();
|
||||
_temResources.Dispose();
|
||||
_allocator.Release();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,338 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Win32.Graphics.D3D12MemoryAllocator;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal class D3D12ResourceDatabase
|
||||
internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
{
|
||||
private struct Slot<T>
|
||||
{
|
||||
public T value;
|
||||
public bool isValid;
|
||||
}
|
||||
|
||||
public struct ResourceInfo : IDisposable
|
||||
{
|
||||
public readonly Allocation allocation;
|
||||
public readonly uint cpuFenceValue;
|
||||
public ResourceState state;
|
||||
|
||||
public readonly bool Allocated => allocation.IsNotNull;
|
||||
|
||||
public ResourceInfo(in Allocation allocation, uint cpuFenceValue, ResourceState state)
|
||||
{
|
||||
this.allocation = allocation;
|
||||
this.cpuFenceValue = cpuFenceValue;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public readonly void Dispose()
|
||||
{
|
||||
if (allocation.IsNull)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
allocation.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private UnsafeSlotMap<ResourceInfo> _resources;
|
||||
|
||||
// NOTE: We use a simple list for shaders since they are not frequently added/removed. This can save 4 bytes for each ecs component.
|
||||
private readonly DynamicArray<Slot<Mesh>> _meshDatas;
|
||||
private readonly DynamicArray<Slot<Shader>> _shaders;
|
||||
|
||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public D3D12ResourceDatabase(D3D12DescriptorAllocator descriptorAllocator)
|
||||
{
|
||||
_resources = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
||||
|
||||
_meshDatas = new(64);
|
||||
_shaders = new(16);
|
||||
|
||||
_descriptorAllocator = descriptorAllocator;
|
||||
}
|
||||
|
||||
~D3D12ResourceDatabase()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public ResourceHandle AddResource(ref readonly Allocation allocation, uint cpuFenceValue, ResourceState initialState)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var id = _resources.Add(new ResourceInfo(allocation, cpuFenceValue, initialState), out var generation);
|
||||
return new ResourceHandle(id, generation);
|
||||
}
|
||||
|
||||
public ref ResourceInfo GetResourceInfo(ResourceHandle handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
ref var info = ref _resources.GetElementReferenceAt(handle.id, handle.generation, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
throw new KeyNotFoundException($"Resource with handle {handle} not found.");
|
||||
}
|
||||
|
||||
return ref info;
|
||||
}
|
||||
|
||||
public ref ResourceInfo GetResourceInfo(ResourceHandle handle, out bool exist)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return ref _resources.GetElementReferenceAt(handle.id, handle.generation, out exist);
|
||||
}
|
||||
|
||||
public unsafe T* GetResource<T>(ResourceHandle handle)
|
||||
where T : unmanaged
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (typeof(T) != typeof(ID3D12Resource))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var info = GetResourceInfo(handle);
|
||||
if (!info.Allocated)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (T*)info.allocation.Resource;
|
||||
}
|
||||
|
||||
public unsafe ID3D12Resource* GetResource(ResourceHandle handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var info = GetResourceInfo(handle);
|
||||
if (!info.Allocated)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return info.allocation.Resource;
|
||||
}
|
||||
|
||||
public ResourceState GetResourceState(ResourceHandle handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return GetResourceInfo(handle).state;
|
||||
}
|
||||
|
||||
public void SetResourceState(ResourceHandle handle, ResourceState state)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
ref var info = ref _resources.GetElementReferenceAt(handle.id, handle.generation, out var exist);
|
||||
if (!exist || info.Allocated == false)
|
||||
{
|
||||
throw new KeyNotFoundException($"Resource with handle {handle} not found.");
|
||||
}
|
||||
|
||||
info.state = state;
|
||||
}
|
||||
|
||||
public void RemoveResource(ResourceHandle handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
ref var info = ref _resources.GetElementReferenceAt(handle.id, handle.generation, out var exist);
|
||||
if (!exist || info.Allocated == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
info.Dispose();
|
||||
|
||||
_resources.Remove(handle.id, handle.generation);
|
||||
}
|
||||
|
||||
public Identifier<Mesh> AddMesh(ref readonly Mesh mesh)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var id = new Identifier<Mesh>(_meshDatas.Count);
|
||||
_meshDatas.Add(new()
|
||||
{
|
||||
value = mesh,
|
||||
isValid = true
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public bool HasMesh(Identifier<Mesh> id)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
return id.value >= 0 && id.value < _meshDatas.Count && _meshDatas[id.value].isValid;
|
||||
}
|
||||
|
||||
public Mesh GetMesh(Identifier<Mesh> id)
|
||||
{
|
||||
if (!HasMesh(id))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(id), $"Mesh ID {id.value} is out of range.");
|
||||
}
|
||||
|
||||
return _meshDatas[id.value].value;
|
||||
}
|
||||
|
||||
public ref Mesh GetMeshReference(Identifier<Mesh> id)
|
||||
{
|
||||
if (!HasMesh(id))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(id), $"Mesh ID {id.value} is out of range.");
|
||||
}
|
||||
|
||||
return ref _meshDatas[id.value].value;
|
||||
}
|
||||
|
||||
public void RemoveMesh(Identifier<Mesh> id)
|
||||
{
|
||||
if (!HasMesh(id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref var meshSlot = ref _meshDatas[id.value];
|
||||
if (!meshSlot.isValid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref var mesh = ref meshSlot.value;
|
||||
mesh.ReleaseCpuResources();
|
||||
|
||||
RemoveResource(mesh.vertexBuffer.ResourceHandle);
|
||||
RemoveResource(mesh.indexBuffer.ResourceHandle);
|
||||
|
||||
_descriptorAllocator.Release(mesh.vertexBuffer.BindlessDescriptor);
|
||||
_descriptorAllocator.Release(mesh.indexBuffer.BindlessDescriptor);
|
||||
}
|
||||
|
||||
public Identifier<Shader> AddShader(ref readonly Shader shader)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var id = new Identifier<Shader>(_shaders.Count);
|
||||
_shaders.Add(new()
|
||||
{
|
||||
value = shader,
|
||||
isValid = true
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public bool HasShader(Identifier<Shader> id)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
return id.value >= 0 && id.value < _shaders.Count && _shaders[id.value].isValid;
|
||||
}
|
||||
|
||||
public Shader GetShader(Identifier<Shader> id)
|
||||
{
|
||||
if (!HasShader(id))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(id), $"Shader ID {id.value} is out of range.");
|
||||
}
|
||||
|
||||
return _shaders[id.value].value;
|
||||
}
|
||||
|
||||
public ref Shader GetShaderReference(Identifier<Shader> id)
|
||||
{
|
||||
if (!HasShader(id))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(id), $"Shader ID {id.value} is out of range.");
|
||||
}
|
||||
|
||||
return ref _shaders[id.value].value;
|
||||
}
|
||||
|
||||
public void RemoveShader(Identifier<Shader> id)
|
||||
{
|
||||
if (!HasShader(id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref var shader = ref _shaders[id.value];
|
||||
|
||||
shader.value.Dispose();
|
||||
shader.value = default;
|
||||
shader.isValid = false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
if (_resources.Count > 0)
|
||||
{
|
||||
throw new InvalidOperationException($"ResourceAllocator is being disposed with {_resources.Count} allocations still registered. Ensure all resources are released before disposing.");
|
||||
}
|
||||
|
||||
if (_meshDatas.Count > 0)
|
||||
{
|
||||
throw new InvalidOperationException($"ResourceAllocator is being disposed with {_meshDatas.Count} meshes still registered. Ensure all meshes are released before disposing.");
|
||||
}
|
||||
|
||||
if (_shaders.Count > 0)
|
||||
{
|
||||
throw new InvalidOperationException($"ResourceAllocator is being disposed with {_shaders.Count} shaders still registered. Ensure all shaders are released before disposing.");
|
||||
}
|
||||
#endif
|
||||
|
||||
_resources.Dispose();
|
||||
|
||||
foreach (var mesh in _meshDatas)
|
||||
{
|
||||
if (!mesh.isValid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
mesh.value.ReleaseCpuResources();
|
||||
|
||||
RemoveResource(mesh.value.vertexBuffer.ResourceHandle);
|
||||
RemoveResource(mesh.value.indexBuffer.ResourceHandle);
|
||||
|
||||
_descriptorAllocator.Release(mesh.value.vertexBuffer.BindlessDescriptor);
|
||||
_descriptorAllocator.Release(mesh.value.indexBuffer.BindlessDescriptor);
|
||||
}
|
||||
|
||||
foreach (var shader in _shaders)
|
||||
{
|
||||
if (!shader.isValid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
shader.value.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -65,21 +65,25 @@ internal unsafe static class D3D12ShaderCompiler
|
||||
};
|
||||
}
|
||||
|
||||
public static CompileResult Compile(Shader shader, ShaderStage stage, CompilerVersion version)
|
||||
public static CompileResult Compile(string shaderPath, ShaderStage stage, CompilerVersion version)
|
||||
{
|
||||
using ComPtr<IDxcCompiler3> compiler = default;
|
||||
using ComPtr<IDxcUtils> utils = default;
|
||||
using ComPtr<IDxcIncludeHandler> includeHandler = default;
|
||||
|
||||
// Create DXC compiler and utils
|
||||
DxcCreateInstance(CLSID_DxcCompiler, __uuidof<IDxcCompiler3>(), compiler.GetVoidAddressOf());
|
||||
DxcCreateInstance(CLSID_DxcUtils, __uuidof<IDxcUtils>(), utils.GetVoidAddressOf());
|
||||
DxcCreateInstance(in CLSID_DxcCompiler, __uuidof<IDxcCompiler3>(), compiler.GetVoidAddressOf());
|
||||
DxcCreateInstance(in CLSID_DxcUtils, __uuidof<IDxcUtils>(), utils.GetVoidAddressOf());
|
||||
utils.Get()->CreateDefaultIncludeHandler(includeHandler.GetAddressOf());
|
||||
|
||||
// Create source blob
|
||||
using ComPtr<IDxcBlobEncoding> sourceBlob = default;
|
||||
var sourceBytes = System.Text.Encoding.UTF8.GetBytes(shader.Source);
|
||||
fixed (byte* sourceBytesPtr = sourceBytes)
|
||||
//var sourceBytes = System.Text.Encoding.UTF8.GetBytes(shaderPath);
|
||||
|
||||
fixed (char* pShaderPath = shaderPath.AsSpan())
|
||||
{
|
||||
utils.Get()->CreateBlob(sourceBytesPtr, (uint)sourceBytes.Length, DXC_CP_UTF8, sourceBlob.GetAddressOf());
|
||||
utils.Get()->LoadFile(pShaderPath, null, sourceBlob.GetAddressOf());
|
||||
//utils.Get()->CreateBlob(sourceBytesPtr, (uint)sourceBytes.Length, DXC_CP_UTF8, sourceBlob.GetAddressOf());
|
||||
}
|
||||
|
||||
// Prepare compilation arguments - NOTE: NO -Qstrip_reflect to keep reflection data
|
||||
@@ -116,7 +120,7 @@ internal unsafe static class D3D12ShaderCompiler
|
||||
Encoding = DXC_CP_UTF8
|
||||
};
|
||||
|
||||
compiler.Get()->Compile(&buffer, (char**)argsPtr, (uint)argsArray.Length, null, __uuidof<IDxcResult>(), result.GetVoidAddressOf());
|
||||
compiler.Get()->Compile(&buffer, (char**)argsPtr, (uint)argsArray.Length, includeHandler.Get(), __uuidof<IDxcResult>(), result.GetVoidAddressOf());
|
||||
}
|
||||
|
||||
// Check compilation result
|
||||
@@ -203,15 +207,17 @@ internal unsafe static class D3D12ShaderCompiler
|
||||
ShaderDescription shaderDesc;
|
||||
reflection.Get()->GetDesc(&shaderDesc);
|
||||
|
||||
var cbufferRegistry = shader.ConstantBuffers.ToDictionary(cb => cb.Name);
|
||||
var textureRegistry = shader.RegularTextures.ToDictionary(t => t.Name);
|
||||
var cbufferRegistry = new Dictionary<string, CBufferInfo>();
|
||||
var textureRegistry = new Dictionary<string, TextureInfo>();
|
||||
|
||||
for (uint i = 0; i < shaderDesc.BoundResources; i++)
|
||||
{
|
||||
ShaderInputBindDescription bindDesc;
|
||||
reflection.Get()->GetResourceBindingDesc(i, &bindDesc);
|
||||
|
||||
if (bindDesc.Type == ShaderInputType.ConstantBuffer)
|
||||
switch (bindDesc.Type)
|
||||
{
|
||||
case ShaderInputType.ConstantBuffer:
|
||||
{
|
||||
var cbufferName = Marshal.PtrToStringAnsi((IntPtr)bindDesc.Name);
|
||||
if (cbufferName == null || cbufferRegistry.ContainsKey(cbufferName))
|
||||
@@ -225,7 +231,6 @@ internal unsafe static class D3D12ShaderCompiler
|
||||
|
||||
var cbufferInfo = new CBufferInfo
|
||||
{
|
||||
Name = cbufferName,
|
||||
Size = cbufferDesc.Size,
|
||||
RegisterSlot = bindDesc.BindPoint
|
||||
};
|
||||
@@ -245,19 +250,18 @@ internal unsafe static class D3D12ShaderCompiler
|
||||
|
||||
var propInfo = new PropertyInfo
|
||||
{
|
||||
Name = variableName,
|
||||
CBufferIndex = cbufferInfo.RegisterSlot,
|
||||
ByteOffset = varDesc.StartOffset,
|
||||
Size = varDesc.Size
|
||||
};
|
||||
|
||||
// Add to the list and create the name-to-ID mapping
|
||||
var newId = shader.Properties.Count;
|
||||
shader.Properties.Add(propInfo);
|
||||
shader.PropertyNameToIdMap.Add(variableName, newId);
|
||||
shader.AddProperty(variableName, propInfo);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else if (bindDesc.Type == ShaderInputType.Texture)
|
||||
|
||||
case ShaderInputType.Texture:
|
||||
{
|
||||
var textureName = Marshal.PtrToStringAnsi((IntPtr)bindDesc.Name);
|
||||
if (textureName == null || textureRegistry.ContainsKey(textureName))
|
||||
@@ -269,19 +273,26 @@ internal unsafe static class D3D12ShaderCompiler
|
||||
// Bindless textures don't use explicit texture inputs - they use ResourceDescriptorHeap[index]
|
||||
var textureInfo = new TextureInfo
|
||||
{
|
||||
Name = textureName,
|
||||
RegisterSlot = bindDesc.BindPoint,
|
||||
RootParameterIndex = (uint)shader.ConstantBuffers.Count // Descriptor table comes after CBVs
|
||||
};
|
||||
|
||||
textureRegistry.Add(textureName, textureInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shader.ConstantBuffers.Clear();
|
||||
shader.ConstantBuffers.AddRange(cbufferRegistry.Values);
|
||||
foreach (var cbuf in cbufferRegistry.Values)
|
||||
{
|
||||
shader.ConstantBuffers.Add(cbuf);
|
||||
}
|
||||
|
||||
shader.RegularTextures.Clear();
|
||||
shader.RegularTextures.AddRange(textureRegistry.Values);
|
||||
foreach (var tex in textureRegistry.Values)
|
||||
{
|
||||
shader.RegularTextures.Add(tex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,11 @@ internal unsafe static class D3D12PipelineResource
|
||||
public const int BACK_BUFFER_COUNT = 2;
|
||||
|
||||
private readonly static InputElementDescription[] s_inputElementDescs = [
|
||||
new InputElementDescription{ SemanticName = Vertex.Semantic.pPositionName, SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 0u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||
new InputElementDescription{ SemanticName = Vertex.Semantic.pNormalName, SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 16u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||
new InputElementDescription{ SemanticName = Vertex.Semantic.pTangentName, SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 32u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||
new InputElementDescription{ SemanticName = Vertex.Semantic.pColorName, SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 48u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||
new InputElementDescription{ SemanticName = Vertex.Semantic.pUVName, SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 64u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 }
|
||||
new InputElementDescription{ SemanticName = Vertex.Semantic.position.GetUnsafePointer(), SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 0u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||
new InputElementDescription{ SemanticName = Vertex.Semantic.normal.GetUnsafePointer(), SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 16u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||
new InputElementDescription{ SemanticName = Vertex.Semantic.tangent.GetUnsafePointer(), SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 32u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||
new InputElementDescription{ SemanticName = Vertex.Semantic.uv.GetUnsafePointer(), SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 48u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||
new InputElementDescription{ SemanticName = Vertex.Semantic.color.GetUnsafePointer(), SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 64u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||
];
|
||||
|
||||
public const Format SWAP_CHAIN_BACK_BUFFER_FORMAT = Format.B8G8R8A8Unorm;
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
using Ghost.Graphics.D3D12;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Helpers;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Graphics.Data;
|
||||
|
||||
internal struct CBufferCache : IDisposable
|
||||
internal struct CBufferCache
|
||||
{
|
||||
public UnsafeArray<byte> CpuData
|
||||
{
|
||||
@@ -14,33 +11,18 @@ internal struct CBufferCache : IDisposable
|
||||
set;
|
||||
}
|
||||
|
||||
public GraphicsBuffer GpuResource
|
||||
public BufferHandle GpuResource
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
private readonly uint _alignedSize;
|
||||
|
||||
internal unsafe CBufferCache(uint bufferSize)
|
||||
internal unsafe CBufferCache(BufferHandle buffer, uint bufferSize)
|
||||
{
|
||||
_alignedSize = (bufferSize + 255u) & ~255u;
|
||||
|
||||
CpuData = new((int)_alignedSize, Allocator.Persistent);
|
||||
GpuResource = GraphicsBuffer.Create(_alignedSize, GraphicsBuffer.Usage.Constant);
|
||||
GpuResource.Name = "Material_CBufferCache";
|
||||
|
||||
UploadToGpu();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly void UploadToGpu()
|
||||
{
|
||||
GpuResource.SetData(CpuData.AsSpan(), 0);
|
||||
}
|
||||
|
||||
public readonly void Dispose()
|
||||
{
|
||||
CpuData.Dispose();
|
||||
GpuResource.Dispose();
|
||||
GpuResource = buffer;
|
||||
}
|
||||
}
|
||||
@@ -7,14 +7,14 @@ namespace Ghost.Graphics.Data;
|
||||
/// Represents a color with 4 bytes components.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = 4)]
|
||||
public struct Color4 : IEquatable<Color4>
|
||||
public struct Color32 : IEquatable<Color32>
|
||||
{
|
||||
public byte r;
|
||||
public byte g;
|
||||
public byte b;
|
||||
public byte a;
|
||||
|
||||
public Color4(byte r, byte g, byte b, byte a)
|
||||
public Color32(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
@@ -22,24 +22,24 @@ public struct Color4 : IEquatable<Color4>
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public Color4(Color color)
|
||||
public Color32(Color color)
|
||||
: this(color.R, color.G, color.B, color.A)
|
||||
{
|
||||
}
|
||||
|
||||
public Color4(Color16 color128)
|
||||
public Color32(Color128 color128)
|
||||
: this((byte)(color128.r * 255.0f), (byte)(color128.g * 255.0f), (byte)(color128.b * 255.0f), (byte)(color128.a * 255.0f))
|
||||
{
|
||||
}
|
||||
|
||||
public readonly bool Equals(Color4 other)
|
||||
public readonly bool Equals(Color32 other)
|
||||
{
|
||||
return r == other.r && g == other.g && b == other.b && a == other.a;
|
||||
}
|
||||
|
||||
public override readonly bool Equals(object? obj)
|
||||
{
|
||||
return obj is Color4 color && Equals(color);
|
||||
return obj is Color32 color && Equals(color);
|
||||
}
|
||||
|
||||
public override readonly int GetHashCode()
|
||||
@@ -47,12 +47,12 @@ public struct Color4 : IEquatable<Color4>
|
||||
return HashCode.Combine(r, g, b, a);
|
||||
}
|
||||
|
||||
public static bool operator ==(Color4 left, Color4 right)
|
||||
public static bool operator ==(Color32 left, Color32 right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Color4 left, Color4 right)
|
||||
public static bool operator !=(Color32 left, Color32 right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
@@ -62,14 +62,14 @@ public struct Color4 : IEquatable<Color4>
|
||||
/// Represents a color with 16 bytes components.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = 16)]
|
||||
public struct Color16 : IEquatable<Color16>
|
||||
public struct Color128 : IEquatable<Color128>
|
||||
{
|
||||
public float r;
|
||||
public float g;
|
||||
public float b;
|
||||
public float a;
|
||||
|
||||
public Color16(float r, float g, float b, float a)
|
||||
public Color128(float r, float g, float b, float a)
|
||||
{
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
@@ -77,24 +77,24 @@ public struct Color16 : IEquatable<Color16>
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public Color16(Color color)
|
||||
public Color128(Color color)
|
||||
: this(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, color.A / 255.0f)
|
||||
{
|
||||
}
|
||||
|
||||
public Color16(Color4 color32)
|
||||
public Color128(Color32 color32)
|
||||
: this(color32.r / 255.0f, color32.g / 255.0f, color32.b / 255.0f, color32.a / 255.0f)
|
||||
{
|
||||
}
|
||||
|
||||
public readonly bool Equals(Color16 other)
|
||||
public readonly bool Equals(Color128 other)
|
||||
{
|
||||
return r.Equals(other.r) && g.Equals(other.g) && b.Equals(other.b) && a.Equals(other.a);
|
||||
}
|
||||
|
||||
public override readonly bool Equals(object? obj)
|
||||
{
|
||||
return obj is Color16 color && Equals(color);
|
||||
return obj is Color128 color && Equals(color);
|
||||
}
|
||||
|
||||
public readonly override int GetHashCode()
|
||||
@@ -102,12 +102,12 @@ public struct Color16 : IEquatable<Color16>
|
||||
return HashCode.Combine(r, g, b, a);
|
||||
}
|
||||
|
||||
public static bool operator ==(Color16 left, Color16 right)
|
||||
public static bool operator ==(Color128 left, Color128 right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Color16 left, Color16 right)
|
||||
public static bool operator !=(Color128 left, Color128 right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
namespace Ghost.Graphics.Data;
|
||||
|
||||
public struct BatchMaterialID : IEquatable<BatchMaterialID>
|
||||
{
|
||||
public uint value;
|
||||
|
||||
public static BatchMaterialID Null => new() { value = 0 };
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return value.GetHashCode();
|
||||
}
|
||||
|
||||
public readonly override bool Equals(object? obj)
|
||||
{
|
||||
return obj is BatchMaterialID id && Equals(id);
|
||||
}
|
||||
|
||||
public readonly bool Equals(BatchMaterialID other)
|
||||
{
|
||||
return value == other.value;
|
||||
}
|
||||
|
||||
public readonly int CompareTo(BatchMaterialID other)
|
||||
{
|
||||
return value.CompareTo(other.value);
|
||||
}
|
||||
|
||||
public static bool operator ==(BatchMaterialID a, BatchMaterialID b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(BatchMaterialID a, BatchMaterialID b)
|
||||
{
|
||||
return !a.Equals(b);
|
||||
}
|
||||
}
|
||||
|
||||
internal class GraphicsResourceManager
|
||||
{
|
||||
private readonly Dictionary<BatchMaterialID, Material> _materials = new();
|
||||
private readonly Dictionary<BatchMeshID, MeshHandle> _meshes = new();
|
||||
|
||||
public Material GetMaterial(BatchMaterialID id)
|
||||
{
|
||||
return _materials.TryGetValue(id, out var material) ? material : Material.Null;
|
||||
}
|
||||
|
||||
public MeshHandle GetMesh(BatchMeshID id)
|
||||
{
|
||||
return _meshes.TryGetValue(id, out var mesh) ? mesh : MeshHandle.Invalid;
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
using Ghost.Graphics.D3D12;
|
||||
using Ghost.Graphics.RHI;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Material implementation for bindless rendering with SM 6.6 support
|
||||
/// </summary>
|
||||
public unsafe class Material : IDisposable
|
||||
{
|
||||
private readonly CBufferCache[] _cbufferCaches;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
internal ReadOnlySpan<CBufferCache> CBufferCaches => _cbufferCaches;
|
||||
|
||||
public Shader Shader
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public Material(Shader shader)
|
||||
{
|
||||
Shader = shader;
|
||||
|
||||
if (shader.ConstantBuffers.Count > 0)
|
||||
{
|
||||
var maxSlot = shader.ConstantBuffers.Max(cb => cb.RegisterSlot);
|
||||
_cbufferCaches = new CBufferCache[maxSlot + 1];
|
||||
|
||||
foreach (var cbufferInfo in shader.ConstantBuffers)
|
||||
{
|
||||
_cbufferCaches[cbufferInfo.RegisterSlot] = new CBufferCache(cbufferInfo.Size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_cbufferCaches = Array.Empty<CBufferCache>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a float property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyId">The ID of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetFloat(int propertyId, in float value)
|
||||
{
|
||||
WriteToCache(propertyId, in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a float property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetFloat(string propertyName, in float value)
|
||||
{
|
||||
SetFloat(Shader.GetPropertyId(propertyName), in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a uint property in the material's constant buffer (useful for texture indices).
|
||||
/// </summary>
|
||||
/// <param name="propertyId">The ID of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetUInt(int propertyId, in uint value)
|
||||
{
|
||||
WriteToCache(propertyId, in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a uint property in the material's constant buffer (useful for texture indices).
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetUInt(string propertyName, in uint value)
|
||||
{
|
||||
SetUInt(Shader.GetPropertyId(propertyName), in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a Vector property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyId">The ID of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetVector(int propertyId, in Vector4 value)
|
||||
{
|
||||
WriteToCache(propertyId, in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a Vector property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetVector(string propertyName, in Vector4 value)
|
||||
{
|
||||
SetVector(Shader.GetPropertyId(propertyName), in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a Matrix property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyId">The ID of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetMatrix(int propertyId, in Matrix4x4 value)
|
||||
{
|
||||
WriteToCache(propertyId, in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a Matrix property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetMatrix(string propertyName, in Matrix4x4 value)
|
||||
{
|
||||
SetMatrix(Shader.GetPropertyId(propertyName), in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a texture index for a shader property (for bindless texture access)
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the shader property (e.g., "_TextureIndex1")</param>
|
||||
/// <param name="texture">The bindless texture to reference</param>
|
||||
public void SetTexture(string propertyName, Texture2D texture)
|
||||
{
|
||||
SetUInt(propertyName, texture.DescriptorIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the mesh buffer indices for bindless vertex and index buffer access
|
||||
/// </summary>
|
||||
/// <param name="mesh">The mesh whose buffer indices to set</param>
|
||||
/// <param name="vertexBufferIndexProperty">The name of the vertex buffer index property (e.g., "_VertexBufferIndex")</param>
|
||||
/// <param name="indexBufferIndexProperty">The name of the index buffer index property (e.g., "_IndexBufferIndex")</param>
|
||||
public void SetMeshBuffer(Mesh mesh, string vertexBufferIndexProperty = "_VertexBufferIndex", string indexBufferIndexProperty = "_IndexBufferIndex")
|
||||
{
|
||||
SetUInt(vertexBufferIndexProperty, mesh.VertexBufferDescriptorIndex);
|
||||
SetUInt(indexBufferIndexProperty, mesh.IndexBufferDescriptorIndex);
|
||||
}
|
||||
|
||||
private unsafe void WriteToCache<T>(int propertyId, in T value)
|
||||
where T : unmanaged
|
||||
{
|
||||
if (propertyId == -1)
|
||||
{
|
||||
throw new ArgumentException("Property ID is invalid.");
|
||||
}
|
||||
|
||||
var propInfo = Shader.Properties[propertyId];
|
||||
if (propInfo.Size != sizeof(T))
|
||||
{
|
||||
throw new ArgumentException($"Property '{propInfo.Name}' has a size mismatch. Expected {propInfo.Size} bytes, but got {sizeof(T)} bytes.");
|
||||
}
|
||||
|
||||
var cache = _cbufferCaches[propInfo.CBufferIndex];
|
||||
|
||||
Unsafe.WriteUnaligned(ref cache.CpuData[(int)propInfo.ByteOffset], value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uploads the material data to the GPU.
|
||||
/// </summary>
|
||||
public void UploadMaterialData()
|
||||
{
|
||||
foreach (var cache in _cbufferCaches)
|
||||
{
|
||||
cache.UploadToGpu();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var cache in _cbufferCaches)
|
||||
{
|
||||
cache.Dispose();
|
||||
}
|
||||
|
||||
// NOTE: We don't dispose the textures here as they might be shared
|
||||
// The user is responsible for disposing BindlessTexture2D instances
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
376
Ghost.Graphics/Data/MaterialClass.cs
Normal file
376
Ghost.Graphics/Data/MaterialClass.cs
Normal file
@@ -0,0 +1,376 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Graphics.Data;
|
||||
|
||||
public struct Material
|
||||
{
|
||||
internal UnsafeArray<CBufferCache> _cBufferCaches;
|
||||
|
||||
public Identifier<Shader> Shader
|
||||
{
|
||||
get; internal set;
|
||||
}
|
||||
}
|
||||
|
||||
public ref struct MaterialAccessor
|
||||
{
|
||||
private ref Material _materialData;
|
||||
private ref Shader _shader;
|
||||
|
||||
public MaterialAccessor(ref Material materialData, IResourceDatabase resourceDatabase)
|
||||
{
|
||||
_materialData = ref materialData;
|
||||
_shader = ref resourceDatabase.GetShaderReference(materialData.Shader);
|
||||
}
|
||||
|
||||
private readonly unsafe void WriteToCache<T>(int propertyId, in T value)
|
||||
where T : unmanaged
|
||||
{
|
||||
if (propertyId == -1)
|
||||
{
|
||||
throw new ArgumentException("Property ID is invalid.");
|
||||
}
|
||||
|
||||
var propInfo = _shader.Properties[propertyId];
|
||||
if (propInfo.Size != sizeof(T))
|
||||
{
|
||||
throw new ArgumentException($"Property '{propertyId}' has a size mismatch. Expected {propInfo.Size} bytes, but got {sizeof(T)} bytes.");
|
||||
}
|
||||
|
||||
var cache = _materialData._cBufferCaches[propInfo.CBufferIndex];
|
||||
|
||||
Unsafe.WriteUnaligned(ref cache.CpuData[(int)propInfo.ByteOffset], value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a float property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyId">The ID of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly void SetFloat(int propertyId, in float value)
|
||||
{
|
||||
WriteToCache(propertyId, in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a float property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetFloat(string propertyName, in float value)
|
||||
{
|
||||
SetFloat(_shader.GetPropertyId(propertyName), in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a uint property in the material's constant buffer (useful for texture indices).
|
||||
/// </summary>
|
||||
/// <param name="propertyId">The ID of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly void SetUInt(int propertyId, in uint value)
|
||||
{
|
||||
WriteToCache(propertyId, in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a uint property in the material's constant buffer (useful for texture indices).
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetUInt(string propertyName, in uint value)
|
||||
{
|
||||
SetUInt(_shader.GetPropertyId(propertyName), in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a Vector property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyId">The ID of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly void SetVector(int propertyId, in Vector4 value)
|
||||
{
|
||||
WriteToCache(propertyId, in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a Vector property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetVector(string propertyName, in Vector4 value)
|
||||
{
|
||||
SetVector(_shader.GetPropertyId(propertyName), in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a Matrix property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyId">The ID of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly void SetMatrix(int propertyId, in Matrix4x4 value)
|
||||
{
|
||||
WriteToCache(propertyId, in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a Matrix property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetMatrix(string propertyName, in Matrix4x4 value)
|
||||
{
|
||||
SetMatrix(_shader.GetPropertyId(propertyName), in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a texture index for a shader property (for bindless texture access)
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the shader property (e.g., "_TextureIndex1")</param>
|
||||
/// <param name="texture">The bindless texture to reference</param>
|
||||
public void SetTexture(string propertyName, Texture2D texture)
|
||||
{
|
||||
SetUInt(propertyName, texture.DescriptorIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the mesh buffer indices for bindless vertex and index buffer access
|
||||
/// </summary>
|
||||
/// <param name="mesh">The mesh whose buffer indices to set</param>
|
||||
/// <param name="vertexBufferIndexProperty">The name of the vertex buffer index property (e.g., "_VertexBufferIndex")</param>
|
||||
/// <param name="indexBufferIndexProperty">The name of the index buffer index property (e.g., "_IndexBufferIndex")</param>
|
||||
public void SetMeshBuffer(MeshClass mesh, string vertexBufferIndexProperty = "_VertexBufferIndex", string indexBufferIndexProperty = "_IndexBufferIndex")
|
||||
{
|
||||
SetUInt(vertexBufferIndexProperty, mesh.VertexBufferDescriptorIndex);
|
||||
SetUInt(indexBufferIndexProperty, mesh.IndexBufferDescriptorIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uploads all cached material data to the GPU using the specified command buffer.
|
||||
/// </summary>
|
||||
/// <param name="cmb">The command buffer used to perform the upload operations to the GPU. Cannot be null.</param>
|
||||
public readonly void UploadMaterialData(ICommandBuffer cmb)
|
||||
{
|
||||
foreach (var cache in _materialData._cBufferCaches)
|
||||
{
|
||||
cmb.Upload(cache.GpuResource, cache.CpuData.AsSpan());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Material implementation for bindless rendering with SM 6.6 support
|
||||
/// </summary>
|
||||
public unsafe class MaterialClass : IDisposable
|
||||
{
|
||||
private readonly UnsafeArray<CBufferCache> _cbufferCaches;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
internal ReadOnlySpan<CBufferCache> CBufferCaches => _cbufferCaches.AsSpan();
|
||||
|
||||
public Identifier<Shader> Shader
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public MaterialClass(Identifier<Shader> shader, IResourceAllocator resourceAllocator, IResourceDatabase resourceDatabase)
|
||||
{
|
||||
Shader = shader;
|
||||
|
||||
var shaderResource = resourceDatabase.GetShader(shader);
|
||||
|
||||
if (shaderResource.ConstantBuffers.Count > 0)
|
||||
{
|
||||
var maxSlot = shaderResource.ConstantBuffers.Max(cb => cb.RegisterSlot);
|
||||
_cbufferCaches = new UnsafeArray<CBufferCache>((int)maxSlot + 1, Allocator.Persistent);
|
||||
|
||||
foreach (var cbufferInfo in shaderResource.ConstantBuffers)
|
||||
{
|
||||
var desc = new BufferDesc
|
||||
{
|
||||
Size = cbufferInfo.Size,
|
||||
Usage = BufferUsage.Constant,
|
||||
MemoryType = MemoryType.Default,
|
||||
};
|
||||
|
||||
var buffer = resourceAllocator.CreateBuffer(in desc);
|
||||
|
||||
_cbufferCaches[cbufferInfo.RegisterSlot] = new CBufferCache(buffer, cbufferInfo.Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a float property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyId">The ID of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetFloat(int propertyId, in float value)
|
||||
{
|
||||
WriteToCache(propertyId, in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a float property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetFloat(string propertyName, in float value)
|
||||
{
|
||||
SetFloat(Shader.GetPropertyId(propertyName), in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a uint property in the material's constant buffer (useful for texture indices).
|
||||
/// </summary>
|
||||
/// <param name="propertyId">The ID of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetUInt(int propertyId, in uint value)
|
||||
{
|
||||
WriteToCache(propertyId, in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a uint property in the material's constant buffer (useful for texture indices).
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetUInt(string propertyName, in uint value)
|
||||
{
|
||||
SetUInt(Shader.GetPropertyId(propertyName), in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a Vector property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyId">The ID of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetVector(int propertyId, in Vector4 value)
|
||||
{
|
||||
WriteToCache(propertyId, in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a Vector property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetVector(string propertyName, in Vector4 value)
|
||||
{
|
||||
SetVector(Shader.GetPropertyId(propertyName), in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a Matrix property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyId">The ID of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetMatrix(int propertyId, in Matrix4x4 value)
|
||||
{
|
||||
WriteToCache(propertyId, in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a Matrix property in the material's constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property to set.</param>
|
||||
/// <param name="value">The value to set for the property.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetMatrix(string propertyName, in Matrix4x4 value)
|
||||
{
|
||||
SetMatrix(Shader.GetPropertyId(propertyName), in value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a texture index for a shader property (for bindless texture access)
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the shader property (e.g., "_TextureIndex1")</param>
|
||||
/// <param name="texture">The bindless texture to reference</param>
|
||||
public void SetTexture(string propertyName, Texture2D texture)
|
||||
{
|
||||
SetUInt(propertyName, texture.DescriptorIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the mesh buffer indices for bindless vertex and index buffer access
|
||||
/// </summary>
|
||||
/// <param name="mesh">The mesh whose buffer indices to set</param>
|
||||
/// <param name="vertexBufferIndexProperty">The name of the vertex buffer index property (e.g., "_VertexBufferIndex")</param>
|
||||
/// <param name="indexBufferIndexProperty">The name of the index buffer index property (e.g., "_IndexBufferIndex")</param>
|
||||
public void SetMeshBuffer(MeshClass mesh, string vertexBufferIndexProperty = "_VertexBufferIndex", string indexBufferIndexProperty = "_IndexBufferIndex")
|
||||
{
|
||||
SetUInt(vertexBufferIndexProperty, mesh.VertexBufferDescriptorIndex);
|
||||
SetUInt(indexBufferIndexProperty, mesh.IndexBufferDescriptorIndex);
|
||||
}
|
||||
|
||||
private unsafe void WriteToCache<T>(int propertyId, in T value)
|
||||
where T : unmanaged
|
||||
{
|
||||
if (propertyId == -1)
|
||||
{
|
||||
throw new ArgumentException("Property ID is invalid.");
|
||||
}
|
||||
|
||||
var propInfo = Shader.Properties[propertyId];
|
||||
if (propInfo.Size != sizeof(T))
|
||||
{
|
||||
throw new ArgumentException($"Property '{propInfo.Name}' has a size mismatch. Expected {propInfo.Size} bytes, but got {sizeof(T)} bytes.");
|
||||
}
|
||||
|
||||
var cache = _cbufferCaches[propInfo.CBufferIndex];
|
||||
|
||||
Unsafe.WriteUnaligned(ref cache.CpuData[(int)propInfo.ByteOffset], value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uploads the material data to the GPU.
|
||||
/// </summary>
|
||||
public void UploadMaterialData()
|
||||
{
|
||||
foreach (var cache in _cbufferCaches)
|
||||
{
|
||||
cache.UploadToGpu();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var cache in _cbufferCaches)
|
||||
{
|
||||
cache.Dispose();
|
||||
}
|
||||
|
||||
// NOTE: We don't dispose the textures here as they might be shared
|
||||
// The user is responsible for disposing BindlessTexture2D instances
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
@@ -2,82 +2,68 @@
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Helpers;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using Misaki.HighPerformance.Mathematics.Geometry;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Graphics.Dxgi.Common;
|
||||
|
||||
namespace Ghost.Graphics.Data;
|
||||
|
||||
/// <summary>
|
||||
/// The CPU-side mesh data structure.
|
||||
/// </summary>
|
||||
public struct MeshData
|
||||
public struct Mesh
|
||||
{
|
||||
public UnsafeList<Vertex> vertices;
|
||||
public UnsafeList<int> indices;
|
||||
public UnsafeList<uint> indices;
|
||||
public AABB boundingBox;
|
||||
public MeshHandle handle;
|
||||
|
||||
public MeshData()
|
||||
{
|
||||
handle = MeshHandle.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The GPU-side mesh handle containing buffer references.
|
||||
/// </summary>
|
||||
public struct MeshHandle
|
||||
{
|
||||
public BufferHandle vertexBuffer;
|
||||
public BufferHandle indexBuffer;
|
||||
|
||||
public static MeshHandle Invalid => new() { vertexBuffer = BufferHandle.Invalid, indexBuffer = BufferHandle.Invalid };
|
||||
|
||||
public readonly bool IsValid => vertexBuffer.IsValid && indexBuffer.IsValid;
|
||||
}
|
||||
|
||||
public struct BatchMeshID : IEquatable<BatchMeshID>
|
||||
public Mesh()
|
||||
{
|
||||
public int value;
|
||||
vertexBuffer = BufferHandle.Invalid;
|
||||
indexBuffer = BufferHandle.Invalid;
|
||||
}
|
||||
|
||||
public static BatchMeshID Null => new() { value = -1 };
|
||||
|
||||
public readonly override int GetHashCode()
|
||||
internal Mesh(ReadOnlySpan<Vertex> vertices, ReadOnlySpan<uint> indices, BufferHandle vertexBuffer, BufferHandle indexBuffer)
|
||||
{
|
||||
return value.GetHashCode();
|
||||
this.vertices = new(vertices.Length, Allocator.Persistent);
|
||||
this.indices = new(indices.Length, Allocator.Persistent);
|
||||
this.vertices.CopyFrom(vertices);
|
||||
this.indices.CopyFrom(indices);
|
||||
this.vertexBuffer = vertexBuffer;
|
||||
this.indexBuffer = indexBuffer;
|
||||
|
||||
ComputeBounds();
|
||||
}
|
||||
|
||||
public readonly override bool Equals(object? obj)
|
||||
public void ComputeBounds()
|
||||
{
|
||||
return obj is BatchMeshID id && Equals(id);
|
||||
}
|
||||
|
||||
public readonly bool Equals(BatchMeshID other)
|
||||
if (vertices.Count == 0)
|
||||
{
|
||||
return value == other.value;
|
||||
return;
|
||||
}
|
||||
|
||||
public readonly int CompareTo(BatchMeshID other)
|
||||
var min = new float3(float.MaxValue);
|
||||
var max = new float3(float.MinValue);
|
||||
foreach (var vertex in vertices)
|
||||
{
|
||||
return value.CompareTo(other.value);
|
||||
var pos = vertex.position.xyz;
|
||||
min = math.min(min, pos);
|
||||
max = math.max(max, pos);
|
||||
}
|
||||
|
||||
public static bool operator ==(BatchMeshID a, BatchMeshID b)
|
||||
boundingBox = new AABB(min, max);
|
||||
}
|
||||
|
||||
public void ReleaseCpuResources()
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(BatchMeshID a, BatchMeshID b)
|
||||
{
|
||||
return !a.Equals(b);
|
||||
vertices.Dispose();
|
||||
indices.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe sealed class Mesh : IDisposable
|
||||
public unsafe sealed class MeshClass : IDisposable
|
||||
{
|
||||
private UnsafeList<Vertex> _vertices;
|
||||
private UnsafeList<int> _indices;
|
||||
@@ -132,13 +118,13 @@ public unsafe sealed class Mesh : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public Mesh(int initialVertexCapacity = 256, int initialIndexCapacity = 512)
|
||||
public MeshClass(int initialVertexCapacity = 256, int initialIndexCapacity = 512)
|
||||
{
|
||||
_vertices = new(initialVertexCapacity, Allocator.Persistent);
|
||||
_indices = new(initialIndexCapacity, Allocator.Persistent);
|
||||
}
|
||||
|
||||
public Mesh(ReadOnlySpan<Vertex> vertices, ReadOnlySpan<int> indices)
|
||||
public MeshClass(ReadOnlySpan<Vertex> vertices, ReadOnlySpan<int> indices)
|
||||
: this(vertices.Length, indices.Length)
|
||||
{
|
||||
_vertices = new(vertices.Length, Allocator.Persistent);
|
||||
@@ -148,7 +134,7 @@ public unsafe sealed class Mesh : IDisposable
|
||||
_indices.CopyFrom(indices);
|
||||
}
|
||||
|
||||
~Mesh()
|
||||
~MeshClass()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
@@ -233,18 +219,18 @@ public unsafe sealed class Mesh : IDisposable
|
||||
var v1 = _vertices[i1];
|
||||
var v2 = _vertices[i2];
|
||||
|
||||
var edge1 = v1.Position - v0.Position;
|
||||
var edge2 = v2.Position - v0.Position;
|
||||
var faceNormal = Vector3.Cross(edge1.AsVector3(), edge2.AsVector3());
|
||||
var edge1 = v1.position - v0.position;
|
||||
var edge2 = v2.position - v0.position;
|
||||
var faceNormal = math.cross(edge1.xyz, edge2.xyz);
|
||||
|
||||
_vertices[i0].Normal += faceNormal.AsVector4();
|
||||
_vertices[i1].Normal += faceNormal.AsVector4();
|
||||
_vertices[i2].Normal += faceNormal.AsVector4();
|
||||
_vertices[i0].normal.xyz += faceNormal;
|
||||
_vertices[i1].normal.xyz += faceNormal;
|
||||
_vertices[i2].normal.xyz += faceNormal;
|
||||
}
|
||||
|
||||
for (var i = 0; i < _vertices.Count; i++)
|
||||
{
|
||||
_vertices[i].Normal = Vector4.Normalize(_vertices[i].Normal);
|
||||
_vertices[i].normal = math.normalize(_vertices[i].normal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,7 +242,7 @@ public unsafe sealed class Mesh : IDisposable
|
||||
/// </remarks>
|
||||
public void ComputeTangents()
|
||||
{
|
||||
var bitangents = new Vector4[_vertices.Count];
|
||||
var bitangents = new float4[_vertices.Count];
|
||||
|
||||
for (var i = 0; i < _indices.Count; i += 3)
|
||||
{
|
||||
@@ -268,29 +254,24 @@ public unsafe sealed class Mesh : IDisposable
|
||||
var v1 = _vertices[i1];
|
||||
var v2 = _vertices[i2];
|
||||
|
||||
var uv0 = _vertices[i0].UV;
|
||||
var uv1 = _vertices[i1].UV;
|
||||
var uv2 = _vertices[i2].UV;
|
||||
var uv0 = _vertices[i0].uv;
|
||||
var uv1 = _vertices[i1].uv;
|
||||
var uv2 = _vertices[i2].uv;
|
||||
|
||||
var deltaPos1 = v1.Position - v0.Position;
|
||||
var deltaPos2 = v2.Position - v0.Position;
|
||||
var deltaPos1 = v1.position - v0.position;
|
||||
var deltaPos2 = v2.position - v0.position;
|
||||
var deltaUV1 = uv1 - uv0;
|
||||
var deltaUV2 = uv2 - uv0;
|
||||
|
||||
var r = 1.0f / (deltaUV1.X * deltaUV2.Y - deltaUV1.Y * deltaUV2.X);
|
||||
var tangent = (deltaPos1 * deltaUV2.Y - deltaPos2 * deltaUV1.Y) * r;
|
||||
var bitangent = (deltaPos2 * deltaUV1.X - deltaPos1 * deltaUV2.X) * r;
|
||||
var r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
|
||||
var tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y) * r;
|
||||
var bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x) * r;
|
||||
|
||||
for (var j = 0; j < 3; j++)
|
||||
{
|
||||
var idx = _indices[i + j];
|
||||
var t = _vertices[idx].Tangent;
|
||||
_vertices[idx].Tangent = new Vector4(
|
||||
t.X + tangent.X,
|
||||
t.Y + tangent.Y,
|
||||
t.Z + tangent.Z,
|
||||
0.0f // we’ll fill w later
|
||||
);
|
||||
var t = _vertices[idx].tangent;
|
||||
_vertices[idx].tangent.xyz = t.xyz + tangent.xyz;
|
||||
|
||||
bitangents[idx] += bitangent;
|
||||
}
|
||||
@@ -298,18 +279,16 @@ public unsafe sealed class Mesh : IDisposable
|
||||
|
||||
for (var i = 0; i < _vertices.Count; i++)
|
||||
{
|
||||
var n = _vertices[i].Normal;
|
||||
var t = _vertices[i].Tangent;
|
||||
var n3 = n.AsVector3();
|
||||
var t3 = t.AsVector3();
|
||||
var n = _vertices[i].normal;
|
||||
var t = _vertices[i].tangent;
|
||||
|
||||
var proj = n3 * Vector3.Dot(n3, t3);
|
||||
t3 = Vector3.Normalize(t3 - proj);
|
||||
var proj = n * math.dot(n, t);
|
||||
t = math.normalize(t - proj);
|
||||
|
||||
var b = bitangents[i];
|
||||
var w = Vector3.Dot(Vector3.Cross(n3, t3), b.AsVector3()) < 0.0f ? -1.0f : 1.0f;
|
||||
var w = math.dot(math.cross(n.xyz, t.xyz), b.xyz) < 0.0f ? -1.0f : 1.0f;
|
||||
|
||||
_vertices[i].Tangent = new Vector4(t3.X, t3.Y, t3.Z, w);
|
||||
_vertices[i].tangent = new float4(t.x, t.y, t.z, w);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,20 +299,20 @@ public unsafe sealed class Mesh : IDisposable
|
||||
{
|
||||
if (_vertices.Count == 0)
|
||||
{
|
||||
_boundingBox = Bounds.Zero;
|
||||
_boundingBox = AABB.Zero;
|
||||
return;
|
||||
}
|
||||
|
||||
var min = new Vector3(float.MaxValue);
|
||||
var max = new Vector3(float.MinValue);
|
||||
var min = new float3(float.MaxValue);
|
||||
var max = new float3(float.MinValue);
|
||||
foreach (var vertex in _vertices)
|
||||
{
|
||||
var pos = vertex.Position.AsVector3();
|
||||
min = Vector3.Min(min, pos);
|
||||
max = Vector3.Max(max, pos);
|
||||
var pos = vertex.position.xyz;
|
||||
min = math.min(min, pos);
|
||||
max = math.max(max, pos);
|
||||
}
|
||||
|
||||
_boundingBox = new Bounds(min, max);
|
||||
_boundingBox = new AABB(min, max);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -206,7 +206,7 @@ public unsafe class RenderTexture : Texture
|
||||
/// </summary>
|
||||
/// <param name="commandList">Command list to record clear commands</param>
|
||||
/// <param name="clearColor">Color to clear to</param>
|
||||
public void ClearColor(CommandList commandList, Color16 clearColor)
|
||||
public void ClearColor(CommandList commandList, Color128 clearColor)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ public readonly struct ResourceHandle : IEquatable<ResourceHandle>
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (id * 397) ^ (int)generation;
|
||||
return (id * 397) ^ generation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ public readonly struct ResourceHandle : IEquatable<ResourceHandle>
|
||||
public readonly struct TextureHandle : IEquatable<TextureHandle>
|
||||
{
|
||||
private readonly ResourceHandle _resourceHandle;
|
||||
private readonly BindlessDescriptor _bindlessDescriptor;
|
||||
|
||||
public ResourceHandle ResourceHandle => _resourceHandle;
|
||||
public static TextureHandle Invalid => new(ResourceHandle.Invalid);
|
||||
@@ -59,13 +60,20 @@ public readonly struct TextureHandle : IEquatable<TextureHandle>
|
||||
internal TextureHandle(ResourceHandle resourceHandle)
|
||||
{
|
||||
_resourceHandle = resourceHandle;
|
||||
_bindlessDescriptor = BindlessDescriptor.Invalid;
|
||||
}
|
||||
|
||||
internal TextureHandle(ResourceHandle resourceHandle, BindlessDescriptor descriptor)
|
||||
{
|
||||
_resourceHandle = resourceHandle;
|
||||
_bindlessDescriptor = descriptor;
|
||||
}
|
||||
|
||||
public bool IsValid => _resourceHandle.IsValid;
|
||||
|
||||
public bool Equals(TextureHandle other)
|
||||
{
|
||||
return _resourceHandle.Equals(other._resourceHandle);
|
||||
return _resourceHandle.Equals(other._resourceHandle) && _bindlessDescriptor.Equals(other._bindlessDescriptor);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
@@ -75,7 +83,7 @@ public readonly struct TextureHandle : IEquatable<TextureHandle>
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _resourceHandle.GetHashCode();
|
||||
return HashCode.Combine(_resourceHandle, _bindlessDescriptor);
|
||||
}
|
||||
|
||||
public static bool operator ==(TextureHandle left, TextureHandle right)
|
||||
@@ -115,7 +123,7 @@ public readonly struct BufferHandle : IEquatable<BufferHandle>
|
||||
|
||||
public bool Equals(BufferHandle other)
|
||||
{
|
||||
return _resourceHandle.Equals(other._resourceHandle);
|
||||
return _resourceHandle.Equals(other._resourceHandle) && _bindlessDescriptor.Equals(other._bindlessDescriptor);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
@@ -125,7 +133,7 @@ public readonly struct BufferHandle : IEquatable<BufferHandle>
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _resourceHandle.GetHashCode();
|
||||
return HashCode.Combine(_resourceHandle, _bindlessDescriptor);
|
||||
}
|
||||
|
||||
public static bool operator ==(BufferHandle left, BufferHandle right)
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
namespace Ghost.Graphics.Data;
|
||||
|
||||
internal readonly struct TextureInfo
|
||||
{
|
||||
public required string Name
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public uint RegisterSlot
|
||||
{
|
||||
get; init;
|
||||
@@ -20,11 +18,6 @@ internal readonly struct TextureInfo
|
||||
|
||||
internal readonly struct PropertyInfo
|
||||
{
|
||||
public required string Name
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public uint CBufferIndex
|
||||
{
|
||||
get; init;
|
||||
@@ -43,11 +36,6 @@ internal readonly struct PropertyInfo
|
||||
|
||||
internal readonly struct CBufferInfo
|
||||
{
|
||||
public required string Name
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public uint Size
|
||||
{
|
||||
get; init;
|
||||
@@ -64,36 +52,44 @@ internal readonly struct CBufferInfo
|
||||
/// </summary>
|
||||
|
||||
// TODO: Multi pass and keyword support
|
||||
public unsafe class Shader : IDisposable
|
||||
public struct Shader : IDisposable
|
||||
{
|
||||
private readonly string _source;
|
||||
|
||||
private readonly List<CBufferInfo> _constantBuffers = new();
|
||||
private readonly List<PropertyInfo> _properties = new();
|
||||
private readonly List<TextureInfo> _regularTextures = new(); // Add regular texture support
|
||||
private readonly Dictionary<string, int> _propertyNameToIdMap = new();
|
||||
private UnsafeList<CBufferInfo> _constantBuffers;
|
||||
private UnsafeList<PropertyInfo> _properties;
|
||||
private UnsafeList<TextureInfo> _regularTextures; // Add regular texture support
|
||||
// TODO: Possible to move this to unmanaged heap?
|
||||
private Dictionary<string, int> _propertyNameToIdMap;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
internal string Source => _source;
|
||||
internal readonly UnsafeList<CBufferInfo> ConstantBuffers => _constantBuffers;
|
||||
internal readonly UnsafeList<PropertyInfo> Properties => _properties;
|
||||
internal readonly UnsafeList<TextureInfo> RegularTextures => _regularTextures;
|
||||
internal readonly Dictionary<string, int> PropertyNameToIdMap => _propertyNameToIdMap;
|
||||
|
||||
internal List<CBufferInfo> ConstantBuffers => _constantBuffers;
|
||||
internal List<PropertyInfo> Properties => _properties;
|
||||
internal List<TextureInfo> RegularTextures => _regularTextures;
|
||||
internal Dictionary<string, int> PropertyNameToIdMap => _propertyNameToIdMap;
|
||||
|
||||
// TODO: In real production, we should not load the shader source code directly.
|
||||
internal Shader(string shaderCode)
|
||||
public Shader()
|
||||
{
|
||||
_source = shaderCode;
|
||||
_constantBuffers = new(8, Allocator.Persistent);
|
||||
_properties = new(8, Allocator.Persistent);
|
||||
_regularTextures = new(8, Allocator.Persistent);
|
||||
_propertyNameToIdMap = new(8);
|
||||
|
||||
_disposed = false;
|
||||
}
|
||||
|
||||
internal void AddProperty(string name, PropertyInfo propertyInfo)
|
||||
{
|
||||
var id = _properties.Count;
|
||||
_properties.Add(propertyInfo);
|
||||
_propertyNameToIdMap[name] = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a unique, stable ID for a shader property.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property (e.g., "_Color").</param>
|
||||
/// <param name="propertyName">The name of the shader property.</param>
|
||||
/// <returns>The integer ID of the property, or -1 if not found.</returns>
|
||||
public int GetPropertyId(string propertyName)
|
||||
public readonly int GetPropertyId(string propertyName)
|
||||
{
|
||||
return _propertyNameToIdMap.TryGetValue(propertyName, out var id) ? id : -1;
|
||||
}
|
||||
@@ -105,12 +101,12 @@ public unsafe class Shader : IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
_constantBuffers.Clear();
|
||||
_properties.Clear();
|
||||
_propertyNameToIdMap.Clear();
|
||||
_regularTextures.Clear();
|
||||
_constantBuffers.Dispose();
|
||||
_properties.Dispose();
|
||||
_regularTextures.Dispose();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
_propertyNameToIdMap.Clear();
|
||||
_propertyNameToIdMap = null!;
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Win32.Graphics.Dxgi.Common;
|
||||
|
||||
namespace Ghost.Graphics.Data;
|
||||
@@ -9,59 +8,21 @@ namespace Ghost.Graphics.Data;
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Vertex
|
||||
{
|
||||
public unsafe struct Semantic
|
||||
public unsafe static class Semantic
|
||||
{
|
||||
public const Format ALIGNED_FORMAT = Format.R32G32B32A32Float;
|
||||
public const int COUNT = 5;
|
||||
|
||||
private static readonly byte[] s_positionBytes = Encoding.UTF8.GetBytes("POSITION");
|
||||
private static readonly byte[] s_normalBytes = Encoding.UTF8.GetBytes("NORMAL");
|
||||
private static readonly byte[] s_tangentBytes = Encoding.UTF8.GetBytes("TANGENT");
|
||||
private static readonly byte[] s_colorBytes = Encoding.UTF8.GetBytes("COLOR");
|
||||
private static readonly byte[] s_uvBytes = Encoding.UTF8.GetBytes("TEXCOORD");
|
||||
|
||||
public static byte* pPositionName => (byte*)Unsafe.AsPointer(ref s_positionBytes[0]);
|
||||
public static byte* pNormalName => (byte*)Unsafe.AsPointer(ref s_normalBytes[0]);
|
||||
public static byte* pTangentName => (byte*)Unsafe.AsPointer(ref s_tangentBytes[0]);
|
||||
public static byte* pColorName => (byte*)Unsafe.AsPointer(ref s_colorBytes[0]);
|
||||
public static byte* pUVName => (byte*)Unsafe.AsPointer(ref s_uvBytes[0]);
|
||||
public static readonly FixedString32 position = new("POSITION");
|
||||
public static readonly FixedString32 normal = new("NORMAL");
|
||||
public static readonly FixedString32 tangent = new("TANGENT");
|
||||
public static readonly FixedString32 uv = new("TEXCOORD");
|
||||
public static readonly FixedString32 color = new("COLOR");
|
||||
}
|
||||
|
||||
public float4 Position
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public float4 Normal
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public float4 Tangent
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Color16 Color
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public float4 UV
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Vertex(float4 position, float4 normal, float4 tangent, Color16 color, float4 uv)
|
||||
{
|
||||
Position = position;
|
||||
Normal = normal;
|
||||
Tangent = tangent;
|
||||
Color = color;
|
||||
UV = uv;
|
||||
}
|
||||
public float4 position;
|
||||
public float4 normal;
|
||||
public float4 tangent;
|
||||
public float4 uv;
|
||||
public Color128 color;
|
||||
}
|
||||
@@ -30,6 +30,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Misaki.HighPerformance">
|
||||
<HintPath>..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance\bin\Release\net9.0\Misaki.HighPerformance.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Misaki.HighPerformance.Image">
|
||||
<HintPath>..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Image\bin\Release\net9.0\Misaki.HighPerformance.Image.dll</HintPath>
|
||||
</Reference>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using Ghost.Graphics.D3D12;
|
||||
using Ghost.Graphics.Data;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
@@ -7,6 +10,11 @@ namespace Ghost.Graphics.RHI;
|
||||
/// </summary>
|
||||
public interface ICommandBuffer : IDisposable
|
||||
{
|
||||
public CommandBufferType Type
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begins recording commands into this command buffer
|
||||
/// </summary>
|
||||
@@ -22,7 +30,7 @@ public interface ICommandBuffer : IDisposable
|
||||
/// </summary>
|
||||
/// <param name="renderTarget">Render target to render into</param>
|
||||
/// <param name="clearColor">Color to clear the render target with</param>
|
||||
public void BeginRenderPass(IRenderTarget renderTarget, Color16 clearColor);
|
||||
public void BeginRenderPass(IRenderTarget renderTarget, Color128 clearColor);
|
||||
|
||||
/// <summary>
|
||||
/// Ends the current render pass
|
||||
@@ -66,7 +74,7 @@ public interface ICommandBuffer : IDisposable
|
||||
/// </summary>
|
||||
/// <param name="mesh">The mesh to be drawn. Must not be null.</param>
|
||||
/// <param name="material">The material to use for rendering the mesh. Must not be null.</param>
|
||||
public void DrawMesh(Mesh mesh, Material material);
|
||||
public void DrawMesh(MeshClass mesh, MaterialClass material);
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches compute threads
|
||||
@@ -75,6 +83,26 @@ public interface ICommandBuffer : IDisposable
|
||||
/// <param name="threadGroupCountY">Thread groups in Y dimension</param>
|
||||
/// <param name="threadGroupCountZ">Thread groups in Z dimension</param>
|
||||
public void Dispatch(uint threadGroupCountX, uint threadGroupCountY = 1, uint threadGroupCountZ = 1);
|
||||
|
||||
/// <summary>
|
||||
/// Uploads the specified data to the buffer represented by the given handle.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The unmanaged value type of the elements to upload to the buffer.</typeparam>
|
||||
/// <param name="buffer">A handle to the buffer that will receive the uploaded data.</param>
|
||||
/// <param name="data">A read-only span containing the data to upload to the buffer. The span must contain elements of type
|
||||
/// <typeparamref name="T"/>.</param>
|
||||
public void Upload<T>(BufferHandle buffer, ReadOnlySpan<T> data)
|
||||
where T : unmanaged;
|
||||
|
||||
/// <summary>
|
||||
/// Uploads texture data to the specified texture resource starting at the given subresource index.
|
||||
/// </summary>
|
||||
/// <param name="texture">The texture resource to which the subresource data will be uploaded. Must be a valid, initialized texture handle.</param>
|
||||
/// <param name="firstSubresource">The index of the first subresource in the texture to receive data. Must be less than the total number of subresources in the texture.</param>
|
||||
/// <param name="subresources">A reference to the structure containing the subresource data to upload. The data must match the format and layout expected by the texture.</param>
|
||||
/// <param name="numSubresources">The number of subresources to upload, starting from <paramref name="firstSubresource"/>.
|
||||
/// Must be greater than zero and not exceed the remaining subresources in the texture.</param>
|
||||
public void Upload(TextureHandle texture, uint firstSubresource, ref SubResourceData subresources, uint numSubresources);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -82,21 +110,21 @@ public interface ICommandBuffer : IDisposable
|
||||
/// </summary>
|
||||
public struct ViewportDesc
|
||||
{
|
||||
public float X;
|
||||
public float Y;
|
||||
public float Width;
|
||||
public float Height;
|
||||
public float MinDepth;
|
||||
public float MaxDepth;
|
||||
public float x;
|
||||
public float y;
|
||||
public float width;
|
||||
public float height;
|
||||
public float minDepth;
|
||||
public float maxDepth;
|
||||
|
||||
public ViewportDesc(float width, float height)
|
||||
{
|
||||
X = 0;
|
||||
Y = 0;
|
||||
Width = width;
|
||||
Height = height;
|
||||
MinDepth = 0.0f;
|
||||
MaxDepth = 1.0f;
|
||||
x = 0;
|
||||
y = 0;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
minDepth = 0.0f;
|
||||
maxDepth = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,17 +150,28 @@ public struct RectDesc
|
||||
/// <summary>
|
||||
/// Resource states
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ResourceState
|
||||
{
|
||||
Common = 0,
|
||||
VertexAndConstantBuffer = 0x1,
|
||||
IndexBuffer = 0x2,
|
||||
RenderTarget = 0x4,
|
||||
UnorderedAccess = 0x8,
|
||||
DepthWrite = 0x10,
|
||||
DepthRead = 0x20,
|
||||
PixelShaderResource = 0x80,
|
||||
CopyDest = 0x400,
|
||||
CopySource = 0x800,
|
||||
VertexAndConstantBuffer = 1 << 0,
|
||||
IndexBuffer = 1 << 1,
|
||||
RenderTarget = 1 << 2,
|
||||
UnorderedAccess = 1 << 3,
|
||||
DepthWrite = 1 << 4,
|
||||
DepthRead = 1 << 5,
|
||||
PixelShaderResource = 1 << 6,
|
||||
CopyDest = 1 << 7,
|
||||
CopySource = 1 << 8,
|
||||
GenericRead = 1 << 9,
|
||||
IndirectArgument = 1 << 10,
|
||||
Present = 0,
|
||||
}
|
||||
|
||||
|
||||
public struct SubResourceData
|
||||
{
|
||||
public unsafe void* pData;
|
||||
public nint rowPitch;
|
||||
public nint slicePitch;
|
||||
}
|
||||
@@ -7,6 +7,11 @@ public interface IGraphicsEngine : IDisposable
|
||||
get;
|
||||
}
|
||||
|
||||
public IResourceDatabase ResourceDatabase
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public IResourceAllocator ResourceAllocator
|
||||
{
|
||||
get;
|
||||
@@ -27,4 +32,14 @@ public interface IGraphicsEngine : IDisposable
|
||||
/// <param name="desc">Swap chain description</param>
|
||||
/// <returns>A new swap chain instance</returns>
|
||||
public ISwapChain CreateSwapChain(SwapChainDesc desc);
|
||||
|
||||
/// <summary>
|
||||
/// Begins a new rendering frame, preparing the graphics context for drawing operations.
|
||||
/// </summary>
|
||||
public void BeginFrame();
|
||||
|
||||
/// <summary>
|
||||
/// Completes the current rendering frame and performs any necessary finalization steps.
|
||||
/// </summary>
|
||||
public void EndFrame();
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Data;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
@@ -15,11 +16,9 @@ public interface IShaderPipeline
|
||||
|
||||
public interface IPipelineStateController
|
||||
{
|
||||
public void ColectionShader(ReadOnlySpan<Shader> shaders);
|
||||
|
||||
public void CompileCollected();
|
||||
public void CompileShader(Identifier<Shader> id, string shaderPath);
|
||||
|
||||
public void PreCookPipelineState();
|
||||
|
||||
public IShaderPipeline GetShaderPipeline(Shader shader);
|
||||
public IShaderPipeline GetShaderPipeline(Identifier<Shader> id);
|
||||
}
|
||||
@@ -31,7 +31,7 @@ public interface IRenderDevice : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command buffer types matching D3D12 command list types
|
||||
/// Command buffer types
|
||||
/// </summary>
|
||||
public enum CommandBufferType
|
||||
{
|
||||
|
||||
@@ -173,7 +173,8 @@ public struct RenderTargetDesc
|
||||
Format = desc.Format,
|
||||
Dimension = desc.Dimension,
|
||||
MipLevels = desc.MipLevels,
|
||||
Usage = usage
|
||||
Usage = usage,
|
||||
CreationFlags = TextureCreationFlags.Bindless
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -245,6 +246,15 @@ public struct TextureDesc
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Texture creation flags
|
||||
/// </summary>
|
||||
public TextureCreationFlags CreationFlags
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -341,3 +351,10 @@ public enum MemoryType
|
||||
Upload, // CPU-to-GPU memory
|
||||
Readback // GPU-to-CPU memory
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum TextureCreationFlags
|
||||
{
|
||||
None = 0,
|
||||
Bindless = 1 << 0
|
||||
}
|
||||
@@ -4,40 +4,26 @@ namespace Ghost.Graphics.RHI;
|
||||
|
||||
public interface IResourceAllocator
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a render target for off-screen rendering
|
||||
/// </summary>
|
||||
/// <param name="desc">Render target description</param>
|
||||
/// <returns>A new render target instance</returns>
|
||||
public IRenderTarget CreateRenderTarget(ref readonly RenderTargetDesc desc, bool tempResource = false);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a texture resource
|
||||
/// </summary>
|
||||
/// <param name="desc">Texture description</param>
|
||||
/// <returns>A new texture handle point to the resource</returns>
|
||||
public TextureHandle CreateTextureHandle(ref readonly TextureDesc desc, bool tempResource = false);
|
||||
public TextureHandle CreateTexture(ref readonly TextureDesc desc, bool tempResource = false);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a texture resource
|
||||
/// Creates a render target for off-screen rendering
|
||||
/// </summary>
|
||||
/// <param name="desc">Texture description</param>
|
||||
/// <returns>A new texture instance</returns>
|
||||
public ITexture CreateTexture(ref readonly TextureDesc desc, bool tempResource = false);
|
||||
/// <param name="desc">Render target description</param>
|
||||
/// <returns>A new render target instance</returns>
|
||||
public TextureHandle CreateRenderTarget(ref readonly RenderTargetDesc desc, bool tempResource = false);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a buffer resource
|
||||
/// </summary>
|
||||
/// <param name="desc">Buffer description</param>
|
||||
/// <returns>A new buffer handle point to the resource</returns>
|
||||
public BufferHandle CreateBufferHandle(ref readonly BufferDesc desc, bool tempResource = false);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a buffer resource
|
||||
/// </summary>
|
||||
/// <param name="desc">Buffer description</param>
|
||||
/// <returns>A new buffer instance</returns>
|
||||
public IBuffer CreateBuffer(ref readonly BufferDesc desc, bool tempResource = false);
|
||||
public BufferHandle CreateBuffer(ref readonly BufferDesc desc, bool tempResource = false);
|
||||
|
||||
/// <summary>
|
||||
/// Release a resource given its handle
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Data;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
@@ -32,4 +33,24 @@ public interface IResourceDatabase
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the resource to be removed.</param>
|
||||
public void RemoveResource(ResourceHandle handle);
|
||||
|
||||
public Identifier<Mesh> AddMesh(ref readonly Mesh mesh);
|
||||
|
||||
public bool HasMesh(Identifier<Mesh> id);
|
||||
|
||||
public Mesh GetMesh(Identifier<Mesh> id);
|
||||
|
||||
public ref Mesh GetMeshReference(Identifier<Mesh> id);
|
||||
|
||||
public void RemoveMesh(Identifier<Mesh> id);
|
||||
|
||||
public Identifier<Shader> AddShader(ref readonly Shader shader);
|
||||
|
||||
public bool HasShader(Identifier<Shader> id);
|
||||
|
||||
public Shader GetShader(Identifier<Shader> id);
|
||||
|
||||
public ref Shader GetShaderReference(Identifier<Shader> id);
|
||||
|
||||
public void RemoveShader(Identifier<Shader> id);
|
||||
}
|
||||
@@ -100,9 +100,9 @@ float4 PSMain(PixelInput input) : SV_TARGET
|
||||
";
|
||||
|
||||
// High-level bindless objects
|
||||
private Mesh? _mesh;
|
||||
private MeshClass? _mesh;
|
||||
private Shader? _shader;
|
||||
private Material? _material;
|
||||
private MaterialClass? _material;
|
||||
private Texture2D[]? _textures;
|
||||
|
||||
// Texture file paths for this demo
|
||||
@@ -118,7 +118,7 @@ float4 PSMain(PixelInput input) : SV_TARGET
|
||||
_mesh = MeshBuilder.CreateCube(0.75f);
|
||||
_mesh.UploadMeshData();
|
||||
|
||||
_shader = new Shader(_HLSL_SOURCE);
|
||||
_shader = new ShaderData(_HLSL_SOURCE);
|
||||
_material = new Material(_shader);
|
||||
|
||||
_textures = new Texture2D[_textureFiles.Length];
|
||||
|
||||
@@ -34,7 +34,7 @@ internal class RenderSystem
|
||||
|
||||
private const uint _FRAME_COUNT = 2;
|
||||
|
||||
private readonly IGraphicsEngine _engine = null!;
|
||||
private readonly IGraphicsEngine _graphicsEngine = null!;
|
||||
private readonly FrameResource[] _frameResources = null!;
|
||||
private readonly Thread _renderThread = null!;
|
||||
private readonly AutoResetEvent _shutdownEvent = null!;
|
||||
@@ -47,14 +47,14 @@ internal class RenderSystem
|
||||
private bool _isRunning;
|
||||
private bool _disposed;
|
||||
|
||||
public IGraphicsEngine GraphicsEngine => _engine;
|
||||
public IGraphicsEngine GraphicsEngine => _graphicsEngine;
|
||||
public uint CPUFenceValue => _cpuFenceValue;
|
||||
public uint GPUFenceValue => _gpuFenceValue;
|
||||
public bool IsRunning => _isRunning;
|
||||
|
||||
public RenderSystem(GraphicsAPI api)
|
||||
{
|
||||
_engine = api switch
|
||||
_graphicsEngine = api switch
|
||||
{
|
||||
GraphicsAPI.Direct3D12 => new D3D12.D3D12GraphicsEngine(this),
|
||||
_ => throw new NotSupportedException($"Graphics API {api} is not supported.")
|
||||
@@ -84,7 +84,7 @@ internal class RenderSystem
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var renderer = _engine.CreateRenderer();
|
||||
var renderer = _graphicsEngine.CreateRenderer();
|
||||
ImmutableInterlocked.Update(ref _renderers, renderers => renderers.Add(renderer));
|
||||
return renderer;
|
||||
}
|
||||
@@ -166,12 +166,16 @@ internal class RenderSystem
|
||||
// Only proceed if CPU ready event was signaled
|
||||
if (waitResult == 0)
|
||||
{
|
||||
_graphicsEngine.BeginFrame();
|
||||
|
||||
foreach (var renderer in _renderers)
|
||||
{
|
||||
renderer.ExecutePendingResize();
|
||||
renderer.Render();
|
||||
}
|
||||
|
||||
_graphicsEngine.EndFrame();
|
||||
|
||||
_gpuFenceValue++;
|
||||
frameResource.gpuReadyEvent.Set();
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ public static class MeshBuilder
|
||||
/// <summary>
|
||||
/// Creates a unit cube centered at the origin with size 1.
|
||||
/// </summary>
|
||||
public static Mesh CreateCube(float size = 1.0f, Color16 color = default)
|
||||
public static MeshClass CreateCube(float size = 1.0f, Color128 color = default)
|
||||
{
|
||||
var half = size * 0.5f;
|
||||
var mesh = new Mesh(24, 36);
|
||||
var mesh = new MeshClass(24, 36);
|
||||
|
||||
var corners = new Vector4[]
|
||||
{
|
||||
@@ -40,11 +40,11 @@ public static class MeshBuilder
|
||||
{
|
||||
var vertex = new Vertex
|
||||
{
|
||||
Position = corners[face[i]],
|
||||
Normal = Vector4.Zero,
|
||||
Tangent = Vector4.Zero,
|
||||
Color = color,
|
||||
UV = uvs[i].AsVector4()
|
||||
position = corners[face[i]],
|
||||
normal = Vector4.Zero,
|
||||
tangent = Vector4.Zero,
|
||||
color = color,
|
||||
uv = uvs[i].AsVector4()
|
||||
};
|
||||
mesh.AddVertex(vertex);
|
||||
}
|
||||
@@ -61,11 +61,11 @@ public static class MeshBuilder
|
||||
/// <summary>
|
||||
/// Creates a plane on the XZ axis centered at the origin.
|
||||
/// </summary>
|
||||
public static Mesh CreatePlane(float width = 1.0f, float depth = 1.0f, Color16 color = default)
|
||||
public static MeshClass CreatePlane(float width = 1.0f, float depth = 1.0f, Color128 color = default)
|
||||
{
|
||||
var hw = width * 0.5f;
|
||||
var hd = depth * 0.5f;
|
||||
var mesh = new Mesh(4, 6);
|
||||
var mesh = new MeshClass(4, 6);
|
||||
|
||||
mesh.AddVertex(new(new(-hw, 0.0f, -hd, 0.0f), Vector4.Zero, Vector4.Zero, color, new(0.0f)));
|
||||
mesh.AddVertex(new(new(hw, 0.0f, -hd, 0.0f), Vector4.Zero, Vector4.Zero, color, new(1.0f, 0.0f, 0.0f, 0.0f)));
|
||||
@@ -83,9 +83,9 @@ public static class MeshBuilder
|
||||
/// <summary>
|
||||
/// Creates a UV sphere centered at the origin.
|
||||
/// </summary>
|
||||
public static Mesh CreateSphere(int latitudeSegments = 16, int longitudeSegments = 24, float radius = 0.5f, Color16 color = default)
|
||||
public static MeshClass CreateSphere(int latitudeSegments = 16, int longitudeSegments = 24, float radius = 0.5f, Color128 color = default)
|
||||
{
|
||||
var mesh = new Mesh((latitudeSegments + 1) * (longitudeSegments + 1), latitudeSegments * longitudeSegments * 6);
|
||||
var mesh = new MeshClass((latitudeSegments + 1) * (longitudeSegments + 1), latitudeSegments * longitudeSegments * 6);
|
||||
|
||||
// Vertices
|
||||
for (var lat = 0; lat <= latitudeSegments; lat++)
|
||||
@@ -107,11 +107,11 @@ public static class MeshBuilder
|
||||
mesh.AddVertex(
|
||||
new()
|
||||
{
|
||||
Position = new Vector4(x, y, z, 0.0f) * radius,
|
||||
Normal = Vector4.Zero,
|
||||
Tangent = Vector4.Zero,
|
||||
Color = color,
|
||||
UV = new Vector4((float)lon / longitudeSegments, (float)lat / latitudeSegments, 0.0f, 0.0f)
|
||||
position = new Vector4(x, y, z, 0.0f) * radius,
|
||||
normal = Vector4.Zero,
|
||||
tangent = Vector4.Zero,
|
||||
color = color,
|
||||
uv = new Vector4((float)lon / longitudeSegments, (float)lat / latitudeSegments, 0.0f, 0.0f)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<!-- Other merged dictionaries here -->
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.UI.Xaml/DensityStyles/Compact.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<!-- Other app resources here -->
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -34,7 +34,7 @@ public sealed partial class GraphicsTestWindow : Window
|
||||
_renderSystem = new (GraphicsAPI.Direct3D12);
|
||||
_renderer = _renderSystem.CreateRenderer();
|
||||
|
||||
_swapChain = _renderSystem.GraphicsEngine.Device.CreateSwapChain(new SwapChainDesc((uint)AppWindow.Size.Width, (uint)AppWindow.Size.Height, SwapChainTarget.FromCompositionSurface(Panel)));
|
||||
_swapChain = _renderSystem.GraphicsEngine.CreateSwapChain(new SwapChainDesc((uint)AppWindow.Size.Width, (uint)AppWindow.Size.Height, SwapChainTarget.FromCompositionSurface(Panel)));
|
||||
_renderer.SetSwapChain(_swapChain);
|
||||
|
||||
CompositionTarget.Rendering += OnRendering;
|
||||
|
||||
Reference in New Issue
Block a user