Refactor error handling and improve type safety

Refactored error handling across the codebase by replacing exceptions with `Result`-based error handling for better robustness and consistency.

- Updated `ResultExtensions` to use `EqualityComparer` for comparisons.
- Enhanced `RenderingContext` with `GetValueOrThrow` for resource validation and added type constraints for texture methods.
- Introduced `CommandError` and `RecordError` in `D3D12CommandBuffer` for improved error tracking.
- Refactored `D3D12ResourceDatabase` to use `Result` objects for resource queries.
- Updated `ICommandBuffer` and `IResourceDatabase` interfaces to return `Result` objects.
- Improved type safety by replacing `int` with `uint` where appropriate.
- Simplified texture handling in `MeshRenderPass` with new `CreateTexture` logic.
- Cleaned up project files by removing unused and redundant entries.

These changes enhance code maintainability, improve error reporting, and ensure type safety throughout the project.
This commit is contained in:
2025-11-30 19:06:31 +09:00
parent 0ec318a9ab
commit 85280c746d
15 changed files with 244 additions and 126 deletions

View File

@@ -178,7 +178,7 @@ public static class ResultExtensions
public static T GetValueOrThrow<T, S>(this Result<T, S> result, S expect) public static T GetValueOrThrow<T, S>(this Result<T, S> result, S expect)
where S : Enum where S : Enum
{ {
if (result.Status?.Equals(expect) ?? false) if (!EqualityComparer<S>.Default.Equals(result.Status, expect))
{ {
throw new InvalidOperationException($"Operation failed: expected status {expect}, but got {result.Status}"); throw new InvalidOperationException($"Operation failed: expected status {expect}, but got {result.Status}");
} }

View File

@@ -58,18 +58,6 @@
<ProjectReference Include="..\Ghost.Test.Core\Ghost.Test.Core.csproj" /> <ProjectReference Include="..\Ghost.Test.Core\Ghost.Test.Core.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Page Update="Windows\DebugOutputWindow.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Controls\DebugConsole.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<!-- <!--
Defining the "HasPackageAndPublishMenuAddedByProject" property here allows the Solution Defining the "HasPackageAndPublishMenuAddedByProject" property here allows the Solution
Explorer "Package and Publish" context menu entry to be enabled for this project even if Explorer "Package and Publish" context menu entry to be enabled for this project even if
@@ -96,13 +84,4 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
<DebugType>embedded</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DebugType>embedded</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<DebugType>embedded</DebugType>
</PropertyGroup>
</Project> </Project>

View File

@@ -102,8 +102,11 @@ public readonly unsafe ref struct RenderingContext
public void UploadMesh(Handle<Mesh> mesh, bool markMeshStatic) public void UploadMesh(Handle<Mesh> mesh, bool markMeshStatic)
{ {
ref var meshData = ref ResourceDatabase.GetMeshReference(mesh); ref var meshData = ref ResourceDatabase.GetMeshReference(mesh);
var vertexState = ResourceDatabase.GetResourceState(meshData.VertexBuffer.AsResource()); var vertexState = ResourceDatabase.GetResourceState(meshData.VertexBuffer.AsResource())
var indexState = ResourceDatabase.GetResourceState(meshData.IndexBuffer.AsResource()); .GetValueOrThrow(ResultStatus.Success);
var indexState = ResourceDatabase.GetResourceState(meshData.IndexBuffer.AsResource())
.GetValueOrThrow(ResultStatus.Success);
var needVertexTransition = vertexState != ResourceState.CopyDest; var needVertexTransition = vertexState != ResourceState.CopyDest;
var needIndexTransition = indexState != ResourceState.CopyDest; var needIndexTransition = indexState != ResourceState.CopyDest;
@@ -149,7 +152,9 @@ public readonly unsafe ref struct RenderingContext
}; };
var bufferHandle = meshData.ObjectDataBuffer.AsResource(); var bufferHandle = meshData.ObjectDataBuffer.AsResource();
var state = ResourceDatabase.GetResourceState(bufferHandle); var state = ResourceDatabase.GetResourceState(bufferHandle)
.GetValueOrThrow(ResultStatus.Success);
var needTransition = state != ResourceState.CopyDest; var needTransition = state != ResourceState.CopyDest;
if (needTransition) if (needTransition)
{ {
@@ -164,17 +169,31 @@ public readonly unsafe ref struct RenderingContext
} }
} }
public Handle<Texture> CreateTexture(ref readonly TextureDesc desc, bool tempResource = false) public Handle<Texture> CreateTexture<T>(ref readonly TextureDesc desc, ReadOnlySpan<T> data, bool tempResource = false)
where T : unmanaged
{ {
return ResourceAllocator.CreateTexture(in desc, tempResource); var handle = ResourceAllocator.CreateTexture(in desc, tempResource);
UploadTexture(handle, data);
return handle;
} }
public void UploadTexture(Handle<Texture> texture, ReadOnlySpan<byte> data) public void UploadTexture<T>(Handle<Texture> texture, ReadOnlySpan<T> data)
where T : unmanaged
{ {
var desc = ResourceDatabase.GetResourceDescription(texture.AsResource()); 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 _); .GetValueOrThrow(ResultStatus.Success);
if (data.Length * sizeof(T) != desc.TextureDescription.GetTotalBytes())
{
throw new ArgumentException("Data size does not match texture size.");
}
desc.TextureDescription.Format.GetSurfaceInfo(desc.TextureDescription.Width, desc.TextureDescription.Height, out var rowPitch, out var slicePitch, out _);
var sateBefore = ResourceDatabase.GetResourceState(texture.AsResource())
.GetValueOrThrow(ResultStatus.Success);
var sateBefore = ResourceDatabase.GetResourceState(texture.AsResource());
var needTransition = sateBefore != ResourceState.CopyDest; var needTransition = sateBefore != ResourceState.CopyDest;
if (needTransition) if (needTransition)
@@ -182,7 +201,7 @@ public readonly unsafe ref struct RenderingContext
_directCmd.ResourceBarrier(texture.AsResource(), sateBefore, ResourceState.CopyDest); _directCmd.ResourceBarrier(texture.AsResource(), sateBefore, ResourceState.CopyDest);
} }
fixed (byte* pData = data) fixed (T* pData = data)
{ {
var subresourceData = new SubResourceData var subresourceData = new SubResourceData
{ {

View File

@@ -5,6 +5,7 @@ using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.LowLevel.Utilities;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows; using TerraFX.Interop.Windows;
@@ -26,6 +27,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
private readonly D3D12DescriptorAllocator _descriptorAllocator; private readonly D3D12DescriptorAllocator _descriptorAllocator;
private readonly CommandBufferType _type; private readonly CommandBufferType _type;
private CommandError _lastError;
private ushort _commandCount; private ushort _commandCount;
private bool _isRecording; private bool _isRecording;
private bool _disposed; private bool _disposed;
@@ -124,6 +126,25 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_commandCount++; _commandCount++;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#if DEBUG
[DoesNotReturn]
#endif
private void RecordError(string cmdName, ResultStatus status)
{
#if DEBUG
throw new InvalidOperationException($"Error at {cmdName} with {status}");
#else
_lastError = new CommandError
{
CommandName = cmdName,
CommandIndex = _commandCount,
Status = status
};
#endif
}
public void Begin() public void Begin()
{ {
void ResetCommandList() void ResetCommandList()
@@ -154,13 +175,20 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_isRecording = true; _isRecording = true;
} }
public void End() public Result End()
{ {
ThrowIfDisposed(); ThrowIfDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
_commandList.Get()->Close(); _commandList.Get()->Close();
_isRecording = false; _isRecording = false;
if (_lastError.Status != ResultStatus.Success)
{
return Result.Failure($"Command buffer ended with errors at {_lastError.CommandIndex}, command '{_lastError.CommandName}': {_lastError.Status}");
}
return Result.Success();
} }
public void SetScissorRect(RectDesc rect) public void SetScissorRect(RectDesc rect)
@@ -182,7 +210,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
var count = 0u; var count = 0u;
var pBarriers = stackalloc D3D12_RESOURCE_BARRIER[barrierDescs.Length]; var pBarriers = stackalloc D3D12_RESOURCE_BARRIER[barrierDescs.Length];
for (int i = 0; i < barrierDescs.Length; i++) for (var i = 0; i < barrierDescs.Length; i++)
{ {
var desc = barrierDescs[i]; var desc = barrierDescs[i];
if (desc.StateBefore == desc.StateAfter) if (desc.StateBefore == desc.StateAfter)
@@ -192,23 +220,32 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
if (!desc.Resource.IsValid) if (!desc.Resource.IsValid)
{ {
throw new ArgumentException($"Barrier resource at index {i} is not a valid resource handle"); RecordError(nameof(ResourceBarrier), ResultStatus.InvalidArgument);
continue;
} }
ref var resourceRecord = ref _resourceDatabase.GetResourceRecord(desc.Resource.AsResource()); var recordResult = _resourceDatabase.GetResourceRecord(desc.Resource);
if (resourceRecord.state != desc.StateBefore) if (recordResult.Status != ResultStatus.Success)
{ {
throw new InvalidOperationException($"Resource state mismatch: expected {desc.StateBefore}, actual {resourceRecord.state}"); RecordError(nameof(ResourceBarrier), recordResult.Status);
continue;
} }
var barrier = D3D12_RESOURCE_BARRIER.InitTransition(resourceRecord.ResourcePtr, ref var record = ref recordResult.Value;
if (record.state != desc.StateBefore)
{
RecordError(nameof(ResourceBarrier), ResultStatus.InvalidState);
continue;
}
var barrier = D3D12_RESOURCE_BARRIER.InitTransition(record.ResourcePtr,
desc.StateBefore.ToD3D12States(), desc.StateAfter.ToD3D12States()); desc.StateBefore.ToD3D12States(), desc.StateAfter.ToD3D12States());
pBarriers[count] = barrier; pBarriers[count] = barrier;
count++; count++;
// Update the resource state in the database // Update the resource state in the database
resourceRecord.state = desc.StateAfter; record.state = desc.StateAfter;
} }
_commandList.Get()->ResourceBarrier(count, pBarriers); _commandList.Get()->ResourceBarrier(count, pBarriers);
@@ -216,28 +253,49 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
public void ResourceBarrier(Handle<GPUResource> resource, ResourceState stateBefore, ResourceState stateAfter) public void ResourceBarrier(Handle<GPUResource> resource, ResourceState stateBefore, ResourceState stateAfter)
{ {
ThrowIfDisposed();
ThrowIfNotRecording();
IncrementCommandCount();
if (stateBefore == stateAfter) if (stateBefore == stateAfter)
{ {
return; return;
} }
ref var resourceRecord = ref _resourceDatabase.GetResourceRecord(resource); var recordResult = _resourceDatabase.GetResourceRecord(resource);
if (resourceRecord.state != stateBefore) if (recordResult.Status != ResultStatus.Success)
{ {
throw new InvalidOperationException($"Resource state mismatch: expected {stateBefore}, actual {resourceRecord.state}"); RecordError(nameof(ResourceBarrier), recordResult.Status);
return;
} }
var barrier = D3D12_RESOURCE_BARRIER.InitTransition(resourceRecord.ResourcePtr, ref var record = ref recordResult.Value;
var barrier = D3D12_RESOURCE_BARRIER.InitTransition(record.ResourcePtr,
stateBefore.ToD3D12States(), stateAfter.ToD3D12States()); stateBefore.ToD3D12States(), stateAfter.ToD3D12States());
_commandList.Get()->ResourceBarrier(1, &barrier); _commandList.Get()->ResourceBarrier(1, &barrier);
resourceRecord.state = stateAfter; record.state = stateAfter;
} }
public void ResourceBarrier(Handle<GPUResource> resource, ResourceState stateAfter) public void ResourceBarrier(Handle<GPUResource> resource, ResourceState stateAfter)
{ {
var stateBefore = _resourceDatabase.GetResourceState(resource); ThrowIfDisposed();
ResourceBarrier(resource, stateBefore, stateAfter); ThrowIfNotRecording();
IncrementCommandCount();
var recordResult = _resourceDatabase.GetResourceRecord(resource);
if (recordResult.Status != ResultStatus.Success)
{
RecordError(nameof(ResourceBarrier), recordResult.Status);
return;
}
ref var record = ref recordResult.Value;
var barrier = D3D12_RESOURCE_BARRIER.InitTransition(record.ResourcePtr,
record.state.ToD3D12States(), stateAfter.ToD3D12States());
_commandList.Get()->ResourceBarrier(1, &barrier);
record.state = stateAfter;
} }
public void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget) public void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget)
@@ -247,25 +305,44 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
IncrementCommandCount(); IncrementCommandCount();
var pRtvHandles = stackalloc D3D12_CPU_DESCRIPTOR_HANDLE[renderTargets.Length]; var pRtvHandles = stackalloc D3D12_CPU_DESCRIPTOR_HANDLE[renderTargets.Length];
var rtvCount = 0u;
for (var i = 0; i < renderTargets.Length; i++) for (var i = 0; i < renderTargets.Length; i++)
{ {
var handle = renderTargets[i]; var handle = renderTargets[i];
if (!handle.IsValid) if (!handle.IsValid)
{ {
throw new ArgumentException($"Render target at index {i} is not a valid texture handle"); RecordError(nameof(SetRenderTargets), ResultStatus.InvalidArgument);
continue;
} }
var descriptor = _resourceDatabase.GetResourceRecord(handle.AsResource()).viewGroup; var recordResult = _resourceDatabase.GetResourceRecord(handle.AsResource());
pRtvHandles[i] = _descriptorAllocator.GetCpuHandle(descriptor.rtv); if (recordResult.Status != ResultStatus.Success)
{
RecordError(nameof(SetRenderTargets), recordResult.Status);
continue;
}
var viewGroup = recordResult.Value.viewGroup;
pRtvHandles[i] = _descriptorAllocator.GetCpuHandle(viewGroup.rtv);
rtvCount++;
} }
var pDsvHandle = stackalloc D3D12_CPU_DESCRIPTOR_HANDLE[depthTarget.IsValid ? 1 : 0]; var pDsvHandle = stackalloc D3D12_CPU_DESCRIPTOR_HANDLE[depthTarget.IsValid ? 1 : 0];
if (pDsvHandle != null) if (pDsvHandle != null)
{ {
pDsvHandle[0] = _descriptorAllocator.GetCpuHandle(_resourceDatabase.GetResourceRecord(depthTarget.AsResource()).viewGroup.dsv); var recordResult = _resourceDatabase.GetResourceRecord(depthTarget.AsResource());
if (recordResult.Status != ResultStatus.Success)
{
RecordError(nameof(SetRenderTargets), recordResult.Status);
return;
} }
_commandList.Get()->OMSetRenderTargets((uint)renderTargets.Length, pRtvHandles, FALSE, pDsvHandle); var viewGroup = recordResult.Value.viewGroup;
pDsvHandle[0] = _descriptorAllocator.GetCpuHandle(viewGroup.dsv);
}
_commandList.Get()->OMSetRenderTargets(rtvCount, pRtvHandles, FALSE, pDsvHandle);
} }
public void BeginRenderPass(ReadOnlySpan<PassRenderTargetDesc> rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false) public void BeginRenderPass(ReadOnlySpan<PassRenderTargetDesc> rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false)
@@ -280,11 +357,21 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
var rtDesc = rtDescs[i]; var rtDesc = rtDescs[i];
if (!rtDesc.Texture.IsValid) if (!rtDesc.Texture.IsValid)
{ {
throw new ArgumentException($"Render target at index {i} is not a valid texture handle"); RecordError(nameof(BeginRenderPass), ResultStatus.InvalidArgument);
continue;
} }
var resourceInfo = _resourceDatabase.GetResourceRecord(rtDesc.Texture.AsResource()); var recordResult = _resourceDatabase.GetResourceRecord(rtDesc.Texture.AsResource());
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceInfo.viewGroup.rtv); if (recordResult.Status != ResultStatus.Success)
{
RecordError(nameof(BeginRenderPass), recordResult.Status);
continue;
}
var record = recordResult.Value;
var cpuHandle = _descriptorAllocator.GetCpuHandle(record.viewGroup.rtv);
var format = record.desc.TextureDescription.Format.ToDXGIFormat();
var clearColor = rtDesc.ClearColor;
var desc = new D3D12_RENDER_PASS_RENDER_TARGET_DESC var desc = new D3D12_RENDER_PASS_RENDER_TARGET_DESC
{ {
@@ -294,10 +381,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
Type = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR, Type = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR,
Clear = new D3D12_RENDER_PASS_BEGINNING_ACCESS_CLEAR_PARAMETERS Clear = new D3D12_RENDER_PASS_BEGINNING_ACCESS_CLEAR_PARAMETERS
{ {
ClearValue = new D3D12_CLEAR_VALUE ClearValue = new D3D12_CLEAR_VALUE(format, (float*)&clearColor)
{
Format = resourceInfo.desc.TextureDescription.Format.ToDXGIFormat(),
}
} }
}, },
EndingAccess = new D3D12_RENDER_PASS_ENDING_ACCESS EndingAccess = new D3D12_RENDER_PASS_ENDING_ACCESS
@@ -306,19 +390,22 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
} }
}; };
desc.BeginningAccess.Clear.ClearValue.Color[0] = rtDesc.ClearColor.r;
desc.BeginningAccess.Clear.ClearValue.Color[1] = rtDesc.ClearColor.g;
desc.BeginningAccess.Clear.ClearValue.Color[2] = rtDesc.ClearColor.b;
desc.BeginningAccess.Clear.ClearValue.Color[3] = rtDesc.ClearColor.a;
pRtvDescs[i] = desc; pRtvDescs[i] = desc;
} }
var pDsvDesc = stackalloc D3D12_RENDER_PASS_DEPTH_STENCIL_DESC[depthDesc.Texture.IsValid ? 1 : 0]; var pDsvDesc = stackalloc D3D12_RENDER_PASS_DEPTH_STENCIL_DESC[depthDesc.Texture.IsValid ? 1 : 0];
if (pDsvDesc != null) if (pDsvDesc != null)
{ {
var resourceInfo = _resourceDatabase.GetResourceRecord(depthDesc.Texture.AsResource()); var recordResult = _resourceDatabase.GetResourceRecord(depthDesc.Texture.AsResource());
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceInfo.viewGroup.dsv); if (recordResult.Status != ResultStatus.Success)
{
RecordError(nameof(BeginRenderPass), recordResult.Status);
return;
}
var record = recordResult.Value;
var cpuHandle = _descriptorAllocator.GetCpuHandle(record.viewGroup.dsv);
var format = record.desc.TextureDescription.Format.ToDXGIFormat();
var desc = new D3D12_RENDER_PASS_DEPTH_STENCIL_DESC var desc = new D3D12_RENDER_PASS_DEPTH_STENCIL_DESC
{ {
@@ -328,15 +415,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
Type = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR, Type = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR,
Clear = new D3D12_RENDER_PASS_BEGINNING_ACCESS_CLEAR_PARAMETERS Clear = new D3D12_RENDER_PASS_BEGINNING_ACCESS_CLEAR_PARAMETERS
{ {
ClearValue = new D3D12_CLEAR_VALUE ClearValue = new D3D12_CLEAR_VALUE(format, depthDesc.ClearDepth, depthDesc.ClearStencil)
{
Format = resourceInfo.desc.TextureDescription.Format.ToDXGIFormat(),
DepthStencil = new D3D12_DEPTH_STENCIL_VALUE
{
Depth = depthDesc.ClearDepth,
Stencil = depthDesc.ClearStencil
}
}
} }
} }
}; };
@@ -402,12 +481,19 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
ThrowIfNotRecording(); ThrowIfNotRecording();
IncrementCommandCount(); IncrementCommandCount();
var resource = _resourceDatabase.GetResource(buffer.AsResource()); var recordResult = _resourceDatabase.GetResourceRecord(buffer.AsResource());
if (recordResult.Status != ResultStatus.Success)
{
RecordError(nameof(BeginRenderPass), recordResult.Status);
return;
}
var record = recordResult.Value;
var vbView = new D3D12_VERTEX_BUFFER_VIEW var vbView = new D3D12_VERTEX_BUFFER_VIEW
{ {
BufferLocation = resource.Get()->GetGPUVirtualAddress() + offset, BufferLocation = record.ResourcePtr.Get()->GetGPUVirtualAddress() + offset,
SizeInBytes = (uint)(resource.Get()->GetDesc().Width - offset), SizeInBytes = (uint)(record.ResourcePtr.Get()->GetDesc().Width - offset),
StrideInBytes = _resourceDatabase.GetResourceDescription(buffer.AsResource()).BufferDescription.Stride StrideInBytes = record.desc.BufferDescription.Stride
}; };
_commandList.Get()->IASetVertexBuffers(slot, 1, &vbView); _commandList.Get()->IASetVertexBuffers(slot, 1, &vbView);
@@ -541,8 +627,8 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
d3d12Subresources[i] = new D3D12_SUBRESOURCE_DATA d3d12Subresources[i] = new D3D12_SUBRESOURCE_DATA
{ {
pData = subresources[i].pData, pData = subresources[i].pData,
RowPitch = subresources[i].rowPitch, RowPitch = (nint)subresources[i].rowPitch,
SlicePitch = subresources[i].slicePitch SlicePitch = (nint)subresources[i].slicePitch
}; };
} }
@@ -566,7 +652,8 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
var pSrcResource = _resourceDatabase.GetResource(src.AsResource()); var pSrcResource = _resourceDatabase.GetResource(src.AsResource());
if (pSrcResource == null || pDestResource == null) if (pSrcResource == null || pDestResource == null)
{ {
throw new ArgumentException("Source or destination buffer is not valid"); RecordError(nameof(CopyBuffer), ResultStatus.InvalidArgument);
return;
} }
if (numBytes == 0) if (numBytes == 0)

