Refactor and enhance resource management and rendering

Updated multiple components to improve encapsulation, maintainability, and performance. Key changes include:

- Upgraded package dependencies in project files.
- Refactored `Mesh` and `RenderingContext` to use properties and added support for per-object constant buffers.
- Improved resource management in `D3D12CommandBuffer`, `D3D12CommandQueue`, and `D3D12ResourceAllocator` with better encapsulation and disposal handling.
- Added validation for constant buffer sizes in `D3D12PipelineLibrary`.
- Simplified `MeshBuilder` methods to accept allocators and removed hardcoded values.
- Enhanced debugging with `GPUResourceLeakException` and resource tracking updates.
- Updated shaders and rendering logic for testing, including hardcoded triangle rendering.
- Removed redundant base classes and interfaces for cleaner code structure.
This commit is contained in:
2025-11-26 01:48:24 +09:00
parent dfe786a2aa
commit 0720444c2c
40 changed files with 1008 additions and 903 deletions

View File

@@ -18,9 +18,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Misaki.HighPerformance" Version="1.0.0" />
<PackageReference Include="Misaki.HighPerformance" Version="1.0.1" />
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="1.1.0" />
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.2.0" />
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.2.1" />
<PackageReference Include="Misaki.HighPerformance.Mathematics" Version="1.2.6" />
<PackageReference Include="System.IO.Hashing" Version="10.0.0" />
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.26100.5" />

View File

