feat(render): add meshlet rendering and ECS query ref API

Introduces meshlet-based rendering pipeline with new HLSL structures and push constant layouts. Refactors meshlet upload/cooking, updates RenderGraphContext for global/view/instance data, and enhances ECS QueryBuilder with ref returns and [UnscopedRef] for fluent chaining. Improves resource management and disposal patterns, updates D3D12 interop for compatibility, and refines test/app infrastructure. Includes dependency updates, bug fixes, and code cleanups.
This commit is contained in:
2026-03-25 20:27:46 +09:00
parent b729ca86f5
commit 447a4e6904
28 changed files with 407 additions and 165 deletions

View File

@@ -1,4 +1,4 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Ghost.Shader.Test")]
[assembly: InternalsVisibleTo("Ghost.Graphics")]
[assembly: InternalsVisibleTo("Ghost.Graphics.Test")]

View File

@@ -48,6 +48,6 @@ internal partial class ConsoleViewModel : ObservableObject
[RelayCommand]
private void ClearLogs()
{
Logger.Clear();
//Logger.Clear();
}
}

View File

@@ -19,9 +19,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Misaki.HighPerformance" Version="1.0.4" />
<PackageReference Include="Misaki.HighPerformance" Version="1.0.5" />
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="1.5.3" />
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.5.2">
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.5.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -92,6 +92,7 @@ public class RenderExtractionSystem : ISystem
var rtSize = new uint2(rtResult.Value.TextureDescription.Width, rtResult.Value.TextureDescription.Height);
var aspectScreen = (float)rtSize.x / rtSize.y;
// TODO: Classify transparent objects into a separate render list and render via oit.
var renderList = new RenderList(1, 64, Allocator.FreeList);
var transparentRenderList = new RenderList(1, 64, Allocator.FreeList);
var shadowCasterRenderList = new RenderList(1, 64, Allocator.FreeList);
@@ -215,7 +216,7 @@ public class RenderExtractionSystem : ISystem
depthTarget = camRef.depthTarget,
opaqueRenderList = renderList,
shadowCasterRenderList = shadowCasterRenderList,
transparentRenderList = default, // TODO: Classify transparent objects into a separate render list and render via oit.
transparentRenderList = transparentRenderList,
renderFunc = camRef.renderFunc,
view = new RenderView
{

View File

@@ -636,6 +636,10 @@ public ref partial struct QueryBuilder : IDisposable
{
Dispose();
}
else
{
Clear();
}
return query;
}

View File

@@ -340,7 +340,7 @@ public abstract class SystemGroup : ISystem
public sealed class DefaultSystemGroup : SystemGroup;
public sealed class SystemManager
public sealed class SystemManager : IDisposable
{
private readonly World _world;
@@ -415,4 +415,9 @@ public sealed class SystemManager
system.Cleanup(in systemAPI);
}
}
public void Dispose()
{
CleanupAll(default);
}
}

View File