View File

@@ -1,3 +1,4 @@
using Ghost.Core;
using Ghost.Graphics.Contracts; using Ghost.Graphics.Contracts;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Ghost.Graphics.Utilities; using Ghost.Graphics.Utilities;
@@ -134,7 +135,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
{ {
ThrowIfDisposed(); ThrowIfDisposed();
_copyCommandBuffer.End(); _copyCommandBuffer.End().ThrowIfFailed();
_resourceAllocator.ReleaseTempResources(); _resourceAllocator.ReleaseTempResources();
_descriptorAllocator.ResetCbvSrvUavDynamicHeap(); _descriptorAllocator.ResetCbvSrvUavDynamicHeap();
_descriptorAllocator.ResetDSVDynamicHeap(); _descriptorAllocator.ResetDSVDynamicHeap();

View File

@@ -198,7 +198,7 @@ internal class D3D12Renderer : IRenderer
// BlitToDestination(_renderTarget, backBufferRT, frame.commandBuffer); // BlitToDestination(_renderTarget, backBufferRT, frame.commandBuffer);
// } // }
frame.commandBuffer.End(); frame.commandBuffer.End().ThrowIfFailed();
_graphicsEngine.Device.GraphicsQueue.Submit(frame.commandBuffer); _graphicsEngine.Device.GraphicsQueue.Submit(frame.commandBuffer);
_swapChain?.Present(); _swapChain?.Present();

View File

@@ -10,6 +10,7 @@ using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows; using TerraFX.Interop.Windows;
using static TerraFX.Aliases.D3D12_Alias; using static TerraFX.Aliases.D3D12_Alias;
using static TerraFX.Aliases.D3D12MA_Alias; using static TerraFX.Aliases.D3D12MA_Alias;
using static TerraFX.Aliases.DXGI_Alias; using static TerraFX.Aliases.DXGI_Alias;

View File

@@ -6,6 +6,7 @@ using Misaki.HighPerformance.Collections;
using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;
@@ -13,7 +14,7 @@ namespace Ghost.Graphics.D3D12;
internal class D3D12ResourceDatabase : IResourceDatabase internal class D3D12ResourceDatabase : IResourceDatabase
{ {
internal unsafe struct ResourceRecord internal unsafe record struct ResourceRecord
{ {
[StructLayout(LayoutKind.Explicit)] [StructLayout(LayoutKind.Explicit)]
public struct ResourceUnion public struct ResourceUnion
@@ -171,17 +172,17 @@ internal class D3D12ResourceDatabase : IResourceDatabase
return _resources.Contains(handle.id, handle.generation); return _resources.Contains(handle.id, handle.generation);
} }
public ref ResourceRecord GetResourceRecord(Handle<GPUResource> handle) public RefResult<ResourceRecord, ResultStatus> GetResourceRecord(Handle<GPUResource> handle)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
ref var info = ref _resources.GetElementReferenceAt(handle.id, handle.generation, out var exist); ref var info = ref _resources.GetElementReferenceAt(handle.id, handle.generation, out var exist);
if (!exist) if (!exist)
{ {
throw new KeyNotFoundException($"Resource with handle {handle} not found."); return Result.CreateRef(ref Unsafe.NullRef<ResourceRecord>(), ResultStatus.NotFound);
} }
return ref info; return Result.CreateRef(ref info, ResultStatus.Success);
} }
public ref ResourceRecord GetResourceRecord(Handle<GPUResource> handle, out bool exist) public ref ResourceRecord GetResourceRecord(Handle<GPUResource> handle, out bool exist)
@@ -190,23 +191,30 @@ internal class D3D12ResourceDatabase : IResourceDatabase
return ref _resources.GetElementReferenceAt(handle.id, handle.generation, out exist); return ref _resources.GetElementReferenceAt(handle.id, handle.generation, out exist);
} }
public unsafe SharedPtr<ID3D12Resource> GetResource(Handle<GPUResource> handle) public SharedPtr<ID3D12Resource> GetResource(Handle<GPUResource> handle)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
ref var info = ref GetResourceRecord(handle); var r = GetResourceRecord(handle);
if (!info.Allocated) if (r.Status != ResultStatus.Success)
{ {
return null; return null;
} }
return info.ResourcePtr; return r.Value.ResourcePtr;
} }
public ResourceState GetResourceState(Handle<GPUResource> handle) public Result<ResourceState, ResultStatus> GetResourceState(Handle<GPUResource> handle)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
return GetResourceRecord(handle).state;
var r = GetResourceRecord(handle);
if (r.Status != ResultStatus.Success)
{
return Result.Create(ResourceState.Common, r.Status);
}
return Result.Create(r.Value.state, ResultStatus.Success);
} }
public void SetResourceState(Handle<GPUResource> handle, ResourceState state) public void SetResourceState(Handle<GPUResource> handle, ResourceState state)
@@ -222,10 +230,18 @@ internal class D3D12ResourceDatabase : IResourceDatabase
info.state = state; info.state = state;
} }
public ResourceDesc GetResourceDescription(Handle<GPUResource> handle) public Result<ResourceDesc, ResultStatus> GetResourceDescription(Handle<GPUResource> handle)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
return GetResourceRecord(handle).desc;
var r = GetResourceRecord(handle);
if (r.Status != ResultStatus.Success)
{
return Result.Create(default(ResourceDesc), r.Status);
}
return Result.Create(r.Value.desc, ResultStatus.Success);
} }
public Result<uint, ResultStatus> GetBindlessIndex(Handle<GPUResource> handle) public Result<uint, ResultStatus> GetBindlessIndex(Handle<GPUResource> handle)
@@ -238,7 +254,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
return Result.Create(0u, ResultStatus.NotFound); return Result.Create(0u, ResultStatus.NotFound);
} }
return Result.Create((uint)info.viewGroup.srv.value, ResultStatus.NotFound); return Result.Create((uint)info.viewGroup.srv.value, ResultStatus.Success);
} }
public string? GetResourceName(Handle<GPUResource> handle) public string? GetResourceName(Handle<GPUResource> handle)