@@ -64,17 +64,11 @@ internal static unsafe partial class Win32Utility
if (ptr != null)
{
uPtr = default;
MemoryLeakException.ThrowIfRefCountNonZero(ptr->Release());
ptr->Release();
//MemoryLeakException.ThrowIfRefCountNonZero(ptr->Release());
}
}
[Conditional("DEBUG")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Assert(this HRESULT hr)
{
Debug.Assert(hr.SUCCEEDED, hr.ToString());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Result ToResult(this HRESULT hr, [CallerArgumentExpression(nameof(hr))] string? op = null)
{

View File

@@ -12,8 +12,8 @@
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250606001" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.250402" />
</ItemGroup>

View File

@@ -75,12 +75,11 @@
<ItemGroup>
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250402" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.TabbedCommandBar" Version="8.2.250402" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.6" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250606001" />
<PackageReference Include="System.Private.Uri" Version="4.3.2" />
<PackageReference Include="WinUIEx" Version="2.6.0" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
<PackageReference Include="WinUIEx" Version="2.9.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ghost.Editor.Core\Ghost.Editor.Core.csproj" />

View File

@@ -30,7 +30,7 @@ public struct QueryFilter : IDisposable
UnsafeBitSet absentMask = default;
var result = new UnsafeBitSet(world.EntityManager.EntityCount, allocator);
result.SetAll();
result.ClearAll();
using (AllocationManager.CreateStackScope())
{
@@ -83,7 +83,7 @@ public struct QueryFilter : IDisposable
if (absentMask.IsCreated)
{
result.And(~absentMask);
result.ANDC(absentMask);
}
}

View File

@@ -84,7 +84,7 @@ public unsafe ref struct QueryEnumerable<T0>
public bool MoveNext()
{
_index = _filterMask.NextSetBit(_index + 1);
if (_index < 0 || _count <= 0 || _index >= _entities.Length)
if (_index < 0 || _count <= 0)
{
return false;
}

View File

@@ -1,4 +1,4 @@
<#@ template language="C#" #>
<#@ template language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Text" #>

View File

@@ -46,11 +46,11 @@
<ProjectCapability Include="Msix" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.TestPlatform.TestHost" Version="17.14.1" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250606001" />
<PackageReference Include="MSTest.TestAdapter" Version="3.9.3" />
<PackageReference Include="MSTest.TestFramework" Version="3.9.3" />
<PackageReference Include="Microsoft.TestPlatform.TestHost" Version="18.0.1" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
<PackageReference Include="MSTest.TestAdapter" Version="4.0.2" />
<PackageReference Include="MSTest.TestFramework" Version="4.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ghost.Editor.Core\Ghost.Editor.Core.csproj" />

View File

@@ -3,6 +3,8 @@ using Ghost.Graphics.RHI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using Misaki.HighPerformance.LowLevel.Buffer;
using TerraFX.Interop.WinRT;
using WinRT;
namespace Ghost.Graphics.Test.Windows;

View File

@@ -141,7 +141,7 @@ public readonly struct ShaderReflectionData
}
}
public unsafe interface IShaderCompiler
public interface IShaderCompiler : IDisposable
{
Result<CompileResult> Compile(ref readonly CompilerConfig config, Allocator allocator);
Result<GraphicsCompiledResult> CompilePass(IPassDescriptor descriptor);

View File

@@ -1,8 +1,8 @@
using System.Runtime.InteropServices;
using TerraFX.Interop.Windows;
namespace Ghost.Graphics.Contracts;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>")]
public unsafe readonly struct ISwapChainPanelNative : ISwapChainPanelNative.Interface, IDisposable
{
[ComImport]

View File

@@ -18,7 +18,9 @@ internal struct CBufferCache : IResourceReleasable
public readonly Handle<GraphicsBuffer> GpuResource => _gpuResource;
public readonly uint AlignedSize => _alignedSize;
public unsafe CBufferCache(Handle<GraphicsBuffer> buffer, uint bufferSize)
public readonly bool IsCreated => _gpuResource.IsValid && _cpuData.IsCreated;
public CBufferCache(Handle<GraphicsBuffer> buffer, uint bufferSize)
{
_alignedSize = (bufferSize + 255u) & ~255u;
@@ -65,6 +67,11 @@ public struct Material : IResourceReleasable, IHandleType
var pass = database.GetShaderPass(shader.GetPassKey(i));
var cbufferInfo = pass.CBuffer;
if (cbufferInfo.SizeInBytes == 0)
{
continue;
}
var desc = new BufferDesc
{
Size = cbufferInfo.SizeInBytes,
@@ -91,7 +98,7 @@ public struct Material : IResourceReleasable, IHandleType
public ref struct MaterialAccessor
{
private ref Material _materialData;
private Shader _shader;
private readonly Shader _shader;
private readonly IResourceDatabase _resourceDatabase;

View File

@@ -1,5 +1,6 @@
using Ghost.Core;
using Ghost.Graphics.RHI;
using Ghost.Graphics.Utilities;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Utilities;
@@ -10,42 +11,118 @@ namespace Ghost.Graphics.Core;
public struct Mesh : IResourceReleasable, IHandleType
{
public UnsafeList<Vertex> vertices;
public UnsafeList<uint> indices;
public AABB boundingBox;
public Handle<GraphicsBuffer> vertexBuffer;
public Handle<GraphicsBuffer> indexBuffer;
internal bool IsMeshDataDirty
{
get; private set;
}
/// <summary>
/// Gets or sets the collection of vertices that define the geometry.
/// </summary>
public UnsafeList<Vertex> Vertices
{
readonly get => field;
set
{
field = value;
VertexCount = value.Count;
IsMeshDataDirty = true;
}
}
/// <summary>
/// Gets or sets the collection of indices that define the order of vertices.
/// </summary>
public UnsafeList<uint> Indices
{
readonly get => field;
set
{
field = value;
IndexCount = value.Count;
IsMeshDataDirty = true;
}
}
/// <summary>
/// Get the number of vertices in the mesh.
/// </summary>
public int VertexCount
{
get; private set;
}
/// <summary>
/// Get the number of indices in the mesh.
/// </summary>
public int IndexCount
{
get; private set;
}
/// <summary>
/// Gets or sets the axis-aligned bounding box (AABB) of the mesh.
/// </summary>
public AABB BoundingBox
{
get; set;
}
/// <summary>
/// Gets the handle to the vertex buffer on the GPU.
/// </summary>
public Handle<GraphicsBuffer> VertexBuffer
{
get; internal set;
}
/// <summary>
/// Gets the handle to the index buffer on the GPU.
/// </summary>
public Handle<GraphicsBuffer> IndexBuffer
{
get; internal set;
}
/// <summary>
/// Gets the handle to the mesh data buffer on the GPU.
/// </summary>
public Handle<GraphicsBuffer> ObjectDataBuffer
{
get; internal set;
}
public Mesh()
{
vertexBuffer = Handle<GraphicsBuffer>.Invalid;
indexBuffer = Handle<GraphicsBuffer>.Invalid;
VertexBuffer = Handle<GraphicsBuffer>.Invalid;
IndexBuffer = Handle<GraphicsBuffer>.Invalid;
}
internal Mesh(ReadOnlySpan<Vertex> vertices, ReadOnlySpan<uint> indices, Handle<GraphicsBuffer> vertexBuffer, Handle<GraphicsBuffer> indexBuffer)
{
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;
Vertices = new(vertices.Length, Allocator.Persistent);
Indices = new(indices.Length, Allocator.Persistent);
Vertices.CopyFrom(vertices);
Indices.CopyFrom(indices);
VertexBuffer = vertexBuffer;
IndexBuffer = indexBuffer;
this.ComputeBounds();
}
public void ReleaseCpuResources()
public readonly void ReleaseCpuResources()
{
vertices.Dispose();
indices.Dispose();
Vertices.Dispose();
Indices.Dispose();
}
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
{
ReleaseCpuResources();
database.ReleaseResource(vertexBuffer.AsResource());
database.ReleaseResource(indexBuffer.AsResource());
database.ReleaseResource(VertexBuffer.AsResource());
database.ReleaseResource(IndexBuffer.AsResource());
database.ReleaseResource(ObjectDataBuffer.AsResource());
}
}
@@ -56,21 +133,21 @@ public static class MeshExtension
/// </summary>
public static void ComputeBounds(ref this Mesh mesh)
{
if (mesh.vertices.Count == 0)
if (mesh.Vertices.Count == 0)
{
return;
}
var min = new float3(float.MaxValue);
var max = new float3(float.MinValue);
foreach (var vertex in mesh.vertices)
foreach (var vertex in mesh.Vertices)
{
var pos = vertex.position.xyz;
min = math.min(min, pos);
max = math.max(max, pos);
}
mesh.boundingBox = new AABB(min, max);
mesh.BoundingBox = new AABB(min, max);
}
/// <summary>
@@ -81,35 +158,7 @@ public static class MeshExtension
/// </remarks>
public static void ComputeNormal(ref this Mesh mesh)
{
if (!mesh.vertices.IsCreated || mesh.vertices.Count < 3
|| !mesh.indices.IsCreated || mesh.indices.Count < 3)
{
return;
}
for (var i = 0; i < mesh.indices.Count; i += 3)
{
var i0 = mesh.indices[i];
var i1 = mesh.indices[i + 1];
var i2 = mesh.indices[i + 2];
var v0 = mesh.vertices[i0];
var v1 = mesh.vertices[i1];
var v2 = mesh.vertices[i2];
var edge1 = v1.position - v0.position;
var edge2 = v2.position - v0.position;
var faceNormal = math.cross(edge1.xyz, edge2.xyz);
mesh.vertices[i0].normal.xyz += faceNormal;
mesh.vertices[i1].normal.xyz += faceNormal;
mesh.vertices[i2].normal.xyz += faceNormal;
}
for (var i = 0; i < mesh.vertices.Count; i++)
{
mesh.vertices[i].normal = math.normalize(mesh.vertices[i].normal);
}
MeshBuilder.ComputeNormal(mesh.Vertices, mesh.Indices);
}
/// <summary>
@@ -120,53 +169,6 @@ public static class MeshExtension
/// </remarks>
public static void ComputeTangents(ref this Mesh mesh)
{
var bitangents = new float4[mesh.vertices.Count];
for (var i = 0; i < mesh.indices.Count; i += 3)
{
var i0 = mesh.indices[i];
var i1 = mesh.indices[i + 1];
var i2 = mesh.indices[i + 2];
var v0 = mesh.vertices[i0];
var v1 = mesh.vertices[i1];
var v2 = mesh.vertices[i2];
var uv0 = mesh.vertices[i0].uv;
var uv1 = mesh.vertices[i1].uv;
var uv2 = mesh.vertices[i2].uv;
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;
for (var j = 0; j < 3; j++)
{
var idx = mesh.indices[i + j];
var t = mesh.vertices[idx].tangent;
mesh.vertices[idx].tangent.xyz = t.xyz + tangent.xyz;
bitangents[idx] += bitangent;
}
}
for (var i = 0; i < mesh.vertices.Count; i++)
{
var n = mesh.vertices[i].normal;
var t = mesh.vertices[i].tangent;
var proj = n * math.dot(n, t);
t = math.normalize(t - proj);
var b = bitangents[i];
var w = math.dot(math.cross(n.xyz, t.xyz), b.xyz) < 0.0f ? -1.0f : 1.0f;
mesh.vertices[i].tangent = new float4(t.x, t.y, t.z, w);
}
MeshBuilder.ComputeTangents(mesh.Vertices, mesh.Indices);
}
}

View File

@@ -4,6 +4,7 @@ using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Utilities;
using Misaki.HighPerformance.Mathematics;
namespace Ghost.Graphics.Core;
@@ -72,6 +73,11 @@ public unsafe readonly ref struct RenderingContext
return CreateMesh(vertexList, indexList);
}
public MaterialAccessor GetMaterialAccessor(Handle<Material> material)
{
return new MaterialAccessor(material, ResourceDatabase);
}
// TODO: Make one memory pool for upload.
/// <summary>
@@ -82,32 +88,32 @@ public unsafe readonly ref struct RenderingContext
public void UploadMesh(Handle<Mesh> mesh, bool markMeshStatic)
{
ref var meshData = ref ResourceDatabase.GetMeshReference(mesh);
var vertexState = ResourceDatabase.GetResourceState(meshData.vertexBuffer.AsResource());
var indexState = ResourceDatabase.GetResourceState(meshData.indexBuffer.AsResource());
var vertexState = ResourceDatabase.GetResourceState(meshData.VertexBuffer.AsResource());
var indexState = ResourceDatabase.GetResourceState(meshData.IndexBuffer.AsResource());
var needVertexTransition = vertexState != ResourceState.CopyDest;
var needIndexTransition = indexState != ResourceState.CopyDest;
if (needVertexTransition)
{
_directCmd.ResourceBarrier(meshData.vertexBuffer.AsResource(), vertexState, ResourceState.CopyDest);
_directCmd.ResourceBarrier(meshData.VertexBuffer.AsResource(), vertexState, ResourceState.CopyDest);
}
if (needIndexTransition)
{
_directCmd.ResourceBarrier(meshData.indexBuffer.AsResource(), indexState, ResourceState.CopyDest);
_directCmd.ResourceBarrier(meshData.IndexBuffer.AsResource(), indexState, ResourceState.CopyDest);
}
_directCmd.UploadBuffer(meshData.vertexBuffer, meshData.vertices.AsSpan());
_directCmd.UploadBuffer(meshData.indexBuffer, meshData.indices.AsSpan());
_directCmd.UploadBuffer(meshData.VertexBuffer, meshData.Vertices.AsSpan());
_directCmd.UploadBuffer(meshData.IndexBuffer, meshData.Indices.AsSpan());
if (needVertexTransition)
{
_directCmd.ResourceBarrier(meshData.vertexBuffer.AsResource(), ResourceState.CopyDest, vertexState);
_directCmd.ResourceBarrier(meshData.VertexBuffer.AsResource(), ResourceState.CopyDest, ResourceState.VertexAndConstantBuffer);
}
if (needIndexTransition)
{
_directCmd.ResourceBarrier(meshData.indexBuffer.AsResource(), ResourceState.CopyDest, indexState);
_directCmd.ResourceBarrier(meshData.IndexBuffer.AsResource(), ResourceState.CopyDest, ResourceState.IndexBuffer);
}
if (markMeshStatic)
@@ -116,6 +122,34 @@ public unsafe readonly ref struct RenderingContext
}
}
public void UpdateObjectData(Handle<Mesh> mesh, float4x4 localToWorld)
{
ref var meshData = ref ResourceDatabase.GetMeshReference(mesh);
var data = new PerObjectData
{
localToWorld = localToWorld,
worldBoundsMin = meshData.BoundingBox.Min,
worldBoundsMax = meshData.BoundingBox.Max,
vertexBuffer = (uint)_engine.ResourceDatabase.GetBindlessIndex(meshData.VertexBuffer.AsResource()),
indexBuffer = (uint)_engine.ResourceDatabase.GetBindlessIndex(meshData.IndexBuffer.AsResource()),
};
var bufferHandle = meshData.ObjectDataBuffer.AsResource();
var state = ResourceDatabase.GetResourceState(bufferHandle);
var needTransition = state != ResourceState.CopyDest;
if (needTransition)
{
_directCmd.ResourceBarrier(bufferHandle, state, ResourceState.CopyDest);
}
_directCmd.UploadBuffer(meshData.ObjectDataBuffer, [data]);
if (needTransition)
{
_directCmd.ResourceBarrier(bufferHandle, ResourceState.CopyDest, ResourceState.VertexAndConstantBuffer);
}
}
public Handle<Texture> CreateTexture(ref readonly TextureDesc desc, bool tempResource = false)
{
return ResourceAllocator.CreateTexture(in desc, tempResource);
@@ -126,13 +160,6 @@ public unsafe readonly ref struct RenderingContext
var desc = ResourceDatabase.GetResourceDescription(texture.AsResource());
desc.TextureDescription.Format.GetSurfaceInfo((int)desc.TextureDescription.Width, (int)desc.TextureDescription.Height, out var rowPitch, out var slicePitch, out _);
var subresourceData = new SubResourceData
{
pData = data.GetUnsafePtr(),
rowPitch = rowPitch,
slicePitch = slicePitch
};
var sateBefore = ResourceDatabase.GetResourceState(texture.AsResource());
var needTransition = sateBefore != ResourceState.CopyDest;
@@ -141,7 +168,17 @@ public unsafe readonly ref struct RenderingContext
_directCmd.ResourceBarrier(texture.AsResource(), sateBefore, ResourceState.CopyDest);
}
fixed (byte* pData = data)
{
var subresourceData = new SubResourceData
{
pData = pData,
rowPitch = rowPitch,
slicePitch = slicePitch
};
_directCmd.UploadTexture(texture, subresourceData);
}
if (needTransition)
{
@@ -169,9 +206,14 @@ public unsafe readonly ref struct RenderingContext
var pipelineKey = hash.GetKey();
_directCmd.SetPipelineState(pipelineKey);
_directCmd.SetConstantBufferView(RootSignatureLayout.PER_OBJECT_BUFFER_SLOT, meshRef.ObjectDataBuffer);
// NOTE: We use fixed root signature layout for bindless rendering.
ref var cache = ref materialRef.GetPassCache(passIndex);
if (cache.IsCreated)
{
_directCmd.SetConstantBufferView(RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT, cache.GpuResource);
}
// NOTE: Since we are using true bindless resources, we only need to set the descriptor heaps, not individual tables.
// TODO: Maybe handle the traditional bindless model?
@@ -180,7 +222,7 @@ public unsafe readonly ref struct RenderingContext
_commandList.Get()->SetGraphicsRootDescriptorTable(rootParamIndex, samplerGpuHandle);
#endif
var threadGroupCountX = ((uint)meshRef.indices.Count + numThreadsX - 1) / numThreadsX;
_directCmd.DispatchMesh(threadGroupCountX, 1, 1);
//var threadGroupCountX = ((uint)meshRef.IndexCount + numThreadsX - 1) / numThreadsX;
_directCmd.DispatchMesh(1, 1, 1);
}
}

View File

@@ -1,3 +1,6 @@
using Misaki.HighPerformance.Mathematics;
using System.Runtime.InteropServices;
namespace Ghost.Graphics.Core;
/// <summary>
@@ -41,3 +44,13 @@ public static class RootSignatureLayout
#endif
;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct PerObjectData
{
public float4x4 localToWorld;
public float3 worldBoundsMin;
public uint vertexBuffer;
public float3 worldBoundsMax;
public uint indexBuffer;
};

View File

@@ -19,10 +19,11 @@ public class ShaderPass : IResourceReleasable
{
_cbufferInfo = info;
_propertyLookup = new Dictionary<string, int>(info.Properties.Count);
for (var i = 0; i < info.Properties.Count; i++)
var capacity = info.Properties?.Count ?? 0;
_propertyLookup = new Dictionary<string, int>(capacity);
for (var i = 0; i < capacity; i++)
{
_propertyLookup[info.Properties[i].Name] = i;
_propertyLookup[info.Properties![i].Name] = i;
}
}

View File

@@ -8,14 +8,16 @@ using Misaki.HighPerformance.LowLevel.Utilities;
using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
using static TerraFX.Aliases.D3D_Alias;
using static TerraFX.Aliases.D3D12_Alias;
using static TerraFX.Aliases.DXGI_Alias;
namespace Ghost.Graphics.D3D12;
internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList10>, ICommandBuffer
internal unsafe class D3D12CommandBuffer : ICommandBuffer
{
private UniquePtr<ID3D12GraphicsCommandList10> _commandList;
private UniquePtr<ID3D12CommandAllocator> _allocator;
private readonly D3D12PipelineLibrary _pipelineLibrary;
@@ -26,12 +28,28 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
private ushort _commandCount;
private bool _isRecording;
private bool _disposed;
public ID3D12GraphicsCommandList10* NativeCommandList => nativeObject.Get();
public SharedPtr<ID3D12GraphicsCommandList10> NativeCommandList => _commandList.Get();
public CommandBufferType Type => _type;
public bool IsEmpty => _commandCount == 0;
public string Name
{
get => field;
set
{
if (field == value)
{
return;
}
field = value;
_commandList.Get()->SetName(value);
}
} = string.Empty;
public D3D12CommandBuffer(
D3D12RenderDevice device,
D3D12PipelineLibrary stateController,
@@ -46,11 +64,11 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ID3D12GraphicsCommandList10* pCommandList = default;
var commandListType = ConvertCommandBufferType(type);
device.NativeDevice->CreateCommandAllocator(commandListType, __uuidof(pAllocator), (void**)&pAllocator);
device.NativeDevice->CreateCommandList1(0u, commandListType, D3D12_COMMAND_LIST_FLAG_NONE, __uuidof(pCommandList), (void**)&pCommandList);
device.NativeDevice.Get()->CreateCommandAllocator(commandListType, __uuidof(pAllocator), (void**)&pAllocator);
device.NativeDevice.Get()->CreateCommandList1(0u, commandListType, D3D12_COMMAND_LIST_FLAG_NONE, __uuidof(pCommandList), (void**)&pCommandList);
_allocator.Attach(pAllocator);
nativeObject.Attach(pCommandList);
_commandList.Attach(pCommandList);
_pipelineLibrary = stateController;
_resourceDatabase = resourceDatabase;
@@ -60,6 +78,11 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
_isRecording = false;
}
~D3D12CommandBuffer()
{
Dispose();
}
private static D3D12_COMMAND_LIST_TYPE ConvertCommandBufferType(CommandBufferType type)
{
return type switch
@@ -71,6 +94,12 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ThrowIfDisposed()
{
ObjectDisposedException.ThrowIf(_disposed, this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ThrowIfRecording()
{
@@ -100,7 +129,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
void ResetCommandList()
{
ThrowIfFailed(_allocator.Get()->Reset());
ThrowIfFailed(nativeObject.Get()->Reset(_allocator.Get(), null));
ThrowIfFailed(_commandList.Get()->Reset(_allocator.Get(), null));
}
void SetBindlessHeap()
@@ -108,14 +137,18 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
var heaps = stackalloc ID3D12DescriptorHeap*[2];
heaps[0] = _descriptorAllocator.GetCbvSrvUavHeap(); // Bindless resource heap
heaps[1] = _descriptorAllocator.GetSamplerHeap(); // Bindless sampler heap
nativeObject.Get()->SetDescriptorHeaps(2, heaps);
_commandList.Get()->SetDescriptorHeaps(2, heaps);
}
ThrowIfDisposed();
ThrowIfRecording();
ResetCommandList();
if (Type == CommandBufferType.Graphics || Type == CommandBufferType.Compute)
{
SetBindlessHeap();
}
_commandCount = 0;
_isRecording = true;
@@ -126,7 +159,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfDisposed();
ThrowIfNotRecording();
nativeObject.Get()->Close();
_commandList.Get()->Close();
_isRecording = false;
}
@@ -137,7 +170,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
IncrementCommandCount();
var d3d12Rect = new RECT((int)rect.Left, (int)rect.Top, (int)rect.Right, (int)rect.Bottom);
nativeObject.Get()->RSSetScissorRects(1, &d3d12Rect);
_commandList.Get()->RSSetScissorRects(1, &d3d12Rect);
}
public void ResourceBarrier(Handle<GPUResource> resource, ResourceState before, ResourceState after)
@@ -150,7 +183,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
var barrier = D3D12_RESOURCE_BARRIER.InitTransition(d3d12Resource,
before.ToD3D12States(), after.ToD3D12States());
nativeObject.Get()->ResourceBarrier(1, &barrier);
_commandList.Get()->ResourceBarrier(1, &barrier);
}
public void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget)
@@ -168,17 +201,17 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
throw new ArgumentException($"Render target at index {i} is not a valid texture handle");
}
var descriptor = _resourceDatabase.GetResourceInfo(handle.AsResource()).viewGroup;
var descriptor = _resourceDatabase.GetResourceRecord(handle.AsResource()).viewGroup;
pRtvHandles[i] = _descriptorAllocator.GetCpuHandle(descriptor.rtv);
}
var pDsvHandle = stackalloc D3D12_CPU_DESCRIPTOR_HANDLE[depthTarget.IsValid ? 1 : 0];
if (pDsvHandle != null)
{
pDsvHandle[0] = _descriptorAllocator.GetCpuHandle(_resourceDatabase.GetResourceInfo(depthTarget.AsResource()).viewGroup.dsv);
pDsvHandle[0] = _descriptorAllocator.GetCpuHandle(_resourceDatabase.GetResourceRecord(depthTarget.AsResource()).viewGroup.dsv);
}
nativeObject.Get()->OMSetRenderTargets((uint)renderTargets.Length, pRtvHandles, FALSE, pDsvHandle);
_commandList.Get()->OMSetRenderTargets((uint)renderTargets.Length, pRtvHandles, FALSE, pDsvHandle);
}
public void BeginRenderPass(ReadOnlySpan<PassRenderTargetDesc> rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false)
@@ -196,7 +229,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
throw new ArgumentException($"Render target at index {i} is not a valid texture handle");
}
var resourceInfo = _resourceDatabase.GetResourceInfo(rtDesc.Texture.AsResource());
var resourceInfo = _resourceDatabase.GetResourceRecord(rtDesc.Texture.AsResource());
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceInfo.viewGroup.rtv);
var desc = new D3D12_RENDER_PASS_RENDER_TARGET_DESC
@@ -212,6 +245,10 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
Format = resourceInfo.desc.TextureDescription.Format.ToDXGIFormat(),
}
}
},
EndingAccess = new D3D12_RENDER_PASS_ENDING_ACCESS
{
Type = D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE
}
};
@@ -226,7 +263,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
var pDsvDesc = stackalloc D3D12_RENDER_PASS_DEPTH_STENCIL_DESC[depthDesc.Texture.IsValid ? 1 : 0];
if (pDsvDesc != null)
{
var resourceInfo = _resourceDatabase.GetResourceInfo(depthDesc.Texture.AsResource());
var resourceInfo = _resourceDatabase.GetResourceRecord(depthDesc.Texture.AsResource());
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceInfo.viewGroup.dsv);
var desc = new D3D12_RENDER_PASS_DEPTH_STENCIL_DESC
@@ -253,7 +290,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
pDsvDesc[0] = desc;
}
nativeObject.Get()->BeginRenderPass((uint)rtDescs.Length, pRtvDescs, pDsvDesc,
_commandList.Get()->BeginRenderPass((uint)rtDescs.Length, pRtvDescs, pDsvDesc,
allowUAVWrites ? D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES : D3D12_RENDER_PASS_FLAG_NONE);
}
@@ -263,7 +300,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
nativeObject.Get()->EndRenderPass();
_commandList.Get()->EndRenderPass();
}
public void SetViewport(ViewportDesc viewport)
@@ -272,8 +309,8 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
var d3d12Viewport = new D3D12_VIEWPORT(viewport.Width, viewport.Height, viewport.X, viewport.Y, viewport.MinDepth, viewport.MaxDepth);
nativeObject.Get()->RSSetViewports(1, &d3d12Viewport);
var d3d12Viewport = new D3D12_VIEWPORT(viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinDepth, viewport.MaxDepth);
_commandList.Get()->RSSetViewports(1, &d3d12Viewport);
}
public void SetPipelineState(GraphicsPipelineKey pipelineKey)
@@ -288,9 +325,11 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
#if DEBUG || GHOST_EDITOR
Logger.LogError($"Failed to get graphics pipeline state object for key {pipelineKey}: {psor.Status}");
#endif
return;
}
nativeObject.Get()->SetPipelineState(psor.Value);
_commandList.Get()->SetGraphicsRootSignature(_pipelineLibrary.DefaultRootSignature);
_commandList.Get()->SetPipelineState(psor.Value);
}
public void SetConstantBufferView(uint slot, Handle<GraphicsBuffer> buffer)
@@ -300,7 +339,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
IncrementCommandCount();
var resource = _resourceDatabase.GetResource(buffer.AsResource());
nativeObject.Get()->SetGraphicsRootConstantBufferView(RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT, resource->GetGPUVirtualAddress());
_commandList.Get()->SetGraphicsRootConstantBufferView(slot, resource.Get()->GetGPUVirtualAddress());
}
public void SetVertexBuffer(uint slot, Handle<GraphicsBuffer> buffer, ulong offset = 0)
@@ -309,15 +348,15 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
var pResource = _resourceDatabase.GetResource(buffer.AsResource());
var resource = _resourceDatabase.GetResource(buffer.AsResource());
var vbView = new D3D12_VERTEX_BUFFER_VIEW
{
BufferLocation = pResource->GetGPUVirtualAddress() + offset,
SizeInBytes = (uint)(pResource->GetDesc().Width - offset),
BufferLocation = resource.Get()->GetGPUVirtualAddress() + offset,
SizeInBytes = (uint)(resource.Get()->GetDesc().Width - offset),
StrideInBytes = _resourceDatabase.GetResourceDescription(buffer.AsResource()).BufferDescription.Stride
};
nativeObject.Get()->IASetVertexBuffers(slot, 1, &vbView);
_commandList.Get()->IASetVertexBuffers(slot, 1, &vbView);
}
public void SetIndexBuffer(Handle<GraphicsBuffer> buffer, IndexType type, ulong offset = 0)
@@ -326,15 +365,15 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
var pResource = _resourceDatabase.GetResource(buffer.AsResource());
var resource = _resourceDatabase.GetResource(buffer.AsResource());
var ibView = new D3D12_INDEX_BUFFER_VIEW
{
BufferLocation = pResource->GetGPUVirtualAddress() + offset,
SizeInBytes = (uint)(pResource->GetDesc().Width - offset),
BufferLocation = resource.Get()->GetGPUVirtualAddress() + offset,
SizeInBytes = (uint)(resource.Get()->GetDesc().Width - offset),
Format = type == IndexType.UInt16 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT
};
nativeObject.Get()->IASetIndexBuffer(&ibView);
_commandList.Get()->IASetIndexBuffer(&ibView);
}
public void SetPrimitiveTopology(PrimitiveTopology topology)
@@ -351,7 +390,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
_ => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST
};
nativeObject.Get()->IASetPrimitiveTopology(d3d12Topology);
_commandList.Get()->IASetPrimitiveTopology(d3d12Topology);
}
public void Draw(uint vertexCount, uint instanceCount = 1, uint startVertex = 0, uint startInstance = 0)
@@ -360,7 +399,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
nativeObject.Get()->DrawInstanced(vertexCount, instanceCount, startVertex, startInstance);
_commandList.Get()->DrawInstanced(vertexCount, instanceCount, startVertex, startInstance);
}
public void DrawIndexed(uint indexCount, uint instanceCount = 1, uint startIndex = 0, int baseVertex = 0, uint startInstance = 0)
@@ -369,7 +408,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
nativeObject.Get()->DrawIndexedInstanced(indexCount, instanceCount, startIndex, baseVertex, startInstance);
_commandList.Get()->DrawIndexedInstanced(indexCount, instanceCount, startIndex, baseVertex, startInstance);
}
public void DispatchCompute(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ)
@@ -378,7 +417,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
nativeObject.Get()->Dispatch(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
_commandList.Get()->Dispatch(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
}
public void DispatchMesh(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ)
@@ -387,7 +426,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
nativeObject.Get()->DispatchMesh(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
_commandList.Get()->DispatchMesh(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
}
public void DispatchRay()
@@ -398,7 +437,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
// ThrowIfNotRecording();
// IncrementCommandCount();
// nativeObject.Get()->DispatchRays();
// _device.Get()->DispatchRays();
}
public void UploadBuffer<T>(Handle<GraphicsBuffer> buffer, ReadOnlySpan<T> data)
@@ -411,19 +450,19 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
var sizeInBytes = (uint)(data.Length * sizeof(T));
var uploadHandle = _resourceAllocator.CreateUploadBuffer(sizeInBytes);
var pUploadResource = _resourceDatabase.GetResource(uploadHandle.AsResource());
var uploadResource = _resourceDatabase.GetResource(uploadHandle.AsResource());
void* pMappedData;
pUploadResource->Map(0, null, &pMappedData);
uploadResource.Get()->Map(0, null, &pMappedData);
fixed (T* pData = data)
{
MemoryUtility.MemCpy(pMappedData, pData, sizeInBytes);
MemoryUtility.MemCpy(pData, pMappedData, sizeInBytes);
}
pUploadResource->Unmap(0, null);
uploadResource.Get()->Unmap(0, null);
var pResource = _resourceDatabase.GetResource(buffer.AsResource());
nativeObject.Get()->CopyBufferRegion(pResource, 0, pUploadResource, 0, sizeInBytes);
_commandList.Get()->CopyBufferRegion(pResource, 0, uploadResource, 0, sizeInBytes);
}
public void UploadTexture(Handle<Texture> texture, params ReadOnlySpan<SubResourceData> subresources)
@@ -432,10 +471,10 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
var pResource = _resourceDatabase.GetResource(texture.AsResource());
var resource = _resourceDatabase.GetResource(texture.AsResource());
var resourceDesc = pResource->GetDesc();
var requiredSize = GetRequiredIntermediateSize(pResource, 0, (uint)subresources.Length);
var resourceDesc = resource.Get()->GetDesc();
var requiredSize = GetRequiredIntermediateSize(resource, 0, (uint)subresources.Length);
var uploadHandle = _resourceAllocator.CreateUploadBuffer(requiredSize);
var pUploadResource = _resourceDatabase.GetResource(uploadHandle.AsResource());
@@ -452,8 +491,8 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
}
UpdateSubresources(
(ID3D12GraphicsCommandList*)nativeObject.Get(),
pResource,
(ID3D12GraphicsCommandList*)_commandList.Get(),
resource,
pUploadResource,
0,
0,
@@ -476,17 +515,17 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
if (numBytes == 0)
{
nativeObject.Get()->CopyResource(pDestResource, pSrcResource);
_commandList.Get()->CopyResource(pDestResource, pSrcResource);
}
else
{
nativeObject.Get()->CopyBufferRegion(pDestResource, destOffset, pSrcResource, srcOffset, numBytes);
_commandList.Get()->CopyBufferRegion(pDestResource, destOffset, pSrcResource, srcOffset, numBytes);
}
}
protected override void Dispose(bool disposing)
public void Dispose()
{
if (Disposed)
if (_disposed)
{
return;
}
@@ -496,9 +535,11 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
throw new InvalidOperationException("Command buffer is still recording");
}
_commandList.Dispose();
_allocator.Dispose();
_commandCount = 0;
base.Dispose(disposing);
_disposed = true;
GC.SuppressFinalize(this);
}
}

