feat(engine)!: refactor graphics, ECS, and logging APIs
Major refactor of graphics and ECS infrastructure: - Removed IResourceManager, IRenderSystem, IFenceSynchronizer interfaces; ResourceManager and RenderSystem are now concrete classes. - Updated all render graph, pipeline, and context code to use concrete ResourceManager. - Refactored camera/frustum math and render extraction for clarity and correctness; frustum now uses inline arrays. - RenderingLayerMask is now an immutable struct with bitwise operators. - Meshlet and meshlet group data structures improved; meshlet build callback signature updated. - Logging system overhauled: LogMessage is now a class, LogCollection supports change events, and Logger is used directly in the debug console. - ECS query API: ChunkView.Count renamed to EntityCount; query builder/iterators use VirtualStack.Scope. - Updated render pipeline and passes for new resource manager and render list APIs. - Cleaned up obsolete files, improved code style, and updated documentation. - HLSL meshlet shader updated for new struct layout. - Debug console now uses new logger and log collection. BREAKING CHANGE: Public APIs for resource management, rendering, ECS queries, and logging have changed. Interfaces removed; use new concrete types and updated method signatures.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ghost.Core;
|
||||
@@ -7,10 +8,11 @@ public enum LogLevel
|
||||
{
|
||||
Info,
|
||||
Warning,
|
||||
Error
|
||||
Error,
|
||||
Debug
|
||||
}
|
||||
|
||||
public readonly struct LogMessage
|
||||
public class LogMessage
|
||||
{
|
||||
public LogLevel Level
|
||||
{
|
||||
@@ -51,17 +53,38 @@ public readonly struct LogMessage
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class LogCollection : ReadOnlyObservableCollection<LogMessage>
|
||||
{
|
||||
public LogCollection(ObservableCollection<LogMessage> list)
|
||||
: base(list)
|
||||
{
|
||||
}
|
||||
|
||||
public event NotifyCollectionChangedEventHandler? LogChanged;
|
||||
|
||||
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
base.OnCollectionChanged(args);
|
||||
LogChanged?.Invoke(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
public interface ILogger
|
||||
{
|
||||
ReadOnlyObservableCollection<LogMessage> Logs
|
||||
LogCollection Logs
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public bool CaptureStackTrace
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
void Log(string message, LogLevel level);
|
||||
void Log(Exception exception);
|
||||
void Assert(bool condition, string message);
|
||||
void Clear();
|
||||
void Clear(bool includeFile = false);
|
||||
}
|
||||
|
||||
public static class Logger
|
||||
@@ -70,14 +93,19 @@ public static class Logger
|
||||
private class LoggerImpl : ILogger
|
||||
{
|
||||
private readonly ObservableCollection<LogMessage> _logs = new();
|
||||
private readonly ReadOnlyObservableCollection<LogMessage> _readOnly;
|
||||
private readonly LogCollection _readOnly;
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
public ReadOnlyObservableCollection<LogMessage> Logs => _readOnly;
|
||||
public LogCollection Logs => _readOnly;
|
||||
|
||||
public bool CaptureStackTrace
|
||||
{
|
||||
get; set;
|
||||
} = true;
|
||||
|
||||
public LoggerImpl()
|
||||
{
|
||||
_readOnly = new ReadOnlyObservableCollection<LogMessage>(_logs);
|
||||
_readOnly = new LogCollection(_logs);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
@@ -85,7 +113,8 @@ public static class Logger
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_logs.Add(new LogMessage(level, message));
|
||||
var stackTrace = CaptureStackTrace ? new StackTrace(true).ToString() : null;
|
||||
_logs.Add(new LogMessage(level, message, stackTrace));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,16 +130,13 @@ public static class Logger
|
||||
[StackTraceHidden]
|
||||
public void Assert(bool condition, string message)
|
||||
{
|
||||
lock (_lock)
|
||||
if (!condition)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Log(message, LogLevel.Error);
|
||||
}
|
||||
Log(message, LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
public void Clear(bool includeFile = false)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
@@ -119,9 +145,10 @@ public static class Logger
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly ILogger s_logger = new LoggerImpl();
|
||||
private static readonly LoggerImpl s_logger = new LoggerImpl();
|
||||
|
||||
public static ReadOnlyObservableCollection<LogMessage> Logs => s_logger.Logs;
|
||||
public static ILogger Impl => s_logger;
|
||||
public static LogCollection Logs => s_logger.Logs;
|
||||
|
||||
[StackTraceHidden]
|
||||
public static void Log(LogLevel level, object? message)
|
||||
@@ -207,8 +234,27 @@ public static class Logger
|
||||
s_logger.Assert(condition, message);
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
[StackTraceHidden]
|
||||
[Conditional("DEBUG")]
|
||||
[Conditional("GHOST_EDITOR")]
|
||||
public static void Debug(object? message)
|
||||
{
|
||||
s_logger.Clear();
|
||||
s_logger.Log(message?.ToString() ?? "null", LogLevel.Debug);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
[Conditional("DEBUG")]
|
||||
[Conditional("GHOST_EDITOR")]
|
||||
public static void Debug(string message)
|
||||
{
|
||||
s_logger.Log(message, LogLevel.Debug);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
[Conditional("DEBUG")]
|
||||
[Conditional("GHOST_EDITOR")]
|
||||
public static void Debug(string format, params object?[] args)
|
||||
{
|
||||
s_logger.Log(string.Format(format, args), LogLevel.Debug);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ public unsafe struct Camera : IComponent
|
||||
public float nearClipPlane;
|
||||
public float farClipPlane;
|
||||
|
||||
public float2 sensorSize;
|
||||
public float2 sensorSize; // mm
|
||||
public GateFit gateFit;
|
||||
public float iso;
|
||||
public float shutterSpeed;
|
||||
public float aperture;
|
||||
public float focalLength;
|
||||
public float focusDistance;
|
||||
public float focalLength; // mm
|
||||
public float focusDistance; // m
|
||||
|
||||
public RenderingLayerMask renderingLayerMask;
|
||||
|
||||
@@ -29,5 +29,5 @@ public unsafe struct Camera : IComponent
|
||||
// TODO: Add more render targets like motion vector, etc.
|
||||
|
||||
// Custim render function. If it's not null, the render system will call this function instead of the default render pipeline.
|
||||
public delegate*<ref readonly RenderingContext, ref readonly RenderRequest, void> renderFunc;
|
||||
public delegate*<ref readonly RenderContext, ref readonly RenderRequest, void> renderFunc;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ public struct MeshInstance : IComponent
|
||||
{
|
||||
public Handle<Mesh> mesh;
|
||||
public Identifier<MaterialPalette> materialPalette;
|
||||
public ShadowCastingMode shadowCastingMode;
|
||||
public RenderingLayerMask renderingLayerMask;
|
||||
public ShadowCastingMode shadowCastingMode;
|
||||
public bool staticShadowCaster;
|
||||
}
|
||||
}
|
||||
@@ -107,7 +107,7 @@ public static class SceneManager
|
||||
var entities = chunk.GetEntities();
|
||||
var sceneIDs = chunk.GetComponentData<Components.SceneID>();
|
||||
|
||||
for (var i = 0; i < chunk.Count; i++)
|
||||
for (var i = 0; i < chunk.EntityCount; i++)
|
||||
{
|
||||
if (sceneIDs[i].scene.ID == scene.ID)
|
||||
{
|
||||
@@ -140,7 +140,7 @@ public static class SceneManager
|
||||
var chunkEntities = chunk.GetEntities();
|
||||
var sceneIDs = chunk.GetComponentData<Components.SceneID>();
|
||||
|
||||
for (var i = 0; i < chunk.Count; i++)
|
||||
for (var i = 0; i < chunk.EntityCount; i++)
|
||||
{
|
||||
if (sceneIDs[i].scene.ID == scene.ID)
|
||||
{
|
||||
|
||||
@@ -4,31 +4,24 @@ using Misaki.HighPerformance.Jobs;
|
||||
|
||||
namespace Ghost.Engine;
|
||||
|
||||
public interface IEngineContext : IDisposable
|
||||
{
|
||||
IJobScheduler JobScheduler { get; }
|
||||
IRenderSystem RenderSystem { get; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
internal class EngineEntryAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[EngineEntry]
|
||||
internal sealed partial class EngineCore : IEngineContext
|
||||
public sealed partial class EngineCore : IDisposable
|
||||
{
|
||||
private readonly JobScheduler _jobScheduler;
|
||||
private readonly RenderSystem _renderSystem;
|
||||
|
||||
public IJobScheduler JobScheduler => _jobScheduler;
|
||||
public IRenderSystem RenderSystem => _renderSystem;
|
||||
public JobScheduler JobScheduler => _jobScheduler;
|
||||
public RenderSystem RenderSystem => _renderSystem;
|
||||
|
||||
public EngineCore()
|
||||
internal EngineCore()
|
||||
{
|
||||
_jobScheduler = new JobScheduler(Environment.ProcessorCount - 2); // We -2 here, one for main thread, one for render thread
|
||||
|
||||
// TODO: Remove the windows dependency from RenderSystem.
|
||||
var renderingConfig = new RenderSystemDesc
|
||||
{
|
||||
FrameBufferCount = 2,
|
||||
@@ -40,10 +33,6 @@ internal sealed partial class EngineCore : IEngineContext
|
||||
ComponentRegistry.GetOrRegisterComponentID<ManagedEntityRef>();
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_jobScheduler.Dispose();
|
||||
|
||||
@@ -2,55 +2,241 @@ using Ghost.Core;
|
||||
using Ghost.Engine.Components;
|
||||
using Ghost.Entities;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using Misaki.HighPerformance.Mathematics.Geometry;
|
||||
|
||||
namespace Ghost.Engine.Systems;
|
||||
|
||||
public class RenderExtractionSystem : ISystem
|
||||
{
|
||||
private Identifier<EntityQuery> _queryID;
|
||||
private IGraphicsEngine _graphicsEngine = null!;
|
||||
|
||||
private Identifier<EntityQuery> _cameraQueryID;
|
||||
private Identifier<EntityQuery> _meshQueryID;
|
||||
|
||||
public void Initialize(ref readonly SystemAPI systemAPI)
|
||||
{
|
||||
_queryID = new QueryBuilder()
|
||||
_graphicsEngine = systemAPI.World.GetResource<IGraphicsEngine>();
|
||||
|
||||
var builder = new QueryBuilder();
|
||||
|
||||
_cameraQueryID = builder
|
||||
.WithAll<Camera, LocalToWorld>()
|
||||
.Build(systemAPI.World, false);
|
||||
|
||||
_meshQueryID = builder
|
||||
.WithAll<MeshInstance, LocalToWorld>()
|
||||
.Build(systemAPI.World);
|
||||
.Build(systemAPI.World, true);
|
||||
}
|
||||
|
||||
public void Update(ref readonly SystemAPI systemAPI)
|
||||
private static float3 IntersectFrustumPlanes(float4 p0, float4 p1, float4 p2)
|
||||
{
|
||||
if (_queryID.IsInvalid)
|
||||
float3 n0 = p0.xyz;
|
||||
float3 n1 = p1.xyz;
|
||||
float3 n2 = p2.xyz;
|
||||
|
||||
float det = math.dot(math.cross(n0, n1), n2);
|
||||
return (math.cross(n2, n1) * p0.w + math.cross(n0, n2) * p1.w - math.cross(n0, n1) * p2.w) * (1.0f / det);
|
||||
}
|
||||
|
||||
private static Frustum CreateFrustum(Camera camRef, float4x4 vp, float3 viewDir, float3 viewPos)
|
||||
{
|
||||
var frustum = new Frustum();
|
||||
Frustum.CalculateFrustumPlanes(vp, ref frustum.planes);
|
||||
|
||||
// We need to recalculate the near and far planes otherwise it does not work for oblique projection matrices used for reflection.
|
||||
var nearPlane = Plane.CreateFromUnitNormalAndPointInPlane(viewDir, viewPos);
|
||||
nearPlane.Distance -= camRef.nearClipPlane;
|
||||
|
||||
var farPlane = Plane.CreateFromUnitNormalAndPointInPlane(-viewDir, viewPos);
|
||||
farPlane.Distance += camRef.farClipPlane;
|
||||
|
||||
frustum.planes[4] = nearPlane;
|
||||
frustum.planes[5] = farPlane;
|
||||
|
||||
frustum.corners[0] = IntersectFrustumPlanes(frustum.planes[0], frustum.planes[3], frustum.planes[4]);
|
||||
frustum.corners[1] = IntersectFrustumPlanes(frustum.planes[1], frustum.planes[3], frustum.planes[4]);
|
||||
frustum.corners[2] = IntersectFrustumPlanes(frustum.planes[0], frustum.planes[2], frustum.planes[4]);
|
||||
frustum.corners[3] = IntersectFrustumPlanes(frustum.planes[1], frustum.planes[2], frustum.planes[4]);
|
||||
frustum.corners[4] = IntersectFrustumPlanes(frustum.planes[0], frustum.planes[3], frustum.planes[5]);
|
||||
frustum.corners[5] = IntersectFrustumPlanes(frustum.planes[1], frustum.planes[3], frustum.planes[5]);
|
||||
frustum.corners[6] = IntersectFrustumPlanes(frustum.planes[0], frustum.planes[2], frustum.planes[5]);
|
||||
frustum.corners[7] = IntersectFrustumPlanes(frustum.planes[1], frustum.planes[2], frustum.planes[5]);
|
||||
return frustum;
|
||||
}
|
||||
|
||||
public unsafe void Update(ref readonly SystemAPI systemAPI)
|
||||
{
|
||||
if (_meshQueryID.IsInvalid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref var query = ref systemAPI.World.ComponentManager.GetEntityQueryReference(_queryID);
|
||||
var renderList = new RenderList(1, 64, Allocator.Temp);
|
||||
ref var cameraQuery = ref systemAPI.World.ComponentManager.GetEntityQueryReference(_cameraQueryID);
|
||||
ref var meshQuery = ref systemAPI.World.ComponentManager.GetEntityQueryReference(_meshQueryID);
|
||||
|
||||
// TODO: We should extract the render record for each camera because different cameras may have different culling results.
|
||||
// TODO: This chould be done in parallel jobs.
|
||||
foreach (var chunk in query.GetChunkIterator())
|
||||
foreach (var (cam, camLtw) in cameraQuery.GetComponentIterator<Camera, LocalToWorld>())
|
||||
{
|
||||
var meshInstances = chunk.GetComponentData<MeshInstance>();
|
||||
var localToWorlds = chunk.GetComponentData<LocalToWorld>();
|
||||
ref readonly var camRef = ref cam.Get();
|
||||
ref readonly var camLtwRef = ref camLtw.Get();
|
||||
|
||||
for (var i = 0; i < chunk.Count; i++)
|
||||
var rtResult = _graphicsEngine.ResourceDatabase.GetResourceDescription(camRef.colorTarget.AsResource());
|
||||
if (rtResult.IsFailure)
|
||||
{
|
||||
ref readonly var meshInstance = ref meshInstances[i];
|
||||
ref readonly var localToWorld = ref localToWorlds[i];
|
||||
|
||||
renderList.Add(new RenderRecord
|
||||
{
|
||||
localToWorld = localToWorld.matrix,
|
||||
mesh = meshInstance.mesh,
|
||||
materialPalette = meshInstance.materialPalette,
|
||||
renderingLayerMask = meshInstance.renderingLayerMask,
|
||||
|
||||
}, 0);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Send render list to render pipeline.
|
||||
var rtSize = new uint2(rtResult.Value.TextureDescription.Width, rtResult.Value.TextureDescription.Height);
|
||||
var aspectScreen = (float)rtSize.x / rtSize.y;
|
||||
|
||||
var renderList = new RenderList(1, 64, Allocator.Temp);
|
||||
var shadowCasterRenderList = new RenderList(1, 64, Allocator.Temp);
|
||||
|
||||
// TODO: This chould be done in parallel jobs.
|
||||
foreach (var chunk in meshQuery.GetChunkIterator())
|
||||
{
|
||||
var meshInstances = chunk.GetComponentData<MeshInstance>();
|
||||
var localToWorlds = chunk.GetComponentData<LocalToWorld>();
|
||||
|
||||
for (var i = 0; i < chunk.EntityCount; i++)
|
||||
{
|
||||
ref readonly var meshInstance = ref meshInstances[i];
|
||||
if ((meshInstance.renderingLayerMask & camRef.renderingLayerMask) == 0u)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ref readonly var meshLtw = ref localToWorlds[i];
|
||||
|
||||
var meshPosition = meshLtw.matrix.c3.xyz;
|
||||
var camPosition = camLtwRef.matrix.c3.xyz;
|
||||
var distance = math.distance(meshPosition, camPosition);
|
||||
|
||||
if (distance < camRef.nearClipPlane || distance > camRef.farClipPlane)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (meshInstance.shadowCastingMode != ShadowCastingMode.ShadowsOnly)
|
||||
{
|
||||
renderList.Add(new RenderRecord
|
||||
{
|
||||
localToWorld = meshLtw.matrix,
|
||||
mesh = meshInstance.mesh,
|
||||
materialPalette = meshInstance.materialPalette,
|
||||
renderingLayerMask = meshInstance.renderingLayerMask,
|
||||
}, 0);
|
||||
}
|
||||
|
||||
if (meshInstance.shadowCastingMode != ShadowCastingMode.Off)
|
||||
{
|
||||
shadowCasterRenderList.Add(new RenderRecord
|
||||
{
|
||||
localToWorld = meshLtw.matrix,
|
||||
mesh = meshInstance.mesh,
|
||||
materialPalette = meshInstance.materialPalette,
|
||||
renderingLayerMask = meshInstance.renderingLayerMask,
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: We assume camera's scale is always (1, 1, 1). Otherwise fastinverse will fail and we need to use regular inverse which is more expensive.
|
||||
var viewMatrix = math.fastinverse(camLtwRef.matrix);
|
||||
|
||||
var vfov = 2.0f * math.atan(camRef.sensorSize.y / 2.0f * camRef.focalLength);
|
||||
var hfov = 2.0f * math.atan(camRef.sensorSize.x / 2.0f * camRef.focalLength);
|
||||
var aspectSensor = camRef.sensorSize.x / camRef.sensorSize.y;
|
||||
|
||||
float vfovF;
|
||||
switch (camRef.gateFit)
|
||||
{
|
||||
case GateFit.Vertical:
|
||||
vfovF = vfov;
|
||||
break;
|
||||
|
||||
case GateFit.Horizontal:
|
||||
// Adjust VFOV so that the sensor width fits the screen width
|
||||
var horizontalAspectBuffer = math.tan(hfov * 0.5f);
|
||||
vfovF = 2.0f * math.atan(horizontalAspectBuffer / aspectScreen);
|
||||
break;
|
||||
|
||||
case GateFit.Fill:
|
||||
if (aspectSensor > aspectScreen)
|
||||
{
|
||||
goto case GateFit.Vertical;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto case GateFit.Horizontal;
|
||||
}
|
||||
|
||||
case GateFit.Overscan:
|
||||
if (aspectSensor > aspectScreen)
|
||||
{
|
||||
goto case GateFit.Horizontal;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto case GateFit.Vertical;
|
||||
}
|
||||
default:
|
||||
vfovF = vfov;
|
||||
break;
|
||||
}
|
||||
|
||||
var m_00 = 1.0f / aspectScreen * math.tan(vfovF * 0.5f);
|
||||
var m_11 = 1.0f / math.tan(vfovF * 0.5f);
|
||||
var m_22 = -(camRef.farClipPlane + camRef.nearClipPlane) / (camRef.farClipPlane - camRef.nearClipPlane);
|
||||
var m_23 = -(2.0f * camRef.farClipPlane * camRef.nearClipPlane) / (camRef.farClipPlane - camRef.nearClipPlane);
|
||||
|
||||
var projectionMatrix = new float4x4
|
||||
(
|
||||
m_00, 0, 0, 0,
|
||||
0, m_11, 0, 0,
|
||||
0, 0, m_22, m_23,
|
||||
0, 0, -1, 0
|
||||
);
|
||||
|
||||
var vp = math.mul(projectionMatrix, viewMatrix);
|
||||
var viewDir = math.normalize(camLtwRef.matrix.c2.xyz);
|
||||
var viewPos = camLtwRef.matrix.c3.xyz;
|
||||
var frustum = CreateFrustum(camRef, vp, viewDir, viewPos);
|
||||
|
||||
// TODO: Send this to render pipeline.
|
||||
var request = new RenderRequest
|
||||
{
|
||||
colorTarget = camRef.colorTarget,
|
||||
depthTarget = camRef.depthTarget,
|
||||
opaqueRenderList = renderList,
|
||||
shadowCasterRenderList = shadowCasterRenderList,
|
||||
transparentRenderList = default,
|
||||
renderFunc = camRef.renderFunc,
|
||||
view = new RenderView
|
||||
{
|
||||
viewMatrix = viewMatrix,
|
||||
projectionMatrix = projectionMatrix,
|
||||
position = camLtwRef.matrix.c3.xyz,
|
||||
|
||||
frustum = frustum,
|
||||
nearClipPlane = camRef.nearClipPlane,
|
||||
farClipPlane = camRef.farClipPlane,
|
||||
|
||||
sensorSize = camRef.sensorSize,
|
||||
gateFit = camRef.gateFit,
|
||||
iso = camRef.iso,
|
||||
shutterSpeed = camRef.shutterSpeed,
|
||||
aperture = camRef.aperture,
|
||||
focalLength = camRef.focalLength,
|
||||
focusDistance = camRef.focusDistance,
|
||||
|
||||
renderingLayerMask = camRef.renderingLayerMask,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public void Cleanup(ref readonly SystemAPI systemAPI)
|
||||
|
||||
@@ -96,7 +96,7 @@ public readonly unsafe ref struct ChunkView
|
||||
private readonly int _structuralVersion;
|
||||
private readonly int _currentVersion;
|
||||
|
||||
public readonly int Count => _entityCount;
|
||||
public readonly int EntityCount => _entityCount;
|
||||
|
||||
internal ChunkView(ref readonly Archetype archetype, ref readonly Chunk chunk)
|
||||
{
|
||||
@@ -478,7 +478,7 @@ public unsafe partial struct EntityQuery : IDisposable
|
||||
|
||||
public ref partial struct QueryBuilder : IDisposable
|
||||
{
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
|
||||
private UnsafeList<Identifier<IComponent>> _all;
|
||||
private UnsafeList<Identifier<IComponent>> _any;
|
||||
@@ -666,6 +666,10 @@ public ref partial struct QueryBuilder : IDisposable
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
return queryID;
|
||||
}
|
||||
|
||||
@@ -338,9 +338,9 @@ public abstract class SystemGroup : ISystem
|
||||
}
|
||||
}
|
||||
|
||||
public class DefaultSystemGroup : SystemGroup;
|
||||
public sealed class DefaultSystemGroup : SystemGroup;
|
||||
|
||||
public class SystemManager
|
||||
public sealed class SystemManager
|
||||
{
|
||||
private readonly World _world;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
@@ -216,7 +216,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
@@ -423,7 +423,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
@@ -640,7 +640,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
@@ -867,7 +867,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
@@ -1104,7 +1104,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
@@ -1351,7 +1351,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
@@ -1608,7 +1608,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
|
||||
@@ -56,7 +56,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
|
||||
@@ -41,7 +41,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
@@ -245,7 +245,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
@@ -459,7 +459,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
@@ -683,7 +683,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
@@ -917,7 +917,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
@@ -1161,7 +1161,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
@@ -1415,7 +1415,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
@@ -1679,7 +1679,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
|
||||
@@ -60,7 +60,7 @@ public unsafe partial struct EntityQuery
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private readonly Stack.Scope _scope;
|
||||
private readonly VirtualStack.Scope _scope;
|
||||
private UnsafeList<int> _changedComponentIDs;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Ghost.Core;
|
||||
using Misaki.HighPerformance.Jobs;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Ghost.Entities;
|
||||
|
||||
@@ -85,6 +86,8 @@ public partial class World : IDisposable, IEquatable<World>
|
||||
private readonly ComponentManager _componentManager;
|
||||
private readonly SystemManager _systemManager;
|
||||
|
||||
private readonly Dictionary<Type, object> _globalResource;
|
||||
|
||||
private int _version;
|
||||
private bool _disposed = false;
|
||||
|
||||
@@ -137,6 +140,8 @@ public partial class World : IDisposable, IEquatable<World>
|
||||
_componentManager = new ComponentManager(this);
|
||||
_systemManager = new SystemManager(this);
|
||||
|
||||
_globalResource = new Dictionary<Type, object>();
|
||||
|
||||
if (jobScheduler != null)
|
||||
{
|
||||
_threadLocalECBs = new EntityCommandBuffer[jobScheduler.WorkerCount];
|
||||
@@ -186,6 +191,41 @@ public partial class World : IDisposable, IEquatable<World>
|
||||
return _threadLocalECBs[threadIndex];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers or overwrites a global resource in the world.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetResource<T>(T resource)
|
||||
where T : class
|
||||
{
|
||||
_globalResource[typeof(T)] = resource;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a global resource from the world.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T GetResource<T>()
|
||||
where T : class
|
||||
{
|
||||
if (_globalResource.TryGetValue(typeof(T), out var resource))
|
||||
{
|
||||
return (T)resource;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Resource of type {typeof(T).FullName} has not been registered in the World.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a global resource exists.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool HasResource<T>()
|
||||
where T : class
|
||||
{
|
||||
return _globalResource.ContainsKey(typeof(T));
|
||||
}
|
||||
|
||||
public bool Equals(World? other)
|
||||
{
|
||||
return other is not null && _id == other._id;
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public interface IFenceSynchronizer
|
||||
{
|
||||
uint CPUFenceValue
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
uint GPUFenceValue
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
uint FrameIndex
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
uint MaxFrameLatency
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
bool WaitForGPUReady(int timeOut = -1);
|
||||
void SignalCPUReady();
|
||||
void WaitIdle();
|
||||
}
|
||||
@@ -8,5 +8,5 @@ public interface IRenderPass
|
||||
{
|
||||
void Initialize(ref readonly RenderingContext ctx);
|
||||
void Build(RenderGraph graph, Identifier<RGTexture> backbuffer);
|
||||
void Cleanup(IResourceManager resourceManager, IResourceDatabase resourceDatabase);
|
||||
void Cleanup(ResourceManager resourceManager, IResourceDatabase resourceDatabase);
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ public struct Material : IResourceReleasable
|
||||
get; set;
|
||||
}
|
||||
|
||||
public Error SetShader(Identifier<Shader> shaderId, IResourceManager resourceManager, IResourceDatabase resourceDatabase, IResourceAllocator resourceAllocator)
|
||||
public Error SetShader(Identifier<Shader> shaderId, ResourceManager resourceManager, IResourceDatabase resourceDatabase, IResourceAllocator resourceAllocator)
|
||||
{
|
||||
if (!shaderId.IsValid)
|
||||
{
|
||||
@@ -198,7 +198,7 @@ public struct Material : IResourceReleasable
|
||||
_isDirty = true;
|
||||
}
|
||||
|
||||
public Error SetKeyword(IResourceManager manager, int keywordId, bool enabled)
|
||||
public Error SetKeyword(ResourceManager manager, int keywordId, bool enabled)
|
||||
{
|
||||
var r = manager.GetShaderReference(_shader);
|
||||
if (r.IsFailure)
|
||||
@@ -219,7 +219,7 @@ public struct Material : IResourceReleasable
|
||||
return Error.None;
|
||||
}
|
||||
|
||||
public readonly bool IsKeywordEnabled(IResourceManager manager, int keywordId)
|
||||
public readonly bool IsKeywordEnabled(ResourceManager manager, int keywordId)
|
||||
{
|
||||
var r = manager.GetShaderReference(_shader);
|
||||
if (r.IsFailure)
|
||||
|
||||
@@ -6,11 +6,13 @@ using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using Misaki.HighPerformance.Mathematics.Geometry;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Graphics.Core;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Meshlet
|
||||
{
|
||||
public SphereBounds boundingSphere; // 16 bytes
|
||||
@@ -25,6 +27,7 @@ public struct Meshlet
|
||||
public byte lodLevel; // this meshlet's LOD level
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MeshletGroup
|
||||
{
|
||||
public SphereBounds boundingSphere; // 16 bytes
|
||||
@@ -35,6 +38,7 @@ public struct MeshletGroup
|
||||
public uint lodLevel; // group LOD level
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MeshletHierarchyNode
|
||||
{
|
||||
public SphereBounds boundingSphere; // 16 bytes
|
||||
@@ -43,6 +47,7 @@ public struct MeshletHierarchyNode
|
||||
public uint nodeData; // packed leaf/internal metadata
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MeshletMeshData : IDisposable
|
||||
{
|
||||
public UnsafeList<Meshlet> meshlets;
|
||||
@@ -63,14 +68,14 @@ public struct MeshletMeshData : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Support and meshlets.
|
||||
public struct Mesh : IResourceReleasable
|
||||
{
|
||||
private UnsafeList<Vertex> _vertices;
|
||||
private UnsafeList<uint> _indices;
|
||||
private MeshletMeshData _meshletData;
|
||||
|
||||
public MeshletMeshData MeshletData => _meshletData;
|
||||
[UnscopedRef]
|
||||
public readonly ref readonly MeshletMeshData MeshletData => ref _meshletData;
|
||||
|
||||
internal bool IsMeshDataDirty
|
||||
{
|
||||
@@ -219,7 +224,7 @@ public struct Mesh : IResourceReleasable
|
||||
};
|
||||
|
||||
// 2. Map Mesh to ClodMesh
|
||||
ClodMesh clodMesh = new ClodMesh
|
||||
var clodMesh = new ClodMesh
|
||||
{
|
||||
vertexPositions = (float*)_vertices.GetUnsafePtr(),
|
||||
vertexCount = (nuint)_vertices.Count,
|
||||
@@ -233,9 +238,9 @@ public struct Mesh : IResourceReleasable
|
||||
MeshletUtility.Build(config, clodMesh, Unsafe.AsPointer(ref this), MeshletOutputCallback);
|
||||
}
|
||||
|
||||
private static unsafe int MeshletOutputCallback(void* context, ClodGroup group, ClodCluster* clusters, nuint clusterCount)
|
||||
private static unsafe int MeshletOutputCallback(void* context, ClodGroup group, ReadOnlyUnsafeCollection<ClodCluster>clusters)
|
||||
{
|
||||
Mesh* mesh = (Mesh*)context;
|
||||
var mesh = (Mesh*)context;
|
||||
ref var data = ref mesh->_meshletData;
|
||||
|
||||
// Ensure lists are initialized
|
||||
@@ -247,15 +252,15 @@ public struct Mesh : IResourceReleasable
|
||||
var meshletGroup = new MeshletGroup
|
||||
{
|
||||
meshletStartIndex = (uint)data.meshlets.Count,
|
||||
meshletCount = (uint)clusterCount,
|
||||
meshletCount = (uint)clusters.Count,
|
||||
lodLevel = (uint)group.depth
|
||||
};
|
||||
data.groups.Add(meshletGroup);
|
||||
|
||||
for (nuint i = 0; i < clusterCount; i++)
|
||||
for (var i = 0; i < clusters.Count; i++)
|
||||
{
|
||||
var cluster = clusters[i];
|
||||
|
||||
|
||||
var meshlet = new Meshlet
|
||||
{
|
||||
vertexCount = (byte)cluster.vertexCount,
|
||||
|
||||
@@ -17,28 +17,28 @@ public struct RenderList : IDisposable
|
||||
{
|
||||
public unsafe ref struct Enumerator
|
||||
{
|
||||
private readonly UnsafeList<RenderRecord>* pList;
|
||||
private readonly int length;
|
||||
private readonly UnsafeList<RenderRecord>* _pList;
|
||||
private readonly int _length;
|
||||
|
||||
private int _listIndex;
|
||||
private int _itemIndex;
|
||||
|
||||
internal Enumerator(RenderList List)
|
||||
{
|
||||
pList = (UnsafeList<RenderRecord>*)List._threadLocalRecords.GetUnsafePtr();
|
||||
length = List._threadLocalRecords.Length;
|
||||
_pList = (UnsafeList<RenderRecord>*)List._threadLocalRecords.GetUnsafePtr();
|
||||
_length = List._threadLocalRecords.Length;
|
||||
|
||||
_listIndex = 0;
|
||||
_itemIndex = -1;
|
||||
}
|
||||
|
||||
public RenderRecord Current => pList[_listIndex][_itemIndex];
|
||||
public RenderRecord Current => _pList[_listIndex][_itemIndex];
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
while (_listIndex < length)
|
||||
while (_listIndex < _length)
|
||||
{
|
||||
if (_itemIndex < pList[_listIndex].Count)
|
||||
if (_itemIndex < _pList[_listIndex].Count)
|
||||
{
|
||||
_itemIndex++;
|
||||
return true;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Graphics.Core;
|
||||
@@ -11,35 +12,145 @@ public enum GateFit : uint
|
||||
Horizontal,
|
||||
Fill,
|
||||
Overscan,
|
||||
None
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
public struct Frustum
|
||||
{
|
||||
// The data of the 6 planes of the frustum
|
||||
public float3 normal0;
|
||||
public float dist0;
|
||||
public float3 normal1;
|
||||
public float dist1;
|
||||
public float3 normal2;
|
||||
public float dist2;
|
||||
public float3 normal3;
|
||||
public float dist3;
|
||||
public float3 normal4;
|
||||
public float dist4;
|
||||
public float3 normal5;
|
||||
public float dist5;
|
||||
[InlineArray(6)]
|
||||
public struct plane_array
|
||||
{
|
||||
private float4 plane;
|
||||
}
|
||||
|
||||
// The data of the 8 corners of the frustum
|
||||
public float3 corner0;
|
||||
public float3 corner1;
|
||||
public float3 corner2;
|
||||
public float3 corner3;
|
||||
public float3 corner4;
|
||||
public float3 corner5;
|
||||
public float3 corner6;
|
||||
public float3 corner7;
|
||||
[InlineArray(8)]
|
||||
public struct corner_array
|
||||
{
|
||||
private float3 corner;
|
||||
}
|
||||
|
||||
public plane_array planes;
|
||||
public corner_array corners;
|
||||
|
||||
public static void CalculateFrustumPlanes(float4x4 finalMatrix, ref plane_array outPlanes)
|
||||
{
|
||||
const int kPlaneFrustumLeft = 0;
|
||||
const int kPlaneFrustumRight = 1;
|
||||
const int kPlaneFrustumBottom = 2;
|
||||
const int kPlaneFrustumTop = 3;
|
||||
const int kPlaneFrustumNear = 4;
|
||||
const int kPlaneFrustumFar = 5;
|
||||
|
||||
float4 tmpVec = default;
|
||||
float4 otherVec = default;
|
||||
|
||||
tmpVec[0] = finalMatrix[0][3];
|
||||
tmpVec[1] = finalMatrix[1][3];
|
||||
tmpVec[2] = finalMatrix[2][3];
|
||||
tmpVec[3] = finalMatrix[3][3];
|
||||
|
||||
otherVec[0] = finalMatrix[0][0];
|
||||
otherVec[1] = finalMatrix[1][0];
|
||||
otherVec[2] = finalMatrix[2][0];
|
||||
otherVec[3] = finalMatrix[3][0];
|
||||
|
||||
// left & right
|
||||
var leftNormalX = otherVec[0] + tmpVec[0];
|
||||
var leftNormalY = otherVec[1] + tmpVec[1];
|
||||
var leftNormalZ = otherVec[2] + tmpVec[2];
|
||||
var leftDistance = otherVec[3] + tmpVec[3];
|
||||
var leftDot = leftNormalX * leftNormalX + leftNormalY * leftNormalY + leftNormalZ * leftNormalZ;
|
||||
var leftMagnitude = math.sqrt(leftDot);
|
||||
var leftInvMagnitude = 1.0f / leftMagnitude;
|
||||
leftNormalX *= leftInvMagnitude;
|
||||
leftNormalY *= leftInvMagnitude;
|
||||
leftNormalZ *= leftInvMagnitude;
|
||||
leftDistance *= leftInvMagnitude;
|
||||
outPlanes[kPlaneFrustumLeft].xyz = new float3(leftNormalX, leftNormalY, leftNormalZ);
|
||||
outPlanes[kPlaneFrustumLeft].w = leftDistance;
|
||||
|
||||
var rightNormalX = -otherVec[0] + tmpVec[0];
|
||||
var rightNormalY = -otherVec[1] + tmpVec[1];
|
||||
var rightNormalZ = -otherVec[2] + tmpVec[2];
|
||||
var rightDistance = -otherVec[3] + tmpVec[3];
|
||||
var rightDot = rightNormalX * rightNormalX + rightNormalY * rightNormalY + rightNormalZ * rightNormalZ;
|
||||
var rightMagnitude = math.sqrt(rightDot);
|
||||
var rightInvMagnitude = 1.0f / rightMagnitude;
|
||||
rightNormalX *= rightInvMagnitude;
|
||||
rightNormalY *= rightInvMagnitude;
|
||||
rightNormalZ *= rightInvMagnitude;
|
||||
rightDistance *= rightInvMagnitude;
|
||||
outPlanes[kPlaneFrustumRight].xyz = new float3(rightNormalX, rightNormalY, rightNormalZ);
|
||||
outPlanes[kPlaneFrustumRight].w = rightDistance;
|
||||
|
||||
// bottom & top
|
||||
otherVec[0] = finalMatrix[0][1];
|
||||
otherVec[1] = finalMatrix[1][1];
|
||||
otherVec[2] = finalMatrix[2][1];
|
||||
otherVec[3] = finalMatrix[3][1];
|
||||
|
||||
var bottomNormalX = otherVec[0] + tmpVec[0];
|
||||
var bottomNormalY = otherVec[1] + tmpVec[1];
|
||||
var bottomNormalZ = otherVec[2] + tmpVec[2];
|
||||
var bottomDistance = otherVec[3] + tmpVec[3];
|
||||
var bottomDot = bottomNormalX * bottomNormalX + bottomNormalY * bottomNormalY + bottomNormalZ * bottomNormalZ;
|
||||
var bottomMagnitude = math.sqrt(bottomDot);
|
||||
var bottomInvMagnitude = 1.0f / bottomMagnitude;
|
||||
bottomNormalX *= bottomInvMagnitude;
|
||||
bottomNormalY *= bottomInvMagnitude;
|
||||
bottomNormalZ *= bottomInvMagnitude;
|
||||
bottomDistance *= bottomInvMagnitude;
|
||||
outPlanes[kPlaneFrustumBottom].xyz = new float3(bottomNormalX, bottomNormalY, bottomNormalZ);
|
||||
outPlanes[kPlaneFrustumBottom].w = bottomDistance;
|
||||
|
||||
var topNormalX = -otherVec[0] + tmpVec[0];
|
||||
var topNormalY = -otherVec[1] + tmpVec[1];
|
||||
var topNormalZ = -otherVec[2] + tmpVec[2];
|
||||
var topDistance = -otherVec[3] + tmpVec[3];
|
||||
var topDot = topNormalX * topNormalX + topNormalY * topNormalY + topNormalZ * topNormalZ;
|
||||
var topMagnitude = math.sqrt(topDot);
|
||||
var topInvMagnitude = 1.0f / topMagnitude;
|
||||
topNormalX *= topInvMagnitude;
|
||||
topNormalY *= topInvMagnitude;
|
||||
topNormalZ *= topInvMagnitude;
|
||||
topDistance *= topInvMagnitude;
|
||||
outPlanes[kPlaneFrustumTop].xyz = new float3(topNormalX, topNormalY, topNormalZ);
|
||||
outPlanes[kPlaneFrustumTop].w = topDistance;
|
||||
|
||||
// near & far
|
||||
otherVec[0] = finalMatrix[0][2];
|
||||
otherVec[1] = finalMatrix[1][2];
|
||||
otherVec[2] = finalMatrix[2][2];
|
||||
otherVec[3] = finalMatrix[3][2];
|
||||
|
||||
var nearNormalX = otherVec[0] + tmpVec[0];
|
||||
var nearNormalY = otherVec[1] + tmpVec[1];
|
||||
var nearNormalZ = otherVec[2] + tmpVec[2];
|
||||
var nearDistance = otherVec[3] + tmpVec[3];
|
||||
var nearDot = nearNormalX * nearNormalX + nearNormalY * nearNormalY + nearNormalZ * nearNormalZ;
|
||||
var nearMagnitude = math.sqrt(nearDot);
|
||||
var nearInvMagnitude = 1.0f / nearMagnitude;
|
||||
nearNormalX *= nearInvMagnitude;
|
||||
nearNormalY *= nearInvMagnitude;
|
||||
nearNormalZ *= nearInvMagnitude;
|
||||
nearDistance *= nearInvMagnitude;
|
||||
outPlanes[kPlaneFrustumNear].xyz = new float3(nearNormalX, nearNormalY, nearNormalZ);
|
||||
outPlanes[kPlaneFrustumNear].w = nearDistance;
|
||||
|
||||
var farNormalX = -otherVec[0] + tmpVec[0];
|
||||
var farNormalY = -otherVec[1] + tmpVec[1];
|
||||
var farNormalZ = -otherVec[2] + tmpVec[2];
|
||||
var farDistance = -otherVec[3] + tmpVec[3];
|
||||
var farDot = farNormalX * farNormalX + farNormalY * farNormalY + farNormalZ * farNormalZ;
|
||||
var farMagnitude = math.sqrt(farDot);
|
||||
var farInvMagnitude = 1.0f / farMagnitude;
|
||||
farNormalX *= farInvMagnitude;
|
||||
farNormalY *= farInvMagnitude;
|
||||
farNormalZ *= farInvMagnitude;
|
||||
farDistance *= farInvMagnitude;
|
||||
outPlanes[kPlaneFrustumFar].xyz = new float3(farNormalX, farNormalY, farNormalZ);
|
||||
outPlanes[kPlaneFrustumFar].w = farDistance;
|
||||
}
|
||||
}
|
||||
|
||||
// Since we are using ByteAddressBuffer in hlsl, we don't need to care about the 16 bytes alignment of the data like in CBuffer.
|
||||
@@ -54,6 +165,7 @@ public struct RenderView
|
||||
public float nearClipPlane;
|
||||
public float farClipPlane;
|
||||
|
||||
// Maybe use fov directly?
|
||||
public float2 sensorSize;
|
||||
public GateFit gateFit;
|
||||
public float iso;
|
||||
|
||||
@@ -10,18 +10,18 @@ namespace Ghost.Graphics.Core;
|
||||
public readonly unsafe ref struct RenderingContext
|
||||
{
|
||||
private readonly IGraphicsEngine _engine;
|
||||
private readonly IResourceManager _resourceManager;
|
||||
private readonly ResourceManager _resourceManager;
|
||||
private readonly ICommandBuffer _directCmd;
|
||||
|
||||
public ICommandBuffer DirectCommandBuffer => _directCmd;
|
||||
|
||||
public IShaderCompiler ShaderCompiler => _engine.ShaderCompiler;
|
||||
public IResourceManager ResourceManager => _resourceManager;
|
||||
public ResourceManager ResourceManager => _resourceManager;
|
||||
public IResourceAllocator ResourceAllocator => _engine.ResourceAllocator;
|
||||
public IResourceDatabase ResourceDatabase => _engine.ResourceDatabase;
|
||||
public IPipelineLibrary PipelineLibrary => _engine.PipelineLibrary;
|
||||
|
||||
internal RenderingContext(IGraphicsEngine engine, IResourceManager resourceManager, ICommandBuffer directCmd)
|
||||
internal RenderingContext(IGraphicsEngine engine, ResourceManager resourceManager, ICommandBuffer directCmd)
|
||||
{
|
||||
_engine = engine;
|
||||
_resourceManager = resourceManager;
|
||||
@@ -163,7 +163,7 @@ public readonly unsafe ref struct RenderingContext
|
||||
if (r.IsFailure) return;
|
||||
|
||||
ref var meshRef = ref r.Value;
|
||||
var meshletData = meshRef.MeshletData;
|
||||
ref readonly var meshletData = ref meshRef.MeshletData;
|
||||
|
||||
if (!meshletData.meshlets.IsCreated || meshletData.meshlets.Count == 0) return;
|
||||
|
||||
|
||||
@@ -2,35 +2,40 @@ using System.Diagnostics;
|
||||
|
||||
namespace Ghost.Graphics.Core;
|
||||
|
||||
public struct RenderingLayerMask : IEquatable<RenderingLayerMask>
|
||||
public readonly struct RenderingLayerMask : IEquatable<RenderingLayerMask>
|
||||
{
|
||||
private static readonly Dictionary<string, uint> _layerNameToBit = new(32);
|
||||
private static readonly Dictionary<uint, string> _bitToLayerName = new(32);
|
||||
private static readonly Dictionary<string, uint> s_layerNameToBit = new(32);
|
||||
private static readonly Dictionary<uint, string> s_bitToLayerName = new(32);
|
||||
|
||||
internal static void SetLayerName(int layerIndex, string name)
|
||||
{
|
||||
Debug.Assert(layerIndex >= 0 && layerIndex < 32, "Layer index must be between 0 and 31.");
|
||||
|
||||
var bit = 1u << layerIndex;
|
||||
_layerNameToBit[name] = bit;
|
||||
_bitToLayerName[bit] = name;
|
||||
s_layerNameToBit[name] = bit;
|
||||
s_bitToLayerName[bit] = name;
|
||||
}
|
||||
|
||||
public static uint GetLayerBit(string name)
|
||||
{
|
||||
if (_layerNameToBit.TryGetValue(name, out var bit))
|
||||
if (s_layerNameToBit.TryGetValue(name, out var bit))
|
||||
{
|
||||
return bit;
|
||||
}
|
||||
|
||||
return ~0u;
|
||||
return 0u;
|
||||
}
|
||||
|
||||
public uint value;
|
||||
private readonly uint _value;
|
||||
|
||||
public RenderingLayerMask(uint value)
|
||||
{
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public readonly bool Equals(RenderingLayerMask other)
|
||||
{
|
||||
return value == other.value;
|
||||
return _value == other._value;
|
||||
}
|
||||
|
||||
public override readonly bool Equals(object? obj)
|
||||
@@ -53,13 +58,43 @@ public struct RenderingLayerMask : IEquatable<RenderingLayerMask>
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public static RenderingLayerMask operator |(RenderingLayerMask left, RenderingLayerMask right)
|
||||
{
|
||||
return new RenderingLayerMask(left._value | right._value);
|
||||
}
|
||||
|
||||
public static RenderingLayerMask operator &(RenderingLayerMask left, RenderingLayerMask right)
|
||||
{
|
||||
return new RenderingLayerMask(left._value & right._value);
|
||||
}
|
||||
|
||||
public static RenderingLayerMask operator ~(RenderingLayerMask mask)
|
||||
{
|
||||
return new RenderingLayerMask(~mask._value);
|
||||
}
|
||||
|
||||
public static RenderingLayerMask operator ^(RenderingLayerMask left, RenderingLayerMask right)
|
||||
{
|
||||
return new RenderingLayerMask(left._value ^ right._value);
|
||||
}
|
||||
|
||||
public static RenderingLayerMask operator <<(RenderingLayerMask mask, int shift)
|
||||
{
|
||||
return new RenderingLayerMask(mask._value << shift);
|
||||
}
|
||||
|
||||
public static RenderingLayerMask operator >>(RenderingLayerMask mask, int shift)
|
||||
{
|
||||
return new RenderingLayerMask(mask._value >> shift);
|
||||
}
|
||||
|
||||
public static implicit operator uint(RenderingLayerMask mask)
|
||||
{
|
||||
return mask.value;
|
||||
return mask._value;
|
||||
}
|
||||
|
||||
public static implicit operator RenderingLayerMask(uint value)
|
||||
{
|
||||
return new RenderingLayerMask { value = value };
|
||||
return new RenderingLayerMask(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Ghost.Graphics.RenderGraphModule;
|
||||
/// </summary>
|
||||
public sealed class RenderGraph : IDisposable
|
||||
{
|
||||
private readonly IResourceManager _resourceManager;
|
||||
private readonly ResourceManager _resourceManager;
|
||||
private readonly IResourceAllocator _resourceAllocator;
|
||||
private readonly IResourceDatabase _resourceDatabase;
|
||||
|
||||
@@ -37,7 +37,7 @@ public sealed class RenderGraph : IDisposable
|
||||
|
||||
public RenderGraphBlackboard Blackboard => _blackboard;
|
||||
|
||||
public RenderGraph(IResourceManager resourceManager, IResourceAllocator resourceAllocator, IResourceDatabase resourceDatabase, IPipelineLibrary pipelineLibrary, IShaderCompiler shaderCompiler)
|
||||
public RenderGraph(ResourceManager resourceManager, IResourceAllocator resourceAllocator, IResourceDatabase resourceDatabase, IPipelineLibrary pipelineLibrary, IShaderCompiler shaderCompiler)
|
||||
{
|
||||
_resourceManager = resourceManager;
|
||||
_resourceAllocator = resourceAllocator;
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Ghost.Graphics.RenderGraphModule;
|
||||
/// </summary>
|
||||
internal sealed class RenderGraphCompiler
|
||||
{
|
||||
private readonly IResourceManager _resourceManager;
|
||||
private readonly ResourceManager _resourceManager;
|
||||
private readonly IResourceDatabase _resourceDatabase;
|
||||
private readonly IResourceAllocator _resourceAllocator;
|
||||
private readonly RenderGraphResourceRegistry _resources;
|
||||
@@ -20,7 +20,7 @@ internal sealed class RenderGraphCompiler
|
||||
private Handle<GPUResource> _resourceHeap;
|
||||
|
||||
public RenderGraphCompiler(
|
||||
IResourceManager resourceManager,
|
||||
ResourceManager resourceManager,
|
||||
IResourceDatabase resourceDatabase,
|
||||
IResourceAllocator resourceAllocator,
|
||||
RenderGraphResourceRegistry resources,
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
public interface IRenderGraphContext
|
||||
{
|
||||
IResourceManager ResourceManager { get; }
|
||||
ResourceManager ResourceManager { get; }
|
||||
IResourceDatabase ResourceDatabase { get; }
|
||||
|
||||
Handle<GPUResource> GetActualResource(Identifier<RGResource> resource);
|
||||
@@ -41,7 +41,7 @@ public interface IUnsafeRenderContext : IRasterRenderContext, IRenderGraphContex
|
||||
|
||||
internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderContext, IUnsafeRenderContext
|
||||
{
|
||||
private readonly IResourceManager _resourceManager;
|
||||
private readonly ResourceManager _resourceManager;
|
||||
private readonly IResourceDatabase _resourceDatabase;
|
||||
private readonly IPipelineLibrary _pipelineLibrary;
|
||||
private readonly IShaderCompiler _shaderCompiler;
|
||||
@@ -58,14 +58,14 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
||||
private Handle<GraphicsBuffer> _activePerMeshData;
|
||||
private int _activeMeshIndexCount;
|
||||
|
||||
public IResourceManager ResourceManager => _resourceManager;
|
||||
public ResourceManager ResourceManager => _resourceManager;
|
||||
public IResourceDatabase ResourceDatabase => _resourceDatabase;
|
||||
|
||||
public int ActiveMeshIndexCount => _activeMeshIndexCount;
|
||||
|
||||
public ICommandBuffer CommandBuffer => _commandBuffer;
|
||||
|
||||
internal RenderGraphContext(IResourceManager resourceManager, IResourceDatabase resourceDatabase, IPipelineLibrary pipelineLibrary, IShaderCompiler shaderCompiler, RenderGraphResourceRegistry resources)
|
||||
internal RenderGraphContext(ResourceManager resourceManager, IResourceDatabase resourceDatabase, IPipelineLibrary pipelineLibrary, IShaderCompiler shaderCompiler, RenderGraphResourceRegistry resources)
|
||||
{
|
||||
_resourceManager = resourceManager;
|
||||
_resourceDatabase = resourceDatabase;
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
internal sealed class RenderGraphExecutor
|
||||
{
|
||||
private readonly IResourceManager _resourceManager;
|
||||
private readonly ResourceManager _resourceManager;
|
||||
private readonly IResourceDatabase _resourceDatabase;
|
||||
private readonly RenderGraphResourceRegistry _resources;
|
||||
private readonly RenderGraphContext _context;
|
||||
@@ -13,7 +13,7 @@ internal sealed class RenderGraphExecutor
|
||||
private uint _frameIndex;
|
||||
|
||||
public RenderGraphExecutor(
|
||||
IResourceManager resourceManager,
|
||||
ResourceManager resourceManager,
|
||||
IResourceDatabase resourceDatabase,
|
||||
RenderGraphResourceRegistry resources,
|
||||
RenderGraphContext context)
|
||||
|
||||
@@ -1,337 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.DSL.ShaderCompiler;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.Core.Contracts;
|
||||
using Ghost.Graphics.RenderGraphModule;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Ghost.Graphics.Utilities;
|
||||
using Misaki.HighPerformance.Image;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using Misaki.HighPerformance.Utilities;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Graphics.RenderPasses;
|
||||
|
||||
internal class MeshRenderPassData
|
||||
{
|
||||
public Handle<Mesh> mesh;
|
||||
public Handle<Material> material;
|
||||
public Identifier<RGTexture> renderTarget;
|
||||
}
|
||||
|
||||
internal class BlitPassData
|
||||
{
|
||||
public Identifier<RGTexture> source;
|
||||
public Identifier<RGTexture> destination;
|
||||
|
||||
public Handle<Material> blitMaterial;
|
||||
public Identifier<Sampler> sampler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simplified bindless mesh render pass using high-level bindless APIs with fully bindless vertex/index buffer access
|
||||
/// </summary>
|
||||
internal class MeshRenderPass : IRenderPass
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct ShaderProperties_MyShader_Standard
|
||||
{
|
||||
public float4 color;
|
||||
public uint texture1;
|
||||
public uint texture2;
|
||||
public uint texture3;
|
||||
public uint texture4;
|
||||
public uint tex_sampler;
|
||||
|
||||
private readonly uint _padding1;
|
||||
private readonly uint _padding2;
|
||||
private readonly uint _padding3;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct ShaderProperties_Hidden_Blit
|
||||
{
|
||||
public uint mainTex;
|
||||
public uint sampler_mainTex;
|
||||
private readonly uint _padding1;
|
||||
private readonly uint _padding2;
|
||||
}
|
||||
|
||||
private Handle<Mesh> _mesh;
|
||||
private Identifier<Shader> _shader;
|
||||
private Handle<Material> _material;
|
||||
private Handle<Texture>[]? _textures;
|
||||
private Identifier<Sampler> _sampler;
|
||||
|
||||
private Identifier<Shader> _blitShader;
|
||||
private Handle<Material> _blitMaterial;
|
||||
|
||||
// Texture file paths for this demo
|
||||
private readonly string[] _textureFiles = [
|
||||
"C:/Users/Misaki/Downloads/Im/Icon.png",
|
||||
"C:/Users/Misaki/Downloads/Im/Backdrop.jpg",
|
||||
"C:/Users/Misaki/Downloads/Im/101167591_p0.png",
|
||||
"C:/Users/Misaki/Downloads/Im/yande.re 1134666 blue_archive nakamasa_ichika sugarhigh.jpg"
|
||||
];
|
||||
|
||||
private static IEnumerable<ReadOnlyMemory<string>> GetAllVariantCombination(KeywordsGroup[] keywordsGroups)
|
||||
{
|
||||
if (keywordsGroups.Length == 0)
|
||||
{
|
||||
yield return ReadOnlyMemory<string>.Empty;
|
||||
yield break;
|
||||
}
|
||||
|
||||
var firstGroup = keywordsGroups[0];
|
||||
var remainingGroups = keywordsGroups[1..];
|
||||
|
||||
foreach (var combination in GetAllVariantCombination(remainingGroups))
|
||||
{
|
||||
yield return combination;
|
||||
}
|
||||
|
||||
foreach (var keyword in firstGroup.keywords)
|
||||
{
|
||||
foreach (var combination in GetAllVariantCombination(remainingGroups))
|
||||
{
|
||||
var array = new string[combination.Length + 1];
|
||||
array[0] = keyword;
|
||||
combination.Span.CopyTo(array.AsSpan(1));
|
||||
|
||||
yield return array;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.ResourceManager.CreateGraphicsShader(shaderDescriptor);
|
||||
_blitMaterial = ctx.ResourceManager.CreateMaterial(_blitShader);
|
||||
|
||||
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);
|
||||
|
||||
ctx.ShaderCompiler.CompilePass(in pass, in config, variantKey).GetValueOrThrow();
|
||||
}
|
||||
|
||||
public void Initialize(ref readonly RenderingContext ctx)
|
||||
{
|
||||
CompileBlitShader(in ctx);
|
||||
|
||||
var shaderDescriptor = DSLShaderCompiler.CompileShader("F:/csharp/GhostEngine/src/Runtime/Ghost.Graphics/test.gshdr", "C:/Users/Misaki/Downloads/Archive").GetValueOrThrow();
|
||||
|
||||
_shader = ctx.ResourceManager.CreateGraphicsShader(shaderDescriptor);
|
||||
_material = ctx.ResourceManager.CreateMaterial(_shader);
|
||||
|
||||
for (var i = 0; i < shaderDescriptor.passes.Length; i++)
|
||||
{
|
||||
ref var pass = ref shaderDescriptor.passes[i];
|
||||
var config = new ShaderCompilationConfig
|
||||
{
|
||||
optimizeLevel = CompilerOptimizeLevel.O3,
|
||||
options = CompilerOption.KeepReflections,
|
||||
tier = CompilerTier.Tier2
|
||||
};
|
||||
|
||||
// TODO: Ideally, in editor mode, we compile a single variant when it's needed during rendering. Before the compilation is done, we fallback to a special "compilation in progress" shader.
|
||||
// During the build process, we can precompile all the variants and store them in the cache for fast loading in runtime.
|
||||
// After the compilation, we should store the compiled result in the disk cache even in editor mode. This allows us to avoid recompiling the same variant, same code hash and same version) multiple times.
|
||||
if (pass.keywords.Length == 0)
|
||||
{
|
||||
var emptyKeywords = new LocalKeywordSet();
|
||||
var variantKey = RHIUtility.CreateShaderVariantKey(
|
||||
RHIUtility.CreateShaderPassKey(pass.identifier),
|
||||
in emptyKeywords);
|
||||
|
||||
ctx.ShaderCompiler.CompilePass(in pass, in config, variantKey).GetValueOrThrow();
|
||||
}
|
||||
else
|
||||
{
|
||||
var shaderResult = ctx.ResourceManager.GetShaderReference(_shader);
|
||||
if (shaderResult.IsFailure)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to get shader reference.");
|
||||
}
|
||||
|
||||
ref readonly var shaderRef = ref shaderResult.Value;
|
||||
foreach (var keyGroup in GetAllVariantCombination(pass.keywords))
|
||||
{
|
||||
config.defines = keyGroup.Span;
|
||||
var keywordsSet = new LocalKeywordSet();
|
||||
|
||||
foreach (var key in keyGroup.Span)
|
||||
{
|
||||
var localIndex = shaderRef.GetLocalKeywordIndex(Shader.GetKeywordID(key));
|
||||
if (localIndex == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
keywordsSet.SetKeyword(localIndex, true);
|
||||
}
|
||||
|
||||
var variantKey = RHIUtility.CreateShaderVariantKey(
|
||||
RHIUtility.CreateShaderPassKey(pass.identifier),
|
||||
in keywordsSet);
|
||||
|
||||
ctx.ShaderCompiler.CompilePass(in pass, in config, variantKey).GetValueOrThrow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MeshBuilder.CreateCube(0.75f, default, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent, out var vertices, out var indices);
|
||||
|
||||
_mesh = ctx.CreateMesh(vertices, indices, true);
|
||||
|
||||
// Cook meshlets for the mesh
|
||||
var meshRef = ctx.ResourceManager.GetMeshReference(_mesh);
|
||||
if (meshRef.IsSuccess)
|
||||
{
|
||||
meshRef.Value.CookMeshlets();
|
||||
}
|
||||
|
||||
ctx.UploadMeshlets(_mesh);
|
||||
|
||||
ctx.UpdateObjectData(_mesh, float4x4.identity);
|
||||
|
||||
_textures = new Handle<Texture>[_textureFiles.Length];
|
||||
for (var i = 0; i < _textureFiles.Length; i++)
|
||||
{
|
||||
using var stream = File.OpenRead(_textureFiles[i]);
|
||||
using var imageData = ImageResult.FromStream(stream, ColorComponents.RGBA);
|
||||
var desc = new TextureDesc
|
||||
{
|
||||
Width = imageData.Width,
|
||||
Height = imageData.Height,
|
||||
Dimension = TextureDimension.Texture2D,
|
||||
Format = TextureFormat.R8G8B8A8_UNorm,
|
||||
MipLevels = 1,
|
||||
Slice = 1,
|
||||
Usage = TextureUsage.ShaderResource,
|
||||
};
|
||||
|
||||
_textures[i] = ctx.CreateTexture<byte>(in desc, imageData.AsSpan(), $"Texture_{i}");
|
||||
}
|
||||
|
||||
var samplerDesc = new SamplerDesc
|
||||
{
|
||||
AddressU = TextureAddressMode.Repeat,
|
||||
AddressV = TextureAddressMode.Repeat,
|
||||
AddressW = TextureAddressMode.Repeat,
|
||||
FilterMode = TextureFilterMode.Bilinear,
|
||||
MaxAnisotropy = 16,
|
||||
};
|
||||
|
||||
_sampler = ctx.ResourceAllocator.CreateSampler(in samplerDesc);
|
||||
|
||||
var meshResult = ctx.ResourceManager.GetMaterialReference(_material);
|
||||
if (meshResult.IsFailure)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to get material reference.");
|
||||
}
|
||||
|
||||
ref var matRef = ref meshResult.Value;
|
||||
var matProps = new ShaderProperties_MyShader_Standard
|
||||
{
|
||||
color = new float4(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
texture1 = ctx.ResourceDatabase.GetBindlessIndex(_textures[0].AsResource()),
|
||||
texture2 = ctx.ResourceDatabase.GetBindlessIndex(_textures[1].AsResource()),
|
||||
texture3 = ctx.ResourceDatabase.GetBindlessIndex(_textures[2].AsResource()),
|
||||
texture4 = ctx.ResourceDatabase.GetBindlessIndex(_textures[3].AsResource()),
|
||||
tex_sampler = (uint)_sampler.Value,
|
||||
};
|
||||
|
||||
matRef.SetPropertyCache(in matProps).ThrowIfFailed();
|
||||
matRef.UploadData(ctx.DirectCommandBuffer, ctx.ResourceDatabase);
|
||||
}
|
||||
|
||||
public void Build(RenderGraph graph, Identifier<RGTexture> backbuffer)
|
||||
{
|
||||
Identifier<RGTexture> renderTarget;
|
||||
using (var builder = graph.AddRasterRenderPass<MeshRenderPassData>("Mesh Render Pass", out var passData))
|
||||
{
|
||||
passData.mesh = _mesh;
|
||||
passData.material = _material;
|
||||
|
||||
passData.renderTarget = builder.CreateTexture(RGTextureDesc.Relative(1.0f, TextureFormat.R8G8B8A8_UNorm), "Render Target");
|
||||
builder.SetColorAttachment(passData.renderTarget, 0);
|
||||
|
||||
renderTarget = passData.renderTarget;
|
||||
|
||||
builder.SetRenderFunc<MeshRenderPassData>(static (data, ctx) =>
|
||||
{
|
||||
ctx.SetActiveMaterial(data.material);
|
||||
ctx.SetActiveMesh(data.mesh);
|
||||
|
||||
var threadGroupCountX = ((uint)ctx.ActiveMeshIndexCount + 2u) / 3u;
|
||||
ctx.DispatchMesh(new uint3(threadGroupCountX, 1u, 1u));
|
||||
});
|
||||
}
|
||||
|
||||
using (var builder = graph.AddUnsafeRenderPass<BlitPassData>("Blit Pass", out var passData))
|
||||
{
|
||||
passData.source = renderTarget;
|
||||
passData.destination = backbuffer;
|
||||
passData.blitMaterial = _blitMaterial;
|
||||
passData.sampler = _sampler;
|
||||
|
||||
builder.UseTexture(passData.source, AccessFlags.Read);
|
||||
builder.UseTexture(passData.destination, AccessFlags.WriteAll);
|
||||
|
||||
builder.SetRenderFunc<BlitPassData>(static (data, ctx) =>
|
||||
{
|
||||
var r = ctx.ResourceManager.GetMaterialReference(data.blitMaterial);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref var matRef = ref r.Value;
|
||||
var blitProps = new ShaderProperties_Hidden_Blit
|
||||
{
|
||||
mainTex = ctx.ResourceDatabase.GetBindlessIndex(ctx.GetActualResource(data.source.AsResource())),
|
||||
sampler_mainTex = (uint)data.sampler.Value,
|
||||
};
|
||||
|
||||
matRef.SetPropertyCache(in blitProps).ThrowIfFailed();
|
||||
matRef.UploadData(ctx.CommandBuffer, ctx.ResourceDatabase);
|
||||
|
||||
ctx.CommandBuffer.SetRenderTargets([ctx.GetActualTexture(data.destination)], Handle<Texture>.Invalid);
|
||||
|
||||
ctx.SetActiveMaterial(data.blitMaterial);
|
||||
ctx.SetActiveMesh(Handle<Mesh>.Invalid); // Generate a full-screen triangle dynamically in mesh shader.
|
||||
ctx.DispatchMesh(new uint3(1, 1, 1));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void Cleanup(IResourceManager resourceManager, IResourceDatabase resourceDatabase)
|
||||
{
|
||||
resourceManager.ReleaseMaterial(_blitMaterial);
|
||||
|
||||
resourceManager.ReleaseMaterial(_material);
|
||||
resourceManager.ReleaseShader(_shader);
|
||||
resourceManager.ReleaseMesh(_mesh);
|
||||
resourceDatabase.ReleaseSampler(_sampler);
|
||||
|
||||
if (_textures != null)
|
||||
{
|
||||
foreach (var texture in _textures)
|
||||
{
|
||||
resourceDatabase.ReleaseResource(texture.AsResource());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RenderGraphModule;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Graphics.RenderPipeline;
|
||||
|
||||
public partial class GhostRenderPipeline
|
||||
{
|
||||
private class MeshRenderPassData
|
||||
{
|
||||
public RenderList renderList;
|
||||
public Identifier<RGTexture> renderTarget;
|
||||
}
|
||||
|
||||
private class BlitPassData
|
||||
{
|
||||
public Identifier<RGTexture> source;
|
||||
public Identifier<RGTexture> destination;
|
||||
|
||||
public Handle<Material> blitMaterial;
|
||||
public Identifier<Sampler> sampler;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct ShaderProperties_MyShader_Standard
|
||||
{
|
||||
public float4 color;
|
||||
public uint texture1;
|
||||
public uint texture2;
|
||||
public uint texture3;
|
||||
public uint texture4;
|
||||
public uint tex_sampler;
|
||||
|
||||
private readonly uint _padding1;
|
||||
private readonly uint _padding2;
|
||||
private readonly uint _padding3;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct ShaderProperties_Hidden_Blit
|
||||
{
|
||||
public uint mainTex;
|
||||
public uint sampler_mainTex;
|
||||
private readonly uint _padding1;
|
||||
private readonly uint _padding2;
|
||||
}
|
||||
|
||||
private void RenderTest(RenderGraph graph, Identifier<RGTexture> backbuffer)
|
||||
{
|
||||
Identifier<RGTexture> renderTarget;
|
||||
using (var builder = graph.AddRasterRenderPass<MeshRenderPassData>("Mesh Render Pass", out var passData))
|
||||
{
|
||||
passData.mesh = _mesh;
|
||||
passData.material = _material;
|
||||
|
||||
passData.renderTarget = builder.CreateTexture(RGTextureDesc.Relative(1.0f, TextureFormat.R8G8B8A8_UNorm), "Render Target");
|
||||
builder.SetColorAttachment(passData.renderTarget, 0);
|
||||
|
||||
renderTarget = passData.renderTarget;
|
||||
|
||||
builder.SetRenderFunc<MeshRenderPassData>(static (data, ctx) =>
|
||||
{
|
||||
ctx.SetActiveMaterial(data.material);
|
||||
ctx.SetActiveMesh(data.mesh);
|
||||
|
||||
var threadGroupCountX = ((uint)ctx.ActiveMeshIndexCount + 2u) / 3u;
|
||||
ctx.DispatchMesh(new uint3(threadGroupCountX, 1u, 1u));
|
||||
});
|
||||
}
|
||||
|
||||
using (var builder = graph.AddUnsafeRenderPass<BlitPassData>("Blit Pass", out var passData))
|
||||
{
|
||||
passData.source = renderTarget;
|
||||
passData.destination = backbuffer;
|
||||
passData.blitMaterial = _blitMaterial;
|
||||
passData.sampler = _sampler;
|
||||
|
||||
builder.UseTexture(passData.source, AccessFlags.Read);
|
||||
builder.UseTexture(passData.destination, AccessFlags.WriteAll);
|
||||
|
||||
builder.SetRenderFunc<BlitPassData>(static (data, ctx) =>
|
||||
{
|
||||
var r = ctx.ResourceManager.GetMaterialReference(data.blitMaterial);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref var matRef = ref r.Value;
|
||||
var blitProps = new ShaderProperties_Hidden_Blit
|
||||
{
|
||||
mainTex = ctx.ResourceDatabase.GetBindlessIndex(ctx.GetActualResource(data.source.AsResource())),
|
||||
sampler_mainTex = (uint)data.sampler.Value,
|
||||
};
|
||||
|
||||
matRef.SetPropertyCache(in blitProps).ThrowIfFailed();
|
||||
matRef.UploadData(ctx.CommandBuffer, ctx.ResourceDatabase);
|
||||
|
||||
ctx.CommandBuffer.SetRenderTargets([ctx.GetActualTexture(data.destination)], Handle<Texture>.Invalid);
|
||||
|
||||
ctx.SetActiveMaterial(data.blitMaterial);
|
||||
ctx.SetActiveMesh(Handle<Mesh>.Invalid); // Generate a full-screen triangle dynamically in mesh shader.
|
||||
ctx.DispatchMesh(new uint3(1, 1, 1));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ namespace Ghost.Graphics.RenderPipeline;
|
||||
|
||||
public sealed class GhostRenderPipelineSettings : IRenderPipelineSettings
|
||||
{
|
||||
public static IRenderPipeline CreatePipeline(IRenderSystem renderSystem)
|
||||
public static IRenderPipeline CreatePipeline(RenderSystem renderSystem)
|
||||
{
|
||||
return new GhostRenderPipeline(renderSystem);
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public unsafe partial class GhostRenderPipeline : IRenderPipeline
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
}
|
||||
|
||||
internal GhostRenderPipeline(IRenderSystem renderSystem)
|
||||
internal GhostRenderPipeline(RenderSystem renderSystem)
|
||||
{
|
||||
_renderGraph = new RenderGraph(renderSystem.ResourceManager,
|
||||
renderSystem.GraphicsEngine.ResourceAllocator,
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Ghost.Graphics.RenderPipeline;
|
||||
|
||||
public interface IRenderPipelineSettings
|
||||
{
|
||||
static abstract IRenderPipeline CreatePipeline(IRenderSystem renderSystem);
|
||||
static abstract IRenderPipeline CreatePipeline(RenderSystem renderSystem);
|
||||
}
|
||||
|
||||
public interface IRenderPipeline : IDisposable
|
||||
|
||||
@@ -21,7 +21,7 @@ struct Meshlet
|
||||
};
|
||||
|
||||
[numthreads(64, 1, 1)] // 64 threads for max 64 vertices and up to 124 triangles
|
||||
[OUTPUT_TRIANGLE_TOPOLOGY]
|
||||
[outputtopology("triangle")]
|
||||
void MSMain(
|
||||
uint3 groupThreadID : SV_GroupThreadID,
|
||||
uint groupID : SV_GroupID,
|
||||
@@ -6,34 +6,12 @@ using System.Collections.Concurrent;
|
||||
|
||||
namespace Ghost.Graphics;
|
||||
|
||||
public interface IRenderSystem : IFenceSynchronizer, IDisposable
|
||||
{
|
||||
IGraphicsEngine GraphicsEngine
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
IResourceManager ResourceManager
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
bool IsRunning
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
void RequestSwapChainResize(ISwapChain swapChain, uint2 newSize);
|
||||
}
|
||||
|
||||
public enum GraphicsAPI
|
||||
internal enum GraphicsAPI
|
||||
{
|
||||
Direct3D12
|
||||
}
|
||||
|
||||
public struct RenderSystemDesc
|
||||
internal struct RenderSystemDesc
|
||||
{
|
||||
public GraphicsAPI GraphicsAPI
|
||||
{
|
||||
@@ -50,9 +28,8 @@ public struct RenderSystemDesc
|
||||
/// Application-level render system that orchestrates multiple renderers
|
||||
/// and handles frame synchronization
|
||||
/// </summary>
|
||||
internal class RenderSystem : IRenderSystem
|
||||
public class RenderSystem : IDisposable
|
||||
{
|
||||
// TODO: Thread local command buffers.
|
||||
private struct FrameResource : IDisposable
|
||||
{
|
||||
public required AutoResetEvent CpuReadyEvent
|
||||
@@ -85,7 +62,7 @@ internal class RenderSystem : IRenderSystem
|
||||
|
||||
private readonly RenderSystemDesc _config;
|
||||
private readonly IGraphicsEngine _graphicsEngine;
|
||||
private readonly IResourceManager _resourceManager;
|
||||
private readonly ResourceManager _resourceManager;
|
||||
|
||||
private readonly FrameResource[] _frameResources;
|
||||
private readonly Thread _renderThread;
|
||||
@@ -100,7 +77,7 @@ internal class RenderSystem : IRenderSystem
|
||||
private bool _disposed;
|
||||
|
||||
public IGraphicsEngine GraphicsEngine => _graphicsEngine;
|
||||
public IResourceManager ResourceManager => _resourceManager;
|
||||
public ResourceManager ResourceManager => _resourceManager;
|
||||
public bool IsRunning => _isRunning;
|
||||
|
||||
public uint CPUFenceValue => _cpuFenceValue;
|
||||
@@ -108,7 +85,7 @@ internal class RenderSystem : IRenderSystem
|
||||
public uint FrameIndex => _frameIndex;
|
||||
public uint MaxFrameLatency => _config.FrameBufferCount;
|
||||
|
||||
public RenderSystem(RenderSystemDesc desc)
|
||||
internal RenderSystem(RenderSystemDesc desc)
|
||||
{
|
||||
_config = desc;
|
||||
|
||||
@@ -169,67 +146,6 @@ internal class RenderSystem : IRenderSystem
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (_isRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isRunning = true;
|
||||
_renderThread.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (!_isRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isRunning = false;
|
||||
_shutdownEvent.Set();
|
||||
_renderThread.Join();
|
||||
}
|
||||
|
||||
public void RequestSwapChainResize(ISwapChain swapChain, uint2 newSize)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
_resizeRequest.AddOrUpdate(swapChain, newSize, (_, _) => newSize);
|
||||
}
|
||||
|
||||
public bool WaitForGPUReady(int timeOut = -1)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var eventIndex = (int)(_cpuFenceValue % _config.FrameBufferCount);
|
||||
return _frameResources[eventIndex].GpuReadyEvent.WaitOne(timeOut);
|
||||
}
|
||||
|
||||
public void SignalCPUReady()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var eventIndex = (int)(_cpuFenceValue % _config.FrameBufferCount);
|
||||
_frameResources[eventIndex].CpuReadyEvent.Set();
|
||||
_cpuFenceValue++;
|
||||
}
|
||||
|
||||
public void WaitIdle()
|
||||
{
|
||||
foreach (var frameResource in _frameResources)
|
||||
{
|
||||
if (frameResource.FenceValue > 0)
|
||||
{
|
||||
_graphicsEngine.Device.GraphicsQueue.WaitForValue(frameResource.FenceValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderLoop()
|
||||
{
|
||||
var waitHandles = new WaitHandle[] { null!, _shutdownEvent };
|
||||
@@ -272,14 +188,16 @@ internal class RenderSystem : IRenderSystem
|
||||
resource.CommandAllocator.Reset();
|
||||
}
|
||||
|
||||
foreach (var kvp in _resizeRequest)
|
||||
var keys = _resizeRequest.Keys.ToArray();
|
||||
foreach (var swapChain in keys)
|
||||
{
|
||||
var swapChain = kvp.Key;
|
||||
var newSize = kvp.Value;
|
||||
swapChain.Resize(newSize.x, newSize.y);
|
||||
if (_resizeRequest.TryRemove(swapChain, out var newSize))
|
||||
{
|
||||
swapChain.Resize(newSize.x, newSize.y);
|
||||
}
|
||||
}
|
||||
|
||||
_resizeRequest.Clear();
|
||||
frameResource.GpuReadyEvent.Set();
|
||||
|
||||
continue; // Skip rendering this frame since we just resized and may have invalid render targets
|
||||
}
|
||||
@@ -304,6 +222,67 @@ internal class RenderSystem : IRenderSystem
|
||||
}
|
||||
}
|
||||
|
||||
internal void Start()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (_isRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isRunning = true;
|
||||
_renderThread.Start();
|
||||
}
|
||||
|
||||
internal void Stop()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (!_isRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isRunning = false;
|
||||
_shutdownEvent.Set();
|
||||
_renderThread.Join();
|
||||
}
|
||||
|
||||
internal void SignalCPUReady()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var eventIndex = (int)(_cpuFenceValue % _config.FrameBufferCount);
|
||||
_frameResources[eventIndex].CpuReadyEvent.Set();
|
||||
_cpuFenceValue++;
|
||||
}
|
||||
|
||||
internal void RequestSwapChainResize(ISwapChain swapChain, uint2 newSize)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
_resizeRequest.AddOrUpdate(swapChain, newSize, (_, _) => newSize);
|
||||
}
|
||||
|
||||
public bool WaitForGPUReady(int timeOut = -1)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var eventIndex = (int)(_cpuFenceValue % _config.FrameBufferCount);
|
||||
return _frameResources[eventIndex].GpuReadyEvent.WaitOne(timeOut);
|
||||
}
|
||||
|
||||
public void WaitIdle()
|
||||
{
|
||||
foreach (var frameResource in _frameResources)
|
||||
{
|
||||
if (frameResource.FenceValue > 0)
|
||||
{
|
||||
_graphicsEngine.Device.GraphicsQueue.WaitForValue(frameResource.FenceValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
|
||||
@@ -7,124 +7,7 @@ using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
namespace Ghost.Graphics;
|
||||
|
||||
public interface IResourceManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new mesh from the specified vertex and index data.
|
||||
/// </summary>
|
||||
/// <param name="vertices">A UnsafeList containing the vertices that define the geometry of the mesh. Must contain at least one vertex.</param>
|
||||
/// <param name="indices">A UnsafeList containing the indices that specify how vertices are connected to form primitives. Must contain at least one index.</param>
|
||||
/// <returns>An <see cref="Identifier{Mesh}"/> representing the newly created mesh.</returns>
|
||||
Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new material instance using the specified shader.
|
||||
/// </summary>
|
||||
/// <param name="shader">The identifier of the shader to associate with the new material.</param>
|
||||
/// <returns>An <see cref="Identifier{Material}"/> representing the newly created material.</returns>
|
||||
Handle<Material> CreateMaterial(Identifier<Shader> shader);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new shader and returns its unique identifier.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="Identifier{Shader}"/> representing the newly created shader.</returns>
|
||||
/// <param name="descriptor">The viewGroup containing the shader's properties and passes.</param>
|
||||
Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a mesh with the specified Handle exists.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the mesh to check for existence. Cannot be null.</param>
|
||||
/// <returns>true if a mesh with the specified Handle exists; otherwise, false.</returns>
|
||||
bool HasMesh(Handle<Mesh> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a reference to the mesh associated with the specified handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the mesh to retrieve. Must refer to a valid mesh; otherwise, the behavior is undefined.</param>
|
||||
/// <returns>A result containing a reference to the mesh corresponding to the specified handle, or an error status if the handle is invalid.</returns>
|
||||
RefResult<Mesh, Error> GetMeshReference(Handle<Mesh> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Releases the mesh resource associated with the specified handle, freeing any resources held by it. Includes both CPU and GPU resources.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the mesh to release. Must refer to a mesh that was previously created and not already released.</param>
|
||||
void ReleaseMesh(Handle<Mesh> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a material with the specified handle exists in the collection.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the material to check for existence.</param>
|
||||
/// <returns>true if a material with the specified handle exists; otherwise, false.</returns>
|
||||
bool HasMaterial(Handle<Material> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the material associated with the specified handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the material to retrieve. Must refer to a valid material.</param>
|
||||
/// <returns>A result containing a reference to the material corresponding to the specified handle, or an error status if the handle is invalid.</returns>
|
||||
RefResult<Material, Error> GetMaterialReference(Handle<Material> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Releases the material associated with the specified handle, making it available for reuse or disposal.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the material to release. Must refer to a material that has been previously acquired.</param>
|
||||
void ReleaseMaterial(Handle<Material> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Returns an existing material palette index for the specified material sequence or creates a new one.
|
||||
/// </summary>
|
||||
/// <param name="materials">The ordered material list for the palette.</param>
|
||||
/// <returns>The palette index. Index 0 represents an empty palette.</returns>
|
||||
int GetOrCreateMaterialPalette(ReadOnlySpan<Handle<Material>> materials);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified material palette index is valid.
|
||||
/// </summary>
|
||||
/// <param name="paletteID">The palette index to validate.</param>
|
||||
bool HasMaterialPalette(Identifier<MaterialPalette> paletteID);
|
||||
|
||||
/// <summary>
|
||||
/// Gets metadata for a material palette entry.
|
||||
/// </summary>
|
||||
/// <param name="paletteID">The palette index to query.</param>
|
||||
MaterialPalette GetMaterialPaletteInfo(Identifier<MaterialPalette> paletteID);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a material handle from a palette entry by local material index.
|
||||
/// </summary>
|
||||
/// <param name="paletteID">The palette index to query.</param>
|
||||
/// <param name="localMaterialIndex">The material slot inside the palette.</param>
|
||||
Handle<Material> GetMaterialPaletteMaterial(Identifier<MaterialPalette> paletteID, int localMaterialIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Releases a material palette reference previously returned by <see cref="GetOrCreateMaterialPalette(ReadOnlySpan{Handle{Material}})"/>.
|
||||
/// </summary>
|
||||
/// <param name="paletteID">The palette index to release.</param>
|
||||
void ReleaseMaterialPalette(Identifier<MaterialPalette> paletteID);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a shader with the specified identifier exists in the collection.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the shader to check for existence.</param>
|
||||
/// <returns>true if a shader with the specified identifier exists; otherwise, false.</returns>
|
||||
bool HasShader(Identifier<Shader> id);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a reference to the shader associated with the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the shader to retrieve. Must refer to a valid shader.</param>
|
||||
/// <returns>A result containing a reference to the shader corresponding to the specified identifier, or an error status if the identifier is invalid.</returns>
|
||||
RefResult<Shader, Error> GetShaderReference(Identifier<Shader> id);
|
||||
|
||||
/// <summary>
|
||||
/// Releases the shader associated with the specified identifier, freeing any resources allocated to it.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the shader to release. Must refer to a valid, previously created shader.</param>
|
||||
void ReleaseShader(Identifier<Shader> id);
|
||||
}
|
||||
|
||||
internal sealed class ResourceManager : IResourceManager, IDisposable
|
||||
public sealed class ResourceManager : IDisposable
|
||||
{
|
||||
private readonly IResourceAllocator _resourceAllocator;
|
||||
private readonly IResourceDatabase _resourceDatabase;
|
||||
@@ -153,6 +36,12 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new mesh from the specified vertex and index data.
|
||||
/// </summary>
|
||||
/// <param name="vertices">A UnsafeList containing the vertices that define the geometry of the mesh. Must contain at least one vertex.</param>
|
||||
/// <param name="indices">A UnsafeList containing the indices that specify how vertices are connected to form primitives. Must contain at least one index.</param>
|
||||
/// <returns>An <see cref="Identifier{Mesh}"/> representing the newly created mesh.</returns>
|
||||
public unsafe Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
@@ -198,6 +87,11 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
||||
return new Handle<Mesh>(id, generation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new material instance using the specified shader.
|
||||
/// </summary>
|
||||
/// <param name="shader">The identifier of the shader to associate with the new material.</param>
|
||||
/// <returns>An <see cref="Identifier{Material}"/> representing the newly created material.</returns>
|
||||
public Handle<Material> CreateMaterial(Identifier<Shader> shader)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
@@ -212,6 +106,11 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
||||
return new Handle<Material>(id, generation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new shader and returns its unique identifier.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="Identifier{Shader}"/> representing the newly created shader.</returns>
|
||||
/// <param name="descriptor">The viewGroup containing the shader's properties and passes.</param>
|
||||
public Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
@@ -223,12 +122,22 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
||||
return new Identifier<Shader>(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a mesh with the specified Handle exists.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the mesh to check for existence. Cannot be null.</param>
|
||||
/// <returns>true if a mesh with the specified Handle exists; otherwise, false.</returns>
|
||||
public bool HasMesh(Handle<Mesh> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _meshes.Contains(handle.ID, handle.Generation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a reference to the mesh associated with the specified handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the mesh to retrieve. Must refer to a valid mesh; otherwise, the behavior is undefined.</param>
|
||||
/// <returns>A result containing a reference to the mesh corresponding to the specified handle, or an error status if the handle is invalid.</returns>
|
||||
public RefResult<Mesh, Error> GetMeshReference(Handle<Mesh> handle)
|
||||
{
|
||||
ref var mesh = ref _meshes.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
@@ -240,6 +149,10 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
||||
return RefResult<Mesh, Error>.Success(ref mesh);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the mesh resource associated with the specified handle, freeing any resources held by it. Includes both CPU and GPU resources.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the mesh to release. Must refer to a mesh that was previously created and not already released.</param>
|
||||
public void ReleaseMesh(Handle<Mesh> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
@@ -253,12 +166,22 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
||||
mesh.ReleaseResource(_resourceDatabase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a material with the specified handle exists in the collection.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the material to check for existence.</param>
|
||||
/// <returns>true if a material with the specified handle exists; otherwise, false.</returns>
|
||||
public bool HasMaterial(Handle<Material> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _materials.Contains(handle.ID, handle.Generation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the material associated with the specified handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the material to retrieve. Must refer to a valid material.</param>
|
||||
/// <returns>A result containing a reference to the material corresponding to the specified handle, or an error status if the handle is invalid.</returns>
|
||||
public RefResult<Material, Error> GetMaterialReference(Handle<Material> handle)
|
||||
{
|
||||
ref var material = ref _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
@@ -270,6 +193,10 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
||||
return RefResult<Material, Error>.Success(ref material);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the material associated with the specified handle, making it available for reuse or disposal.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the material to release. Must refer to a material that has been previously acquired.</param>
|
||||
public void ReleaseMaterial(Handle<Material> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
@@ -284,6 +211,11 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
||||
material.ReleaseResource(_resourceDatabase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an existing material palette index for the specified material sequence or creates a new one.
|
||||
/// </summary>
|
||||
/// <param name="materials">The ordered material list for the palette.</param>
|
||||
/// <returns>The palette index. Index 0 represents an empty palette.</returns>
|
||||
public int GetOrCreateMaterialPalette(ReadOnlySpan<Handle<Material>> materials)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
@@ -299,36 +231,63 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
||||
return _materialPalettes.InsertOrGet(materials);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified material palette index is valid.
|
||||
/// </summary>
|
||||
/// <param name="paletteID">The palette index to validate.</param>
|
||||
public bool HasMaterialPalette(Identifier<MaterialPalette> paletteID)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _materialPalettes.IsValid(paletteID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets metadata for a material palette entry.
|
||||
/// </summary>
|
||||
/// <param name="paletteID">The palette index to query.</param>
|
||||
public MaterialPalette GetMaterialPaletteInfo(Identifier<MaterialPalette> paletteID)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _materialPalettes.GetInfo(paletteID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a material handle from a palette entry by local material index.
|
||||
/// </summary>
|
||||
/// <param name="paletteID">The palette index to query.</param>
|
||||
/// <param name="localMaterialIndex">The material slot inside the palette.</param>
|
||||
public Handle<Material> GetMaterialPaletteMaterial(Identifier<MaterialPalette> paletteID, int localMaterialIndex)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _materialPalettes.GetMaterial(paletteID, localMaterialIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases a material palette reference previously returned by <see cref="GetOrCreateMaterialPalette(ReadOnlySpan{Handle{Material}})"/>.
|
||||
/// </summary>
|
||||
/// <param name="paletteID">The palette index to release.</param>
|
||||
public void ReleaseMaterialPalette(Identifier<MaterialPalette> paletteID)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
_materialPalettes.Release(paletteID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a shader with the specified identifier exists in the collection.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the shader to check for existence.</param>
|
||||
/// <returns>true if a shader with the specified identifier exists; otherwise, false.</returns>
|
||||
public bool HasShader(Identifier<Shader> id)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return id.Value >= 0 && id.Value < _shaders.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a reference to the shader associated with the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the shader to retrieve. Must refer to a valid shader.</param>
|
||||
/// <returns>A result containing a reference to the shader corresponding to the specified identifier, or an error status if the identifier is invalid.</returns>
|
||||
public RefResult<Shader, Error> GetShaderReference(Identifier<Shader> id)
|
||||
{
|
||||
if (!HasShader(id))
|
||||
@@ -339,6 +298,10 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
||||
return RefResult<Shader, Error>.Success(ref _shaders[id.Value]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the shader associated with the specified identifier, freeing any resources allocated to it.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the shader to release. Must refer to a valid, previously created shader.</param>
|
||||
public void ReleaseShader(Identifier<Shader> id)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
@@ -49,10 +49,6 @@ struct Vertex
|
||||
#define SAMPLE_TEXTURE2D_ARRAY(texId, sampId, uvw) SampleTextureArray(texId, sampId, uvw)
|
||||
|
||||
|
||||
#define OUTPUT_TRIANGLE_TOPOLOGY outputtopology("triangle")
|
||||
#define OUTPUT_LINE_TOPOLOGY outputtopology("line")
|
||||
|
||||
|
||||
#define ZERO_INIT(T) (T)0
|
||||
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ public unsafe struct ClodCluster
|
||||
/// <summary>
|
||||
/// Delegate type for processing generated LOD groups.
|
||||
/// </summary>
|
||||
public unsafe delegate int ClodOutputDelegate(void* context, ClodGroup group, ClodCluster* clusters, nuint clusterCount);
|
||||
public unsafe delegate int ClodOutputDelegate(void* context, ClodGroup group, ReadOnlyUnsafeCollection<ClodCluster> clusters);
|
||||
|
||||
// FIX: UnsafeList and UnsafeArray are not same as std::vector.
|
||||
|
||||
@@ -383,7 +383,7 @@ public static unsafe class MeshletUtility
|
||||
|
||||
var clodGroup = new ClodGroup { depth = depth, simplified = simplified };
|
||||
var result = outputCallback != null
|
||||
? outputCallback(outputContext, clodGroup, (ClodCluster*)groupClusters.GetUnsafePtr(), (nuint)groupClusters.Count)
|
||||
? outputCallback(outputContext, clodGroup, groupClusters.AsReadOnly())
|
||||
: -1;
|
||||
|
||||
return result;
|
||||
|
||||
@@ -21,7 +21,7 @@ shader "MyShader/Standard"
|
||||
color_mask = all;
|
||||
}
|
||||
|
||||
mesh "F:/csharp/GhostEngine/src/Runtime//Ghost.Graphics/RenderPasses/ShaderCode.hlsl" : "MSMain";
|
||||
pixel "F:/csharp/GhostEngine/src/Runtime//Ghost.Graphics/RenderPasses/ShaderCode.hlsl" : "PSMain";
|
||||
mesh "F:/csharp/GhostEngine/src/Runtime//Ghost.Graphics/RenderPipeline/ShaderCode.hlsl" : "MSMain";
|
||||
pixel "F:/csharp/GhostEngine/src/Runtime//Ghost.Graphics/RenderPipeline/ShaderCode.hlsl" : "PSMain";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user