View File

@@ -186,16 +186,11 @@ internal unsafe class D3D12SwapChain : ISwapChain
_resourceDatabase.ReleaseResource(_backBuffers[i].AsResource()); _resourceDatabase.ReleaseResource(_backBuffers[i].AsResource());
} }
// Resize the swap chain ThrowIfFailed(_swapChain.Get()->ResizeBuffers(BufferCount, width, height, DXGI_FORMAT_B8G8R8A8_UNORM, (uint)DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING));
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.");
}
Width = width; Width = width;
Height = height; Height = height;
// Recreate back buffers
CreateBackBuffers(); CreateBackBuffers();
} }

View File

@@ -5,19 +5,15 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<LangVersion>preview</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<IsAotCompatible>True</IsAotCompatible>
<IsTrimmable>True</IsTrimmable>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<IsAotCompatible>True</IsAotCompatible> <IsAotCompatible>True</IsAotCompatible>
<IsTrimmable>True</IsTrimmable> <IsTrimmable>True</IsTrimmable>
<DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -263,8 +263,8 @@ public struct RectDesc
public struct SubResourceData public struct SubResourceData
{ {
public unsafe void* pData; public unsafe void* pData;
public nint rowPitch; public uint rowPitch;
public nint slicePitch; public uint slicePitch;
} }
public struct PassRenderTargetDesc public struct PassRenderTargetDesc
@@ -303,7 +303,7 @@ public struct PassDepthStencilDesc
public struct BarrierDesc public struct BarrierDesc
{ {
public Handle<GraphicsBuffer> Resource public Handle<GPUResource> Resource
{ {
get; set; get; set;
} }
@@ -681,6 +681,24 @@ public struct BufferDesc
} }
} }
public struct CommandError
{
public int CommandIndex
{
get; set;
}
public string CommandName
{
get; set;
}
public ResultStatus Status
{
get; set;
}
}
/// <summary> /// <summary>
/// Swap chain description /// Swap chain description
/// </summary> /// </summary>