View File

@@ -1,5 +1,4 @@
using Ghost.Core.Utilities;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel;
using TerraFX.Interop.DirectX;
@@ -10,18 +9,21 @@ namespace Ghost.Graphics.D3D12;
/// <summary>
/// D3D12 implementation of command queue interface
/// </summary>
internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, ICommandQueue
internal unsafe class D3D12CommandQueue : ICommandQueue
{
private UniquePtr<ID3D12CommandQueue> _commandQueue;
private UniquePtr<ID3D12Fence1> _fence;
private readonly AutoResetEvent _fenceEvent;
private ulong _fenceValue;
private bool _disposed;
public CommandQueueType Type
{
get;
}
public ID3D12CommandQueue* NativeQueue => nativeObject.Get();
public SharedPtr<ID3D12CommandQueue> NativeQueue => _commandQueue.Get();
public D3D12CommandQueue(ID3D12Device14* pDevice, CommandQueueType type)
{
@@ -41,10 +43,15 @@ internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, IComm
ThrowIfFailed(pDevice->CreateCommandQueue(&queueDesc, __uuidof(pQueue), (void**)&pQueue));
ThrowIfFailed(pDevice->CreateFence(0, D3D12_FENCE_FLAGS.D3D12_FENCE_FLAG_NONE, __uuidof(pFence), (void**)&pFence));
nativeObject.Attach(pQueue);
_commandQueue.Attach(pQueue);
_fence.Attach(pFence);
}
~D3D12CommandQueue()
{
Dispose();
}
private static D3D12_COMMAND_LIST_TYPE ConvertCommandQueueType(CommandQueueType type)
{
return type switch
@@ -58,7 +65,7 @@ internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, IComm
public void Submit(ICommandBuffer commandBuffer)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
if (commandBuffer.IsEmpty)
{
@@ -68,8 +75,8 @@ internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, IComm
if (commandBuffer is D3D12CommandBuffer d3d12CommandBuffer)
{
var commandList = d3d12CommandBuffer.NativeCommandList;
var commandListPtr = (ID3D12CommandList*)commandList;
nativeObject.Get()->ExecuteCommandLists(1, &commandListPtr);
var commandListPtr = (ID3D12CommandList*)commandList.Get();
_commandQueue.Get()->ExecuteCommandLists(1, &commandListPtr);
}
else
{
@@ -79,7 +86,7 @@ internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, IComm
public void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
Span<int> executableIndices = stackalloc int[commandBuffers.Length];
executableIndices.Fill(-1);
@@ -107,7 +114,7 @@ internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, IComm
if (commandBuffers[cmdIndex] is D3D12CommandBuffer d3d12CommandBuffer)
{
ppCommandLists[currentIndex] = (ID3D12CommandList*)d3d12CommandBuffer.NativeCommandList;
ppCommandLists[currentIndex] = (ID3D12CommandList*)d3d12CommandBuffer.NativeCommandList.Get();
}
else
{
@@ -117,21 +124,21 @@ internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, IComm
currentIndex++;
}
nativeObject.Get()->ExecuteCommandLists((uint)currentIndex, ppCommandLists);
_commandQueue.Get()->ExecuteCommandLists((uint)currentIndex, ppCommandLists);
}
public ulong Signal(ulong value)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
_fenceValue = value;
nativeObject.Get()->Signal((ID3D12Fence*)_fence.Get(), _fenceValue);
_commandQueue.Get()->Signal((ID3D12Fence*)_fence.Get(), _fenceValue);
return _fenceValue;
}
public void WaitForValue(ulong value)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
if (_fence.Get()->GetCompletedValue() < value)
{
@@ -145,28 +152,30 @@ internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, IComm
public ulong GetCompletedValue()
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
return _fence.Get()->GetCompletedValue();
}
public void WaitIdle()
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
var fenceValue = Signal(Interlocked.Increment(ref _fenceValue));
WaitForValue(fenceValue);
}
protected override void Dispose(bool disposing)
public void Dispose()
{
if (Disposed)
if (_disposed)
{
return;
}
_fenceEvent?.Dispose();
_commandQueue.Dispose();
_fence.Dispose();
_fenceEvent?.Dispose();
base.Dispose(disposing);
_disposed = true;
GC.SuppressFinalize(this);
}
}

