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

@@ -57,18 +57,6 @@
<ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" /> <ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" />
<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
@@ -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;
}
var viewGroup = recordResult.Value.viewGroup;
pDsvHandle[0] = _descriptorAllocator.GetCpuHandle(viewGroup.dsv);
} }
_commandList.Get()->OMSetRenderTargets((uint)renderTargets.Length, pRtvHandles, FALSE, pDsvHandle); _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

@@ -91,7 +91,7 @@ internal class MeshRenderPass : IRenderPass
{ {
using var stream = File.OpenRead(_textureFiles[i]); using var stream = File.OpenRead(_textureFiles[i]);
using var imageData = ImageResult.FromStream(stream, ColorComponents.RGBA); using var imageData = ImageResult.FromStream(stream, ColorComponents.RGBA);
var desc = new TextureDesc var desc = new TextureDesc
{ {
Width = imageData.Width, Width = imageData.Width,
@@ -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