@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace Ghost.Entities;
@@ -10,12 +11,13 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types and those component(s) must be enabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAll<T0>()
[UnscopedRef]
public ref QueryBuilder WithAll<T0>()
where T0 : unmanaged, IComponent
{
_all.Add(ComponentTypeID<T0>.Value);
return this;
return ref this;
}
/// <summary>
@@ -23,13 +25,14 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types and those component(s) must be enabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAllRW<T0>()
[UnscopedRef]
public ref QueryBuilder WithAllRW<T0>()
where T0 : unmanaged, IComponent
{
_all.Add(ComponentTypeID<T0>.Value);
_rw.Add(ComponentTypeID<T0>.Value);
return this;
return ref this;
}
/// <summary>
@@ -37,12 +40,13 @@ public ref partial struct QueryBuilder
/// Targets entities that have at least one of the specified component types and those component(s) must be enabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAny<T0>()
[UnscopedRef]
public ref QueryBuilder WithAny<T0>()
where T0 : unmanaged, IComponent
{
_any.Add(ComponentTypeID<T0>.Value);
return this;
return ref this;
}
/// <summary>
@@ -50,12 +54,13 @@ public ref partial struct QueryBuilder
/// Targets entities that do not have any of the specified component types.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAbsent<T0>()
[UnscopedRef]
public ref QueryBuilder WithAbsent<T0>()
where T0 : unmanaged, IComponent
{
_absent.Add(ComponentTypeID<T0>.Value);
return this;
return ref this;
}
/// <summary>
@@ -63,12 +68,13 @@ public ref partial struct QueryBuilder
/// Targets entities that do not have any of the specified component types, or those component(s) are disabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithNone<T0>()
[UnscopedRef]
public ref QueryBuilder WithNone<T0>()
where T0 : unmanaged, IComponent
{
_none.Add(ComponentTypeID<T0>.Value);
return this;
return ref this;
}
/// <summary>
@@ -76,12 +82,13 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types and those component(s) are disabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithDisabled<T0>()
[UnscopedRef]
public ref QueryBuilder WithDisabled<T0>()
where T0 : unmanaged, IEnableableComponent
{
_disabled.Add(ComponentTypeID<T0>.Value);
return this;
return ref this;
}
/// <summary>
@@ -89,12 +96,13 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithPresent<T0>()
[UnscopedRef]
public ref QueryBuilder WithPresent<T0>()
where T0 : unmanaged, IComponent
{
_present.Add(ComponentTypeID<T0>.Value);
return this;
return ref this;
}
/// <summary>
@@ -102,13 +110,14 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithPresentRW<T0>()
[UnscopedRef]
public ref QueryBuilder WithPresentRW<T0>()
where T0 : unmanaged, IComponent
{
_present.Add(ComponentTypeID<T0>.Value);
_rw.Add(ComponentTypeID<T0>.Value);
return this;
return ref this;
}
/// <summary>
@@ -116,14 +125,15 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types and those component(s) must be enabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAll<T0, T1>()
[UnscopedRef]
public ref QueryBuilder WithAll<T0, T1>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
{
_all.Add(ComponentTypeID<T0>.Value);
_all.Add(ComponentTypeID<T1>.Value);
return this;
return ref this;
}
/// <summary>
@@ -131,7 +141,8 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types and those component(s) must be enabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAllRW<T0, T1>()
[UnscopedRef]
public ref QueryBuilder WithAllRW<T0, T1>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
{
@@ -140,7 +151,7 @@ public ref partial struct QueryBuilder
_all.Add(ComponentTypeID<T1>.Value);
_rw.Add(ComponentTypeID<T1>.Value);
return this;
return ref this;
}
/// <summary>
@@ -148,14 +159,15 @@ public ref partial struct QueryBuilder
/// Targets entities that have at least one of the specified component types and those component(s) must be enabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAny<T0, T1>()
[UnscopedRef]
public ref QueryBuilder WithAny<T0, T1>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
{
_any.Add(ComponentTypeID<T0>.Value);
_any.Add(ComponentTypeID<T1>.Value);
return this;
return ref this;
}
/// <summary>
@@ -163,14 +175,15 @@ public ref partial struct QueryBuilder
/// Targets entities that do not have any of the specified component types.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAbsent<T0, T1>()
[UnscopedRef]
public ref QueryBuilder WithAbsent<T0, T1>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
{
_absent.Add(ComponentTypeID<T0>.Value);
_absent.Add(ComponentTypeID<T1>.Value);
return this;
return ref this;
}
/// <summary>
@@ -178,14 +191,15 @@ public ref partial struct QueryBuilder
/// Targets entities that do not have any of the specified component types, or those component(s) are disabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithNone<T0, T1>()
[UnscopedRef]
public ref QueryBuilder WithNone<T0, T1>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
{
_none.Add(ComponentTypeID<T0>.Value);
_none.Add(ComponentTypeID<T1>.Value);
return this;
return ref this;
}
/// <summary>
@@ -193,14 +207,15 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types and those component(s) are disabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithDisabled<T0, T1>()
[UnscopedRef]
public ref QueryBuilder WithDisabled<T0, T1>()
where T0 : unmanaged, IEnableableComponent
where T1 : unmanaged, IEnableableComponent
{
_disabled.Add(ComponentTypeID<T0>.Value);
_disabled.Add(ComponentTypeID<T1>.Value);
return this;
return ref this;
}
/// <summary>
@@ -208,14 +223,15 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithPresent<T0, T1>()
[UnscopedRef]
public ref QueryBuilder WithPresent<T0, T1>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
{
_present.Add(ComponentTypeID<T0>.Value);
_present.Add(ComponentTypeID<T1>.Value);
return this;
return ref this;
}
/// <summary>
@@ -223,7 +239,8 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithPresentRW<T0, T1>()
[UnscopedRef]
public ref QueryBuilder WithPresentRW<T0, T1>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
{
@@ -232,7 +249,7 @@ public ref partial struct QueryBuilder
_present.Add(ComponentTypeID<T1>.Value);
_rw.Add(ComponentTypeID<T1>.Value);
return this;
return ref this;
}
/// <summary>
@@ -240,7 +257,8 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types and those component(s) must be enabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAll<T0, T1, T2>()
[UnscopedRef]
public ref QueryBuilder WithAll<T0, T1, T2>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -249,7 +267,7 @@ public ref partial struct QueryBuilder
_all.Add(ComponentTypeID<T1>.Value);
_all.Add(ComponentTypeID<T2>.Value);
return this;
return ref this;
}
/// <summary>
@@ -257,7 +275,8 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types and those component(s) must be enabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAllRW<T0, T1, T2>()
[UnscopedRef]
public ref QueryBuilder WithAllRW<T0, T1, T2>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -269,7 +288,7 @@ public ref partial struct QueryBuilder
_all.Add(ComponentTypeID<T2>.Value);
_rw.Add(ComponentTypeID<T2>.Value);
return this;
return ref this;
}
/// <summary>
@@ -277,7 +296,8 @@ public ref partial struct QueryBuilder
/// Targets entities that have at least one of the specified component types and those component(s) must be enabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAny<T0, T1, T2>()
[UnscopedRef]
public ref QueryBuilder WithAny<T0, T1, T2>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -286,7 +306,7 @@ public ref partial struct QueryBuilder
_any.Add(ComponentTypeID<T1>.Value);
_any.Add(ComponentTypeID<T2>.Value);
return this;
return ref this;
}
/// <summary>
@@ -294,7 +314,8 @@ public ref partial struct QueryBuilder
/// Targets entities that do not have any of the specified component types.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAbsent<T0, T1, T2>()
[UnscopedRef]
public ref QueryBuilder WithAbsent<T0, T1, T2>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -303,7 +324,7 @@ public ref partial struct QueryBuilder
_absent.Add(ComponentTypeID<T1>.Value);
_absent.Add(ComponentTypeID<T2>.Value);
return this;
return ref this;
}
/// <summary>
@@ -311,7 +332,8 @@ public ref partial struct QueryBuilder
/// Targets entities that do not have any of the specified component types, or those component(s) are disabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithNone<T0, T1, T2>()
[UnscopedRef]
public ref QueryBuilder WithNone<T0, T1, T2>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -320,7 +342,7 @@ public ref partial struct QueryBuilder
_none.Add(ComponentTypeID<T1>.Value);
_none.Add(ComponentTypeID<T2>.Value);
return this;
return ref this;
}
/// <summary>
@@ -328,7 +350,8 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types and those component(s) are disabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithDisabled<T0, T1, T2>()
[UnscopedRef]
public ref QueryBuilder WithDisabled<T0, T1, T2>()
where T0 : unmanaged, IEnableableComponent
where T1 : unmanaged, IEnableableComponent
where T2 : unmanaged, IEnableableComponent
@@ -337,7 +360,7 @@ public ref partial struct QueryBuilder
_disabled.Add(ComponentTypeID<T1>.Value);
_disabled.Add(ComponentTypeID<T2>.Value);
return this;
return ref this;
}
/// <summary>
@@ -345,7 +368,8 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithPresent<T0, T1, T2>()
[UnscopedRef]
public ref QueryBuilder WithPresent<T0, T1, T2>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -354,7 +378,7 @@ public ref partial struct QueryBuilder
_present.Add(ComponentTypeID<T1>.Value);
_present.Add(ComponentTypeID<T2>.Value);
return this;
return ref this;
}
/// <summary>
@@ -362,7 +386,8 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithPresentRW<T0, T1, T2>()
[UnscopedRef]
public ref QueryBuilder WithPresentRW<T0, T1, T2>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -374,7 +399,7 @@ public ref partial struct QueryBuilder
_present.Add(ComponentTypeID<T2>.Value);
_rw.Add(ComponentTypeID<T2>.Value);
return this;
return ref this;
}
}