View File

@@ -105,7 +105,7 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
public ISwapChain CreateSwapChain(SwapChainDesc desc)
{
ThrowIfDisposed();
return new D3D12SwapChain(_resourceDatabase, _device.DXGIFactory, ((D3D12CommandQueue)_device.GraphicsQueue).NativeQueue, desc);
return new D3D12SwapChain(_resourceDatabase, _descriptorAllocator, _device, desc);
}
public void BeginFrame()
@@ -152,6 +152,7 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
_resourceDatabase.Dispose();
_descriptorAllocator.Dispose();
_shaderCompiler.Dispose();
_device.Dispose();
#if DEBUG
_debugLayer.Dispose();

View File

@@ -1,132 +0,0 @@
using Ghost.Core.Utilities;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel;
using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
namespace Ghost.Graphics.D3D12;
internal abstract class D3D12RHIObject : IRHIObject
{
public string Name
{
get; set;
} = string.Empty;
}
internal abstract unsafe class D3D12Object<T> : IRHIObject, IDisposable
where T : unmanaged, ID3D12Object.Interface
{
private bool _disposed;
private string _name = string.Empty;
protected UniquePtr<T> nativeObject;
protected bool Disposed => _disposed;
public string Name
{
get => _name;
set
{
if (_name == value)
{
return;
}
_name = value;
if (nativeObject.Get() != null)
{
nativeObject.Get()->SetName(value);
}
}
}
~D3D12Object()
{
Dispose(false);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void ThrowIfDisposed()
{
ObjectDisposedException.ThrowIf(_disposed, this);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
nativeObject.Dispose();
_disposed = true;
}
}
internal abstract class IUnknownObject<T> : IRHIObject, IDisposable
where T : unmanaged, IUnknown.Interface
{
private bool _disposed;
private string _name = string.Empty;
protected UniquePtr<T> nativeObject;
protected bool Disposed => _disposed;
public string Name
{
get => _name;
set
{
if (_name == value)
{
return;
}
_name = value;
}
}
~IUnknownObject()
{
Dispose(false);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void ThrowIfDisposed()
{
ObjectDisposedException.ThrowIf(_disposed, this);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
nativeObject.Dispose();
_disposed = true;
}
}

View File

@@ -31,7 +31,7 @@ internal struct D3D12PipelineState : IDisposable
}
}
internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
{
private readonly D3D12RenderDevice _device;
private readonly D3D12ResourceDatabase _resourceDatabase;
@@ -153,7 +153,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
}
ID3D12RootSignature* pRootSignature = default;
ThrowIfFailed(_device.NativeDevice->CreateRootSignature(0, pSignature->GetBufferPointer(), pSignature->GetBufferSize(),
ThrowIfFailed(_device.NativeDevice.Get()->CreateRootSignature(0, pSignature->GetBufferPointer(), pSignature->GetBufferSize(),
__uuidof(pRootSignature), (void**)&pRootSignature));
_defaultRootSignature.Attach(pRootSignature);
@@ -183,12 +183,12 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
var fileBytes = File.ReadAllBytes(filePath!);
fixed (byte* pFileBytes = fileBytes)
{
ThrowIfFailed(_device.NativeDevice->CreatePipelineLibrary(pFileBytes, (nuint)fileBytes.Length, __uuidof(pLibrary), (void**)&pLibrary));
ThrowIfFailed(_device.NativeDevice.Get()->CreatePipelineLibrary(pFileBytes, (nuint)fileBytes.Length, __uuidof(pLibrary), (void**)&pLibrary));
}
}
else
{
ThrowIfFailed(_device.NativeDevice->CreatePipelineLibrary(null, 0, __uuidof(pLibrary), (void**)&pLibrary));
ThrowIfFailed(_device.NativeDevice.Get()->CreatePipelineLibrary(null, 0, __uuidof(pLibrary), (void**)&pLibrary));
}
_library.Attach(pLibrary);
@@ -213,18 +213,26 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
private static Result<CBufferInfo> ValidateReflectionData(ShaderReflectionData reflectionData)
{
CBufferInfo cbufferInfo;
var cbufferInfo = default(CBufferInfo);
foreach (var info in reflectionData.ResourcesBindings)
{
if (info.BindPoint > 3)
if (info.BindPoint >= RootSignatureLayout.ROOT_PARAMETER_COUNT)
{
return Result.Failure($"Resource binding point {info.BindPoint} is out of range. Only binding points 0-3 are supported in the current root signature.");
}
if (info.Type != ShaderInputType.ConstantBuffer)
{
return Result.Failure($"Resource binding type {info.Type} is not supported. Only constant buffers are supported in the current root signature.");
return Result.Failure($"Resource binding type {info.Type} is not supported. Please consider using bindless resources for buffers, textures and samplers.");
}
if (info.BindPoint == RootSignatureLayout.PER_OBJECT_BUFFER_SLOT)
{
if (info.Size != sizeof(PerObjectData))
{
return Result.Failure($"Per-object constant buffer size mismatch. Expected size: {sizeof(PerObjectData)}, Actual size: {info.Size}");
}
}
if (info.BindPoint == RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT)
@@ -237,19 +245,15 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
SizeInBytes = info.Size,
Properties = info.Properties ?? Array.Empty<CBufferPropertyInfo>(),
};
}
}
return Result.Success(cbufferInfo);
}
}
return Result.Failure("Per-material constant buffer not found in shader reflection data.");
// TODO: Validate Cbuffer sizes and bindings.
}
private static D3D12_COMPARISON_FUNC ToD3DCompare(ZTestOptions z) => z switch
{
ZTestOptions.Disabled => D3D12_COMPARISON_FUNC_ALWAYS,
ZTestOptions.Disabled => D3D12_COMPARISON_FUNC_NEVER,
ZTestOptions.Less => D3D12_COMPARISON_FUNC_LESS,
ZTestOptions.LessEqual => D3D12_COMPARISON_FUNC_LESS_EQUAL,
ZTestOptions.Equal => D3D12_COMPARISON_FUNC_EQUAL,
@@ -284,7 +288,8 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
return Result.Failure("Validation of pixel shader reflection data failed: " + psr.Message);
}
if (msr.Value != psr.Value)
if (msr.Value.Properties != null
&& msr.Value.SizeInBytes != psr.Value.SizeInBytes)
{
return Result.Failure("Mesh shader and pixel shader constant buffer layouts do not match.");
}
@@ -297,12 +302,14 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
return Result.Failure("Validation of task shader reflection data failed: " + tsr.Message);
}
if (tsr.Value != msr.Value)
if (tsr.Value.Properties != null
&& tsr.Value.SizeInBytes != psr.Value.SizeInBytes)
{
return Result.Failure("Task shader and mesh shader constant buffer layouts do not match.");
return Result.Failure("Task shader and pixel shader constant buffer layouts do not match.");
}
}
// ts and ms may not use per material cbuffer at all, so we return the psr value.
return psr.Value;
}
@@ -396,7 +403,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
if (hr == E.E_INVALIDARG)
{
// Pipeline not found in the library, create a new one.
ThrowIfFailed(_device.NativeDevice->CreatePipelineState(&streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState));
ThrowIfFailed(_device.NativeDevice.Get()->CreatePipelineState(&streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState));
ThrowIfFailed(_library.Get()->StorePipeline(pKeyStr, pPipelineState));
}
else

View File