View File

@@ -38,7 +38,7 @@ public interface ICommandBuffer : IDisposable
/// <summary> /// <summary>
/// Ends recording commands and prepares for submission /// Ends recording commands and prepares for submission
/// </summary> /// </summary>
void End(); Result End();
/// <summary> /// <summary>
/// Sets the viewport for rendering /// Sets the viewport for rendering

View File

@@ -39,7 +39,7 @@ public interface IResourceDatabase : IDisposable
/// </summary> /// </summary>
/// <param name="handle">The handle that uniquely identifies the resource whose state is to be retrieved.</param> /// <param name="handle">The handle that uniquely identifies the resource whose state is to be retrieved.</param>
/// <returns>A ResourceState Value representing the current state of the resource associated with the specified handle.</returns> /// <returns>A ResourceState Value representing the current state of the resource associated with the specified handle.</returns>
ResourceState GetResourceState(Handle<GPUResource> handle); Result<ResourceState, ResultStatus> GetResourceState(Handle<GPUResource> handle);
/// <summary> /// <summary>
/// Sets the state of the specified resource handle to the given Value. /// Sets the state of the specified resource handle to the given Value.
@@ -53,7 +53,7 @@ public interface IResourceDatabase : IDisposable
/// </summary> /// </summary>
/// <param name="handle">A handle that identifies the GPU resource for which to obtain the description. Must reference a valid resource.</param> /// <param name="handle">A handle that identifies the GPU resource for which to obtain the description. Must reference a valid resource.</param>
/// <returns>A ResourceDesc structure containing details about the specified GPU resource.</returns> /// <returns>A ResourceDesc structure containing details about the specified GPU resource.</returns>
ResourceDesc GetResourceDescription(Handle<GPUResource> handle); Result<ResourceDesc, ResultStatus> GetResourceDescription(Handle<GPUResource> handle);
/// <summary> /// <summary>
/// Retrieves the bindless index associated with the specified GPU resource handle. /// Retrieves the bindless index associated with the specified GPU resource handle.

