Refactor render graph error handling and resource APIs

- RenderGraph.Compile/Execute now return Error for better failure detection; error handling is propagated throughout compiler and executor.
- Renamed ScheduleReleaseResource to ReleaseResource for clarity; updated all usages.
- ResourceManager now calls ReleaseResource directly on Mesh, Material, and Shader types.
- Camera exposes Actual/Virtual size properties and Render returns Error.
- RenderingContext now uses IResourceManager for mesh/resource ops.
- Replaced custom BinaryWriter with BufferWriter in RenderGraphHasher.
- Improved variable naming, interface signatures, and code formatting.
- Added Error extension for IsSuccess/IsFailure.
- Minor FMOD/native interop and test code cleanups.
- No breaking API changes except for new Error return values on some methods.
This commit is contained in:
2026-02-25 19:08:54 +09:00
parent 30090f84ab
commit 162b71f309
93 changed files with 537 additions and 593 deletions

View File

@@ -23,12 +23,12 @@ public readonly struct Handle<T> : IEquatable<Handle<T>>
public readonly bool IsValid => this != Invalid;
public readonly bool IsInvalid => this == Invalid;
public readonly override int GetHashCode()
public override readonly int GetHashCode()
{
return ID + (Generation << 16);
}
public readonly override bool Equals(object? obj)
public override readonly bool Equals(object? obj)
{
return obj is Handle<T> id && Equals(id);
}
@@ -76,12 +76,12 @@ public readonly struct Identifier<T> : IEquatable<Identifier<T>>
public readonly bool IsValid => this != Invalid;
public readonly bool IsInvalid => this == Invalid;
public readonly override int GetHashCode()
public override readonly int GetHashCode()
{
return Value;
}
public readonly override bool Equals(object? obj)
public override readonly bool Equals(object? obj)
{
return obj is Identifier<T> id && Equals(id);
}
@@ -152,7 +152,7 @@ public readonly struct Key64<T> : IEquatable<Key64<T>>
public bool IsValid => this != Invalid;
public bool IsInvalid => this == Invalid;
public readonly override int GetHashCode()
public override readonly int GetHashCode()
{
return Value.GetHashCode();
}
@@ -167,7 +167,7 @@ public readonly struct Key64<T> : IEquatable<Key64<T>>
return Value.CompareTo(other.Value);
}
public readonly override bool Equals(object? obj)
public override readonly bool Equals(object? obj)
{
return obj is Key64<T> id && Equals(id);
}
@@ -205,7 +205,7 @@ public readonly struct Key128<T> : IEquatable<Key128<T>>
public bool IsValid => this != Invalid;
public bool IsInvalid => this == Invalid;
public readonly override int GetHashCode()
public override readonly int GetHashCode()
{
return Value.GetHashCode();
}
@@ -220,7 +220,7 @@ public readonly struct Key128<T> : IEquatable<Key128<T>>
return Value.CompareTo(other.Value);
}
public readonly override bool Equals(object? obj)
public override readonly bool Equals(object? obj)
{
return obj is Key128<T> id && Equals(id);
}

View File