@@ -12,8 +12,9 @@ namespace Ghost.Graphics.D3D12;
/// <summary>
/// D3D12 implementation of the render device interface
/// </summary>
internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDevice
internal unsafe class D3D12RenderDevice : IRenderDevice
{
private UniquePtr<ID3D12Device14> _device;
private UniquePtr<IDXGIFactory7> _dxgiFactory;
private UniquePtr<IDXGIAdapter1> _adapter;
@@ -21,21 +22,35 @@ internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDe
private readonly D3D12CommandQueue _computeQueue;
private readonly D3D12CommandQueue _copyQueue;
private bool _disposed;
public ICommandQueue GraphicsQueue => _graphicsQueue;
public ICommandQueue ComputeQueue => _computeQueue;
public ICommandQueue CopyQueue => _copyQueue;
public IDXGIFactory7* DXGIFactory => _dxgiFactory.Get();
public ID3D12Device14* NativeDevice => nativeObject.Get();
public IDXGIAdapter1* Adapter => _adapter.Get();
public SharedPtr<IDXGIFactory7> DXGIFactory => _dxgiFactory.Get();
public SharedPtr<ID3D12Device14> NativeDevice => _device.Get();
public SharedPtr<IDXGIAdapter1> Adapter => _adapter.Get();
public SharedPtr<ID3D12CommandQueue> NativeGraphicsQueue => _graphicsQueue.NativeQueue;
public SharedPtr<ID3D12CommandQueue> NativeComputeQueue => _computeQueue.NativeQueue;
public SharedPtr<ID3D12CommandQueue> NativeCopyQueue => _copyQueue.NativeQueue;
public D3D12RenderDevice()
{
IDXGIFactory7* pFactory = default;
#if DEBUG
ThrowIfFailed(CreateDXGIFactory2(TRUE, __uuidof(pFactory), (void**)&pFactory));
#else
ThrowIfFailed(CreateDXGIFactory2(FALSE, __uuidof(pFactory), (void**)&pFactory));
#endif
_dxgiFactory.Attach(pFactory);
InitializeDevice();
_graphicsQueue = new D3D12CommandQueue(nativeObject.Get(), CommandQueueType.Graphics);
_computeQueue = new D3D12CommandQueue(nativeObject.Get(), CommandQueueType.Compute);
_copyQueue = new D3D12CommandQueue(nativeObject.Get(), CommandQueueType.Copy);
_graphicsQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Graphics);
_computeQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Compute);
_copyQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Copy);
}
~D3D12RenderDevice()
@@ -45,15 +60,6 @@ internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDe
private void InitializeDevice()
{
IDXGIFactory7* pFactory = default;
#if DEBUG
CreateDXGIFactory2(TRUE, __uuidof(pFactory), (void**)&pFactory);
#else
CreateDXGIFactory2(FALSE, __uuidof(pFactory), (void**)&pFactory);
#endif
_dxgiFactory.Attach(pFactory);
ID3D12Device14* pDevice = default;
IDXGIAdapter1* pAdapter = default;
@@ -86,17 +92,17 @@ internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDe
throw new PlatformNotSupportedException("Cannot create ID3D12Device with feature level 12.0");
}
nativeObject.Attach(pDevice);
_device.Attach(pDevice);
}
public FeatureSupport GetFeatureSupport()
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
FeatureSupport support = FeatureSupport.None;
D3D12_FEATURE_DATA_D3D12_OPTIONS options = default;
if (nativeObject.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS)).SUCCEEDED)
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS)).SUCCEEDED)
{
if (options.ResourceBindingTier == D3D12_RESOURCE_BINDING_TIER_3)
{
@@ -105,7 +111,7 @@ internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDe
}
D3D12_FEATURE_DATA_D3D12_OPTIONS5 options5 = default;
if (nativeObject.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &options5, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS5)).SUCCEEDED)
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &options5, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS5)).SUCCEEDED)
{
if (options5.RaytracingTier != D3D12_RAYTRACING_TIER_NOT_SUPPORTED)
{
@@ -114,7 +120,7 @@ internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDe
}
D3D12_FEATURE_DATA_D3D12_OPTIONS6 options6 = default;
if (nativeObject.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS6, &options6, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS6)).SUCCEEDED)
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS6, &options6, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS6)).SUCCEEDED)
{
if (options6.VariableShadingRateTier != D3D12_VARIABLE_SHADING_RATE_TIER_NOT_SUPPORTED)
{
@@ -123,7 +129,7 @@ internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDe
}
D3D12_FEATURE_DATA_D3D12_OPTIONS7 options7 = default;
if (nativeObject.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &options7, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS7)).SUCCEEDED)
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &options7, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS7)).SUCCEEDED)
{
if (options7.MeshShaderTier != D3D12_MESH_SHADER_TIER_NOT_SUPPORTED)
{
@@ -139,9 +145,9 @@ internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDe
return support;
}
protected override void Dispose(bool disposing)
public void Dispose()
{
if (Disposed)
if (_disposed)
{
return;
}
@@ -150,9 +156,11 @@ internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDe
_computeQueue.Dispose();
_copyQueue.Dispose();
_device.Dispose();
_dxgiFactory.Dispose();
_adapter.Dispose();
base.Dispose(disposing);
_disposed = true;
GC.SuppressFinalize(this);
}
}

View File

@@ -10,16 +10,17 @@ namespace Ghost.Graphics.D3D12;
/// <summary>
/// D3D12 implementation of the renderer interface using RHI abstractions
/// </summary>
internal unsafe class D3D12Renderer : IRenderer
internal class D3D12Renderer : IRenderer
{
private struct FrameResource : IDisposable
{
public ICommandBuffer commandBuffer;
public ulong fenceValue;
public FrameResource(D3D12GraphicsEngine graphicsEngine)
public FrameResource(D3D12GraphicsEngine graphicsEngine, int index)
{
commandBuffer = graphicsEngine.CreateCommandBuffer();
commandBuffer.Name = $"Frame Command Buffer {index}";
fenceValue = 0;
}
@@ -67,7 +68,7 @@ internal unsafe class D3D12Renderer : IRenderer
_frameResources = new FrameResource[D3D12PipelineResource.BACK_BUFFER_COUNT];
for (var i = 0; i < _frameResources.Length; i++)
{
_frameResources[i] = new FrameResource(graphicsEngine);
_frameResources[i] = new FrameResource(graphicsEngine, i);
}
_renderTarget = Handle<Texture>.Invalid;
@@ -108,6 +109,12 @@ internal unsafe class D3D12Renderer : IRenderer
CreateOffScreenRenderTarget(swapChain.Width, swapChain.Height);
}
var newSize = swapChain != null ? new uint2(swapChain.Width, swapChain.Height) : _currentSize;
if (!math.all(newSize == _currentSize))
{
RequestResize(newSize);
}
_swapChain = swapChain;
}
@@ -171,9 +178,22 @@ internal unsafe class D3D12Renderer : IRenderer
frame.commandBuffer.Begin();
// NOTE: Temperary solution: render directly to the swap chain back buffer if available.
// HACK: This is hard coded for testing purposes only.
var rt = _swapChain?.GetCurrentBackBuffer() ?? _renderTarget;
if(_swapChain != null)
{
frame.commandBuffer.ResourceBarrier(rt.AsResource(), ResourceState.Present, ResourceState.RenderTarget);
}
RenderScene(rt, frame.commandBuffer);
if (_swapChain != null)
{
frame.commandBuffer.ResourceBarrier(rt.AsResource(), ResourceState.RenderTarget, ResourceState.Present);
}
// if (_swapChain != null)
// {
// var backBufferRT = _swapChain.GetCurrentBackBuffer();
@@ -196,12 +216,14 @@ internal unsafe class D3D12Renderer : IRenderer
{
var clearColor = new Color128 { r = 1.0f, g = 0.0f, b = 1.0f, a = 1.0f };
Span<PassRenderTargetDesc> rtDesc = stackalloc PassRenderTargetDesc[1];
rtDesc[0] = new PassRenderTargetDesc
Span<PassRenderTargetDesc> rtDesc =
[
new PassRenderTargetDesc
{
Texture = target,
ClearColor = clearColor,
};
},
];
var depthDesc = new PassDepthStencilDesc
{
@@ -217,11 +239,10 @@ internal unsafe class D3D12Renderer : IRenderer
_pass.Initialize(ref ctx);
}
cmd.BeginRenderPass(rtDesc, depthDesc, false);
var viewport = new ViewportDesc { Width = _currentSize.x, Height = _currentSize.y, MinDepth = 0, MaxDepth = 1 };
var scissor = new RectDesc { Right = _currentSize.x, Bottom = _currentSize.y };
cmd.BeginRenderPass(rtDesc, depthDesc, false);
cmd.SetViewport(viewport);
cmd.SetScissorRect(scissor);

View File