View File

@@ -2,7 +2,7 @@ namespace Ghost.Graphics.RHI;
internal static class RHIUtility internal static class RHIUtility
{ {
public static int GetBytesPerPixel(this TextureFormat format) public static uint GetBytesPerPixel(this TextureFormat format)
{ {
return format switch return format switch
{ {
@@ -16,12 +16,17 @@ internal static class RHIUtility
}; };
} }
public static void GetSurfaceInfo(this TextureFormat format, int width, int height, out int rowPitch, out int slicePitch, out int rowCount) public static uint GetTotalBytes(this TextureDesc desc)
{
return desc.Format.GetBytesPerPixel() * desc.Width * desc.Height * desc.Slice;
}
public static void GetSurfaceInfo(this TextureFormat format, uint width, uint height, out uint rowPitch, out uint slicePitch, out uint rowCount)
{ {
var bc = false; var bc = false;
var packed = false; var packed = false;
var planar = false; var planar = false;
var bpe = 0; var bpe = 0u;
//switch (Format) //switch (Format)
//{ //{
@@ -86,31 +91,33 @@ internal static class RHIUtility
if (bc) if (bc)
{ {
var numBlocksWide = 0; var numBlocksWide = 0u;
if (width > 0) if (width > 0)
{ {
numBlocksWide = Math.Max(1, (width + 3) / 4); numBlocksWide = Math.Max(1u, (width + 3) / 4u);
} }
var numBlocksHigh = 0;
var numBlocksHigh = 0u;
if (height > 0) if (height > 0)
{ {
numBlocksHigh = Math.Max(1, (height + 3) / 4); numBlocksHigh = Math.Max(1u, (height + 3) / 4u);
} }
rowPitch = numBlocksWide * bpe; rowPitch = numBlocksWide * bpe;
rowCount = numBlocksHigh; rowCount = numBlocksHigh;
slicePitch = rowPitch * numBlocksHigh; slicePitch = rowPitch * numBlocksHigh;
} }
else if (packed) else if (packed)
{ {
rowPitch = ((width + 1) >> 1) * bpe; rowPitch = ((width + 1u) >> 1) * bpe;
rowCount = height; rowCount = height;
slicePitch = rowPitch * height; slicePitch = rowPitch * height;
} }
else if (planar) else if (planar)
{ {
rowPitch = ((width + 1) >> 1) * bpe; rowPitch = ((width + 1u) >> 1) * bpe;
slicePitch = (rowPitch * height) + ((rowPitch * height + 1) >> 1); slicePitch = (rowPitch * height) + ((rowPitch * height + 1) >> 1);
rowCount = (int)(height + ((height + 1u) >> 1)); rowCount = height + ((height + 1u) >> 1);
} }
else else
{ {

View File

@@ -103,8 +103,7 @@ internal class MeshRenderPass : IRenderPass
Usage = TextureUsage.ShaderResource, Usage = TextureUsage.ShaderResource,
}; };
_textures[i] = ctx.CreateTexture(ref desc); _textures[i] = ctx.CreateTexture(ref desc, imageData.AsSpan());
ctx.UploadTexture(_textures[i], imageData.AsSpan());
} }
var samplerDesc = new SamplerDesc var samplerDesc = new SamplerDesc