Refactor instance update flow, asset registry, and texture IO
- Renamed AddInstanceRequest to UpdateInstanceRequest; unified add/update logic for GPU instances - Introduced UpdateGPUInstanceSystem to handle changed MeshInstance components - Replaced QueryBuilder.Create() with QueryBuilder.New() for consistency - Switched versioning in ChunkView HasChanged/HasStructuralChanged to uint - Added extension-to-AssetType mapping in AssetHandlerRegistry - Changed TextureAssetHandler/Processor to use nint for image data - Enhanced DDS cache: read mipmap count, handle invalid files - Updated ProjectBrowserViewModel to use IAssetRegistry - Upgraded Misaki.HighPerformance and System.IO.Hashing packages - Set DependencyChainCapacity in JobSchedulerDesc - Fixed instance buffer logic in GhostRenderPipeline - Miscellaneous cleanups and namespace improvements
This commit is contained in:
@@ -20,13 +20,13 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Misaki.HighPerformance" Version="1.0.8" />
|
||||
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="3.0.0" />
|
||||
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.6.14">
|
||||
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="3.1.0" />
|
||||
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.6.15">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Misaki.HighPerformance.Mathematics" Version="1.3.3" />
|
||||
<PackageReference Include="System.IO.Hashing" Version="10.0.6" />
|
||||
<PackageReference Include="System.IO.Hashing" Version="10.0.7" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.26100.6" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ public sealed partial class EngineCore : IDisposable
|
||||
{
|
||||
ThreadCount = Environment.ProcessorCount - 2, // We -2 here, one for main thread, one for render thread
|
||||
ThreadPriority = ThreadPriority.Normal,
|
||||
DependencyChainCapacity = 8192,
|
||||
};
|
||||
|
||||
_jobScheduler = new JobScheduler(in desc);
|
||||
|
||||
@@ -41,13 +41,13 @@ internal partial class GhostRenderPipeline
|
||||
private static unsafe Handle<GPUBuffer> CreateUpdateInstanceBuffer(GhostRenderPayload ghostPayload, ResourceManager resourceManager, IResourceDatabase resourceDatabase, out int count)
|
||||
{
|
||||
// TODO: This should also include update requests like transform update, material update, etc.
|
||||
var totalUpdateCount = ghostPayload.AddRequest.Count; // + ghostPayload.UpdateRequest.Count;
|
||||
var totalUpdateCount = ghostPayload.UpdateRequest.Count; // + ghostPayload.UpdateRequest.Count;
|
||||
|
||||
if (!ghostPayload.AddRequest.IsEmpty)
|
||||
if (!ghostPayload.UpdateRequest.IsEmpty)
|
||||
{
|
||||
var addDesc = new BufferDesc
|
||||
{
|
||||
Size = (nuint)ghostPayload.AddRequest.Count * MemoryUtility.SizeOf<UpdateInstanceData>(),
|
||||
Size = (nuint)ghostPayload.UpdateRequest.Count * MemoryUtility.SizeOf<UpdateInstanceData>(),
|
||||
Stride = (uint)MemoryUtility.SizeOf<UpdateInstanceData>(),
|
||||
Usage = BufferUsage.Structured | BufferUsage.ShaderResource,
|
||||
HeapType = HeapType.Upload
|
||||
@@ -57,7 +57,7 @@ internal partial class GhostRenderPipeline
|
||||
var pAddData = (UpdateInstanceData*)resourceDatabase.MapResource(addBuffer.AsResource(), 0, null);
|
||||
|
||||
var i = 0;
|
||||
while (ghostPayload.AddRequest.TryDequeue(out var addRequest))
|
||||
while (ghostPayload.UpdateRequest.TryDequeue(out var addRequest))
|
||||
{
|
||||
var (mesh, error) = resourceManager.GetMeshReference(addRequest.meshInstance.mesh);
|
||||
if (error.IsFailure)
|
||||
@@ -95,7 +95,7 @@ internal partial class GhostRenderPipeline
|
||||
{
|
||||
var addDesc = new BufferDesc
|
||||
{
|
||||
Size = (nuint)ghostPayload.AddRequest.Count * MemoryUtility.SizeOf<RemoveInstanceData>(),
|
||||
Size = (nuint)ghostPayload.UpdateRequest.Count * MemoryUtility.SizeOf<RemoveInstanceData>(),
|
||||
Stride = (uint)MemoryUtility.SizeOf<RemoveInstanceData>(),
|
||||
Usage = BufferUsage.Structured | BufferUsage.ShaderResource,
|
||||
HeapType = HeapType.Upload
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Ghost.Engine.RenderPipeline;
|
||||
|
||||
internal sealed class GhostRenderPayload : IRenderPayload
|
||||
{
|
||||
public struct AddInstanceRequest
|
||||
public struct UpdateInstanceRequest
|
||||
{
|
||||
public MeshInstance meshInstance;
|
||||
public float4x4 localToWorld;
|
||||
@@ -27,7 +27,7 @@ internal sealed class GhostRenderPayload : IRenderPayload
|
||||
|
||||
private UnsafeList<RenderRequest> _renderRequests;
|
||||
|
||||
private readonly ConcurrentQueue<AddInstanceRequest> _addRequest;
|
||||
private readonly ConcurrentQueue<UpdateInstanceRequest> _updateRequest;
|
||||
private readonly ConcurrentQueue<RemoveInstanceRequest> _removeRequest;
|
||||
|
||||
private uint _instanceCountBefore;
|
||||
@@ -35,7 +35,7 @@ internal sealed class GhostRenderPayload : IRenderPayload
|
||||
|
||||
public ReadOnlySpan<RenderRequest> RenderRequests => _renderRequests;
|
||||
|
||||
public ConcurrentQueue<AddInstanceRequest> AddRequest => _addRequest;
|
||||
public ConcurrentQueue<UpdateInstanceRequest> UpdateRequest => _updateRequest;
|
||||
public ConcurrentQueue<RemoveInstanceRequest> RemoveRequest => _removeRequest;
|
||||
public uint InstanceCountBefore => _instanceCountBefore;
|
||||
public uint InstanceCount => _instanceCount;
|
||||
@@ -45,7 +45,7 @@ internal sealed class GhostRenderPayload : IRenderPayload
|
||||
_renderPipeline = renderPipeline;
|
||||
|
||||
_renderRequests = new UnsafeList<RenderRequest>(4, Misaki.HighPerformance.LowLevel.Buffer.AllocationHandle.Persistent);
|
||||
_addRequest = new ConcurrentQueue<AddInstanceRequest>();
|
||||
_updateRequest = new ConcurrentQueue<UpdateInstanceRequest>();
|
||||
_removeRequest = new ConcurrentQueue<RemoveInstanceRequest>();
|
||||
}
|
||||
|
||||
@@ -59,10 +59,15 @@ internal sealed class GhostRenderPayload : IRenderPayload
|
||||
{
|
||||
var index = _renderPipeline.GPUScene.AddInstance();
|
||||
|
||||
_addRequest.Enqueue(new AddInstanceRequest { instanceId = index, localToWorld = ltw, meshInstance = meshInstance });
|
||||
_updateRequest.Enqueue(new UpdateInstanceRequest { instanceId = index, localToWorld = ltw, meshInstance = meshInstance });
|
||||
return index;
|
||||
}
|
||||
|
||||
public void UpdateInstance(uint instanceId, float4x4 ltw, ref readonly MeshInstance meshInstance)
|
||||
{
|
||||
_updateRequest.Enqueue(new UpdateInstanceRequest { instanceId = instanceId, localToWorld = ltw, meshInstance = meshInstance });
|
||||
}
|
||||
|
||||
public void RemoveInstance(uint instanceId)
|
||||
{
|
||||
var swapWithInstanceId = _renderPipeline.GPUScene.RemoveInstance(instanceId);
|
||||
@@ -81,13 +86,13 @@ internal sealed class GhostRenderPayload : IRenderPayload
|
||||
{
|
||||
// We capture the count here to prevent that main thread continues to add more requests for next frame while the render thread is still processing current frame's requests.
|
||||
_instanceCount = _renderPipeline.GPUScene.InstanceCount;
|
||||
Logger.DebugAssert(_instanceCount == _instanceCountBefore + (uint)_addRequest.Count - (uint)_removeRequest.Count);
|
||||
Logger.DebugAssert(_instanceCount == _instanceCountBefore + (uint)_updateRequest.Count - (uint)_removeRequest.Count);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_renderRequests.Clear();
|
||||
_addRequest.Clear();
|
||||
_updateRequest.Clear();
|
||||
_removeRequest.Clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ using Misaki.HighPerformance.Utilities;
|
||||
|
||||
namespace Ghost.Engine.Systems;
|
||||
|
||||
[UpdateAfter<RemoveGPUInstanceSystem>]
|
||||
[RenderPipelineSystem<GhostRenderPipelineSettings>]
|
||||
[UpdateAfter<UpdateGPUInstanceSystem>]
|
||||
internal class AddGPUInstanceSystem : SystemBase
|
||||
{
|
||||
private RenderSystem _renderSystem = null!;
|
||||
@@ -19,7 +19,7 @@ internal class AddGPUInstanceSystem : SystemBase
|
||||
{
|
||||
_renderSystem = systemAPI.World.GetService<RenderSystem>();
|
||||
|
||||
_meshInstanceQueryID = QueryBuilder.Create()
|
||||
_meshInstanceQueryID = QueryBuilder.New()
|
||||
.WithAll<MeshInstance, LocalToWorld>()
|
||||
.WithAbsent<GPUInstanceRef>()
|
||||
.Build(systemAPI.World, true);
|
||||
|
||||
@@ -18,7 +18,7 @@ internal class RemoveGPUInstanceSystem : SystemBase
|
||||
{
|
||||
_renderSystem = systemAPI.World.GetService<RenderSystem>();
|
||||
|
||||
_gpuInstanceQueryID = QueryBuilder.Create()
|
||||
_gpuInstanceQueryID = QueryBuilder.New()
|
||||
.WithAll<GPUInstanceRef>()
|
||||
.WithAbsent<MeshInstance>()
|
||||
.Build(systemAPI.World, true);
|
||||
@@ -29,6 +29,7 @@ internal class RemoveGPUInstanceSystem : SystemBase
|
||||
protected override void OnUpdate(ref readonly SystemAPI systemAPI)
|
||||
{
|
||||
var payload = (GhostRenderPayload)_renderSystem.GetCurrentFramePayload();
|
||||
payload.BeginRecord();
|
||||
|
||||
ref var gpuInstanceQuery = ref systemAPI.World.ComponentManager.GetEntityQueryReference(_gpuInstanceQueryID);
|
||||
|
||||
@@ -47,4 +48,4 @@ internal class RemoveGPUInstanceSystem : SystemBase
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
56
src/Runtime/Ghost.Engine/Systems/UpdateGPUInstanceSystem.cs
Normal file
56
src/Runtime/Ghost.Engine/Systems/UpdateGPUInstanceSystem.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Engine.Components;
|
||||
using Ghost.Engine.RenderPipeline;
|
||||
using Ghost.Entities;
|
||||
using Ghost.Graphics;
|
||||
using Misaki.HighPerformance.Utilities;
|
||||
|
||||
namespace Ghost.Engine.Systems;
|
||||
|
||||
[RenderPipelineSystem<GhostRenderPipelineSettings>]
|
||||
[UpdateAfter<RemoveGPUInstanceSystem>]
|
||||
[UpdateBefore<AddGPUInstanceSystem>]
|
||||
internal class UpdateGPUInstanceSystem : SystemBase
|
||||
{
|
||||
private RenderSystem _renderSystem = null!;
|
||||
private Identifier<EntityQuery> _gpuInstanceQueryID;
|
||||
|
||||
protected override void OnInitialize(ref readonly SystemAPI systemAPI)
|
||||
{
|
||||
_renderSystem = systemAPI.World.GetService<RenderSystem>();
|
||||
|
||||
_gpuInstanceQueryID = QueryBuilder.New()
|
||||
.WithAll<LocalToWorld, MeshInstance, GPUInstanceRef>()
|
||||
.Build(systemAPI.World, true);
|
||||
|
||||
RequireQueryForUpdate(_gpuInstanceQueryID);
|
||||
}
|
||||
|
||||
protected override void OnUpdate(ref readonly SystemAPI systemAPI)
|
||||
{
|
||||
var playload = (GhostRenderPayload)_renderSystem.GetCurrentFramePayload();
|
||||
|
||||
ref var instanceQuery = ref systemAPI.World.ComponentManager.GetEntityQueryReference(_gpuInstanceQueryID);
|
||||
|
||||
foreach (var chunk in instanceQuery.GetChunkIterator())
|
||||
{
|
||||
if (!chunk.HasChanged<MeshInstance>(LastSystemVersion))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var ltws = chunk.GetComponentData<LocalToWorld>();
|
||||
var meshs = chunk.GetComponentData<MeshInstance>();
|
||||
var gpuInstances = chunk.GetComponentData<GPUInstanceRef>();
|
||||
|
||||
for (var i = 0; i < chunk.EntityCount; i++)
|
||||
{
|
||||
ref readonly var ltw = ref ltws.GetElementUnsafe(i);
|
||||
ref readonly var mesh = ref meshs.GetElementUnsafe(i);
|
||||
ref readonly var instance = ref gpuInstances.GetElementUnsafe(i);
|
||||
|
||||
playload.UpdateInstance(instance.gpuSceneIndex, ltw.matrix, in mesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,7 +130,7 @@ public readonly unsafe ref struct ChunkView
|
||||
/// <param name="version">The version number to compare against the component's current version. Must be greater than or equal to zero.</param>
|
||||
/// <returns>true if the component's current version is less than or equal to the specified version; otherwise, false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool HasChanged(Identifier<IComponent> id, int version)
|
||||
public bool HasChanged(Identifier<IComponent> id, uint version)
|
||||
{
|
||||
var layout = GetLayout(id);
|
||||
return version < _pVersion[layout.versionIndex];
|
||||
@@ -144,7 +144,7 @@ public readonly unsafe ref struct ChunkView
|
||||
/// <param name="version">The version number to compare against the current version of the component.</param>
|
||||
/// <returns>true if the component of space T has changed since the specified version; otherwise, false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool HasChanged<T>(int version)
|
||||
public readonly bool HasChanged<T>(uint version)
|
||||
where T : unmanaged, IComponent
|
||||
{
|
||||
var layout = GetLayout(ComponentTypeID<T>.Value);
|
||||
@@ -157,7 +157,7 @@ public readonly unsafe ref struct ChunkView
|
||||
/// <param name="version">The version number to compare against the chunk's structural version.</param>
|
||||
/// <returns>true if the chunk's structure has changed since the specified version; otherwise, false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool HasStructuralChanged(int version)
|
||||
public readonly bool HasStructuralChanged(uint version)
|
||||
{
|
||||
return version < _structuralVersion;
|
||||
}
|
||||
@@ -502,7 +502,7 @@ public ref partial struct QueryBuilder : IDisposable
|
||||
_rw = new UnsafeList<Identifier<IComponent>>(4, _scope.AllocationHandle);
|
||||
}
|
||||
|
||||
public static QueryBuilder Create()
|
||||
public static QueryBuilder New()
|
||||
{
|
||||
return new QueryBuilder();
|
||||
}
|
||||
|
||||
@@ -28,11 +28,17 @@ public abstract class SystemBase : ISystem
|
||||
{
|
||||
private UnsafeList<int> _requiredQueries;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the world that the system is running on currently.
|
||||
/// </summary>
|
||||
public World World
|
||||
{
|
||||
get; init;
|
||||
} = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last version that the system update.
|
||||
/// </summary>
|
||||
public uint LastSystemVersion
|
||||
{
|
||||
get; internal set;
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Ghost.Graphics.Services;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Graphics;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user