@@ -3,6 +3,7 @@ using Ghost.Core.Graphics;
using Ghost.Core.Utilities;
using Ghost.Graphics.Core;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.Mathematics;
using System.Runtime.CompilerServices;
@@ -132,13 +133,13 @@ internal sealed unsafe partial class D3D12ResourceAllocator
var resourceDesc = pResource->GetDesc();
var srvDesc = new D3D12_SHADER_RESOURCE_VIEW_DESC
{
Format = resourceDesc.Format,
ViewDimension = D3D12_SRV_DIMENSION_BUFFER,
Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING
};
if (isRaw)
{
srvDesc.Format = DXGI_FORMAT_R32_TYPELESS;
srvDesc.Buffer.FirstElement = 0;
srvDesc.Buffer.NumElements = (uint)(resourceDesc.Width / 4u);
srvDesc.Buffer.StructureByteStride = 0;
@@ -146,6 +147,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator
}
else // Assumes Structured
{
srvDesc.Format = resourceDesc.Format;
srvDesc.Buffer.FirstElement = 0;
srvDesc.Buffer.NumElements = (uint)(resourceDesc.Width / stride);
srvDesc.Buffer.StructureByteStride = stride;
@@ -406,12 +408,12 @@ internal sealed unsafe partial class D3D12ResourceAllocator
var resourceDesc = pResource->GetDesc();
var uavDesc = new D3D12_UNORDERED_ACCESS_VIEW_DESC
{
Format = resourceDesc.Format,
ViewDimension = D3D12_UAV_DIMENSION_BUFFER,
};
if (isRaw)
{
uavDesc.Format = DXGI_FORMAT_R32_TYPELESS;
uavDesc.Buffer.FirstElement = 0;
uavDesc.Buffer.NumElements = (uint)(resourceDesc.Width / 4u);
uavDesc.Buffer.StructureByteStride = 0;
@@ -419,6 +421,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator
}
else // Assumes Structured
{
uavDesc.Format = resourceDesc.Format;
uavDesc.Buffer.FirstElement = 0;
uavDesc.Buffer.NumElements = (uint)(resourceDesc.Width / stride);
uavDesc.Buffer.StructureByteStride = stride;
@@ -521,6 +524,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator
// Default to Common, but check for specific roles
var state = D3D12_RESOURCE_STATE_COMMON;
return state;
if (usage.HasFlag(BufferUsage.Vertex) || usage.HasFlag(BufferUsage.Constant))
{
@@ -591,8 +595,10 @@ internal sealed unsafe partial class D3D12ResourceAllocator
// TODO: Thread safety for resource allocator
// A common solution is to use ticket. Each pAllocation request create a ticket and put it into a thread-safe queue. A dedicated thread process the queue and fulfill the requests.
internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D12MA_Allocator>, IResourceAllocator
internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
{
private UniquePtr<D3D12MA_Allocator> _d3d12MA;
private readonly IFenceSynchronizer _fenceSynchronizer;
private readonly D3D12RenderDevice _device;
private readonly D3D12DescriptorAllocator _descriptorAllocator;
@@ -601,6 +607,8 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
private UnsafeQueue<Handle<GPUResource>> _temResources;
private bool _disposed;
public D3D12ResourceAllocator(
IFenceSynchronizer fenceSynchronizer,
D3D12RenderDevice device,
@@ -610,14 +618,14 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
{
var desc = new D3D12MA_ALLOCATOR_DESC
{
pAdapter = (IDXGIAdapter*)device.Adapter,
pDevice = (ID3D12Device*)device.NativeDevice,
pAdapter = (IDXGIAdapter*)device.Adapter.Get(),
pDevice = (ID3D12Device*)device.NativeDevice.Get(),
Flags = D3D12MA_ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED | D3D12MA_ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED,
};
D3D12MA_Allocator* pAllocator = default;
ThrowIfFailed(D3D12MA_CreateAllocator(&desc, &pAllocator));
nativeObject.Attach(pAllocator);
_d3d12MA.Attach(pAllocator);
_fenceSynchronizer = fenceSynchronizer;
_device = device;
@@ -648,7 +656,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
public Handle<Texture> CreateTexture(ref readonly TextureDesc desc, bool isTemp = false)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
CheckTexture2DSize(desc.Width, desc.Height);
@@ -706,7 +714,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
var initialState = DetermineInitialTextureState(desc.Usage);
D3D12MA_Allocation* pAllocation = default;
ThrowIfFailed(nativeObject.Get()->CreateResource(&allocationDesc, &resourceDesc, initialState, null, &pAllocation, Win32Utility.IID_NULL, null));
ThrowIfFailed(_d3d12MA.Get()->CreateResource(&allocationDesc, &resourceDesc, initialState, null, &pAllocation, Win32Utility.IID_NULL, null));
var resourceDescriptor = ResourceViewGroup.Invalid;
if (desc.Usage.HasFlag(TextureUsage.ShaderResource))
@@ -717,7 +725,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
var isCubeMap = desc.Dimension == TextureDimension.TextureCube || desc.Dimension == TextureDimension.TextureCubeArray;
var srvDesc = CreateTextureSrvDesc(pAllocation->GetResource(), mipLevels, desc.Slice, isCubeMap);
_device.NativeDevice->CreateShaderResourceView(pAllocation->GetResource(), &srvDesc, cpuHandle);
_device.NativeDevice.Get()->CreateShaderResourceView(pAllocation->GetResource(), &srvDesc, cpuHandle);
}
if (desc.Usage.HasFlag(TextureUsage.RenderTarget))
@@ -726,7 +734,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.rtv);
var rtvDesc = CreateRtvDesc(pAllocation->GetResource());
_device.NativeDevice->CreateRenderTargetView(pAllocation->GetResource(), &rtvDesc, cpuHandle);
_device.NativeDevice.Get()->CreateRenderTargetView(pAllocation->GetResource(), &rtvDesc, cpuHandle);
}
if (desc.Usage.HasFlag(TextureUsage.DepthStencil))
@@ -735,7 +743,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.dsv);
var dsvDesc = CreateDsvDesc(pAllocation->GetResource());
_device.NativeDevice->CreateDepthStencilView(pAllocation->GetResource(), &dsvDesc, cpuHandle);
_device.NativeDevice.Get()->CreateDepthStencilView(pAllocation->GetResource(), &dsvDesc, cpuHandle);
}
if (desc.Usage.HasFlag(TextureUsage.UnorderedAccess))
@@ -744,7 +752,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav);
var uavDesc = CreateTextureUavDesc(pAllocation->GetResource());
_device.NativeDevice->CreateUnorderedAccessView(pAllocation->GetResource(), null, &uavDesc, cpuHandle);
_device.NativeDevice.Get()->CreateUnorderedAccessView(pAllocation->GetResource(), null, &uavDesc, cpuHandle);
}
var handle = TrackResource(pAllocation, initialState, resourceDescriptor, ResourceDesc.Texture(desc), isTemp);
@@ -754,24 +762,27 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
public Handle<Texture> CreateRenderTarget(ref readonly RenderTargetDesc desc, bool isTemp = false)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
var textureDesc = desc.ToTextureDescripton();
return CreateTexture(ref textureDesc, isTemp);
return CreateTexture(in textureDesc, isTemp);
}
public Handle<GraphicsBuffer> CreateBuffer(ref readonly BufferDesc desc, bool isTemp = false)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
CheckBufferSize(desc.Size);
var resourceDescription = D3D12_RESOURCE_DESC.Buffer(desc.Size, ConvertBufferUsage(desc.Usage));
var isRaw = desc.Usage.HasFlag(BufferUsage.Raw);
if (isRaw)
var alignedSize = desc.Size;
if (desc.Usage.HasFlag(BufferUsage.Constant))
{
resourceDescription.Format = DXGI_FORMAT_R32_TYPELESS;
// D3D12 CBV size must be 256-byte aligned
alignedSize = (uint)(desc.Size + 255) & ~255u;
}
var resourceDescription = D3D12_RESOURCE_DESC.Buffer(alignedSize, ConvertBufferUsage(desc.Usage));
var isRaw = desc.Usage.HasFlag(BufferUsage.Raw);
var allocationDesc = new D3D12MA_ALLOCATION_DESC
{
HeapType = ConvertMemoryType(desc.MemoryType),
@@ -781,42 +792,41 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
var initialState = DetermineInitialBufferState(desc.Usage, desc.MemoryType);
D3D12MA_Allocation* pAllocation = default;
ThrowIfFailed(nativeObject.Get()->CreateResource(&allocationDesc, &resourceDescription, initialState, null, &pAllocation, Win32Utility.IID_NULL, null));
var iid = IID.IID_NULL;
ThrowIfFailed(_d3d12MA.Get()->CreateResource(&allocationDesc, &resourceDescription, initialState, null, &pAllocation, &iid, null));
var resourceDescriptor = ResourceViewGroup.Invalid;
var pResource = pAllocation->GetResource();
if (desc.Usage.HasFlag(BufferUsage.Constant))
{
// D3D12 CBV size must be 256-byte aligned
var alignedSize = (uint)(desc.Size + 255) & ~255u;
resourceDescriptor.cbv = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
var cpuHandle = _descriptorAllocator.GetCpuHandleShaderVisible(resourceDescriptor.cbv);
var cbvDesc = new D3D12_CONSTANT_BUFFER_VIEW_DESC
{
BufferLocation = pResource->GetGPUVirtualAddress(),
SizeInBytes = alignedSize
SizeInBytes = (uint)alignedSize
};
_device.NativeDevice->CreateConstantBufferView(&cbvDesc, _descriptorAllocator.GetCpuHandle(resourceDescriptor.cbv));
_device.NativeDevice.Get()->CreateConstantBufferView(&cbvDesc, cpuHandle);
}
if (desc.Usage.HasFlag(BufferUsage.ShaderResource))
{
resourceDescriptor.srv = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.srv);
var cpuHandle = _descriptorAllocator.GetCpuHandleShaderVisible(resourceDescriptor.srv);
var srvDesc = CreateBufferSrvDesc(pAllocation->GetResource(), desc.Stride, isRaw);
_device.NativeDevice->CreateShaderResourceView(pResource, &srvDesc, cpuHandle);
_device.NativeDevice.Get()->CreateShaderResourceView(pResource, &srvDesc, cpuHandle);
}
if (desc.Usage.HasFlag(BufferUsage.UnorderedAccess))
{
resourceDescriptor.uav = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav);
var cpuHandle = _descriptorAllocator.GetCpuHandleShaderVisible(resourceDescriptor.uav);
var uavDesc = CreateBufferUavDesc(pAllocation->GetResource(), desc.Stride, isRaw);
_device.NativeDevice->CreateUnorderedAccessView(pResource, null, &uavDesc, cpuHandle);
_device.NativeDevice.Get()->CreateUnorderedAccessView(pResource, null, &uavDesc, cpuHandle);
}
var handle = TrackResource(pAllocation, initialState, resourceDescriptor, ResourceDesc.Buffer(desc), isTemp);
@@ -825,7 +835,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
public Handle<GraphicsBuffer> CreateUploadBuffer(ulong size, bool isTemp = true)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
var desc = new BufferDesc
{
@@ -834,18 +844,18 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
MemoryType = ResourceMemoryType.Upload,
};
return CreateBuffer(ref desc, isTemp);
return CreateBuffer(in desc, isTemp);
}
public Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
var vertexBufferDesc = new BufferDesc
{
Size = (uint)(vertices.Count * Unsafe.SizeOf<Vertex>()),
Stride = (uint)Unsafe.SizeOf<Vertex>(),
Usage = BufferUsage.Vertex | BufferUsage.ShaderResource,
Size = (uint)(vertices.Count * sizeof(Vertex)),
Stride = (uint)sizeof(Vertex),
Usage = BufferUsage.Vertex | BufferUsage.ShaderResource | BufferUsage.Raw,
MemoryType = ResourceMemoryType.Default,
};
@@ -853,37 +863,47 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
{
Size = (uint)(indices.Count * sizeof(uint)),
Stride = sizeof(uint),
Usage = BufferUsage.Index | BufferUsage.ShaderResource,
Usage = BufferUsage.Index | BufferUsage.ShaderResource | BufferUsage.Raw,
MemoryType = ResourceMemoryType.Default,
};
var vertexBuffer = CreateBuffer(ref vertexBufferDesc);
var indexBuffer = CreateBuffer(ref indexBufferDesc);
var objectBufferDesc = new BufferDesc
{
Size = (uint)sizeof(PerObjectData),
Stride = (uint)sizeof(PerObjectData),
Usage = BufferUsage.Constant,
MemoryType = ResourceMemoryType.Default,
};
var vertexBuffer = CreateBuffer(in vertexBufferDesc);
var indexBuffer = CreateBuffer(in indexBufferDesc);
var objectBuffer = CreateBuffer(in objectBufferDesc);
var data = new Mesh
{
vertices = vertices,
indices = indices,
vertexBuffer = vertexBuffer,
indexBuffer = indexBuffer,
Vertices = vertices,
Indices = indices,
VertexBuffer = vertexBuffer,
IndexBuffer = indexBuffer,
ObjectDataBuffer = objectBuffer,
};
return _resourceDatabase.AddMesh(ref data);
return _resourceDatabase.AddMesh(in data);
}
public Handle<Material> CreateMaterial(Identifier<Shader> shader)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
var material = new Material();
material.SetShader(shader, this, _resourceDatabase);
return _resourceDatabase.AddMaterial(ref material);
return _resourceDatabase.AddMaterial(in material);
}
public Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
var shader = new Shader(descriptor);
foreach (var pass in descriptor.passes)
@@ -893,6 +913,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
continue;
}
// TODO: Cache the pass key because we hash it multiple times right now.
var passKey = new ShaderPassKey(fullPass.Identifier);
var cbr = _pipelineLibrary.GetCBufferInfo(passKey);
if (cbr.Status != ResultStatus.Success)
@@ -908,7 +929,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
public void ReleaseTempResources()
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
while (_temResources.Count > 0)
{
@@ -933,9 +954,9 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
}
}
protected override void Dispose(bool disposing)
public void Dispose()
{
if (Disposed)
if (_disposed)
{
return;
}
@@ -952,8 +973,10 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
_resourceDatabase.ReleaseResource(handle);
}
_d3d12MA.Dispose();
_temResources.Dispose();
base.Dispose(disposing);
_disposed = true;
GC.SuppressFinalize(this);
}
}

View File

