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:
2025-09-19 23:20:15 +09:00
parent 6a504cefc8
commit a39f377533
39 changed files with 1669 additions and 826 deletions

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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();

View File

@@ -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),
desc.Width,
desc.Height,
mipLevels: (ushort)desc.MipLevels,
arraySize: (ushort)desc.Slice,
flags: ConvertTextureUsage(desc.Usage)
);
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: 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)),
//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}");
}
_device->CreateShaderResourceView(allocation.Resource, &srvDesc, _descriptorAllocator.GetCpuHandle(descriptorHandle));
}
return new(handle);
}
public BufferHandle CreateBufferHandle(ref readonly BufferDesc desc, bool tempResource = false)
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);
}
}

View File

@@ -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);
}
}

View File

@@ -65,32 +65,36 @@ 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
var argsArray = new string[]
{
"-T", GetProfileString(stage, version), // Target profile (vs_6_6, ps_6_6)
"-E", GetEntryPoint(stage), // Entry point
"-HV", "2021", // HLSL version 2021 (required for SM 6.6)
"-enable-16bit-types", // Enable 16-bit types
"-O3", // Optimization level
"-Qstrip_debug" // Strip debug info but KEEP reflection
"-T", GetProfileString(stage, version), // Target profile (vs_6_6, ps_6_6)
"-E", GetEntryPoint(stage), // Entry point
"-HV", "2021", // HLSL version 2021 (required for SM 6.6)
"-enable-16bit-types", // Enable 16-bit types
"-O3", // Optimization level
"-Qstrip_debug" // Strip debug info but KEEP reflection
};
// Convert to wide strings (DXC expects LPCWSTR)
@@ -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,85 +207,92 @@ 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)
{
var cbufferName = Marshal.PtrToStringAnsi((IntPtr)bindDesc.Name);
if (cbufferName == null || cbufferRegistry.ContainsKey(cbufferName))
case ShaderInputType.ConstantBuffer:
{
continue;
}
var cbuffer = reflection.Get()->GetConstantBufferByName(bindDesc.Name);
ShaderBufferDescription cbufferDesc;
cbuffer->GetDesc(&cbufferDesc);
var cbufferInfo = new CBufferInfo
{
Name = cbufferName,
Size = cbufferDesc.Size,
RegisterSlot = bindDesc.BindPoint
};
cbufferRegistry.Add(cbufferName, cbufferInfo);
for (uint j = 0; j < cbufferDesc.Variables; j++)
{
var variable = cbuffer->GetVariableByIndex(j);
ShaderVariableDescription varDesc;
variable->GetDesc(&varDesc);
var variableName = Marshal.PtrToStringAnsi((IntPtr)varDesc.Name);
if (variableName == null || shader.PropertyNameToIdMap.ContainsKey(variableName))
var cbufferName = Marshal.PtrToStringAnsi((IntPtr)bindDesc.Name);
if (cbufferName == null || cbufferRegistry.ContainsKey(cbufferName))
{
continue;
}
var propInfo = new PropertyInfo
var cbuffer = reflection.Get()->GetConstantBufferByName(bindDesc.Name);
ShaderBufferDescription cbufferDesc;
cbuffer->GetDesc(&cbufferDesc);
var cbufferInfo = new CBufferInfo
{
Name = variableName,
CBufferIndex = cbufferInfo.RegisterSlot,
ByteOffset = varDesc.StartOffset,
Size = varDesc.Size
Size = cbufferDesc.Size,
RegisterSlot = bindDesc.BindPoint
};
cbufferRegistry.Add(cbufferName, cbufferInfo);
for (uint j = 0; j < cbufferDesc.Variables; j++)
{
var variable = cbuffer->GetVariableByIndex(j);
ShaderVariableDescription varDesc;
variable->GetDesc(&varDesc);
var variableName = Marshal.PtrToStringAnsi((IntPtr)varDesc.Name);
if (variableName == null || shader.PropertyNameToIdMap.ContainsKey(variableName))
{
continue;
}
var propInfo = new PropertyInfo
{
CBufferIndex = cbufferInfo.RegisterSlot,
ByteOffset = varDesc.StartOffset,
Size = varDesc.Size
};
shader.AddProperty(variableName, propInfo);
}
break;
}
case ShaderInputType.Texture:
{
var textureName = Marshal.PtrToStringAnsi((IntPtr)bindDesc.Name);
if (textureName == null || textureRegistry.ContainsKey(textureName))
{
continue;
}
// ALL texture input slots are regular textures!
// Bindless textures don't use explicit texture inputs - they use ResourceDescriptorHeap[index]
var textureInfo = new TextureInfo
{
RegisterSlot = bindDesc.BindPoint,
RootParameterIndex = (uint)shader.ConstantBuffers.Count // Descriptor table comes after CBVs
};
// 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);
textureRegistry.Add(textureName, textureInfo);
break;
}
}
else if (bindDesc.Type == ShaderInputType.Texture)
{
var textureName = Marshal.PtrToStringAnsi((IntPtr)bindDesc.Name);
if (textureName == null || textureRegistry.ContainsKey(textureName))
{
continue;
}
// ALL texture input slots are regular textures!
// 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);
}
}
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);
}
}
}

View File

@@ -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;