@@ -66,7 +66,7 @@ public readonly struct Result<T>
private readonly bool _isSuccess;
/// <summary>
/// Gets the value. Undefined if the result is a failure.
/// Gets the value. Undefined behavior if the result is a failure.
/// </summary>
public T Value
{
@@ -141,7 +141,7 @@ public readonly struct Result<T, E>
private readonly E _error;
/// <summary>
/// Gets the value. Undefined if the result is a failure.
/// Gets the value. Undefined behavior if the result is a failure.
/// </summary>
public T Value
{
@@ -194,10 +194,10 @@ public readonly ref struct RefResult<T, E>
where E : struct, Enum
{
private readonly ref T _value;
private readonly E _error;
private readonly E _error;
/// <summary>
/// Gets a reference to the value. Undefined if the result is a failure.
/// Gets a reference to the value. Undefined behavior if the result is a failure.
/// </summary>
public ref T Value
{
@@ -249,6 +249,12 @@ public readonly ref struct RefResult<T, E>
public static class ResultExtensions
{
extension(Error error)
{
public bool IsSuccess => error == Error.None;
public bool IsFailure => error != Error.None;
}
public static void ThrowIfFailed(this Error result, [CallerArgumentExpression(nameof(result))] string? op = null)
{
if (result != Error.None)

View File

@@ -1,41 +0,0 @@
using System.Runtime.CompilerServices;
namespace Ghost.Core.Utilities;
public ref struct BinaryWriter
{
private readonly Span<byte> _buffer;
private int _position;
public int Position
{
readonly get => _position;
set => _position = value;
}
public BinaryWriter(Span<byte> buffer)
{
_buffer = buffer;
_position = 0;
}
public unsafe void Write<T>(scoped ref readonly T value)
where T : unmanaged
{
Unsafe.WriteUnaligned(ref _buffer[_position], value);
_position += sizeof(T);
}
public void WriteBytes(ReadOnlySpan<byte> data)
{
data.CopyTo(_buffer.Slice(_position, data.Length));
_position += data.Length;
}
public Span<byte> GetSpan(int length)
{
var span = _buffer.Slice(_position, length);
_position += length;
return span;
}
}

View File

@@ -0,0 +1,53 @@
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using System.Runtime.CompilerServices;
namespace Ghost.Core.Utilities;
public struct BufferWriter : IDisposable
{
private UnsafeList<byte> _buffer;
private int _position;
public int Position
{
readonly get => _position;
set => _position = value;
}
public BufferWriter(int initialCapacity, AllocationHandle allocationHandle)
{
_buffer = new UnsafeList<byte>(initialCapacity, allocationHandle);
_position = 0;
}
public unsafe void Write<T>(T value)
where T : unmanaged
{
Unsafe.WriteUnaligned(ref _buffer[_position], value);
_position += sizeof(T);
}
public void WriteBytes(ReadOnlySpan<byte> data)
{
data.CopyTo(_buffer.AsSpan().Slice(_position, data.Length));
_position += data.Length;
}
public Span<byte> ReserveSpan(int length)
{
var span = _buffer.AsSpan().Slice(_position, length);
_position += length;
return span;
}
public readonly Span<byte> AsSpan()
{
return _buffer.AsSpan();
}
public void Dispose()
{
_buffer.Dispose();
}
}

View File

@@ -1,7 +1,3 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Ghost.Core.Utilities;
internal class EnumUtility

View File

@@ -18,11 +18,11 @@ public static class Hash
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Hash64(ulong a, ulong b, ulong c)
{
ulong h1 = a * _PRIME1;
ulong h2 = b * _PRIME2;
ulong h3 = c * _PRIME3;
var h1 = a * _PRIME1;
var h2 = b * _PRIME2;
var h3 = c * _PRIME3;
ulong h = h1 ^ h2 ^ h3;
var h = h1 ^ h2 ^ h3;
h = (h ^ (h >> 33)) * _PRIME4;
return h ^ (h >> 29);
@@ -31,12 +31,12 @@ public static class Hash
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Hash64(ulong a, ulong b, ulong c, ulong d)
{
ulong h1 = a * _PRIME1;
ulong h2 = b * _PRIME2;
ulong h3 = c * _PRIME3;
ulong h4 = d * _PRIME4;
var h1 = a * _PRIME1;
var h2 = b * _PRIME2;
var h3 = c * _PRIME3;
var h4 = d * _PRIME4;
ulong h = h1 ^ h2 ^ h3 ^ h4;
var h = h1 ^ h2 ^ h3 ^ h4;
h = (h ^ (h >> 33)) * _PRIME1;
return h ^ (h >> 29);

View File

@@ -1,6 +1,4 @@
using Misaki.HighPerformance.LowLevel;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using TerraFX.Interop.Windows;

View File

@@ -4,5 +4,5 @@ internal class EngineData
{
public const string ENGINE_NAME = "Ghost Engine";
public readonly static Version EngineVersion = new(0, 1, 0);
public static readonly Version EngineVersion = new(0, 1, 0);
}

View File

@@ -7,7 +7,7 @@ using System.Runtime.CompilerServices;
namespace Ghost.Entities;
internal unsafe sealed class ChunkDebugView
internal sealed unsafe class ChunkDebugView
{
[DebuggerDisplay("{Name,nq}: {Data}")]
internal class ComponentArrayView
@@ -541,27 +541,27 @@ internal unsafe struct Archetype : IDisposable
ref var chunk = ref _chunks[chunkIndex];
int oldCount = chunk._count;
int removeCount = sortedIndicesToRemove.Length;
int newCount = oldCount - removeCount; // The boundary between "Keep" and "Drop"
var oldCount = chunk._count;
var removeCount = sortedIndicesToRemove.Length;
var newCount = oldCount - removeCount; // The boundary between "Keep" and "Drop"
var chunkBase = chunk.GetUnsafePtr();
var world = World.GetWorldUncheck(_worldID); // Typo fixed from 'wrold'
// Pointers for the swap logic
// 1. 'holePtr' tracks which index in the sorted list we are processing
int holePtr = 0;
var holePtr = 0;
// 2. 'candidateIndex' starts at the end of the OLD array and moves backward
int candidateIndex = oldCount - 1;
var candidateIndex = oldCount - 1;
// 3. 'removalTailPtr' tracks removals at the end of the array to skip them
int removalTailPtr = sortedIndicesToRemove.Length - 1;
var removalTailPtr = sortedIndicesToRemove.Length - 1;
// Iterate through the holes that are strictly INSIDE the new valid range
while (holePtr < removeCount)
{
int holeIndex = sortedIndicesToRemove[holePtr];
var holeIndex = sortedIndicesToRemove[holePtr];
// If the current hole is beyond the new count, it's in the "Drop Zone".
// Since the list is sorted, all subsequent holes are also in the drop zone.
@@ -574,7 +574,7 @@ internal unsafe struct Archetype : IDisposable
while (candidateIndex >= newCount)
{
// Check if the current candidate is actually marked for removal
bool isCandidateRemoved = false;
var isCandidateRemoved = false;
// Because sortedIndices is sorted, we check the end of the list
// to see if the candidateIndex matches a removal request.

View File

@@ -181,7 +181,7 @@ public class ComponentManager : IDisposable
_archetypes.Add(new Archetype(arcID, _world.ID, componentTypeIDs));
_archetypeLookup.Add(signatureHash, arcID);
for (int i = 0; i < _entityQueries.Count; i++)
for (var i = 0; i < _entityQueries.Count; i++)
{
ref var query = ref _entityQueries[i];
query.AddArchetypeIfMatch(in _archetypes[arcID.Value]);

View File

@@ -455,7 +455,7 @@ public unsafe partial struct EntityQuery : IDisposable
return 0;
}
for(var i = 0; i < _matchingArchetypes.Count; i++)
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
var archetypeID = _matchingArchetypes[i];
ref var archetype = ref world.ComponentManager.GetArchetypeReference(archetypeID);

View File

@@ -204,7 +204,7 @@ public abstract class SystemGroup : ISystem
foreach (var sys in systems)
{
var type = sys.GetType();
if (!dependencies.TryGetValue(type, out HashSet<Type>? value))
if (!dependencies.TryGetValue(type, out var value))
{
value = [];
dependencies[type] = value;
@@ -234,7 +234,7 @@ public abstract class SystemGroup : ISystem
// We loop until we have sorted everyone
while (sortedList.Count < systems.Count)
{
bool addedAny = false;
var addedAny = false;
foreach (var sys in systems)
{
@@ -242,7 +242,7 @@ public abstract class SystemGroup : ISystem
if (visited.Contains(type)) continue;
// Check if all dependencies for this system are already visited/sorted
bool canRun = true;
var canRun = true;
if (dependencies.TryGetValue(type, out var deps))
{
foreach (var dep in deps)

View File

@@ -24,7 +24,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 1; i++)
for (var i = 0; i < 1; i++)
{
if (id == compTypeIDs[i])
{
@@ -111,7 +111,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 2; i++)
for (var i = 0; i < 2; i++)
{
if (id == compTypeIDs[i])
{
@@ -202,7 +202,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 3; i++)
for (var i = 0; i < 3; i++)
{
if (id == compTypeIDs[i])
{
@@ -297,7 +297,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 4; i++)
for (var i = 0; i < 4; i++)
{
if (id == compTypeIDs[i])
{
@@ -396,7 +396,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 5; i++)
for (var i = 0; i < 5; i++)
{
if (id == compTypeIDs[i])
{
@@ -499,7 +499,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 6; i++)
for (var i = 0; i < 6; i++)
{
if (id == compTypeIDs[i])
{
@@ -606,7 +606,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 7; i++)
for (var i = 0; i < 7; i++)
{
if (id == compTypeIDs[i])
{
@@ -717,7 +717,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 8; i++)
for (var i = 0; i < 8; i++)
{
if (id == compTypeIDs[i])
{
@@ -808,7 +808,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 1; i++)
for (var i = 0; i < 1; i++)
{
if (id == compTypeIDs[i])
{
@@ -896,7 +896,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 2; i++)
for (var i = 0; i < 2; i++)
{
if (id == compTypeIDs[i])
{
@@ -988,7 +988,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 3; i++)
for (var i = 0; i < 3; i++)
{
if (id == compTypeIDs[i])
{
@@ -1084,7 +1084,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 4; i++)
for (var i = 0; i < 4; i++)
{
if (id == compTypeIDs[i])
{
@@ -1184,7 +1184,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 5; i++)
for (var i = 0; i < 5; i++)
{
if (id == compTypeIDs[i])
{
@@ -1288,7 +1288,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 6; i++)
for (var i = 0; i < 6; i++)
{
if (id == compTypeIDs[i])
{
@@ -1396,7 +1396,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 7; i++)
for (var i = 0; i < 7; i++)
{
if (id == compTypeIDs[i])
{
@@ -1508,7 +1508,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 8; i++)
for (var i = 0; i < 8; i++)
{
if (id == compTypeIDs[i])
{

View File

@@ -1168,7 +1168,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 1; i++)
for (var i = 0; i < 1; i++)
{
if (id == runner.componentIDs[i])
{
@@ -1324,7 +1324,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 1; i++)
for (var i = 0; i < 1; i++)
{
if (id == runner.componentIDs[i])
{
@@ -1507,7 +1507,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 1; i++)
for (var i = 0; i < 1; i++)
{
if (id == runner.componentIDs[i])
{
@@ -1717,7 +1717,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 1; i++)
for (var i = 0; i < 1; i++)
{
if (id == runner.componentIDs[i])
{
@@ -1954,7 +1954,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 1; i++)
for (var i = 0; i < 1; i++)
{
if (id == runner.componentIDs[i])
{
@@ -2218,7 +2218,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 1; i++)
for (var i = 0; i < 1; i++)
{
if (id == runner.componentIDs[i])
{
@@ -2509,7 +2509,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 1; i++)
for (var i = 0; i < 1; i++)
{
if (id == runner.componentIDs[i])
{
@@ -2827,7 +2827,7 @@ public unsafe partial struct EntityQuery
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 1; i++)
for (var i = 0; i < 1; i++)
{
if (id == runner.componentIDs[i])
{

View File

@@ -71,15 +71,15 @@ internal static class {name}
public static void RegisterIComponentTypes()
{{");
foreach (var symbol in components.Distinct(SymbolEqualityComparer.Default))
{
if (symbol is null) continue;
foreach (var symbol in components.Distinct(SymbolEqualityComparer.Default))
{
if (symbol is null) continue;
sb.Append($@"
sb.Append($@"
global::Ghost.Entities.ComponentRegistry.GetOrRegisterComponentID<{symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>();");
}
}
sb.Append(@"
sb.Append(@"
}
}");

View File

@@ -1,4 +1,3 @@
using Ghost.Core.Utilities;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel;

View File

@@ -1,5 +1,4 @@
using Ghost.Core;
using Ghost.Core.Utilities;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel;
@@ -869,8 +868,11 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
#endif
IncrementCommandCount();
var resource = _resourceDatabase.GetResource(argumentBuffer.AsResource());
var countResource = _resourceDatabase.GetResource(countBuffer.AsResource());
// TODO
_commandList.Get()->ExecuteIndirect(null, 0,
resource, argumentOffset, countResource, countBufferOffset);
@@ -947,7 +949,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
d3d12Subresources);
}
public void CopyBuffer(Handle<GraphicsBuffer> dest, Handle<GraphicsBuffer> src, ulong destOffset = 0, ulong srcOffset = 0, ulong numBytes = 0)
public void CopyBuffer(Handle<GraphicsBuffer> dst, Handle<GraphicsBuffer> src, ulong dstOffset = 0, ulong srcOffset = 0, ulong numBytes = 0)
{
ThrowIfDisposed();
ThrowIfNotRecording();
@@ -957,11 +959,16 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
return;
}
#endif
if (dst == src)
{
return;
}
IncrementCommandCount();
var pDestResource = _resourceDatabase.GetResource(dest.AsResource());
var pDstResource = _resourceDatabase.GetResource(dst.AsResource());
var pSrcResource = _resourceDatabase.GetResource(src.AsResource());
if (pSrcResource == null || pDestResource == null)
if (pSrcResource == null || pDstResource == null)
{
RecordError(nameof(CopyBuffer), Error.InvalidArgument);
return;
@@ -969,14 +976,45 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
if (numBytes == 0)
{
_commandList.Get()->CopyResource(pDestResource, pSrcResource);
_commandList.Get()->CopyResource(pDstResource, pSrcResource);
}
else
{
_commandList.Get()->CopyBufferRegion(pDestResource, destOffset, pSrcResource, srcOffset, numBytes);
_commandList.Get()->CopyBufferRegion(pDstResource, dstOffset, pSrcResource, srcOffset, numBytes);
}
}
public void CopyTexture(Handle<Texture> dst, Handle<Texture> src)
{
throw new NotImplementedException();
ThrowIfDisposed();
ThrowIfNotRecording();
#if !DEBUG
if (_lastError.Status != Error.None)
{
return;
}
#endif
if (dst == src)
{
return;
}
IncrementCommandCount();
var pDstResource = _resourceDatabase.GetResource(dst.AsResource());
var pSrcResource = _resourceDatabase.GetResource(src.AsResource());
if (pSrcResource == null || pDstResource == null)
{
RecordError(nameof(CopyTexture), Error.InvalidArgument);
return;
}
// TODO
_commandList.Get()->CopyTextureRegion(null, 0, 0, 0, null, null);
}
public void Dispose()
{
if (_disposed)

View File

@@ -1,4 +1,3 @@
using Ghost.Core.Utilities;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel;

View File

@@ -1,8 +1,6 @@
using Ghost.Core.Utilities;
using Ghost.Graphics.D3D12.Utilities;
using Misaki.HighPerformance.LowLevel;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
using static TerraFX.Aliases.DXGI_Alias;

View File

@@ -95,7 +95,7 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable
#region DSV Methods
public Identifier<DSVDescriptor> AllocateDSV()
public Identifier<DSVDescriptor> AllocateDSV()
{
ObjectDisposedException.ThrowIf(_disposed, this);

View File

@@ -1,10 +1,7 @@
using Ghost.Core;
using Ghost.Core.Graphics;
using Ghost.Core.Utilities;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Collections;
using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
@@ -504,6 +501,11 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
private Handle<GPUResource> TrackAllocation(D3D12MA_Allocation* allocation, ResourceBarrierData barrierData, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string name, bool isTemp)
{
var handle = _resourceDatabase.AddAllocation(allocation, barrierData, resourceDescriptor, desc, name);
if (isTemp)
{
_resourceDatabase.ReleaseResource(handle);
}
return handle;
}
@@ -830,7 +832,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
offset = 0;
var handle = CreateBuffer(in bufferDesc, "TempUploadBuffer", options);
_resourceDatabase.ScheduleReleaseResource(handle.AsResource());
_resourceDatabase.ReleaseResource(handle.AsResource());
return handle;
}
}

View File

@@ -1,7 +1,6 @@
using Ghost.Core;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.Collections;
using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
@@ -279,7 +278,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
return null;
}
public void ScheduleReleaseResource(Handle<GPUResource> handle)
public void ReleaseResource(Handle<GPUResource> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);

View File

@@ -249,7 +249,7 @@ internal unsafe class D3D12SwapChain : ISwapChain
for (var i = 0; i < _backBuffers.Count; i++)
{
_resourceDatabase.ScheduleReleaseResource(_backBuffers[i].AsResource());
_resourceDatabase.ReleaseResource(_backBuffers[i].AsResource());
}
_backBuffers.Dispose();

View File

@@ -114,7 +114,7 @@ public struct Color128 : IEquatable<Color128>
return obj is Color128 color && Equals(color);
}
public readonly override int GetHashCode()
public override readonly int GetHashCode()
{
return HashCode.Combine(r, g, b, a);
}
@@ -587,7 +587,7 @@ public struct ResourceDesc
}
internal resource_union _desc;
public ResourceType Type
{
get; init;

View File

@@ -8,7 +8,7 @@ public interface ICommandQueue : IDisposable
/// <summary>
/// Type of commands this queue can execute
/// </summary>
public CommandQueueType Type
CommandQueueType Type
{
get;
}
@@ -17,35 +17,35 @@ public interface ICommandQueue : IDisposable
/// Submits a single command buffer for execution
/// </summary>
/// <param name="commandBuffer">Command buffer to submit</param>
public void Submit(ICommandBuffer commandBuffer);
void Submit(ICommandBuffer commandBuffer);
/// <summary>
/// Submits multiple command buffers for execution
/// </summary>
/// <param name="commandBuffers">Command buffers to submit</param>
public void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers);
void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers);
/// <summary>
/// Signals a fence with the specified Value
/// </summary>
/// <param name="value">Value to signal</param>
/// <returns>The fence Value that was signaled</returns>
public ulong Signal(ulong value);
ulong Signal(ulong value);
/// <summary>
/// Waits for the fence to reach the specified Value
/// </summary>
/// <param name="value">Value to wait for</param>
public void WaitForValue(ulong value);
void WaitForValue(ulong value);
/// <summary>
/// Gets the last completed fence Value
/// </summary>
/// <returns>Last completed fence Value</returns>
public ulong GetCompletedValue();
ulong GetCompletedValue();
/// <summary>
/// Waits until all submitted commands have finished executing
/// </summary>
public void WaitIdle();
void WaitIdle();
}

View File

@@ -21,7 +21,7 @@ public interface IRenderDevice : IDisposable
/// <summary>
/// Graphics command queue for rendering operations
/// </summary>
public ICommandQueue GraphicsQueue
ICommandQueue GraphicsQueue
{
get;
}
@@ -29,7 +29,7 @@ public interface IRenderDevice : IDisposable
/// <summary>
/// Compute command queue for compute shader operations
/// </summary>
public ICommandQueue ComputeQueue
ICommandQueue ComputeQueue
{
get;
}
@@ -37,12 +37,12 @@ public interface IRenderDevice : IDisposable
/// <summary>
/// Copy command queue for data transfer operations
/// </summary>
public ICommandQueue CopyQueue
ICommandQueue CopyQueue
{
get;
}
public FeatureSupport FeatureSupport
FeatureSupport FeatureSupport
{
get;
}

View File

@@ -1,6 +1,4 @@
using Ghost.Core;
using Ghost.Core.Graphics;
using Misaki.HighPerformance.LowLevel.Collections;
namespace Ghost.Graphics.RHI;

View File

@@ -98,7 +98,7 @@ public interface IResourceDatabase : IDisposable
/// Releases the GPU resource associated with the specified handle, freeing any resources allocated to it.
/// </summary>
/// <param name="handle">The handle of the resource to be removed.</param>
void ScheduleReleaseResource(Handle<GPUResource> handle);
void ReleaseResource(Handle<GPUResource> handle);
/// <summary>
/// Releases the GPU resource associated with the specified handle immediately, freeing any resources allocated to it.

View File

@@ -8,6 +8,7 @@ public class Camera
{
private readonly IRenderer _renderer;
// History buffers.
private Handle<Texture> _colorTexture;
private Handle<Texture> _depthTexture;
@@ -23,14 +24,17 @@ public class Camera
/// Gets the actual width of the camera's render target in pixels. If upscaler is used, this is the width before upscaling.
/// </summary>
public uint ActualWidth => _actualWidth;
/// <summary>
/// Gets the actual height of the camera's render target in pixels. If upscaler is used, this is the height before upscaling.
/// </summary>
public uint ActualHeight => _actualHeight;
/// <summary>
/// Gets the virtual width of the camera's render target in pixels. If upscaler is used, this is the width after upscaling.
/// </summary>
public uint VirtualWidth => _virtualWidth;
/// <summary>
/// Gets the virtual height of the camera's render target in pixels. If upscaler is used, this is the height after upscaling.
/// </summary>
@@ -64,8 +68,17 @@ public class Camera
RenderGraph.Reset();
var view = new ViewState(_virtualWidth, _virtualHeight, _actualWidth, _actualHeight);
RenderGraph.Compile(in view);
RenderGraph.Execute(context.CommandBuffer);
var e = RenderGraph.Compile(in view);
if (e != Error.None)
{
return e;
}
e = RenderGraph.Execute(context.CommandBuffer);
if (e != Error.None)
{
return e;
}
return Error.None;
}

View File

@@ -35,7 +35,7 @@ internal struct CBufferCache : IResourceReleasable
}
_cpuData.Dispose();
database.ScheduleReleaseResource(_gpuResource.AsResource());
database.ReleaseResource(_gpuResource.AsResource());
_gpuResource = Handle<GraphicsBuffer>.Invalid;
_size = 0;
@@ -144,7 +144,6 @@ public struct Material : IResourceReleasable
return _cBufferCache.CpuData.AsSpan(0, (int)_cBufferCache.Size);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe Error SetPropertyCache<T>(scoped ref readonly T data)
where T : unmanaged
{
@@ -166,7 +165,6 @@ public struct Material : IResourceReleasable
return Error.None;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Error SetRawPropertyCache(ReadOnlySpan<byte> data)
{
if (data.Length != _cBufferCache.Size)
@@ -200,7 +198,6 @@ public struct Material : IResourceReleasable
_isDirty = true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Error SetKeyword(IResourceManager manager, int keywordId, bool enabled)
{
var r = manager.GetShaderReference(_shader);
@@ -222,7 +219,6 @@ public struct Material : IResourceReleasable
return Error.None;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool IsKeywordEnabled(IResourceManager manager, int keywordId)
{
var r = manager.GetShaderReference(_shader);
@@ -276,8 +272,7 @@ public struct Material : IResourceReleasable
cmd.ResourceBarrier(desc);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
public void ReleaseResource(IResourceDatabase database)
{
_cBufferCache.ReleaseResource(database);
_passPipelineOverride.Dispose();

View File

@@ -113,13 +113,13 @@ public struct Mesh : IResourceReleasable
_indices.Dispose();
}
readonly void IResourceReleasable.ReleaseResource(IResourceDatabase database)
public readonly void ReleaseResource(IResourceDatabase database)
{
ReleaseCpuResources();
database.ScheduleReleaseResource(VertexBuffer.AsResource());
database.ScheduleReleaseResource(IndexBuffer.AsResource());
database.ScheduleReleaseResource(ObjectDataBuffer.AsResource());
database.ReleaseResource(VertexBuffer.AsResource());
database.ReleaseResource(IndexBuffer.AsResource());
database.ReleaseResource(ObjectDataBuffer.AsResource());
}
}

View File

@@ -10,18 +10,21 @@ namespace Ghost.Graphics.Core;
public readonly unsafe ref struct RenderingContext
{
private readonly IGraphicsEngine _engine;
private readonly IResourceManager _resourceManager;
private readonly ICommandBuffer _directCmd;
public ICommandBuffer DirectCommandBuffer => _directCmd;
public IShaderCompiler ShaderCompiler => _engine.ShaderCompiler;
public IResourceManager ResourceManager => _resourceManager;
public IResourceAllocator ResourceAllocator => _engine.ResourceAllocator;
public IResourceDatabase ResourceDatabase => _engine.ResourceDatabase;
public IPipelineLibrary PipelineLibrary => _engine.PipelineLibrary;
internal RenderingContext(IGraphicsEngine engine, ICommandBuffer directCmd)
internal RenderingContext(IGraphicsEngine engine, IResourceManager resourceManager, ICommandBuffer directCmd)
{
_engine = engine;
_resourceManager = resourceManager;
_directCmd = directCmd;
}
@@ -82,8 +85,8 @@ public readonly unsafe ref struct RenderingContext
public Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices, bool staticMesh)
{
var mesh = ResourceAllocator.CreateMesh(vertices, indices);
var r = ResourceDatabase.GetMeshReference(mesh);
var mesh = _resourceManager.CreateMesh(vertices, indices);
var r = _resourceManager.GetMeshReference(mesh);
if (r.IsFailure)
{
return mesh;
@@ -129,7 +132,7 @@ public readonly unsafe ref struct RenderingContext
/// <param name="markMeshStatic">Whether to mark the mesh as static. If it's true, the cpu buffer of the mesh will not be avaliable any more</param>
public void UploadMesh(Handle<Mesh> mesh, bool markMeshStatic)
{
var r = ResourceDatabase.GetMeshReference(mesh);
var r = _resourceManager.GetMeshReference(mesh);
if (r.IsFailure)
{
return;
@@ -156,7 +159,7 @@ public readonly unsafe ref struct RenderingContext
public void UpdateObjectData(Handle<Mesh> mesh, float4x4 localToWorld)
{
var r = ResourceDatabase.GetMeshReference(mesh);
var r = _resourceManager.GetMeshReference(mesh);
if (r.IsFailure)
{
return;
@@ -192,7 +195,7 @@ public readonly unsafe ref struct RenderingContext
where T : unmanaged
{
var desc = ResourceDatabase.GetResourceDescription(texture.AsResource()).GetValueOrThrow();
//var size = ResourceAllocator.GetSizeInfo(desc).Size;
//if ((ulong)(data.Length * sizeof(T)) != ResourceAllocator.GetSizeInfo(desc).Size)
//{

View File

@@ -3,6 +3,7 @@ using Ghost.Core.Graphics;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ghost.Graphics.Core;
@@ -136,6 +137,7 @@ public partial struct Shader : IResourceReleasable
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal readonly int GetLocalKeywordIndex(int globalKeywordID)
{
if (_keywordIDToLocal.TryGetValue(globalKeywordID, out var localIndex))
@@ -146,6 +148,7 @@ public partial struct Shader : IResourceReleasable
return -1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly int GetPassIndex(Identifier<ShaderPass> passID)
{
if (_passIDToLocal.TryGetValue(passID.Value, out var index))
@@ -156,6 +159,7 @@ public partial struct Shader : IResourceReleasable
return -1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly int GetPassIndex(string passName)
{
if (_passIDToLocal.TryGetValue(GetPassID(passName), out var index))
@@ -166,11 +170,13 @@ public partial struct Shader : IResourceReleasable
return -1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly ref ShaderPass GetPassReference(int index)
{
return ref _shaderPasses[index];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Result<ShaderPass, Error> TryGetPass(Identifier<ShaderPass> passID, out int passIndex)
{
if (_passIDToLocal.TryGetValue(passID.Value, out var index))
@@ -183,7 +189,7 @@ public partial struct Shader : IResourceReleasable
return _shaderPasses[index];
}
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
public void ReleaseResource(IResourceDatabase database)
{
_keywordIDToLocal.Dispose();
_shaderPasses.Dispose();

View File

@@ -1,5 +1,3 @@
using Misaki.HighPerformance.LowLevel;
namespace Ghost.Graphics;
internal unsafe class GPUResourceLeakException : Exception

View File

@@ -5,7 +5,6 @@ namespace Ghost.Graphics.RenderGraphModule;
/// <summary>
/// Main render graph class that manages resource allocation and pass execution.
/// Delegates complex operations to specialized components for better organization.
/// </summary>
public sealed class RenderGraph : IDisposable
{
@@ -172,38 +171,38 @@ public sealed class RenderGraph : IDisposable
/// <summary>
/// Compiles the render graph by culling unused passes and determining resource lifetimes.
/// Delegates to RenderGraphCompiler for the actual compilation work.
/// </summary>
public void Compile(in ViewState viewState)
public Error Compile(in ViewState viewState)
{
if (_compiled)
{
return;
return Error.None;
}
// Resolve texture sizes before computing hash
_resources.ResolveTextureSizes(in viewState);
// Compute structural hash for caching
var graphHash = RenderGraphHasher.ComputeGraphHash(_passes, _resources);
var error = _compiler.Compile(in viewState, graphHash, _passes, _compiledPasses, _nativePasses, _compiledBarriers);
if (error != Error.None)
{
return error;
}
// Delegate to compiler
_compiler.Compile(in viewState, graphHash, _passes, _compiledPasses, _nativePasses, _compiledBarriers);
_compiled = true;
return Error.None;
}
/// <summary>
/// Executes all compiled passes using native render passes where possible.
/// Delegates to RenderGraphExecutor for the actual execution work.
/// </summary>
public void Execute(ICommandBuffer cmd)
public Error Execute(ICommandBuffer cmd)
{
if (!_compiled)
{
throw new InvalidOperationException("Render graph must be compiled before execution. Call Compile(viewState) first.");
return Error.InvalidState;
}
_executor.Execute(cmd, _compiledPasses, _nativePasses, _compiledBarriers);
return _executor.Execute(cmd, _compiledPasses, _nativePasses, _compiledBarriers);
}
public void Dispose()

View File

@@ -94,7 +94,7 @@ internal sealed class ResourceHeap
{
// Check if this offset range conflicts with ANY existing allocations
var canUseOffset = CanPlaceAtOffset(alignedOffset, alignedSize, firstUsePass, lastUsePass);
if (canUseOffset)
{
var waste = block.size - alignedSize;
@@ -173,7 +173,7 @@ internal sealed class ResourceHeap
private bool CanPlaceAtOffset(ulong offset, ulong size, int firstUsePass, int lastUsePass)
{
var endOffset = offset + size;
foreach (var block in _blocks)
{
// Skip free blocks - they don't have lifetime constraints
@@ -185,12 +185,12 @@ internal sealed class ResourceHeap
// Check if this block's memory range overlaps with our target range
var blockEnd = block.offset + block.size;
var memoryOverlap = !(offset >= blockEnd || endOffset <= block.offset);
if (memoryOverlap)
{
// Memory ranges overlap, check if lifetimes also overlap
var lifetimeOverlap = !(firstUsePass > block.lastUsePass || lastUsePass < block.firstUsePass);
if (lifetimeOverlap)
{
// Both memory AND lifetime overlap - cannot place here!
@@ -198,7 +198,7 @@ internal sealed class ResourceHeap
}
}
}
return true;
}
@@ -298,7 +298,7 @@ internal sealed class ResourceAliasingManager
private const ulong _DEFAULT_BUFFER_ALIGNMENT = 65536; // 64KB for D3D12
public ResourceHeap Heap => _heap;
/// <summary>
/// Helper method to get the size of a resource
/// </summary>
@@ -374,8 +374,8 @@ internal sealed class ResourceAliasingManager
foreach (var (logicalIndex, logicalResource) in logicalResources)
{
var size = GetResourceSize(logicalResource);
var alignment = logicalResource.type == RenderGraphResourceType.Texture
? _DEFAULT_TEXTURE_ALIGNMENT
var alignment = logicalResource.type == RenderGraphResourceType.Texture
? _DEFAULT_TEXTURE_ALIGNMENT
: _DEFAULT_BUFFER_ALIGNMENT;
var (success, offset, block) = simulationHeap.TryAllocate(
@@ -393,7 +393,7 @@ internal sealed class ResourceAliasingManager
// Get peak usage from simulation
var peakMemoryUsage = simulationHeap.GetPeakUsage();
// Align peak usage to 64KB (D3D12 requirement)
peakMemoryUsage = AlignUp(peakMemoryUsage, _DEFAULT_TEXTURE_ALIGNMENT);
@@ -405,8 +405,8 @@ internal sealed class ResourceAliasingManager
foreach (var (logicalIndex, logicalResource) in logicalResources)
{
var size = GetResourceSize(logicalResource);
var alignment = logicalResource.type == RenderGraphResourceType.Texture
? _DEFAULT_TEXTURE_ALIGNMENT
var alignment = logicalResource.type == RenderGraphResourceType.Texture
? _DEFAULT_TEXTURE_ALIGNMENT
: _DEFAULT_BUFFER_ALIGNMENT;
var (success, offset, block) = _heap.TryAllocate(
@@ -444,7 +444,7 @@ internal sealed class ResourceAliasingManager
for (var i = 0; i < _placedResources.Count; i++)
{
var placed = _placedResources[i];
// Find all logical resources that share the same heap location
for (var j = 0; j < _placedResources.Count; j++)
{
@@ -454,7 +454,7 @@ internal sealed class ResourceAliasingManager
}
var other = _placedResources[j];
// Check if they're at the same offset
if (other.heapOffset == placed.heapOffset)
{

View File

@@ -1,6 +1,5 @@
using Ghost.Core;
using Ghost.Graphics.RHI;
using System.Runtime.InteropServices;
namespace Ghost.Graphics.RenderGraphModule;
@@ -49,8 +48,8 @@ internal struct ResourceBarrier
public override readonly string ToString()
{
return AliasingPredecessor.IsValid
? $"[Pass {PassIndex}] Aliasing Barrier: {AliasingPredecessor.Value}->{Resource.Value} Target: {TargetState.layout}"
return AliasingPredecessor.IsValid
? $"[Pass {PassIndex}] Aliasing Barrier: {AliasingPredecessor.Value}->{Resource.Value} Target: {TargetState.layout}"
: $"[Pass {PassIndex}] Barrier: {Resource.Value} Target: {TargetState.layout}";
}
}
@@ -87,8 +86,8 @@ internal struct CompiledBarrier
public override readonly string ToString()
{
return AliasingPredecessor.IsValid
? $"[Pass {PassIndex}] Aliasing: {AliasingPredecessor.Value}->{Resource.Value} -> {TargetState.layout}"
return AliasingPredecessor.IsValid
? $"[Pass {PassIndex}] Aliasing: {AliasingPredecessor.Value}->{Resource.Value} -> {TargetState.layout}"
: $"[Pass {PassIndex}] Transition: {Resource.Value} -> {TargetState.layout}";
}
}
@@ -320,7 +319,7 @@ internal static class RenderGraphBarriers
var sync = BarrierSync.PixelShading | BarrierSync.NonPixelShading;
var access = BarrierAccess.ShaderResource;
var resource = resources.GetResource(handle);
if (resource.bufferDesc.Usage.HasFlag(BufferUsage.IndirectArgument))
{

View File

@@ -47,7 +47,7 @@ public interface IRenderGraphBuilder : IDisposable
/// <param name="accessMode">The access mode specifying how the texture will be read or written during the pass.</param>
/// <returns>An identifier for the texture.</returns>
Identifier<RGTexture> UseTexture(Identifier<RGTexture> texture, AccessFlags accessMode);
/// <summary>
/// Registers the specified buffer for use in the current render graph pass with the given access mode.
/// </summary>
@@ -195,7 +195,7 @@ internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGra
_resources.SetProducer(handle.AsResource(), _pass.index);
return handle;
}
public Identifier<RGBuffer> CreateBuffer(in BufferDesc desc, string name)
{
ThrowIfDisposed();
@@ -211,7 +211,7 @@ internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGra
ThrowIfDisposed();
return UseResource(texture.AsResource(), flags, RenderGraphResourceType.Texture).AsTexture();
}
public Identifier<RGBuffer> UseBuffer(Identifier<RGBuffer> buffer, AccessFlags flags)
{
ThrowIfDisposed();

View File

@@ -15,19 +15,19 @@ internal sealed class CachedCompilation
// Culling decisions for each pass
public readonly List<bool> passCulledFlags = new(64);
// Physical resource aliasing mappings (logical index -> physical index)
public readonly Dictionary<int, int> logicalToPhysical = new(128);
// Placed resource metadata
public readonly List<PlacedResourceData> placedResources = new(32);
// Compiled barriers (stores only target states, queries before state from ResourceManager)
public readonly List<CompiledBarrier> compiledBarriers = new(128);
// Real gpu resource
public readonly List<Handle<GPUResource>> backingResources = new(32);
// View state used for this compilation
public ViewState viewState;
@@ -94,18 +94,18 @@ internal sealed class RenderGraphCompilationCache
{
_cachedHash = hash;
_hasCachedData = true;
// Deep copy the data
_cached.Clear();
_cached.compiledPassIndices.AddRange(data.compiledPassIndices);
_cached.passCulledFlags.AddRange(data.passCulledFlags);
foreach (var kvp in data.logicalToPhysical)
{
_cached.logicalToPhysical[kvp.Key] = kvp.Value;
}
_cached.placedResources.AddRange(data.placedResources);
_cached.compiledBarriers.AddRange(data.compiledBarriers);
@@ -137,8 +137,8 @@ internal sealed class RenderGraphCompilationCache
/// </summary>
public (int hits, int misses, double hitRate) GetStatistics()
{
int total = CacheHits + CacheMisses;
double hitRate = total > 0 ? (double)CacheHits / total : 0.0;
var total = CacheHits + CacheMisses;
var hitRate = total > 0 ? (double)CacheHits / total : 0.0;
return (CacheHits, CacheMisses, hitRate);
}
}

View File

@@ -35,7 +35,7 @@ internal sealed class RenderGraphCompiler
/// <summary>
/// Compiles the render graph by culling passes, allocating resources, and preparing barriers.
/// </summary>
public void Compile(
public Error Compile(
in ViewState viewState,
ulong graphHash,
List<RenderGraphPassBase> passes,
@@ -43,6 +43,8 @@ internal sealed class RenderGraphCompiler
List<NativeRenderPass> nativePasses,
List<CompiledBarrier> compiledBarriers)
{
Error error;
// Try to restore from cache
if (_compilationCache.TryGetCached(graphHash, out var cached))
{
@@ -54,7 +56,11 @@ internal sealed class RenderGraphCompiler
RestoreFromCache(cached, compiledPasses, passes, nativePasses, compiledBarriers);
_aliasingManager.AssignPhysicalResources(_resources, passes.Count);
AllocateResources();
error = AllocateResources();
if (error != Error.None)
{
return error;
}
cached.viewState = viewState;
}
@@ -64,7 +70,7 @@ internal sealed class RenderGraphCompiler
RestoreFromCache(cached, compiledPasses, passes, nativePasses, compiledBarriers);
}
return;
return Error.None;
}
// Fresh compilation needed
@@ -87,16 +93,19 @@ internal sealed class RenderGraphCompiler
}
_aliasingManager.AssignPhysicalResources(_resources, passes.Count);
AllocateResources();
error = AllocateResources();
if (error != Error.None)
{
return error;
}
CompileBarriers(compiledPasses, compiledBarriers);
_nativePassBuilder.BuildNativeRenderPasses(compiledPasses, nativePasses, compiledBarriers);
StoreInCache(graphHash, viewState, compiledPasses, passes, compiledBarriers);
return Error.None;
}
/// <summary>
/// Marks passes that write to imported resources as having side effects.
/// </summary>
private void MarkPassesWithSideEffects(List<RenderGraphPassBase> passes)
{
for (var i = 0; i < passes.Count; i++)
@@ -121,9 +130,6 @@ internal sealed class RenderGraphCompiler
}
}
/// <summary>
/// Culls unused passes based on dependency analysis.
/// </summary>
private void CullPasses(List<RenderGraphPassBase> passes)
{
// Mark all passes as culled initially
@@ -143,9 +149,6 @@ internal sealed class RenderGraphCompiler
}
}
/// <summary>
/// Recursively un-culls dependencies of a pass.
/// </summary>
private void UnculDependencies(RenderGraphPassBase pass, List<RenderGraphPassBase> passes)
{
// Un-cull producers of read resources
@@ -180,9 +183,6 @@ internal sealed class RenderGraphCompiler
}
}
/// <summary>
/// Un-culls the producer of a resource.
/// </summary>
private void UnculProducer(Identifier<RGResource> resource, List<RenderGraphPassBase> passes)
{
var res = _resources.GetResource(resource);
@@ -197,10 +197,7 @@ internal sealed class RenderGraphCompiler
}
}
/// <summary>
/// Allocates GPU resources for the render graph.
/// </summary>
private void AllocateResources()
private Error AllocateResources()
{
if (_resourceHeap.IsValid)
{
@@ -211,15 +208,15 @@ internal sealed class RenderGraphCompiler
continue;
}
_resourceManager.ResourceDatabase.ScheduleReleaseResource(res.backingResource);
_resourceManager.ResourceDatabase.ReleaseResource(res.backingResource);
}
_resourceManager.ResourceDatabase.ScheduleReleaseResource(_resourceHeap);
_resourceManager.ResourceDatabase.ReleaseResource(_resourceHeap);
}
if (_aliasingManager.Heap.size == 0)
{
return;
return Error.None; // No resources to allocate
}
var allocationDesc = new AllocationDesc
@@ -231,6 +228,10 @@ internal sealed class RenderGraphCompiler
};
_resourceHeap = _resourceManager.ResourceAllocator.Allocate(in allocationDesc, "RenderGraphResourceHeap");
if (_resourceHeap.IsInvalid)
{
return Error.InvalidState;
}
for (var i = 0; i < _resources.Resources.Count; i++)
{
@@ -263,22 +264,22 @@ internal sealed class RenderGraphCompiler
throw new NotSupportedException();
}
if (res.backingResource.IsInvalid)
{
return Error.InvalidState;
}
_compilationCache.UpdateBackingResource(i, res.backingResource);
}
return Error.None;
}
/// <summary>
/// Compiles all barriers needed for execution.
/// Delegates to RenderGraphBarriers for the actual compilation logic.
/// </summary>
private void CompileBarriers(List<RenderGraphPassBase> compiledPasses, List<CompiledBarrier> compiledBarriers)
{
RenderGraphBarriers.CompileBarriers(compiledPasses, compiledBarriers, _resources, _aliasingManager);
}
/// <summary>
/// Restores the render graph state from cached compilation results.
/// </summary>
private void RestoreFromCache(
CachedCompilation cached,
List<RenderGraphPassBase> compiledPasses,
@@ -323,9 +324,6 @@ internal sealed class RenderGraphCompiler
_nativePassBuilder.BuildNativeRenderPasses(compiledPasses, nativePasses, compiledBarriers);
}
/// <summary>
/// Stores current compilation results in the cache.
/// </summary>
private void StoreInCache(
ulong graphHash,
in ViewState viewState,
@@ -368,9 +366,6 @@ internal sealed class RenderGraphCompiler
_compilationCache.Store(graphHash, cacheData);
}
/// <summary>
/// Releases allocated GPU resources.
/// </summary>
public void Dispose()
{
if (_resourceHeap.IsValid)
@@ -379,11 +374,11 @@ internal sealed class RenderGraphCompiler
{
if (!res.isImported)
{
_resourceManager.ResourceDatabase.ScheduleReleaseResource(res.backingResource);
_resourceManager.ResourceDatabase.ReleaseResource(res.backingResource);
}
}
_resourceManager.ResourceDatabase.ScheduleReleaseResource(_resourceHeap);
_resourceManager.ResourceDatabase.ReleaseResource(_resourceHeap);
_resourceHeap = Handle<GPUResource>.Invalid;
}
}

View File

@@ -53,7 +53,7 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
private int _activeMeshIndexCount;
public IResourceManager ResourceManager => _resourceManager;
public int ActiveMeshIndexCount => _activeMeshIndexCount;
public ICommandBuffer CommandBuffer => _commandBuffer;
@@ -76,7 +76,7 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
internal void SetRenderTargetFormats(ReadOnlySpan<TextureFormat> rtvFormats, TextureFormat dsvFormat)
{
for (int i = 0; i < RHIUtility.MAX_RENDER_TARGETS; i++)
for (var i = 0; i < RHIUtility.MAX_RENDER_TARGETS; i++)
{
_rtvFormats[i] = i < rtvFormats.Length ? rtvFormats[i] : TextureFormat.Unknown;
}

View File

@@ -25,7 +25,7 @@ internal sealed class RenderGraphExecutor
/// <summary>
/// Executes all compiled passes using native render passes where possible.
/// </summary>
public unsafe void Execute(
public unsafe Error Execute(
ICommandBuffer cmd,
List<RenderGraphPassBase> compiledPasses,
List<NativeRenderPass> nativePasses,
@@ -53,7 +53,11 @@ internal sealed class RenderGraphExecutor
for (var i = 0; i < nativePass.mergedPassIndices.Count; i++)
{
var mergedPassIdx = nativePass.mergedPassIndices[i];
ExecuteBarriersForPass(cmd, mergedPassIdx, ref barrierIndex, compiledBarriers);
var e = ExecuteBarriersForPass(cmd, mergedPassIdx, ref barrierIndex, compiledBarriers);
if (e != Error.None)
{
return e;
}
}
// Begin native render pass
@@ -119,19 +123,25 @@ internal sealed class RenderGraphExecutor
else
{
// Compute pass or standalone raster pass (not merged) or Unsafe pass
ExecuteBarriersForPass(cmd, logicalPassIndex, ref barrierIndex, compiledBarriers);
var e = ExecuteBarriersForPass(cmd, logicalPassIndex, ref barrierIndex, compiledBarriers);
if (e != Error.None)
{
return e;
}
pass.Execute(_context);
logicalPassIndex++;
}
}
return Error.None;
}
/// <summary>
/// Executes all barriers for a specific pass.
/// Uses pre-compiled barriers and queries before state from ResourceManager.
/// </summary>
private unsafe void ExecuteBarriersForPass(
private unsafe Error ExecuteBarriersForPass(
ICommandBuffer cmd,
int passIndex,
ref int barrierIndex,
@@ -158,7 +168,13 @@ internal sealed class RenderGraphExecutor
var resourceHandle = resource.backingResource;
// Always query the before state from ResourceManager (single source of truth)
var currentState = _resourceManager.ResourceDatabase.GetResourceBarrierData(resourceHandle).GetValueOrThrow();
var currentStateResult = _resourceManager.ResourceDatabase.GetResourceBarrierData(resourceHandle);
if (currentStateResult.IsFailure)
{
return currentStateResult.Error;
}
var currentState = currentStateResult.Value;
BarrierLayout layoutBefore;
BarrierAccess accessBefore;
@@ -168,7 +184,13 @@ internal sealed class RenderGraphExecutor
if (compiledBarrier.AliasingPredecessor.IsValid)
{
var predHandle = _resources.GetResource(compiledBarrier.AliasingPredecessor).backingResource;
var predState = _resourceManager.ResourceDatabase.GetResourceBarrierData(predHandle).GetValueOrThrow();
var predStateResult = _resourceManager.ResourceDatabase.GetResourceBarrierData(predHandle);
if (predStateResult.IsFailure)
{
return predStateResult.Error;
}
var predState = predStateResult.Value;
layoutBefore = BarrierLayout.Undefined;
accessBefore = BarrierAccess.NoAccess;
@@ -184,9 +206,9 @@ internal sealed class RenderGraphExecutor
var target = compiledBarrier.TargetState;
// Skip if already in target state (optimization)
if (!compiledBarrier.AliasingPredecessor.IsValid &&
layoutBefore == target.layout &&
accessBefore == target.access &&
if (!compiledBarrier.AliasingPredecessor.IsValid &&
layoutBefore == target.layout &&
accessBefore == target.access &&
syncBefore == target.sync)
{
continue;
@@ -218,5 +240,7 @@ internal sealed class RenderGraphExecutor
}
Flush();
return Error.None;
}
}

View File

@@ -1,61 +1,43 @@
using Ghost.Core;
using Ghost.Graphics.Core;
using Ghost.Graphics.RHI;
using Ghost.Core.Utilities;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using System.IO.Hashing;
namespace Ghost.Graphics.RenderGraphModule;
/// <summary>
/// Computes structural hashes of render graphs for compilation caching.
/// Hashes are based on graph topology and resource configurations, not runtime values.
/// </summary>
internal static class RenderGraphHasher
{
/// <summary>
/// Computes a hash of the entire render graph structure.
/// Used for cache invalidation - same hash means same compilation result.
/// </summary>
public static unsafe ulong ComputeGraphHash(List<RenderGraphPassBase> passes, RenderGraphResourceRegistry resources)
public static ulong ComputeGraphHash(List<RenderGraphPassBase> passes, RenderGraphResourceRegistry resources)
{
using var scope = AllocationManager.CreateStackScope();
var bufferPool = new UnsafeList<byte>(2048, scope.AllocationHandle);
var pData = (byte*)bufferPool.GetUnsafePtr();
var offset = 0;
var writer = new BufferWriter(2048, scope.AllocationHandle);
// Hash pass count
*(int*)(pData + offset) = passes.Count;
offset += sizeof(int);
writer.Write(passes.Count);
// Hash each pass structure (excluding names)
for (var i = 0; i < passes.Count; i++)
{
var pass = passes[i];
*(RenderPassType*)(pData + offset) = pass.type;
offset += sizeof(RenderPassType);
*(bool*)(pData + offset) = pass.allowCulling;
offset += sizeof(bool);
*(bool*)(pData + offset) = pass.asyncCompute;
offset += sizeof(bool);
writer.Write(pass.type);
writer.Write(pass.allowCulling);
writer.Write(pass.asyncCompute);
// Hash depth attachment
offset = ComputeTextureHash(pData, offset, pass.depthAccess.id, resources);
ComputeTextureHash(ref writer, pass.depthAccess.id, resources);
pData[offset] = (byte)pass.depthAccess.accessFlags;
offset += sizeof(AccessFlags);
writer.Write(pass.depthAccess.accessFlags);
writer.Write(pass.maxColorIndex);
*(int*)(pData + offset) = pass.maxColorIndex;
offset += sizeof(int);
for (var j = 0; j <= pass.maxColorIndex; j++)
{
offset = ComputeTextureHash(pData, offset, pass.colorAccess[j].id, resources);
pData[offset] = (byte)pass.colorAccess[j].accessFlags;
offset += sizeof(AccessFlags);
ComputeTextureHash(ref writer, pass.colorAccess[j].id, resources);
writer.Write(pass.colorAccess[j].accessFlags);
}
for (var j = 0; j < (int)RenderGraphResourceType.Count; j++)
@@ -64,45 +46,35 @@ internal static class RenderGraphHasher
var writeList = pass.resourceWrites[j];
var createList = pass.resourceCreates[j];
*(int*)(pData + offset) = readList.Count;
offset += sizeof(int);
writer.Write(readList.Count);
for (var k = 0; k < readList.Count; k++)
{
*(int*)(pData + offset) = readList[k].Value;
offset += sizeof(int);
writer.Write(readList[k].Value);
}
*(int*)(pData + offset) = writeList.Count;
offset += sizeof(int);
writer.Write(writeList.Count);
for (var k = 0; k < writeList.Count; k++)
{
*(int*)(pData + offset) = writeList[k].Value;
offset += sizeof(int);
writer.Write(writeList[k].Value);
}
*(int*)(pData + offset) = createList.Count;
offset += sizeof(int);
writer.Write(createList.Count);
for (var k = 0; k < createList.Count; k++)
{
*(int*)(pData + offset) = createList[k].Value;
offset += sizeof(int);
writer.Write(createList[k].Value);
}
*(int*)(pData + offset) = pass.randomAccess.Count;
offset += sizeof(int);
writer.Write(pass.randomAccess.Count);
for (var k = 0; k < pass.randomAccess.Count; k++)
{
*(int*)(pData + offset) = pass.randomAccess[k].Value;
offset += sizeof(int);
writer.Write(pass.randomAccess[k].Value);
}
}
*(int*)(pData + offset) = pass.GetRenderFuncHashCode();
offset += sizeof(int);
writer.Write(pass.GetRenderFuncHashCode());
}
var span = new Span<byte>(pData, offset);
return XxHash64.HashToUInt64(span);
return XxHash64.HashToUInt64(writer.AsSpan());
}
/// <summary>
@@ -110,68 +82,49 @@ internal static class RenderGraphHasher
/// For imported textures, hashes the backing handle.
/// For transient textures, hashes the descriptor (respecting size mode).
/// </summary>
private static unsafe int ComputeTextureHash(byte* pData, int offset, Identifier<RGTexture> texture, RenderGraphResourceRegistry resources)
private static void ComputeTextureHash(ref BufferWriter writer, Identifier<RGTexture> texture, RenderGraphResourceRegistry resources)
{
if (texture.IsInvalid)
{
return offset;
return;
}
var resource = resources.GetResource(texture.AsResource());
// Hash imported flag
*(pData + offset) = resource.isImported ? (byte)1 : (byte)0;
offset += sizeof(byte);
writer.Write(resource.isImported);
// For imported textures, hash the backing resource handle
if (resource.isImported)
{
*(int*)(pData + offset) = resource.backingResource.GetHashCode();
offset += sizeof(int);
return offset;
writer.Write(resource.backingResource.GetHashCode());
return;
}
var desc = resource.rgTextureDesc;
// Hash format (structural)
*(TextureFormat*)(pData + offset) = desc.format;
offset += sizeof(TextureFormat);
// Hash size mode (structural)
*(RGTextureSizeMode*)(pData + offset) = desc.sizeMode;
offset += sizeof(RGTextureSizeMode);
writer.Write(desc.format);
writer.Write(desc.sizeMode);
// Hash size specification based on mode
if (desc.sizeMode == RGTextureSizeMode.Absolute)
{
// Absolute mode: hash actual dimensions
*(uint*)(pData + offset) = desc.width;
offset += sizeof(uint);
*(uint*)(pData + offset) = desc.height;
offset += sizeof(uint);
writer.Write(desc.width);
writer.Write(desc.height);
}
else
{
// Relative mode: hash scale factors (NOT resolved dimensions)
*(float*)(pData + offset) = desc.scaleX;
offset += sizeof(float);
*(float*)(pData + offset) = desc.scaleY;
offset += sizeof(float);
writer.Write(desc.scaleX);
writer.Write(desc.scaleY);
}
// Hash other structural properties
*(TextureDimension*)(pData + offset) = desc.dimension;
offset += sizeof(TextureDimension);
*(uint*)(pData + offset) = desc.mipLevels;
offset += sizeof(uint);
*(TextureUsage*)(pData + offset) = desc.usage;
offset += sizeof(TextureUsage);
*(bool*)(pData + offset) = desc.clearAtFirstUse;
offset += sizeof(bool);
*(bool*)(pData + offset) = desc.discardAtLastUse;
offset += sizeof(bool);
return offset;
writer.Write(desc.dimension);
writer.Write(desc.mipLevels);
writer.Write(desc.usage);
writer.Write(desc.clearAtFirstUse);
writer.Write(desc.discardAtLastUse);
}
}

View File

@@ -1,5 +1,3 @@
using Ghost.Core;
namespace Ghost.Graphics.RenderGraphModule;
/// <summary>
@@ -9,35 +7,35 @@ namespace Ghost.Graphics.RenderGraphModule;
internal sealed class NativeRenderPass
{
public int index;
/// <summary>
/// Indices of logical passes merged into this native render pass.
/// </summary>
public readonly List<int> mergedPassIndices = new(4);
/// <summary>
/// Color attachments shared across all merged passes.
/// </summary>
public RenderTargetInfo[] colorAttachments = new RenderTargetInfo[8];
public int colorAttachmentCount;
/// <summary>
/// Depth-stencil attachment (optional).
/// </summary>
public DepthStencilInfo depthAttachment;
public bool hasDepthAttachment;
/// <summary>
/// Range of logical passes included in this native pass.
/// </summary>
public int firstLogicalPass;
public int lastLogicalPass;
/// <summary>
/// Whether UAV writes are allowed during this render pass.
/// </summary>
public bool allowUAVWrites;
public void Reset()
{
index = -1;

View File

@@ -44,7 +44,7 @@ internal abstract class RenderGraphPassBase
public RenderGraphPassBase()
{
for (int i = 0; i < (int)RenderGraphResourceType.Count; i++)
for (var i = 0; i < (int)RenderGraphResourceType.Count; i++)
{
resourceReads[i] = new List<Identifier<RGResource>>(8);
resourceWrites[i] = new List<Identifier<RGResource>>(4);

View File

@@ -1,5 +1,4 @@
using Ghost.Core;
using Ghost.Graphics.Core;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.Buffer;
@@ -85,15 +84,15 @@ internal sealed class RenderGraphResource
public int index;
public RenderGraphResourceType type;
// Resource descriptors (only one is valid based on type)
public RGTextureDesc rgTextureDesc;
public BufferDesc bufferDesc;
// Resolved dimensions (computed from rgTextureDesc + ViewState for textures)
public uint resolvedWidth;
public uint resolvedHeight;
public bool isImported;
public int firstUsePass = -1;
public int lastUsePass = -1;
@@ -145,8 +144,8 @@ internal sealed class RenderGraphResourceRegistry
{
get
{
int count = 0;
for (int i = 0; i < _resources.Count; i++)
var count = 0;
for (var i = 0; i < _resources.Count; i++)
{
if (_resources[i].type == RenderGraphResourceType.Texture)
count++;
@@ -158,8 +157,8 @@ internal sealed class RenderGraphResourceRegistry
{
get
{
int count = 0;
for (int i = 0; i < _resources.Count; i++)
var count = 0;
for (var i = 0; i < _resources.Count; i++)
{
if (_resources[i].type == RenderGraphResourceType.Buffer)
count++;
@@ -226,7 +225,7 @@ internal sealed class RenderGraphResourceRegistry
return new Identifier<RGTexture>(resource.index);
}
public Identifier<RGBuffer> ImportBuffer(ref readonly BufferDesc desc, Handle<GraphicsBuffer> buffer, string name)
{
var resource = _pool.Rent<RenderGraphResource>();
@@ -245,7 +244,7 @@ internal sealed class RenderGraphResourceRegistry
public Identifier<RGBuffer> CreateBuffer(ref readonly BufferDesc desc, string name)
{
var resource = _pool.Rent<RenderGraphResource>();
resource.name= name;
resource.name = name;
resource.type = RenderGraphResourceType.Buffer;
resource.index = _resources.Count;
resource.bufferDesc = desc;
@@ -260,17 +259,17 @@ internal sealed class RenderGraphResourceRegistry
{
return _resources[resource.Value];
}
public RenderGraphResource GetResource(Identifier<RGTexture> texture)
{
return _resources[texture.Value];
}
public RenderGraphResource GetResource(Identifier<RGBuffer> buffer)
{
return _resources[buffer.Value];
}
/// <summary>
/// Gets resource by global index. Use this when iterating over all resources.
/// </summary>
@@ -299,7 +298,7 @@ internal sealed class RenderGraphResourceRegistry
resource.firstUsePass = passIndex;
}
}
/// <summary>
/// Resolves texture sizes based on current view state.
/// Must be called after all resources are created and before compilation.
@@ -311,7 +310,7 @@ internal sealed class RenderGraphResourceRegistry
var res = _resources[i];
if (res.type != RenderGraphResourceType.Texture || res.isImported)
continue;
var desc = res.rgTextureDesc;
if (desc.sizeMode == RGTextureSizeMode.Absolute)
{

View File

@@ -1,5 +1,4 @@
using Ghost.Core;
using Ghost.Graphics.Core;
using Ghost.Graphics.RHI;
using System.Runtime.CompilerServices;
@@ -22,7 +21,7 @@ public enum RGTextureSizeMode : byte
/// Fixed pixel dimensions (width, height).
/// </summary>
Absolute,
/// <summary>
/// Scale relative to view state (scaleX * viewportWidth, scaleY * viewportHeight).
/// </summary>
@@ -40,7 +39,7 @@ public struct ViewState : IEquatable<ViewState>
// For upscalers that need to know the original render target size before upscaling
public uint actualWidth;
public uint actualHeight;
public ViewState(uint width, uint height, uint actualWidth, uint actualHeight)
{
viewportWidth = width;
@@ -48,28 +47,28 @@ public struct ViewState : IEquatable<ViewState>
this.actualWidth = actualWidth;
this.actualHeight = actualHeight;
}
public readonly bool Equals(ViewState other)
{
return viewportWidth == other.viewportWidth && viewportHeight == other.viewportHeight
&& actualWidth == other.actualWidth && actualHeight == other.actualHeight;
}
public override readonly bool Equals(object? obj)
{
return obj is ViewState other && Equals(other);
}
public override readonly int GetHashCode()
{
return HashCode.Combine(viewportWidth, viewportHeight);
}
public static bool operator ==(ViewState left, ViewState right)
{
return left.Equals(right);
}
public static bool operator !=(ViewState left, ViewState right)
{
return !left.Equals(right);
@@ -82,29 +81,29 @@ public struct ViewState : IEquatable<ViewState>
public struct RGTextureDesc : IEquatable<RGTextureDesc>
{
public RGTextureSizeMode sizeMode;
// Size specification (union-like - only one set is used based on sizeMode)
public uint width; // For Absolute mode
public uint height; // For Absolute mode
public float scaleX; // For Relative mode
public float scaleY; // For Relative mode
// Common texture properties
public TextureFormat format;
public TextureDimension dimension;
public uint mipLevels;
public uint slice;
public TextureUsage usage;
public bool clearAtFirstUse;
public bool discardAtLastUse;
// Clear operation support
public Color128 clearColor;
public float clearDepth;
public byte clearStencil;
/// <summary>
/// Creates a texture descriptor with absolute dimensions.
/// </summary>
@@ -137,7 +136,7 @@ public struct RGTextureDesc : IEquatable<RGTextureDesc>
usage = usage
};
}
/// <summary>
/// Creates a texture descriptor with relative dimensions (uniform scale).
/// </summary>
@@ -169,7 +168,7 @@ public struct RGTextureDesc : IEquatable<RGTextureDesc>
usage = usage
};
}
/// <summary>
/// Creates a texture descriptor with relative dimensions (non-uniform scale).
/// </summary>
@@ -203,7 +202,7 @@ public struct RGTextureDesc : IEquatable<RGTextureDesc>
};
}
/// <summary>
/// Creates a depth texture descriptor with relative dimensions.
/// </summary>
@@ -233,7 +232,7 @@ public struct RGTextureDesc : IEquatable<RGTextureDesc>
};
}
/// <summary>
/// Converts to RHI TextureDesc using resolved dimensions.
/// </summary>
@@ -250,7 +249,7 @@ public struct RGTextureDesc : IEquatable<RGTextureDesc>
Usage = usage
};
}
public readonly bool Equals(RGTextureDesc other)
{
return sizeMode == other.sizeMode &&
@@ -266,12 +265,12 @@ public struct RGTextureDesc : IEquatable<RGTextureDesc>
: scaleX == other.scaleX && scaleY == other.scaleY);
}
public override readonly bool Equals(object? obj)
{
return obj is RGTextureDesc other && Equals(other);
}
public override readonly int GetHashCode()
{
if (sizeMode == RGTextureSizeMode.Absolute)
@@ -283,12 +282,12 @@ public struct RGTextureDesc : IEquatable<RGTextureDesc>
return HashCode.Combine(sizeMode, scaleX, scaleY, format, dimension, mipLevels, slice, usage);
}
}
public static bool operator ==(RGTextureDesc left, RGTextureDesc right)
{
return left.Equals(right);
}
public static bool operator !=(RGTextureDesc left, RGTextureDesc right)
{
return !left.Equals(right);

View File

@@ -107,8 +107,8 @@ internal class MeshRenderPass : IRenderPass
private void CompileBlitShader(ref readonly RenderingContext ctx)
{
var shaderDescriptor = DSLShaderCompiler.CompileShader("F:/csharp/GhostEngine/src/Runtime/Ghost.Graphics/Shaders/Blit.gshdr", "C:/Users/Misaki/Downloads/Archive").GetValueOrThrow();
_blitShader = ctx.ResourceAllocator.CreateGraphicsShader(shaderDescriptor);
_blitMaterial = ctx.ResourceAllocator.CreateMaterial(_blitShader);
_blitShader = ctx.ResourceManager.CreateGraphicsShader(shaderDescriptor);
_blitMaterial = ctx.ResourceManager.CreateMaterial(_blitShader);
var config = new ShaderCompilationConfig
{
@@ -132,8 +132,8 @@ internal class MeshRenderPass : IRenderPass
var shaderDescriptor = DSLShaderCompiler.CompileShader("F:/csharp/GhostEngine/src/Runtime/Ghost.Graphics/test.gshdr", "C:/Users/Misaki/Downloads/Archive").GetValueOrThrow();
_shader = ctx.ResourceAllocator.CreateGraphicsShader(shaderDescriptor);
_material = ctx.ResourceAllocator.CreateMaterial(_shader);
_shader = ctx.ResourceManager.CreateGraphicsShader(shaderDescriptor);
_material = ctx.ResourceManager.CreateMaterial(_shader);
for (var i = 0; i < shaderDescriptor.passes.Length; i++)
{
@@ -320,7 +320,7 @@ internal class MeshRenderPass : IRenderPass
{
foreach (var texture in _textures)
{
resourceManager.ResourceDatabase.ScheduleReleaseResource(texture.AsResource());
resourceManager.ResourceDatabase.ReleaseResource(texture.AsResource());
}
}
}

View File

@@ -222,14 +222,13 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
{
ObjectDisposedException.ThrowIf(_disposed, this);
ref var mesh = ref _meshes.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
if (!exist)
if (!_meshes.TryGetElementAt(handle.ID, handle.Generation, out var mesh))
{
return;
}
ReleaseResource(mesh);
_meshes.Remove(handle.ID, handle.Generation);
mesh.ReleaseResource(_resourceDatabase);
}
public bool HasMaterial(Handle<Material> handle)
@@ -253,14 +252,14 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
{
ObjectDisposedException.ThrowIf(_disposed, this);
ref var material = ref _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
var material = _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
if (!exist)
{
return;
}
ReleaseResource(material);
_materials.Remove(handle.ID, handle.Generation);
material.ReleaseResource(_resourceDatabase);
}
public bool HasShader(Identifier<Shader> id)
@@ -288,14 +287,8 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
return;
}
ref var shader = ref _shaders[id.Value]!;
ReleaseResource(shader);
}
private void ReleaseResource<T>(T resource)
where T : IResourceReleasable
{
resource.ReleaseResource(_resourceDatabase);
var shader = _shaders[id.Value];
shader.ReleaseResource(_resourceDatabase);
}
public void Dispose()
@@ -307,17 +300,17 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
foreach (var mesh in _meshes)
{
ReleaseResource(mesh);
mesh.ReleaseResource(_resourceDatabase);
}
foreach (var material in _materials)
{
ReleaseResource(material);
material.ReleaseResource(_resourceDatabase);
}
foreach (var shader in _shaders)
{
ReleaseResource(shader);
shader.ReleaseResource(_resourceDatabase);
}
_meshes.Dispose();