@@ -8,11 +8,10 @@ using Misaki.HighPerformance.LowLevel.Collections;
using System.Diagnostics;
using System.Runtime.InteropServices;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
namespace Ghost.Graphics.D3D12;
internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
internal class D3D12ResourceDatabase : IResourceDatabase
{
internal unsafe struct ResourceRecord
{
@@ -56,12 +55,12 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
this.desc = desc;
}
public ResourceRecord(ID3D12Resource* resource, ResourceState state)
public ResourceRecord(ID3D12Resource* resource, ResourceState state, ResourceViewGroup viewGroup)
{
this.resourceUnion = new ResourceUnion(resource);
this.isExternal = true;
this.viewGroup = default;
this.viewGroup = viewGroup;
this.cpuFenceValue = ~0u;
this.state = state;
this.desc = ResourceDesc.FromD3D12(resource->GetDesc());
@@ -80,13 +79,13 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
{
refCount = resourceUnion.allocation.Get()->Release();
}
resourceUnion = default;
viewGroup = default;
}
descriptorAllocator.Release(viewGroup);
resourceUnion = default;
viewGroup = default;
return refCount;
}
}
@@ -107,15 +106,15 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public D3D12ResourceDatabase(D3D12DescriptorAllocator descriptorAllocator)
{
_resources = new(64, Allocator.Persistent, AllocationOption.Clear);
_resources = new UnsafeSlotMap<ResourceRecord>(64, Allocator.Persistent, AllocationOption.Clear);
#if DEBUG || GHOST_EDITOR
_resourceName = new(64);
_resourceName = new Dictionary<Handle<GPUResource>, string>(64);
#endif
_meshes = new(64, Allocator.Persistent, AllocationOption.Clear);
_materials = new(16, Allocator.Persistent, AllocationOption.Clear);
_shaders = new(16);
_shaderPasses = new(16);
_meshes = new UnsafeSlotMap<Mesh>(64, Allocator.Persistent, AllocationOption.Clear);
_materials = new UnsafeSlotMap<Material>(16, Allocator.Persistent, AllocationOption.Clear);
_shaders = new DynamicArray<Shader?>(16);
_shaderPasses = new Dictionary<ShaderPassKey, ShaderPass>(16);
_descriptorAllocator = descriptorAllocator;
}
@@ -132,11 +131,11 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
resource = default!;
}
public unsafe Handle<GPUResource> ImportExternalResource(ID3D12Resource* pResource, ResourceState initialState, string? name = null)
public unsafe Handle<GPUResource> ImportExternalResource(ID3D12Resource* pResource, ResourceState initialState, ResourceViewGroup viewGroup, string? name = null)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var id = _resources.Add(new ResourceRecord(pResource, initialState), out var generation);
var id = _resources.Add(new ResourceRecord(pResource, initialState, viewGroup), out var generation);
var handle = new Handle<GPUResource>(id, generation);
#if DEBUG || GHOST_EDITOR
@@ -172,7 +171,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
return _resources.Contains(handle.id, handle.generation);
}
public ref ResourceRecord GetResourceInfo(Handle<GPUResource> handle)
public ref ResourceRecord GetResourceRecord(Handle<GPUResource> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
@@ -191,11 +190,11 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
return ref _resources.GetElementReferenceAt(handle.id, handle.generation, out exist);
}
public unsafe ID3D12Resource* GetResource(Handle<GPUResource> handle)
public unsafe SharedPtr<ID3D12Resource> GetResource(Handle<GPUResource> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
ref var info = ref GetResourceInfo(handle);
ref var info = ref GetResourceRecord(handle);
if (!info.Allocated)
{
return null;
@@ -207,7 +206,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public ResourceState GetResourceState(Handle<GPUResource> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return GetResourceInfo(handle).state;
return GetResourceRecord(handle).state;
}
public void SetResourceState(Handle<GPUResource> handle, ResourceState state)
@@ -226,7 +225,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public ResourceDesc GetResourceDescription(Handle<GPUResource> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return GetResourceInfo(handle).desc;
return GetResourceRecord(handle).desc;
}
public int GetBindlessIndex(Handle<GPUResource> handle)
@@ -273,10 +272,11 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
var refCount = info.Release(_descriptorAllocator);
#if DEBUG || GHOST_EDITOR
_resourceName.Remove(handle, out var name);
if (refCount > 0)
{
throw new GPUResourceLeakException(refCount, info.ResourcePtr, name ?? "Unknown Resource");
}
//if (refCount > 0)
//{
// throw new GPUResourceLeakException(refCount, info.ResourcePtr, name ?? "Unknown Resource");
//}
//Debug.Assert(refCount == 0, "Resource released with non-zero reference count.");
#endif
_resources.Remove(handle.id, handle.generation);
@@ -435,10 +435,9 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public void Dispose()
{
[Conditional("DEBUG"), Conditional("GHOST_EDITOR")]
static void ThrowMemoryLeakException(string resourceType, int count)
{
throw new InvalidOperationException($"ResourceAllocator is being disposed with {count} {resourceType} still registered. Ensure all resources are released before disposing.");
throw new MemoryLeakException($"ResourceAllocator is being disposed with {count} {resourceType} still registered. Ensure all resources are released before disposing.");
}
if (_disposed)

View File

@@ -1,9 +1,10 @@
using Ghost.Core;
using Ghost.Core.Utilities;
using Ghost.Graphics.Core;
using Ghost.Graphics.Contracts;
using Ghost.Graphics.Core;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using System.Runtime.CompilerServices;
@@ -17,12 +18,19 @@ namespace Ghost.Graphics.D3D12;
/// <summary>
/// D3D12 implementation of swap chain interface
/// </summary>
internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapChain
internal unsafe class D3D12SwapChain : ISwapChain
{
private readonly D3D12ResourceDatabase _resourceDatabase;
private readonly D3D12DescriptorAllocator _descriptorAllocator;
private readonly D3D12RenderDevice _renderDevice;
private UniquePtr<IDXGISwapChain4> _swapChain;
private UnsafeArray<Handle<Texture>> _backBuffers;
private object? _compositionSurface;
private bool _disposed;
public uint Width
{
get; private set;
@@ -38,9 +46,11 @@ internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapCha
get;
}
public D3D12SwapChain(D3D12ResourceDatabase resourceDatabase, IDXGIFactory7* pFactory, ID3D12CommandQueue* pCommandQueue, SwapChainDesc desc)
public D3D12SwapChain(D3D12ResourceDatabase resourceDatabase, D3D12DescriptorAllocator descriptorAllocator, D3D12RenderDevice device, SwapChainDesc desc)
{
_resourceDatabase = resourceDatabase;
_descriptorAllocator = descriptorAllocator;
_renderDevice = device;
_backBuffers = new UnsafeArray<Handle<Texture>>(D3D12PipelineResource.BACK_BUFFER_COUNT, Allocator.Persistent);
@@ -48,11 +58,18 @@ internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapCha
Height = desc.height;
BufferCount = D3D12PipelineResource.BACK_BUFFER_COUNT;
CreateSwapChain(pFactory, pCommandQueue, desc);
CreateSwapChain(desc);
CreateBackBuffers();
_compositionSurface = desc.target.compositionSurface;
}
private void CreateSwapChain(IDXGIFactory7* pFactory, ID3D12CommandQueue* commandQueue, SwapChainDesc desc)
~D3D12SwapChain()
{
Dispose();
}
private void CreateSwapChain(SwapChainDesc desc)
{
var swapChainDesc = new DXGI_SWAP_CHAIN_DESC1
{
@@ -71,10 +88,13 @@ internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapCha
IDXGISwapChain1* pTempSwapChain = default;
var pFactory = _renderDevice.DXGIFactory.Get();
var pCommandQueue = _renderDevice.NativeGraphicsQueue.Get();
switch (desc.target.type)
{
case SwapChainTargetType.Composition:
ThrowIfFailed(pFactory->CreateSwapChainForComposition((IUnknown*)commandQueue, &swapChainDesc, null, &pTempSwapChain));
ThrowIfFailed(pFactory->CreateSwapChainForComposition((IUnknown*)pCommandQueue, &swapChainDesc, null, &pTempSwapChain));
// Set the composition surface
if (desc.target.compositionSurface != null)
@@ -91,7 +111,7 @@ internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapCha
};
pFactory->CreateSwapChainForHwnd(
(IUnknown*)commandQueue,
(IUnknown*)pCommandQueue,
new HWND(desc.target.windowHandle.ToPointer()),
&swapChainDesc,
&swapChainFullscreenDesc,
@@ -107,7 +127,7 @@ internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapCha
pTempSwapChain->QueryInterface(__uuidof(pSwapChain), (void**)&pSwapChain);
pTempSwapChain->Release();
nativeObject.Attach(pSwapChain);
_swapChain.Attach(pSwapChain);
}
private void CreateBackBuffers()
@@ -115,33 +135,37 @@ internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapCha
for (uint i = 0; i < BufferCount; i++)
{
ID3D12Resource* pBackBuffer = default;
nativeObject.Get()->GetBuffer(i, __uuidof(pBackBuffer), (void**)&pBackBuffer);
ThrowIfFailed(_swapChain.Get()->GetBuffer(i, __uuidof(pBackBuffer), (void**)&pBackBuffer));
pBackBuffer->SetName($"SwapChain_BackBuffer_{i}");
_backBuffers[i] = _resourceDatabase.ImportExternalResource(pBackBuffer, ResourceState.Present).AsTexture();
var rtv = _descriptorAllocator.AllocateRTV();
_renderDevice.NativeDevice.Get()->CreateRenderTargetView(pBackBuffer, null, _descriptorAllocator.GetCpuHandle(rtv));
var handle = _resourceDatabase.ImportExternalResource(pBackBuffer, ResourceState.Present, new ResourceViewGroup() { rtv = rtv });
_backBuffers[i] = handle.AsTexture();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Handle<Texture> GetCurrentBackBuffer()
{
ThrowIfDisposed();
return _backBuffers[nativeObject.Get()->GetCurrentBackBufferIndex()];
ObjectDisposedException.ThrowIf(_disposed, this);
return _backBuffers[_swapChain.Get()->GetCurrentBackBufferIndex()];
}
public void Present(bool vsync = true)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
var presentFlags = 0u;
var syncInterval = vsync ? 1u : 0u;
ThrowIfFailed(nativeObject.Get()->Present(syncInterval, presentFlags));
ThrowIfFailed(_swapChain.Get()->Present(syncInterval, presentFlags));
}
public void Resize(uint width, uint height)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
if (Width == width && Height == height)
{
@@ -155,7 +179,7 @@ internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapCha
}
// Resize the swap chain
if (nativeObject.Get()->ResizeBuffers(BufferCount, width, height, DXGI_FORMAT_B8G8R8A8_UNORM, (uint)DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING).FAILED)
if (_swapChain.Get()->ResizeBuffers(BufferCount, width, height, DXGI_FORMAT_B8G8R8A8_UNORM, (uint)DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING).FAILED)
{
throw new InvalidOperationException("Failed to resize swap chain buffers.");
}
@@ -167,20 +191,28 @@ internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapCha
CreateBackBuffers();
}
protected override void Dispose(bool disposing)
public void Dispose()
{
if (Disposed)
if (_disposed)
{
return;
}
if (_compositionSurface != null)
{
using var panelNative = ISwapChainPanelNative.FromSwapChainPanel(_compositionSurface);
panelNative.SetSwapChain(IntPtr.Zero);
}
for (var i = 0; i < _backBuffers.Count; i++)
{
_resourceDatabase.ReleaseResource(_backBuffers[i].AsResource());
}
_backBuffers.Dispose();
_swapChain.Dispose();
base.Dispose(disposing);
_disposed = true;
GC.SuppressFinalize(this);
}
}

View File

@@ -66,7 +66,7 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
HeapType = type;
NumDescriptors = numDescriptors;
ShaderVisible = type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV || type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
Stride = device.NativeDevice->GetDescriptorHandleIncrementSize(type);
Stride = device.NativeDevice.Get()->GetDescriptorHandleIncrementSize(type);
_dynamicHeapStart = Math.Clamp(dynamicHeapStart, 0, numDescriptors);
_currentDynamicOffset = 0;
@@ -254,14 +254,14 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
}
var newLocation = AllocateDescriptors(count);
_device.NativeDevice->CopyDescriptorsSimple((uint)count, GetCpuHandle(index), GetCpuHandle(newLocation), HeapType);
_device.NativeDevice.Get()->CopyDescriptorsSimple((uint)count, GetCpuHandle(index), GetCpuHandle(newLocation), HeapType);
return newLocation;
}
public readonly void CopyToShaderVisibleHeap(int index, int count = 1)
{
_device.NativeDevice->CopyDescriptorsSimple((uint)count, GetCpuHandleShaderVisible(index), GetCpuHandle(index), HeapType);
_device.NativeDevice.Get()->CopyDescriptorsSimple((uint)count, GetCpuHandleShaderVisible(index), GetCpuHandle(index), HeapType);
}
private bool AllocateResources(int numDescriptors)
@@ -279,7 +279,7 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
};
ID3D12DescriptorHeap* pHeap = default;
var hr = _device.NativeDevice->CreateDescriptorHeap(&heapDesc, __uuidof(pHeap), (void**)&pHeap);
var hr = _device.NativeDevice.Get()->CreateDescriptorHeap(&heapDesc, __uuidof(pHeap), (void**)&pHeap);
if (hr.FAILED)
{
return false;
@@ -291,11 +291,11 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
if (!_allocatedDescriptors.IsCreated)
{
_allocatedDescriptors = new UnsafeArray<bool>(numDescriptors, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
_allocatedDescriptors = new UnsafeArray<bool>(numDescriptors, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent, Misaki.HighPerformance.LowLevel.Buffer.AllocationOption.Clear);
}
else
{
_allocatedDescriptors.Resize(numDescriptors);
_allocatedDescriptors.Resize(numDescriptors, Misaki.HighPerformance.LowLevel.Buffer.AllocationOption.Clear);
}
if (ShaderVisible)
@@ -303,7 +303,7 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
ID3D12DescriptorHeap* pShaderVisibleHeap = default;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
hr = _device.NativeDevice->CreateDescriptorHeap(&heapDesc, __uuidof(pShaderVisibleHeap), (void**)&pShaderVisibleHeap);
hr = _device.NativeDevice.Get()->CreateDescriptorHeap(&heapDesc, __uuidof(pShaderVisibleHeap), (void**)&pShaderVisibleHeap);
if (hr.FAILED)
{
return false;
@@ -332,11 +332,11 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
return false;
}
_device.NativeDevice->CopyDescriptorsSimple((uint)oldSize, _startCpuHandle, oldHeap->GetCPUDescriptorHandleForHeapStart(), HeapType);
_device.NativeDevice.Get()->CopyDescriptorsSimple((uint)oldSize, _startCpuHandle, oldHeap->GetCPUDescriptorHandleForHeapStart(), HeapType);
if (_shaderVisibleHeap.Get() != null)
{
_device.NativeDevice->CopyDescriptorsSimple((uint)oldSize, _startCpuHandleShaderVisible, oldHeap->GetCPUDescriptorHandleForHeapStart(), HeapType);
_device.NativeDevice.Get()->CopyDescriptorsSimple((uint)oldSize, _startCpuHandleShaderVisible, oldHeap->GetCPUDescriptorHandleForHeapStart(), HeapType);
}
}
finally
@@ -350,12 +350,7 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
/// <inheritdoc />
public void Dispose()
{
#if DEBUG
if (NumAllocatedDescriptors > 0)
{
Debug.WriteLine($"Warning: Descriptor heap of type {HeapType} is being disposed with {NumAllocatedDescriptors} allocated descriptors.");
}
#endif
Debug.Assert(NumAllocatedDescriptors == 0);
_heap.Dispose();
_shaderVisibleHeap.Dispose();

View File

@@ -1,3 +1,5 @@
using Misaki.HighPerformance.LowLevel;
namespace Ghost.Graphics;
internal unsafe class GPUResourceLeakException : Exception
@@ -6,4 +8,12 @@ internal unsafe class GPUResourceLeakException : Exception
: base($"GPU resource leak detected! Resource '{name}' at address {(UIntPtr)address} has a reference count of {refCount} when it should be 0. This indicates that the resource was not properly released before being destroyed, which can lead to memory leaks and other issues. Please ensure that all references to this resource are released appropriately.")
{
}
public static void ThrowIfRefCountNonZero(uint refCount, void* address, string name)
{
if (refCount != 0)
{
throw new GPUResourceLeakException(refCount, address, name);
}
}
}

View File

@@ -166,7 +166,7 @@ public ref struct GraphicsPSODescriptor
}
}
public readonly record struct CBufferPropertyInfo
public readonly struct CBufferPropertyInfo
{
public string Name
{
@@ -184,7 +184,7 @@ public readonly record struct CBufferPropertyInfo
}
}
public readonly record struct CBufferInfo
public readonly struct CBufferInfo
{
public string Name
{

View File

@@ -24,9 +24,6 @@ public interface ICommandBuffer : IDisposable
get;
}
/// <summary>
/// Gets the name of the command buffer.
/// </summary>
string Name
{
get;

View File

@@ -14,7 +14,7 @@ public interface IShaderPipeline
}
}
public interface IPipelineLibrary
public interface IPipelineLibrary : IDisposable
{
/// <summary>
/// Load pipeline library from disk.

View File

@@ -5,7 +5,7 @@ using Ghost.Graphics.Core;
namespace Ghost.Graphics.RHI;
public interface IResourceAllocator
public interface IResourceAllocator : IDisposable
{
/// <summary>
/// Creates a texture resource

View File

@@ -11,7 +11,7 @@ public interface IResourceReleasable
void ReleaseResource(IResourceDatabase database);
}
public interface IResourceDatabase
public interface IResourceDatabase : IDisposable
{
/*
/// <summary>

View File

@@ -6,6 +6,7 @@ using Ghost.Graphics.RHI;
using Ghost.Graphics.Utilities;
using Ghost.SDL.Compiler;
using Misaki.HighPerformance.Image;
using Misaki.HighPerformance.Mathematics;
using Misaki.HighPerformance.Utilities;
using TerraFX.Interop.Windows;
@@ -57,42 +58,40 @@ internal unsafe class MeshRenderPass : IRenderPass
ctx.PipelineLibrary.CompilePSO(in psoDes, in compileResult.GetValueRef()).GetValueOrThrow();
}
MeshBuilder.CreateCube(0.75f, default, out var vertices, out var indices);
MeshBuilder.CreateCube(0.75f, default, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent, out var vertices, out var indices);
_mesh = ctx.CreateMesh(vertices, indices);
ctx.UpdateObjectData(_mesh, float4x4.identity);
ctx.UploadMesh(_mesh, true);
_shader = ctx.ResourceAllocator.CreateGraphicsShader(shaderDescriptor);
_material = ctx.ResourceAllocator.CreateMaterial(_shader);
var imageResults = new ImageResult[_textureFiles.Length];
//_textures = new Handle<Texture>[_textureFiles.Length];
//for (var i = 0; i < _textureFiles.Length; i++)
//{
// using var stream = File.OpenRead(_textureFiles[i]);
// using var imageData = ImageResult.FromStream(stream);
_textures = new Handle<Texture>[_textureFiles.Length];
for (var i = 0; i < _textureFiles.Length; i++)
{
using var stream = File.OpenRead(_textureFiles[i]);
using var imageData = ImageResult.FromStream(stream);
imageResults[i] = imageData;
// var desc = new TextureDesc
// {
// Width = imageData.Width,
// Height = imageData.Height,
// Dimension = TextureDimension.Texture2D,
// Format = TextureFormat.R8G8B8A8_UNorm,
// MipLevels = 1,
// Slice = 1,
// Usage = TextureUsage.ShaderResource,
// };
var desc = new TextureDesc
{
Width = imageData.Width,
Height = imageData.Height,
Dimension = TextureDimension.Texture2D,
Format = TextureFormat.R8G8B8A8_UNorm,
MipLevels = 1,
Slice = 1,
Usage = TextureUsage.ShaderResource,
};
_textures[i] = ctx.CreateTexture(ref desc);
ctx.UploadTexture(_textures[i], new Span<byte>(imageData.Data, (int)imageData.Size));
}
// _textures[i] = ctx.CreateTexture(ref desc);
// ctx.UploadTexture(_textures[i], new Span<byte>(imageData.Data, (int)imageData.Size));
//}
}
public void Execute(ref readonly RenderingContext ctx)
{
ctx.DispatchMesh(_mesh, _material, "Forward", 8);
ctx.DispatchMesh(_mesh, _material, "Forward", 3);
}
public void Cleanup(IResourceDatabase resourceDatabase)

View File

@@ -26,6 +26,7 @@ void MSMain(
out vertices PixelInput outVerts[3],
out indices uint3 outTris[1])
{
#if 0
// Fetch bindless buffers
ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[g_PerObjectData.vertexBuffer];
ByteAddressBuffer indexBuffer = ResourceDescriptorHeap[g_PerObjectData.indexBuffer];
@@ -45,8 +46,7 @@ void MSMain(
v.uv = asfloat(vertexBuffer.Load4(vertexOffset + 64));
SetMeshOutputCounts(3, 1);
v.position = mul(g_PerViewData.cameraMatrix, mul(g_PerObjectData.localToWorld, v.position));
//v.position = mul(g_PerViewData.cameraMatrix, mul(g_PerObjectData.localToWorld, v.position));
// Write vertex output
outVerts[vertexId].position = v.position;
@@ -58,15 +58,50 @@ void MSMain(
{
outTris[0] = uint3(0, 1, 2);
}
#else
// 1. Tell the hardware how much data to expect
SetMeshOutputCounts(3, 1);
// 2. Hardcoded Clip Space Positions (X, Y, Z, W)
// Visible range: X[-1, 1], Y[-1, 1], Z[0, 1]
// W must be 1.0
float4 positions[3] =
{
float4(0.0f, 0.5f, 0.5f, 1.0f), // Top
float4(0.5f, -0.5f, 0.5f, 1.0f), // Bottom Right
float4(-0.5f, -0.5f, 0.5f, 1.0f) // Bottom Left
};
float4 colors[3] =
{
float4(g_PerObjectData.vertexBuffer, 0.0f, 0.0f, 1.0f), // Red
float4(0.0f, g_PerObjectData.indexBuffer, 0.0f, 1.0f), // Green
float4(0.0f, 0.0f, 0.0f, 1.0f) // Blue
};
uint gtid = groupThreadID.x;
// 3. Write Vertex Data (Parallel)
outVerts[gtid].position = positions[gtid];
outVerts[gtid].color = colors[gtid];
// 4. Write Index Data (Only 1st thread needs to do this)
if (gtid == 0)
{
// Clockwise winding (Standard for DX12)
outTris[0] = uint3(0, 1, 2);
}
#endif
}
float4 PSMain(PixelInput input) : SV_TARGET
{
float4 color1 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture1, 0, input.uv.xy);
float4 color2 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture2, 0, input.uv.xy);
float4 color3 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture3, 0, input.uv.xy);
float4 color4 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture4, 0, input.uv.xy);
//float4 color1 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture1, 0, input.uv.xy);
//float4 color2 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture2, 0, input.uv.xy);
//float4 color3 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture3, 0, input.uv.xy);
//float4 color4 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture4, 0, input.uv.xy);
float4 blendedColor = (color1 + color2 + color3 + color4) * 0.25f;
return blendedColor * g_PerMaterialData.color;
//float4 blendedColor = (color1 + color2 + color3 + color4) * 0.25f;
return g_PerMaterialData.color + input.color;;
//return input.color;
}

View File

@@ -172,12 +172,8 @@ internal class RenderSystem : IRenderSystem
_isRunning = false;
_shutdownEvent.Set();
if (_renderThread.IsAlive)
{
_renderThread.Join();
}
}
public bool WaitForGPUReady(int timeOut = -1)
{
@@ -242,7 +238,10 @@ internal class RenderSystem : IRenderSystem
frameResource.Dispose();
}
_graphicsEngine.Dispose();
_shutdownEvent.Dispose();
_disposed = true;
GC.SuppressFinalize(this);
}
}

View File

@@ -115,7 +115,7 @@ internal sealed partial class DxcShaderCompiler
}
}
internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler, IDisposable
internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler
{
private UniquePtr<IDxcCompiler3> _compiler;
private UniquePtr<IDxcUtils> _utils;

View File

@@ -10,12 +10,12 @@ public unsafe static class MeshBuilder
/// <summary>
/// Creates a unit cube centered at the origin with size 1.
/// </summary>
public static void CreateCube(float size, Color128 color, out UnsafeList<Vertex> vertices, out UnsafeList<uint> indices)
public static void CreateCube(float size, Color128 color, Allocator allocator, out UnsafeList<Vertex> vertices, out UnsafeList<uint> indices)
{
var half = size * 0.5f;
vertices = new UnsafeList<Vertex>(24, Allocator.Persistent);
indices = new UnsafeList<uint>(36, Allocator.Persistent);
vertices = new UnsafeList<Vertex>(24, allocator);
indices = new UnsafeList<uint>(36, allocator);
var corners = new float4[]
{
@@ -71,13 +71,13 @@ public unsafe static class MeshBuilder
/// <summary>
/// Creates a plane on the XZ axis centered at the origin.
/// </summary>
public static void CreatePlane(float width, float depth, Color128 color, out UnsafeList<Vertex> vertices, out UnsafeList<uint> indices)
public static void CreatePlane(float width, float depth, Color128 color, Allocator allocator, out UnsafeList<Vertex> vertices, out UnsafeList<uint> indices)
{
var hw = width * 0.5f;
var hd = depth * 0.5f;
vertices = new UnsafeList<Vertex>(4, Allocator.Persistent);
indices = new UnsafeList<uint>(6, Allocator.Persistent);
vertices = new UnsafeList<Vertex>(4, allocator);
indices = new UnsafeList<uint>(6, allocator);
vertices.Add(new Vertex()
{
@@ -129,10 +129,10 @@ public unsafe static class MeshBuilder
/// <summary>
/// Creates a UV sphere centered at the origin.
/// </summary>
public static void CreateSphere(int latitudeSegments, int longitudeSegments, float radius, Color128 color, out UnsafeList<Vertex> vertices, out UnsafeList<uint> indices)
public static void CreateSphere(int latitudeSegments, int longitudeSegments, float radius, Color128 color, Allocator allocator, out UnsafeList<Vertex> vertices, out UnsafeList<uint> indices)
{
vertices = new UnsafeList<Vertex>((latitudeSegments + 1) * (longitudeSegments + 1), Allocator.Persistent);
indices = new UnsafeList<uint>(latitudeSegments * longitudeSegments * 6, Allocator.Persistent);
vertices = new UnsafeList<Vertex>((latitudeSegments + 1) * (longitudeSegments + 1), allocator);
indices = new UnsafeList<uint>(latitudeSegments * longitudeSegments * 6, allocator);
// Vertices
for (var lat = 0; lat <= latitudeSegments; lat++)

View File

@@ -9,17 +9,17 @@ shader "MyShader/Standard"
tex2d_b texture4 = tex2d_b(normal);
}
pipeline
{
ztest = less_equal;
zwrite = on;
cull = back;
blend = opaque;
color_mask = 0;
}
pass "Forward"
{
pipeline
{
ztest = disable;
zwrite = off;
cull = off;
blend = opaque;
color_mask = 15;
}
ms("F:/csharp/GhostEngine/Ghost.Graphics/RenderPasses/ShaderCode.hlsl", "MSMain");
ps("F:/csharp/GhostEngine/Ghost.Graphics/RenderPasses/ShaderCode.hlsl", "PSMain");
}

View File

@@ -211,6 +211,7 @@ internal static class SDLCompiler
var fullPass = new FullPassDescriptor
{
uniqueIdentifier = GetPassUniqueId(semantics, pass),
name = pass.name,
taskShader = pass.taskShader,
meshShader = pass.meshShader,
pixelShader = pass.pixelShader,