View File

@@ -4,6 +4,7 @@
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ include file="Helpers.ttinclude" #>
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace Ghost.Entities;
@@ -21,14 +22,15 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types and those component(s) must be enabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAll<<#= generics #>>()
[UnscopedRef]
public ref QueryBuilder WithAll<<#= generics #>>()
<#= restrictions #>
{
<# for (var j = 0; j < i; j++) { #>
_all.Add(ComponentTypeID<T<#= j #>>.Value);
<# } #>
return this;
return ref this;
}
/// <summary>
@@ -36,7 +38,8 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types and those component(s) must be enabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAllRW<<#= generics #>>()
[UnscopedRef]
public ref QueryBuilder WithAllRW<<#= generics #>>()
<#= restrictions #>
{
<# for (var j = 0; j < i; j++) { #>
@@ -44,7 +47,7 @@ public ref partial struct QueryBuilder
_rw.Add(ComponentTypeID<T<#= j #>>.Value);
<# } #>
return this;
return ref this;
}
/// <summary>
@@ -52,14 +55,15 @@ public ref partial struct QueryBuilder
/// Targets entities that have at least one of the specified component types and those component(s) must be enabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAny<<#= generics #>>()
[UnscopedRef]
public ref QueryBuilder WithAny<<#= generics #>>()
<#= restrictions #>
{
<# for (var j = 0; j < i; j++) { #>
_any.Add(ComponentTypeID<T<#= j #>>.Value);
<# } #>
return this;
return ref this;
}
/// <summary>
@@ -67,14 +71,15 @@ public ref partial struct QueryBuilder
/// Targets entities that do not have any of the specified component types.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAbsent<<#= generics #>>()
[UnscopedRef]
public ref QueryBuilder WithAbsent<<#= generics #>>()
<#= restrictions #>
{
<# for (var j = 0; j < i; j++) { #>
_absent.Add(ComponentTypeID<T<#= j #>>.Value);
<# } #>
return this;
return ref this;
}
/// <summary>
@@ -82,14 +87,15 @@ public ref partial struct QueryBuilder
/// Targets entities that do not have any of the specified component types, or those component(s) are disabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithNone<<#= generics #>>()
[UnscopedRef]
public ref QueryBuilder WithNone<<#= generics #>>()
<#= restrictions #>
{
<# for (var j = 0; j < i; j++) { #>
_none.Add(ComponentTypeID<T<#= j #>>.Value);
<# } #>
return this;
return ref this;
}
/// <summary>
@@ -97,14 +103,15 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types and those component(s) are disabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithDisabled<<#= generics #>>()
[UnscopedRef]
public ref QueryBuilder WithDisabled<<#= generics #>>()
<#= enableRestrictions #>
{
<# for (var j = 0; j < i; j++) { #>
_disabled.Add(ComponentTypeID<T<#= j #>>.Value);
<# } #>
return this;
return ref this;
}
/// <summary>
@@ -112,14 +119,15 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithPresent<<#= generics #>>()
[UnscopedRef]
public ref QueryBuilder WithPresent<<#= generics #>>()
<#= restrictions #>
{
<# for (var j = 0; j < i; j++) { #>
_present.Add(ComponentTypeID<T<#= j #>>.Value);
<# } #>
return this;
return ref this;
}
/// <summary>
@@ -127,7 +135,8 @@ public ref partial struct QueryBuilder
/// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithPresentRW<<#= generics #>>()
[UnscopedRef]
public ref QueryBuilder WithPresentRW<<#= generics #>>()
<#= restrictions #>
{
<# for (var j = 0; j < i; j++) { #>
@@ -135,7 +144,7 @@ public ref partial struct QueryBuilder
_rw.Add(ComponentTypeID<T<#= j #>>.Value);
<# } #>
return this;
return ref this;
}
<# } #>

View File

@@ -268,6 +268,9 @@ public partial class World : IDisposable, IEquatable<World>
}
}
_componentManager.Dispose();
_systemManager.Dispose();
s_freeWorldSlots.Enqueue(_id);
s_worlds[_id] = null;

View File

@@ -10,9 +10,9 @@ namespace Ghost.Graphics.D3D12;
/// <summary>
/// D3D12 implementation of command queue interface
/// </summary>
internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue1>, ICommandQueue
internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, ICommandQueue
{
private UniquePtr<ID3D12Fence1> _fence;
private UniquePtr<ID3D12Fence> _fence;
private readonly AutoResetEvent _fenceEvent;
private ulong _fenceValue;
@@ -22,7 +22,7 @@ internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue1>, ICom
get;
}
private static ID3D12CommandQueue1* CreateCommandQueue(ID3D12Device14* device, CommandQueueType type)
private static ID3D12CommandQueue* CreateCommandQueue(ID3D12Device14* device, CommandQueueType type)
{
var queueDesc = new D3D12_COMMAND_QUEUE_DESC
{
@@ -31,7 +31,7 @@ internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue1>, ICom
Flags = D3D12_COMMAND_QUEUE_FLAGS.D3D12_COMMAND_QUEUE_FLAG_NONE,
};
ID3D12CommandQueue1* pQueue = default;
ID3D12CommandQueue* pQueue = default;
ThrowIfFailed(device->CreateCommandQueue(&queueDesc, __uuidof(pQueue), (void**)&pQueue));
return pQueue;
}
@@ -43,7 +43,7 @@ internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue1>, ICom
_fenceEvent = new AutoResetEvent(false);
_fenceValue = 0;
ID3D12Fence1* pFence = default;
ID3D12Fence* pFence = default;
ThrowIfFailed(device.NativeObject.Get()->CreateFence(0, D3D12_FENCE_FLAGS.D3D12_FENCE_FLAG_NONE, __uuidof(pFence), (void**)&pFence));
_fence.Attach(pFence);

View File

@@ -160,6 +160,16 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
return;
}
while (_commandBufferReturnQueue.TryDequeue(out var entry))
{
entry.commandBuffer.Dispose();
}
while (_commandBufferPool.TryDequeue(out var cmd))
{
cmd.Dispose();
}
_resourceDatabase.ReleaseAllResourcesImmediately();
_resourceAllocator.Dispose();

View File

@@ -29,9 +29,9 @@ internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDe
public SharedPtr<IDXGIFactory7> DXGIFactory => _dxgiFactory.Share();
public SharedPtr<IDXGIAdapter1> Adapter => _adapter.Share();
public SharedPtr<ID3D12CommandQueue1> NativeGraphicsQueue => _graphicsQueue.NativeObject;
public SharedPtr<ID3D12CommandQueue1> NativeComputeQueue => _computeQueue.NativeObject;
public SharedPtr<ID3D12CommandQueue1> NativeCopyQueue => _copyQueue.NativeObject;
public SharedPtr<ID3D12CommandQueue> NativeGraphicsQueue => _graphicsQueue.NativeObject;
public SharedPtr<ID3D12CommandQueue> NativeComputeQueue => _computeQueue.NativeObject;
public SharedPtr<ID3D12CommandQueue> NativeCopyQueue => _copyQueue.NativeObject;
public D3D12RenderDevice()
:base(CreateDevice(out var dxgiFactory, out var adapter))

View File

@@ -53,8 +53,9 @@ internal static unsafe class D3D12Utility
var ptr = uPtr.Get();
if (ptr != null)
{
Debug.Assert(ptr != other);
var refCount = ptr->Release();
Debug.Assert((refCount != 0) || (ptr != other));
Debug.Assert(refCount == 0);
}
uPtr = new UniquePtr<T>(other);
@@ -68,7 +69,7 @@ internal static unsafe class D3D12Utility
if (ptr != null)
{
var refCount = ptr->Release();
Debug.Assert(refCount != 0);
//Debug.Assert(refCount == 0);
}
}
@@ -433,7 +434,7 @@ internal static unsafe class D3D12Utility
{
var flags = D3D12_RESOURCE_FLAG_NONE;
if (usage.HasFlag(BufferUsage.Raw) || usage.HasFlag(BufferUsage.UnorderedAccess))
if (usage.HasFlag(BufferUsage.UnorderedAccess))
{
flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
}

View File

@@ -194,7 +194,7 @@ public struct Mesh : IResourceReleasable
this.ComputeBounds();
}
public readonly void ReleaseCpuResources()
public void ReleaseCpuResources()
{
_vertices.Dispose();
_indices.Dispose();
@@ -238,7 +238,7 @@ public struct Mesh : IResourceReleasable
MeshletUtility.Build(config, clodMesh, Unsafe.AsPointer(ref this), MeshletOutputCallback);
}
private static unsafe int MeshletOutputCallback(void* context, ClodGroup group, ReadOnlyUnsafeCollection<ClodCluster>clusters)
private static unsafe int MeshletOutputCallback(void* context, ClodGroup group, ReadOnlyUnsafeCollection<ClodCluster> clusters)
{
var mesh = (Mesh*)context;
ref var data = ref mesh->_meshletData;
@@ -277,13 +277,13 @@ public struct Mesh : IResourceReleasable
data.meshletVertices.Add(cluster.uniqueVertices[j]);
}
// Add local triangles (packed into uints)
nuint triangleCount = cluster.localIndexCount / 3;
var triangleCount = cluster.localIndexCount / 3;
for (nuint j = 0; j < triangleCount; j++)
{
uint i0 = cluster.localIndices[j * 3 + 0];
uint i1 = cluster.localIndices[j * 3 + 1];
uint i2 = cluster.localIndices[j * 3 + 2];
uint packedTriangle = i0 | (i1 << 8) | (i2 << 16);
var packedTriangle = i0 | (i1 << 8) | (i2 << 16);
data.meshletTriangles.Add(packedTriangle);
}
}
@@ -291,7 +291,7 @@ public struct Mesh : IResourceReleasable
return 0;
}
public readonly void ReleaseResource(IResourceDatabase database)
public void ReleaseResource(IResourceDatabase database)
{
ReleaseCpuResources();

View File

@@ -38,7 +38,7 @@ public struct RenderList : IDisposable
{
while (_listIndex < _length)
{
if (_itemIndex < _pList[_listIndex].Count)
if (_itemIndex < _pList[_listIndex].Count - 1)
{
_itemIndex++;
return true;

View File

@@ -93,7 +93,7 @@ public readonly unsafe ref struct RenderingContext
return mesh;
}
ref readonly var meshData = ref r.Value;
ref var meshData = ref r.Value;
var vertexHandle = meshData.VertexBuffer.AsResource();
var indexHandle = meshData.IndexBuffer.AsResource();
@@ -105,9 +105,12 @@ public readonly unsafe ref struct RenderingContext
if (staticMesh)
{
meshData.CookMeshlets();
meshData.ReleaseCpuResources();
TransitionBarrier(vertexHandle, false, BarrierLayout.Undefined, BarrierAccess.ShaderResource, BarrierSync.VertexShading);
TransitionBarrier(indexHandle, false, BarrierLayout.Undefined, BarrierAccess.IndexBuffer, BarrierSync.IndexInput);
UploadMeshlets(mesh);
}
return mesh;
@@ -205,17 +208,7 @@ public readonly unsafe ref struct RenderingContext
_directCmd.UploadBuffer(meshRef.MeshLetBuffer, meshletData.meshlets.AsSpan());
_directCmd.UploadBuffer(meshRef.MeshletVerticesBuffer, meshletData.meshletVertices.AsSpan());
// Padding for triangle data if needed
if (trianglesSize > meshletData.meshletTriangles.Count)
{
var paddedData = new uint[trianglesSize];
meshletData.meshletTriangles.AsSpan().CopyTo(paddedData);
_directCmd.UploadBuffer(meshRef.MeshletTrianglesBuffer, paddedData.AsSpan());
}
else
{
_directCmd.UploadBuffer(meshRef.MeshletTrianglesBuffer, meshletData.meshletTriangles.AsSpan());
}
TransitionBarrier(meshRef.MeshLetBuffer.AsResource(), false, BarrierLayout.Undefined, BarrierAccess.ShaderResource, BarrierSync.NonPixelShading | BarrierSync.PixelShading);
TransitionBarrier(meshRef.MeshletVerticesBuffer.AsResource(), false, BarrierLayout.Undefined, BarrierAccess.ShaderResource, BarrierSync.NonPixelShading | BarrierSync.PixelShading);

View File

@@ -18,7 +18,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Misaki.HighPerformance.Analyzer" Version="1.0.0">
<PackageReference Include="Misaki.HighPerformance.Analyzer" Version="1.1.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -1,4 +1,5 @@
using Ghost.Core;
#if false
// Obsolete
using Ghost.Core.Graphics;
using Ghost.DSL.ShaderCompiler;
using Ghost.Graphics.Core;
@@ -335,3 +336,4 @@ internal class MeshRenderPass : IRenderPass
}
}
}
#endif

View File

@@ -22,6 +22,9 @@ public interface IRasterRenderContext : IRenderGraphContext
{
int ActiveMeshIndexCount { get; }
void SetGlobalData(uint globalIndex, uint viewIndex);
void SetInstanceIndex(uint instanceIndex);
void SetActiveMaterial(Handle<Material> material);
void SetActiveMaterial(ref readonly Material material);
void SetActiveMesh(Handle<Mesh> mesh);
@@ -58,6 +61,10 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
private Handle<GraphicsBuffer> _activePerMeshData;
private int _activeMeshIndexCount;
private uint _activeGlobalIndex;
private uint _activeViewIndex;
private uint _activeInstanceIndex;
public ResourceManager ResourceManager => _resourceManager;
public IResourceDatabase ResourceDatabase => _resourceDatabase;
@@ -221,12 +228,26 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
_activeMeshIndexCount = mesh.IndexCount;
}
public void SetGlobalData(uint globalIndex, uint viewIndex)
{
_activeGlobalIndex = globalIndex;
_activeViewIndex = viewIndex;
}
public void SetInstanceIndex(uint instanceIndex)
{
_activeInstanceIndex = instanceIndex;
}
public unsafe void DispatchMesh(uint3 threadGroupCount)
{
// TODO: Global and view constants
// TODO: Global, view, and instance constants
var data = new PushConstantsData
{
globalIndex = _activeGlobalIndex,
viewIndex = _activeViewIndex,
objectIndex = _resourceDatabase.GetBindlessIndex(_activePerMeshData.AsResource()),
instanceIndex = _activeInstanceIndex,
materialIndex = _resourceDatabase.GetBindlessIndex(_activePerMaterialData.AsResource()),
};

View File

@@ -8,18 +8,6 @@ struct PixelInput
float4 uv : TEXCOORD0;
};
struct Meshlet
{
float4 boundingSphere;
float3 boundingBoxMin;
float3 boundingBoxMax;
uint vertexOffset;
uint triangleOffset;
uint groupIndex;
float parentError;
uint packedCounts; // byte vertexCount, byte triangleCount, byte localMaterialIndex, byte lodLevel
};
[numthreads(128, 1, 1)] // 128 threads to cover max 64 vertices and 124 triangles
[outputtopology("triangle")]
void MSMain(
@@ -50,7 +38,9 @@ void MSMain(
// Basic MVP transform not needed if already in world space, but usually we need localToWorld and ViewProj
PerViewData perViewData = LoadData<PerViewData>(g_PushConstantData.perViewBuffer, 0);
float4 worldPos = mul(perObjectData.localToWorld, float4(v.position.xyz, 1.0f));
PerInstanceData perInstanceData = LoadData<PerInstanceData>(g_PushConstantData.perInstanceBuffer, 0);
float4 worldPos = mul(perInstanceData.localToWorld, float4(v.position.xyz, 1.0f));
outVerts[groupThreadID.x].position = mul(perViewData.viewMatrix, worldPos);
outVerts[groupThreadID.x].position = mul(perViewData.projectionMatrix, outVerts[groupThreadID.x].position);
@@ -76,13 +66,13 @@ void MSMain(
float4 PSMain(PixelInput input) : SV_TARGET
{
PerMaterialData perMaterialData = LoadData<PerMaterialData>(g_PushConstantData.perMaterialBuffer, 0);
float4 color1 = SAMPLE_TEXTURE2D(perMaterialData.texture1, perMaterialData.tex_sampler, input.uv.xy);
float4 color2 = SAMPLE_TEXTURE2D(perMaterialData.texture2, perMaterialData.tex_sampler, input.uv.xy);
float4 color3 = SAMPLE_TEXTURE2D(perMaterialData.texture3, perMaterialData.tex_sampler, input.uv.xy);
float4 color4 = SAMPLE_TEXTURE2D(perMaterialData.texture4, perMaterialData.tex_sampler, input.uv.xy);
float4 blendedColor = (color1 + color2 + color3 + color4) * 0.25f;
return perMaterialData.color * blendedColor + input.color;
// PerMaterialData perMaterialData = LoadData<PerMaterialData>(g_PushConstantData.perMaterialBuffer, 0);
//
// float4 color1 = SAMPLE_TEXTURE2D(perMaterialData.texture1, perMaterialData.tex_sampler, input.uv.xy);
// float4 color2 = SAMPLE_TEXTURE2D(perMaterialData.texture2, perMaterialData.tex_sampler, input.uv.xy);
// float4 color3 = SAMPLE_TEXTURE2D(perMaterialData.texture3, perMaterialData.tex_sampler, input.uv.xy);
// float4 color4 = SAMPLE_TEXTURE2D(perMaterialData.texture4, perMaterialData.tex_sampler, input.uv.xy);
//
// float4 blendedColor = (color1 + color2 + color3 + color4) * 0.25f;
// return perMaterialData.color * blendedColor + input.color;
}

View File

@@ -422,6 +422,7 @@ public class RenderSystem : IDisposable
}
_renderPipeline.Dispose();
_resourceManager.Dispose();
_graphicsEngine.Dispose();
_shutdownEvent.Dispose();

View File

@@ -10,6 +10,18 @@ struct Vertex
float4 color;
};
struct Meshlet
{
float4 boundingSphere;
float3 boundingBoxMin;
float3 boundingBoxMax;
uint vertexOffset;
uint triangleOffset;
uint groupIndex;
float parentError;
uint packedCounts; // byte vertexCount, byte triangleCount, byte localMaterialIndex, byte lodLevel
};
// Resource descriptor heap definitions
#define GLOBAL_TEXTURE2D_HEAP ResourceDescriptorHeap

View File

@@ -5,11 +5,20 @@
struct PushConstantData
{
BYTE_ADDRESS_BUFFER globalBuffer;
BYTE_ADDRESS_BUFFER perViewBuffer;
BYTE_ADDRESS_BUFFER perObjectBuffer;
BYTE_ADDRESS_BUFFER perInstanceBuffer;
BYTE_ADDRESS_BUFFER perMaterialBuffer;
uint globalIndex;
uint viewIndex;
uint objectIndex;
uint instanceIndex;
uint materialIndex;
};
struct GlobalFrameData
{
uint viewBufferIndex;
uint instanceBufferIndex;
uint viewBufferCount;
uint instanceBufferCount;
uint userBufferIndex;
};
struct PerViewData
@@ -23,6 +32,11 @@ struct PerViewData
float4 screenSize; // xy: size, zw: 1/size
};
struct PerInstanceData
{
float4x4 localToWorld;
};
struct PerObjectData
{
float3 worldBoundsMin;

View File

@@ -2,12 +2,12 @@ shader "MyShader/Standard"
{
properties
{
float4 color = { 1, 1, 1, 1 };
tex2d texture1 = { black };
tex2d texture2 = { white };
tex2d texture3 = { grey };
tex2d texture4 = { normal };
sampler tex_sampler;
//float4 color = { 1, 1, 1, 1 };
//tex2d texture1 = { black };
//tex2d texture2 = { white };
//tex2d texture3 = { grey };
//tex2d texture4 = { normal };
//sampler tex_sampler;
}
pass "Forward"
@@ -21,7 +21,96 @@ shader "MyShader/Standard"
color_mask = all;
}
mesh "F:/csharp/GhostEngine/src/Runtime//Ghost.Graphics/RenderPipeline/ShaderCode.hlsl" : "MSMain";
pixel "F:/csharp/GhostEngine/src/Runtime//Ghost.Graphics/RenderPipeline/ShaderCode.hlsl" : "PSMain";
includes
{
"F:/csharp/GhostEngine/src/Runtime/Ghost.Graphics/Shaders/Includes/Properties.hlsl";
}
hlsl
{
struct PixelInput
{
float4 position : SV_POSITION;
float4 color : COLOR;
float4 uv : TEXCOORD0;
};
[numthreads(128, 1, 1)] // 128 threads to cover max 64 vertices and 124 triangles
[outputtopology("triangle")]
void MSMain(
uint3 groupThreadID : SV_GroupThreadID,
uint3 groupID : SV_GroupID,
out vertices PixelInput outVerts[64],
out indices uint3 outTris[124])
{
PerObjectData perObjectData = LoadData<PerObjectData>(g_PushConstantData.objectIndex, 0);
ByteAddressBuffer meshletBuffer = GET_BUFFER(perObjectData.meshletBuffer);
Meshlet m = meshletBuffer.Load<Meshlet>(groupID.x * sizeof(Meshlet));
uint vertexCount = m.packedCounts & 0xFF;
uint triangleCount = (m.packedCounts >> 8) & 0xFF;
SetMeshOutputCounts(vertexCount, triangleCount);
ByteAddressBuffer meshletVerticesBuffer = GET_BUFFER(perObjectData.meshletVerticesBuffer);
ByteAddressBuffer meshletTrianglesBuffer = GET_BUFFER(perObjectData.meshletTrianglesBuffer);
// Write vertex output
if (groupThreadID.x < vertexCount)
{
uint vertexIndex = meshletVerticesBuffer.Load((m.vertexOffset + groupThreadID.x) * 4);
ByteAddressBuffer vertices = GET_BUFFER(perObjectData.vertexBuffer);
Vertex v = vertices.Load<Vertex>(vertexIndex * sizeof(Vertex));
GlobalFrameData globalFrameData = LoadData<GlobalFrameData>(g_PushConstantData.globalIndex, 0);
PerViewData perViewData = LoadData<PerViewData>(g_PushConstantData.viewIndex, 0);
PerInstanceData perInstanceData = LoadData<PerInstanceData>(globalFrameData.instanceBufferIndex, g_PushConstantData.instanceIndex);
float4 worldPos = mul(perInstanceData.localToWorld, float4(v.position.xyz, 1.0f));
float4 viewPos = mul(perViewData.viewMatrix, worldPos);
outVerts[groupThreadID.x].position = mul(perViewData.projectionMatrix, viewPos);
outVerts[groupThreadID.x].color = v.color;
outVerts[groupThreadID.x].uv = v.uv;
}
// Write triangle output (1 thread processes 1 triangle)
if (groupThreadID.x < triangleCount)
{
uint triangleIndex = groupThreadID.x;
// Load the packed 32-bit integer containing the 3 local indices
uint packedIndices = meshletTrianglesBuffer.Load((m.triangleOffset + triangleIndex) * 4);
uint i0 = packedIndices & 0xFF;
uint i1 = (packedIndices >> 8) & 0xFF;
uint i2 = (packedIndices >> 16) & 0xFF;
outTris[triangleIndex] = uint3(i0, i1, i2);
}
}
float4 PSMain(PixelInput input) : SV_TARGET
{
// PerMaterialData perMaterialData = LoadData<PerMaterialData>(g_PushConstantData.materialIndex, 0);
//
// float4 color1 = SAMPLE_TEXTURE2D(perMaterialData.texture1, perMaterialData.tex_sampler, input.uv.xy);
// float4 color2 = SAMPLE_TEXTURE2D(perMaterialData.texture2, perMaterialData.tex_sampler, input.uv.xy);
// float4 color3 = SAMPLE_TEXTURE2D(perMaterialData.texture3, perMaterialData.tex_sampler, input.uv.xy);
// float4 color4 = SAMPLE_TEXTURE2D(perMaterialData.texture4, perMaterialData.tex_sampler, input.uv.xy);
//
// float4 blendedColor = (color1 + color2 + color3 + color4) * 0.25f;
// return perMaterialData.color * blendedColor + input.color;
// TODO: Randome color on meshlet.
return float4(1, 0, 0, 1);
}
}
mesh "hlsl_block" : "MSMain";
pixel "hlsl_block" : "PSMain";
}
}

View File

@@ -52,6 +52,7 @@
<ProjectReference Include="..\..\Runtime\Ghost.Engine\Ghost.Engine.csproj" />
<ProjectReference Include="..\..\Test\Ghost.Test.Core\Ghost.Test.Core.csproj" />
<ProjectReference Include="..\..\Runtime\Ghost.Graphics\Ghost.Graphics.csproj" />
<ProjectReference Include="..\..\Editor\Ghost.DSL\Ghost.DSL.csproj" />
</ItemGroup>
<!--

View File

@@ -1,9 +1,12 @@
using Ghost.Core;
using Ghost.Core.Graphics;
using Ghost.DSL.ShaderCompiler;
using Ghost.Graphics.Core;
using Ghost.Graphics.RenderGraphModule;
using Ghost.Graphics.RenderPipeline;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.Mathematics;
using Misaki.HighPerformance.Utilities;
namespace Ghost.Graphics.Test.RenderPasses;
@@ -21,10 +24,15 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
{
public Identifier<RGTexture> backbuffer;
public RenderList renderList;
public Handle<Material> material;
public uint globalIndex;
public uint viewIndex;
}
private readonly RenderGraph _renderGraph;
private readonly RenderSystem _renderSystem;
private Identifier<Shader> _meshletShader;
private Handle<Material> _meshletMaterial;
private bool _disposed;
@@ -41,6 +49,25 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
renderSystem.GraphicsEngine.ResourceDatabase,
renderSystem.GraphicsEngine.PipelineLibrary,
renderSystem.GraphicsEngine.ShaderCompiler);
var shaderDescriptor = DSLShaderCompiler.CompileShader("F:/csharp/GhostEngine/src/Runtime/Ghost.Graphics/test.gshdr", "C:/Users/Misaki/Downloads/Archive").GetValueOrThrow();
_meshletShader = renderSystem.ResourceManager.CreateGraphicsShader(shaderDescriptor);
_meshletMaterial = renderSystem.ResourceManager.CreateMaterial(_meshletShader);
var config = new ShaderCompilationConfig
{
optimizeLevel = CompilerOptimizeLevel.O3,
options = CompilerOption.KeepReflections,
tier = CompilerTier.Tier2
};
var pass = shaderDescriptor.passes[0];
var emptyKeywords = new LocalKeywordSet();
var variantKey = RHIUtility.CreateShaderVariantKey(
RHIUtility.CreateShaderPassKey(pass.identifier),
in emptyKeywords);
renderSystem.GraphicsEngine.ShaderCompiler.CompilePass(in pass, in config, variantKey).GetValueOrThrow();
}
public void Render(RenderContext ctx, ReadOnlySpan<RenderRequest> requests)
@@ -137,7 +164,9 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
}
else
{
var backBuffer = _renderGraph.ImportTexture(request.colorTarget, "BackBuffer", clearAtFirstUse: false, discardAtLastUse: false);
var backBuffer = _renderGraph.ImportTexture(request.colorTarget, "BackBuffer", clearAtFirstUse: true, discardAtLastUse: false);
MeshletDebugPass(backBuffer, request.opaqueRenderList, resourceDatabase.GetBindlessIndex(frameBufferResource.AsResource()), resourceDatabase.GetBindlessIndex(viewBufferResource.AsResource()));
}
// We must enqueue a return for the pooled resources so they are freed next frame.
@@ -147,16 +176,35 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
}
}
private void MeshletDebugPass(Identifier<RGTexture> backbuffer, RenderList renderList)
private void MeshletDebugPass(Identifier<RGTexture> backbuffer, RenderList renderList, uint globalIndex, uint viewIndex)
{
using (var builder = _renderGraph.AddRasterRenderPass<MeshletDebugPassData>("Meshlet Debug Pass", out var passData))
{
passData.renderList = renderList;
passData.globalIndex = globalIndex;
passData.viewIndex = viewIndex;
passData.material = _meshletMaterial;
builder.SetColorAttachment(backbuffer, 0);
builder.SetRenderFunc<MeshletDebugPassData>(static (data, ctx)=>
{
ctx.SetGlobalData(data.globalIndex, data.viewIndex);
ctx.SetActiveMaterial(data.material);
var instanceIndex = 0u;
foreach (var record in data.renderList)
{
ctx.SetActiveMesh(record.mesh);
ctx.SetInstanceIndex(instanceIndex);
var meshRefResult = ctx.ResourceManager.GetMeshReference(record.mesh);
if (meshRefResult.IsSuccess)
{
var meshletCount = (uint)meshRefResult.Value.MeshletData.meshlets.Count;
ctx.DispatchMesh(new uint3(meshletCount, 1, 1));
}
instanceIndex++;
}
});
}
}
@@ -168,6 +216,9 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
return;
}
_renderSystem.ResourceManager.ReleaseMaterial(_meshletMaterial);
_renderSystem.ResourceManager.ReleaseShader(_meshletShader);
_renderGraph.Dispose();
_disposed = true;

View File

@@ -2,6 +2,7 @@ using Ghost.Core;
using Ghost.Graphics.Test.Windows;
using Microsoft.UI.Xaml;
using Misaki.HighPerformance.LowLevel.Buffer;
using System.Runtime.InteropServices;
// To learn more about WinUI, the WinUI project structure,
@@ -32,22 +33,22 @@ public partial class UnitTestApp : Application
OperatingSystem.IsMacOS() ? "osx" : "unknown";
var arch = Environment.Is64BitProcess ? "x64" : "x86";
var nativeDllDir = Path.Combine(currentDir, "runtimes", platform + "-" + arch, "native");
//if (Directory.Exists(nativeDllDir))
//{
// foreach (var dll in Directory.EnumerateFiles(nativeDllDir, "*.dll"))
// {
// NativeLibrary.Load(dll);
// }
//}
NativeLibrary.SetDllImportResolver(typeof(UnitTestApp).Assembly, (libraryName, assembly, searchPath) =>
if (Directory.Exists(nativeDllDir))
{
if (libraryName == "dxcompiler")
foreach (var dll in Directory.EnumerateFiles(nativeDllDir, "*.dll"))
{
NativeLibrary.Load(Path.Combine(nativeDllDir, "dxil.dll"));
NativeLibrary.Load(dll);
}
}
//NativeLibrary.SetDllImportResolver(typeof(UnitTestApp).Assembly, (libraryName, assembly, searchPath) =>
//{
// if (libraryName == "dxcompiler")
// {
// NativeLibrary.Load(Path.Combine(nativeDllDir, "dxil.dll"));
// }
return IntPtr.Zero;
});
// return IntPtr.Zero;
//});
}
/// <summary>
@@ -58,6 +59,15 @@ public partial class UnitTestApp : Application
{
LoadDll();
var opts = new AllocationManagerInitOpts
{
ArenaCapacity = 1024 * 1024 * 1024, // 1GB
StackCapacity = 1024 * 1024 * 32, // 32MB
FreeListConcurrencyLevel = Environment.ProcessorCount,
};
AllocationManager.Initialize(opts);
_window = new GraphicsTestWindow();
_window.Activate();

View File

@@ -20,6 +20,8 @@ public sealed partial class GraphicsTestWindow : Window
private ISwapChain? _swapChain;
private World? _world;
private Handle<Mesh> _meshHandle;
private bool _isFirstActivationHandled;
public GraphicsTestWindow()
@@ -64,7 +66,9 @@ public sealed partial class GraphicsTestWindow : Window
_world.AddService(_renderSystem);
// Add Systems
_world.SystemManager.GetSystem<DefaultSystemGroup>().AddSystem<RenderExtractionSystem>();
var group = _world.SystemManager.GetSystem<DefaultSystemGroup>();
group.AddSystem<RenderExtractionSystem>();
group.SortSystems();
_world.SystemManager.InitializeAll(default);
@@ -101,16 +105,8 @@ public sealed partial class GraphicsTestWindow : Window
using var cmdAllocator = _renderSystem.GraphicsEngine.CreateCommandAllocator(CommandBufferType.Graphics);
directCmd.Begin(cmdAllocator);
var meshHandle = ctx.CreateMesh(vertices, indices, true);
var meshRefResult = _renderSystem.ResourceManager.GetMeshReference(meshHandle);
if (meshRefResult.IsSuccess)
{
meshRefResult.Value.CookMeshlets();
}
ctx.UploadMeshlets(meshHandle);
ctx.UpdateObjectData(meshHandle);
_meshHandle = ctx.CreateMesh(vertices, indices, true);
ctx.UpdateObjectData(_meshHandle);
directCmd.End().ThrowIfFailed();
_renderSystem.GraphicsEngine.Device.GraphicsQueue.Submit(directCmd);
@@ -121,8 +117,8 @@ public sealed partial class GraphicsTestWindow : Window
var meshEntity = _world.EntityManager.CreateEntity(meshSet);
_world.EntityManager.SetComponent(meshEntity, new MeshInstance
{
mesh = meshHandle,
renderingLayerMask = new RenderingLayerMask(uint.MaxValue),
mesh = _meshHandle,
renderingLayerMask = RenderingLayerMask.All,
shadowCastingMode = Engine.ShadowCastingMode.On
});
@@ -147,6 +143,8 @@ public sealed partial class GraphicsTestWindow : Window
World.Destroy(_world.ID);
}
_renderSystem?.ResourceManager.ReleaseMesh(_meshHandle);
_swapChain?.Dispose();
_renderSystem?.Dispose();
@@ -188,6 +186,8 @@ public sealed partial class GraphicsTestWindow : Window
var queryID = new QueryBuilder().WithAll<Camera>().Build(_world);
ref var query = ref _world.ComponentManager.GetEntityQueryReference(queryID);
// FIX: A critical bug that resize happens on the render thread, but OnRendering invoke on the UI thread, and there is a chance that our extraction system already send the request to the render thread with old back buffer handle, which is already become invalid after resize.
// A proper solution is to use swap chain manager, camera only reference the id of the swap chain, and we will extract the current back buffer handle on the render thread.
foreach (ref var cam in query.GetComponentIterator<Camera>())
{
cam.colorTarget = _swapChain.GetCurrentBackBuffer();