Fixed compilation errors;
Added MaterialPalette
This commit is contained in:
1125
meshlet-architecture.md
Normal file
1125
meshlet-architecture.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -59,7 +59,7 @@ internal static class ActivationHandler
|
|||||||
((EngineCore)App.GetService<IEngineContext>()).Init();
|
((EngineCore)App.GetService<IEngineContext>()).Init();
|
||||||
});
|
});
|
||||||
|
|
||||||
await ((Core.AssetHandle.AssetService)App.GetService<IAssetService>()).Init();
|
// await ((Core.AssetHandle.AssetService)App.GetService<IAssetService>()).Init();
|
||||||
|
|
||||||
// TODO: Init other subsystems here.
|
// TODO: Init other subsystems here.
|
||||||
// await Task.Delay(10000); // Wait 10 seconds to simulate work.
|
// await Task.Delay(10000); // Wait 10 seconds to simulate work.
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public partial class App : Application
|
|||||||
services.AddSingleton<IProgressService, ProgressService>();
|
services.AddSingleton<IProgressService, ProgressService>();
|
||||||
services.AddSingleton<IInspectorService, InspectorService>();
|
services.AddSingleton<IInspectorService, InspectorService>();
|
||||||
services.AddSingleton<IPreviewService, PreviewService>();
|
services.AddSingleton<IPreviewService, PreviewService>();
|
||||||
services.AddSingleton<IAssetService, AssetService>();
|
// services.AddSingleton<IAssetService, AssetService>();
|
||||||
|
|
||||||
services.AddSingleton<EngineEditorViewModel>();
|
services.AddSingleton<EngineEditorViewModel>();
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
<!-- Named Style -->
|
<!-- Named Style -->
|
||||||
<Style
|
<Style
|
||||||
x:Key="ToolbarButton"
|
x:Key="ToolbarButton"
|
||||||
BasedOn="{ThemeResource SubtleButtonStyle}"
|
BasedOn="{StaticResource SubtleButtonStyle}"
|
||||||
TargetType="Button" />
|
TargetType="Button" />
|
||||||
|
|
||||||
<!-- Named Resource -->
|
<!-- Named Resource -->
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Ghost.Editor.ViewModels.Controls;
|
|||||||
internal partial class ProjectBrowserViewModel : ObservableObject
|
internal partial class ProjectBrowserViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
private readonly IInspectorService _inspectorService;
|
private readonly IInspectorService _inspectorService;
|
||||||
private readonly IAssetService _assetService;
|
// private readonly IAssetService _assetService;
|
||||||
|
|
||||||
private readonly Dictionary<string, ExplorerItem> _pathToDirectoryItemMap = new();
|
private readonly Dictionary<string, ExplorerItem> _pathToDirectoryItemMap = new();
|
||||||
private ExplorerItem? _selectedItem;
|
private ExplorerItem? _selectedItem;
|
||||||
@@ -40,10 +40,10 @@ internal partial class ProjectBrowserViewModel : ObservableObject
|
|||||||
get; set;
|
get; set;
|
||||||
} = string.Empty;
|
} = string.Empty;
|
||||||
|
|
||||||
public ProjectBrowserViewModel(IInspectorService inspectorService, IAssetService assetService)
|
public ProjectBrowserViewModel(IInspectorService inspectorService) // , IAssetService assetService)
|
||||||
{
|
{
|
||||||
_inspectorService = inspectorService;
|
_inspectorService = inspectorService;
|
||||||
_assetService = assetService;
|
// _assetService = assetService;
|
||||||
|
|
||||||
var assetsRootItem = new ExplorerItem(EditorApplication.ASSETS_FOLDER_NAME, Path.Combine(EditorApplication.ProjectPath, EditorApplication.ASSETS_FOLDER_NAME), true);
|
var assetsRootItem = new ExplorerItem(EditorApplication.ASSETS_FOLDER_NAME, Path.Combine(EditorApplication.ProjectPath, EditorApplication.ASSETS_FOLDER_NAME), true);
|
||||||
LoadSubFolderRecursive(assetsRootItem);
|
LoadSubFolderRecursive(assetsRootItem);
|
||||||
@@ -109,7 +109,7 @@ internal partial class ProjectBrowserViewModel : ObservableObject
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_assetService.OpenAsset(SelectedItem.FullName);
|
// _assetService.OpenAsset(SelectedItem.FullName);
|
||||||
return (null, 1);
|
return (null, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
|
|||||||
|
|
||||||
internal partial class ProjectViewModel : ObservableObject
|
internal partial class ProjectViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
private readonly IAssetService _assetService;
|
// private readonly IAssetService _assetService;
|
||||||
|
|
||||||
public ObservableCollection<ExplorerItem> SubDirectories
|
public ObservableCollection<ExplorerItem> SubDirectories
|
||||||
{
|
{
|
||||||
@@ -35,15 +35,15 @@ internal partial class ProjectViewModel : ObservableObject
|
|||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProjectViewModel(IAssetService assetService)
|
// public ProjectViewModel(IAssetService assetService)
|
||||||
{
|
// {
|
||||||
_assetService = assetService;
|
// _assetService = assetService;
|
||||||
|
|
||||||
var assetsRootItem = new ExplorerItem("Assets", Path.Combine(EditorApplication.ProjectPath, EditorApplication.ASSETS_FOLDER_NAME), true);
|
// var assetsRootItem = new ExplorerItem("Assets", Path.Combine(EditorApplication.ProjectPath, EditorApplication.ASSETS_FOLDER_NAME), true);
|
||||||
LoadSubFolderRecursive(ref assetsRootItem);
|
// LoadSubFolderRecursive(ref assetsRootItem);
|
||||||
|
|
||||||
SubDirectories.Add(assetsRootItem);
|
// SubDirectories.Add(assetsRootItem);
|
||||||
}
|
// }
|
||||||
|
|
||||||
private static void LoadSubFolderRecursive(ref ExplorerItem parentItem)
|
private static void LoadSubFolderRecursive(ref ExplorerItem parentItem)
|
||||||
{
|
{
|
||||||
@@ -127,7 +127,7 @@ internal partial class ProjectViewModel : ObservableObject
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_assetService.OpenAsset(SelectedAsset.FullName);
|
// _assetService.OpenAsset(SelectedAsset.FullName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ namespace Ghost.Engine.Components;
|
|||||||
public struct MeshInstance : IComponent
|
public struct MeshInstance : IComponent
|
||||||
{
|
{
|
||||||
public Handle<Mesh> mesh;
|
public Handle<Mesh> mesh;
|
||||||
// NOTE: This will be the first material, we can access other materials by the bindless index of the first material + the local index stored in the meshlet.
|
public int materialPaletteIndex;
|
||||||
public Handle<Material> materialStart;
|
|
||||||
public ShadowCastingMode shadowCastingMode;
|
public ShadowCastingMode shadowCastingMode;
|
||||||
public RenderingLayerMask renderingLayerMask;
|
public RenderingLayerMask renderingLayerMask;
|
||||||
public bool staticShadowCaster;
|
public bool staticShadowCaster;
|
||||||
|
|||||||
@@ -42,9 +42,8 @@ public class RenderExtractionSystem : ISystem
|
|||||||
renderList.Add(new RenderRecord
|
renderList.Add(new RenderRecord
|
||||||
{
|
{
|
||||||
localToWorld = localToWorld.matrix,
|
localToWorld = localToWorld.matrix,
|
||||||
// TODO: Get mesh and material from palette. This requires some changes to ISharedComponent since it's now fully functional right now.
|
mesh = meshInstance.mesh,
|
||||||
// mesh = meshInstance.meshIndex,
|
materialPaletteIndex = meshInstance.materialPaletteIndex,
|
||||||
// material = meshInstance.materialIndex,
|
|
||||||
renderingLayerMask = meshInstance.renderingLayerMask,
|
renderingLayerMask = meshInstance.renderingLayerMask,
|
||||||
|
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Ghost.Entities;
|
|||||||
|
|
||||||
public interface IJobChunk
|
public interface IJobChunk
|
||||||
{
|
{
|
||||||
void Execute(ChunkView view, int threadIndex);
|
void Execute(ChunkView view, ref readonly JobExecutionContext ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal unsafe struct ChunkInfo
|
internal unsafe struct ChunkInfo
|
||||||
@@ -22,12 +22,12 @@ internal unsafe struct JobChunkBatch<TJob> : IJobParallelFor
|
|||||||
public TJob userJob;
|
public TJob userJob;
|
||||||
public ReadOnlyUnsafeCollection<ChunkInfo> chunkInfos;
|
public ReadOnlyUnsafeCollection<ChunkInfo> chunkInfos;
|
||||||
|
|
||||||
public void Execute(int loopIndex, int threadIndex)
|
public void Execute(int loopIndex, ref readonly JobExecutionContext ctx)
|
||||||
{
|
{
|
||||||
var info = chunkInfos[loopIndex];
|
var info = chunkInfos[loopIndex];
|
||||||
var view = new ChunkView(in *info.pArchetype, in *info.pChunk);
|
var view = new ChunkView(in *info.pArchetype, in *info.pChunk);
|
||||||
|
|
||||||
userJob.Execute(view, threadIndex);
|
userJob.Execute(view, in ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ internal struct DisposeJobChunk : IJob
|
|||||||
{
|
{
|
||||||
public UnsafeList<ChunkInfo> list;
|
public UnsafeList<ChunkInfo> list;
|
||||||
|
|
||||||
public void Execute(int threadIndex)
|
public void Execute(ref readonly JobExecutionContext ctx)
|
||||||
{
|
{
|
||||||
list.Dispose();
|
list.Dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@ namespace Ghost.Entities;
|
|||||||
public interface IJobEntity<<#= generics #>>
|
public interface IJobEntity<<#= generics #>>
|
||||||
<#= restrictions #>
|
<#= restrictions #>
|
||||||
{
|
{
|
||||||
void Execute(Entity entity, <#= AppendParameters(i, "ref T{0} component{0}") #>, int threadIndex);
|
void Execute(Entity entity, <#= AppendParameters(i, "ref T{0} component{0}") #>, ref readonly JobExecutionContext ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal unsafe struct JobEntityBatch<TJob, <#= generics #>> : IJobParallelFor
|
internal unsafe struct JobEntityBatch<TJob, <#= generics #>> : IJobParallelFor
|
||||||
@@ -38,12 +38,12 @@ internal unsafe struct JobEntityBatch<TJob, <#= generics #>> : IJobParallelFor
|
|||||||
<# for (var j = 0; j < i; j++){ #>
|
<# for (var j = 0; j < i; j++){ #>
|
||||||
public UnsafeList<int> offsets<#= j #>;
|
public UnsafeList<int> offsets<#= j #>;
|
||||||
public UnsafeList<int> bitsOffsets<#= j #>;
|
public UnsafeList<int> bitsOffsets<#= j #>;
|
||||||
public UnsafeList<int> versionindices<#= j #>;
|
public UnsafeList<int> versionIndices<#= j #>;
|
||||||
|
|
||||||
<# } #>
|
<# } #>
|
||||||
public int version;
|
public int version;
|
||||||
|
|
||||||
public void Execute(int loopIndex, int threadIndex)
|
public void Execute(int loopIndex, ref readonly JobExecutionContext ctx)
|
||||||
{
|
{
|
||||||
// 1. Get the specific pChunk for this thread
|
// 1. Get the specific pChunk for this thread
|
||||||
var pChunk = (byte*)chunks[loopIndex];
|
var pChunk = (byte*)chunks[loopIndex];
|
||||||
@@ -53,7 +53,7 @@ internal unsafe struct JobEntityBatch<TJob, <#= generics #>> : IJobParallelFor
|
|||||||
<# for (var j = 0; j < i; j++){ #>
|
<# for (var j = 0; j < i; j++){ #>
|
||||||
var off<#= j #> = offsets<#= j #>[loopIndex];
|
var off<#= j #> = offsets<#= j #>[loopIndex];
|
||||||
var enableOff<#= j #> = bitsOffsets<#= j #>[loopIndex];
|
var enableOff<#= j #> = bitsOffsets<#= j #>[loopIndex];
|
||||||
var versionIndex<#= j #> = versionindices<#= j #>[loopIndex];
|
var versionIndex<#= j #> = versionIndices<#= j #>[loopIndex];
|
||||||
|
|
||||||
<# } #>
|
<# } #>
|
||||||
var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]);
|
var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]);
|
||||||
@@ -79,7 +79,7 @@ internal unsafe struct JobEntityBatch<TJob, <#= generics #>> : IJobParallelFor
|
|||||||
}
|
}
|
||||||
|
|
||||||
<# } #>
|
<# } #>
|
||||||
userJob.Execute(pEntity[i], <#= AppendParameters(i, "ref ptr{0}[i]") #>, threadIndex);
|
userJob.Execute(pEntity[i], <#= AppendParameters(i, "ref ptr{0}[i]") #>, in ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,10 +102,10 @@ public unsafe partial struct EntityQuery
|
|||||||
<# for (var j = 0; j < i; j++){ #>
|
<# for (var j = 0; j < i; j++){ #>
|
||||||
public UnsafeList<int> offsets<#= j #>;
|
public UnsafeList<int> offsets<#= j #>;
|
||||||
public UnsafeList<int> bitsOffsets<#= j #>;
|
public UnsafeList<int> bitsOffsets<#= j #>;
|
||||||
public UnsafeList<int> versionindices<#= j #>;
|
public UnsafeList<int> versionIndices<#= j #>;
|
||||||
|
|
||||||
<# } #>
|
<# } #>
|
||||||
public void Execute(int threadIndex)
|
public void Execute(ref readonly JobExecutionContext ctx)
|
||||||
{
|
{
|
||||||
chunks.Dispose();
|
chunks.Dispose();
|
||||||
chunkVersions.Dispose();
|
chunkVersions.Dispose();
|
||||||
@@ -115,7 +115,7 @@ public unsafe partial struct EntityQuery
|
|||||||
<# for (var j = 0; j < i; j++){ #>
|
<# for (var j = 0; j < i; j++){ #>
|
||||||
offsets<#= j #>.Dispose();
|
offsets<#= j #>.Dispose();
|
||||||
bitsOffsets<#= j #>.Dispose();
|
bitsOffsets<#= j #>.Dispose();
|
||||||
versionindices<#= j #>.Dispose();
|
versionIndices<#= j #>.Dispose();
|
||||||
|
|
||||||
<# } #>
|
<# } #>
|
||||||
}
|
}
|
||||||
@@ -160,8 +160,7 @@ public unsafe partial struct EntityQuery
|
|||||||
|
|
||||||
// Get offsets ONCE per archetype
|
// Get offsets ONCE per archetype
|
||||||
<# for (var j = 0; j < i; j++){ #>
|
<# for (var j = 0; j < i; j++){ #>
|
||||||
var layout<#= j #> = arch.GetLayout(ComponentTypeID<T<#= j #>>.Value)
|
var layout<#= j #> = arch.GetLayout(ComponentTypeID<T<#= j #>>.Value).GetValueOrThrow();
|
||||||
.GetValueOrThrow();
|
|
||||||
<# } #>
|
<# } #>
|
||||||
|
|
||||||
// Add all chunks from this archetype
|
// Add all chunks from this archetype
|
||||||
@@ -195,7 +194,7 @@ public unsafe partial struct EntityQuery
|
|||||||
<# for (var j = 0; j < i; j++){ #>
|
<# for (var j = 0; j < i; j++){ #>
|
||||||
offsets<#= j #> = offsets<#= j #>,
|
offsets<#= j #> = offsets<#= j #>,
|
||||||
bitsOffsets<#= j #> = bitsOffsets<#= j #>,
|
bitsOffsets<#= j #> = bitsOffsets<#= j #>,
|
||||||
versionindices<#= j #> = versionIndices<#= j #>,
|
versionIndices<#= j #> = versionIndices<#= j #>,
|
||||||
|
|
||||||
<# } #>
|
<# } #>
|
||||||
version = world.Version,
|
version = world.Version,
|
||||||
@@ -229,7 +228,7 @@ public unsafe partial struct EntityQuery
|
|||||||
<# for (var j = 0; j < i; j++){ #>
|
<# for (var j = 0; j < i; j++){ #>
|
||||||
offsets<#= j #> = offsets<#= j #>,
|
offsets<#= j #> = offsets<#= j #>,
|
||||||
bitsOffsets<#= j #> = bitsOffsets<#= j #>,
|
bitsOffsets<#= j #> = bitsOffsets<#= j #>,
|
||||||
versionindices<#= j #> = versionIndices<#= j #>,
|
versionIndices<#= j #> = versionIndices<#= j #>,
|
||||||
|
|
||||||
<# } #>
|
<# } #>
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Graphics.RenderGraphModule;
|
using Ghost.Graphics.RenderGraphModule;
|
||||||
|
using Ghost.Graphics.RHI;
|
||||||
|
|
||||||
namespace Ghost.Graphics.Core.Contracts;
|
namespace Ghost.Graphics.Core.Contracts;
|
||||||
|
|
||||||
@@ -7,5 +8,5 @@ public interface IRenderPass
|
|||||||
{
|
{
|
||||||
void Initialize(ref readonly RenderingContext ctx);
|
void Initialize(ref readonly RenderingContext ctx);
|
||||||
void Build(RenderGraph graph, Identifier<RGTexture> backbuffer);
|
void Build(RenderGraph graph, Identifier<RGTexture> backbuffer);
|
||||||
void Cleanup(IResourceManager resourceManager);
|
void Cleanup(IResourceManager resourceManager, IResourceDatabase resourceDatabase);
|
||||||
}
|
}
|
||||||
|
|||||||
242
src/Runtime/Ghost.Graphics/Core/MaterialPaletteStore.cs
Normal file
242
src/Runtime/Ghost.Graphics/Core/MaterialPaletteStore.cs
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
using Ghost.Core;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.Core;
|
||||||
|
|
||||||
|
public readonly struct MaterialPaletteInfo
|
||||||
|
{
|
||||||
|
public int MaterialCount
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
init;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class MaterialPaletteStore : IDisposable
|
||||||
|
{
|
||||||
|
private struct Entry : IDisposable
|
||||||
|
{
|
||||||
|
public UnsafeList<Handle<Material>> materials;
|
||||||
|
public int refCount;
|
||||||
|
public ulong lookupHash;
|
||||||
|
public int nextFree;
|
||||||
|
|
||||||
|
public readonly bool IsAllocated => materials.IsCreated;
|
||||||
|
public readonly bool IsActive => refCount > 0 && materials.IsCreated;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
materials.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private UnsafeList<Entry> _entries;
|
||||||
|
private UnsafeHashMap<ulong, int> _lookup;
|
||||||
|
private int _freeListHead;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
public MaterialPaletteStore(int initialCapacity = 16)
|
||||||
|
{
|
||||||
|
if (initialCapacity <= 0)
|
||||||
|
{
|
||||||
|
initialCapacity = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
_entries = new UnsafeList<Entry>(initialCapacity + 1, Allocator.Persistent);
|
||||||
|
_lookup = new UnsafeHashMap<ulong, int>(initialCapacity * 2, Allocator.Persistent);
|
||||||
|
_freeListHead = 0;
|
||||||
|
|
||||||
|
_entries.Add(new Entry
|
||||||
|
{
|
||||||
|
materials = default,
|
||||||
|
refCount = int.MaxValue,
|
||||||
|
lookupHash = 0,
|
||||||
|
nextFree = -1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
~MaterialPaletteStore()
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int AllocateEntry()
|
||||||
|
{
|
||||||
|
if (_freeListHead != 0)
|
||||||
|
{
|
||||||
|
int index = _freeListHead;
|
||||||
|
ref var entry = ref _entries[index];
|
||||||
|
_freeListHead = entry.nextFree;
|
||||||
|
entry.nextFree = -1;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
int newIndex = _entries.Count;
|
||||||
|
_entries.Add(default);
|
||||||
|
return newIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ulong ComputeLookupHash(ReadOnlySpan<Handle<Material>> materials, ulong seed)
|
||||||
|
{
|
||||||
|
const ulong offset = 14695981039346656037UL;
|
||||||
|
|
||||||
|
ulong hash = offset ^ seed;
|
||||||
|
hash = Mix(hash, (ulong)materials.Length);
|
||||||
|
|
||||||
|
foreach (var material in materials)
|
||||||
|
{
|
||||||
|
hash = Mix(hash, (uint)material.ID);
|
||||||
|
hash = Mix(hash, (uint)material.Generation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static ulong Mix(ulong hash, ulong value)
|
||||||
|
{
|
||||||
|
const ulong prime = 1099511628211UL;
|
||||||
|
hash ^= value;
|
||||||
|
hash *= prime;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int InsertOrGet(ReadOnlySpan<Handle<Material>> materials)
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
|
if (materials.Length == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong hash = ComputeLookupHash(materials, 0);
|
||||||
|
while (_lookup.TryGetValue(hash, out var existingIndex))
|
||||||
|
{
|
||||||
|
ref var entry = ref _entries[existingIndex];
|
||||||
|
if (entry.IsActive && materials.SequenceEqual(entry.materials.AsSpan()))
|
||||||
|
{
|
||||||
|
entry.refCount++;
|
||||||
|
return existingIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash = ComputeLookupHash(materials, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = AllocateEntry();
|
||||||
|
ref var newEntry = ref _entries[index];
|
||||||
|
newEntry.lookupHash = hash;
|
||||||
|
newEntry.refCount = 1;
|
||||||
|
newEntry.nextFree = -1;
|
||||||
|
|
||||||
|
if (!newEntry.materials.IsCreated)
|
||||||
|
{
|
||||||
|
newEntry.materials = new UnsafeList<Handle<Material>>(materials.Length, Allocator.Persistent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newEntry.materials.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < materials.Length; i++)
|
||||||
|
{
|
||||||
|
newEntry.materials.Add(materials[i]);
|
||||||
|
}
|
||||||
|
_lookup.Add(hash, index);
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValid(int paletteIndex)
|
||||||
|
{
|
||||||
|
if (paletteIndex == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return paletteIndex > 0
|
||||||
|
&& paletteIndex < _entries.Count
|
||||||
|
&& _entries[paletteIndex].IsActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MaterialPaletteInfo GetInfo(int paletteIndex)
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
|
if (!IsValid(paletteIndex))
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paletteIndex == 0)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MaterialPaletteInfo
|
||||||
|
{
|
||||||
|
MaterialCount = _entries[paletteIndex].materials.Count,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Handle<Material> GetMaterial(int paletteIndex, int localMaterialIndex)
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
|
if (!IsValid(paletteIndex) || paletteIndex == 0)
|
||||||
|
{
|
||||||
|
return Handle<Material>.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var entry = ref _entries[paletteIndex];
|
||||||
|
if ((uint)localMaterialIndex >= (uint)entry.materials.Count)
|
||||||
|
{
|
||||||
|
return Handle<Material>.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry.materials[localMaterialIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Release(int paletteIndex)
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
|
if (paletteIndex == 0 || !IsValid(paletteIndex))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var entry = ref _entries[paletteIndex];
|
||||||
|
entry.refCount--;
|
||||||
|
if (entry.refCount > 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lookup.Remove(entry.lookupHash);
|
||||||
|
entry.materials.Clear();
|
||||||
|
entry.nextFree = _freeListHead;
|
||||||
|
_freeListHead = paletteIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _entries.Count; i++)
|
||||||
|
{
|
||||||
|
_entries[i].Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_entries.Dispose();
|
||||||
|
_lookup.Dispose();
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,11 +9,64 @@ using Misaki.HighPerformance.Mathematics.Geometry;
|
|||||||
|
|
||||||
namespace Ghost.Graphics.Core;
|
namespace Ghost.Graphics.Core;
|
||||||
|
|
||||||
// TODO: Support sub-meshes and meshlets.
|
public struct Meshlet
|
||||||
|
{
|
||||||
|
public SphereBounds boundingSphere; // 16 bytes
|
||||||
|
public AABB boundingBox; // 24 bytes
|
||||||
|
public uint vertexOffset; // offset into meshlet vertex index array
|
||||||
|
public uint triangleOffset; // offset into packed triangle array
|
||||||
|
public uint groupIndex; // owning group
|
||||||
|
public float parentError; // geometric refinement error carried into runtime LOD tests
|
||||||
|
public byte vertexCount; // max 64
|
||||||
|
public byte triangleCount; // max 124
|
||||||
|
public byte localMaterialIndex; // mesh-local material slot
|
||||||
|
public byte lodLevel; // this meshlet's LOD level
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct MeshletGroup
|
||||||
|
{
|
||||||
|
public SphereBounds boundingSphere; // 16 bytes
|
||||||
|
public AABB boundingBox; // 24 bytes
|
||||||
|
public float parentError; // error of refining to the previous level
|
||||||
|
public uint meshletStartIndex; // contiguous meshlet range
|
||||||
|
public uint meshletCount; // number of meshlets in the group
|
||||||
|
public uint lodLevel; // group LOD level
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct MeshletHierarchyNode
|
||||||
|
{
|
||||||
|
public SphereBounds boundingSphere; // 16 bytes
|
||||||
|
public AABB boundingBox; // 24 bytes
|
||||||
|
public float maxParentError; // maximum error in this subtree
|
||||||
|
public uint nodeData; // packed leaf/internal metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct MeshletMeshData : IDisposable
|
||||||
|
{
|
||||||
|
public UnsafeList<Meshlet> meshlets;
|
||||||
|
public UnsafeList<MeshletGroup> groups;
|
||||||
|
public UnsafeList<MeshletHierarchyNode> hierarchyNodes;
|
||||||
|
public UnsafeList<uint> meshletVertices;
|
||||||
|
public UnsafeList<byte> meshletTriangles;
|
||||||
|
public int lodLevelCount;
|
||||||
|
public int materialSlotCount;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
meshlets.Dispose();
|
||||||
|
groups.Dispose();
|
||||||
|
hierarchyNodes.Dispose();
|
||||||
|
meshletVertices.Dispose();
|
||||||
|
meshletTriangles.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Support and meshlets.
|
||||||
public struct Mesh : IResourceReleasable
|
public struct Mesh : IResourceReleasable
|
||||||
{
|
{
|
||||||
private UnsafeList<Vertex> _vertices;
|
private UnsafeList<Vertex> _vertices;
|
||||||
private UnsafeList<uint> _indices;
|
private UnsafeList<uint> _indices;
|
||||||
|
private MeshletMeshData _meshletData;
|
||||||
|
|
||||||
internal bool IsMeshDataDirty
|
internal bool IsMeshDataDirty
|
||||||
{
|
{
|
||||||
@@ -88,6 +141,14 @@ public struct Mesh : IResourceReleasable
|
|||||||
get; internal set;
|
get; internal set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the handle to the meshlet buffer on the GPU.
|
||||||
|
/// </summary>
|
||||||
|
public Handle<GraphicsBuffer> MeshLetBuffer
|
||||||
|
{
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the handle to the mesh data buffer on the GPU.
|
/// Gets the handle to the mesh data buffer on the GPU.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -112,6 +173,7 @@ public struct Mesh : IResourceReleasable
|
|||||||
{
|
{
|
||||||
_vertices.Dispose();
|
_vertices.Dispose();
|
||||||
_indices.Dispose();
|
_indices.Dispose();
|
||||||
|
_meshletData.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly void ReleaseResource(IResourceDatabase database)
|
public readonly void ReleaseResource(IResourceDatabase database)
|
||||||
@@ -120,6 +182,7 @@ public struct Mesh : IResourceReleasable
|
|||||||
|
|
||||||
database.ReleaseResource(VertexBuffer.AsResource());
|
database.ReleaseResource(VertexBuffer.AsResource());
|
||||||
database.ReleaseResource(IndexBuffer.AsResource());
|
database.ReleaseResource(IndexBuffer.AsResource());
|
||||||
|
database.ReleaseResource(MeshLetBuffer.AsResource());
|
||||||
database.ReleaseResource(ObjectDataBuffer.AsResource());
|
database.ReleaseResource(ObjectDataBuffer.AsResource());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ using Misaki.HighPerformance.Mathematics;
|
|||||||
|
|
||||||
namespace Ghost.Graphics.Core;
|
namespace Ghost.Graphics.Core;
|
||||||
|
|
||||||
public record struct RenderRecord
|
public struct RenderRecord
|
||||||
{
|
{
|
||||||
public float4x4 localToWorld;
|
public float4x4 localToWorld;
|
||||||
public Handle<Mesh> mesh;
|
public Handle<Mesh> mesh;
|
||||||
public Handle<Material> materialOffset;
|
public int materialPaletteIndex;
|
||||||
public RenderingLayerMask renderingLayerMask;
|
public RenderingLayerMask renderingLayerMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public sealed class RenderGraph : IDisposable
|
|||||||
);
|
);
|
||||||
|
|
||||||
_nativePassBuilder = new RenderGraphNativePassBuilder(_objectPool, _resources);
|
_nativePassBuilder = new RenderGraphNativePassBuilder(_objectPool, _resources);
|
||||||
_compiler = new RenderGraphCompiler(_resourceManager, _resources, _aliasingManager, _nativePassBuilder, _compilationCache);
|
_compiler = new RenderGraphCompiler(_resourceManager, _resourceDatabase, _resourceAllocator, _resources, _aliasingManager, _nativePassBuilder, _compilationCache);
|
||||||
_executor = new RenderGraphExecutor(_resourceManager, _resourceDatabase, _resources, _context);
|
_executor = new RenderGraphExecutor(_resourceManager, _resourceDatabase, _resources, _context);
|
||||||
|
|
||||||
_blackboard = new RenderGraphBlackboard();
|
_blackboard = new RenderGraphBlackboard();
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ namespace Ghost.Graphics.RenderGraphModule;
|
|||||||
internal sealed class RenderGraphCompiler
|
internal sealed class RenderGraphCompiler
|
||||||
{
|
{
|
||||||
private readonly IResourceManager _resourceManager;
|
private readonly IResourceManager _resourceManager;
|
||||||
|
private readonly IResourceDatabase _resourceDatabase;
|
||||||
|
private readonly IResourceAllocator _resourceAllocator;
|
||||||
private readonly RenderGraphResourceRegistry _resources;
|
private readonly RenderGraphResourceRegistry _resources;
|
||||||
private readonly ResourceAliasingManager _aliasingManager;
|
private readonly ResourceAliasingManager _aliasingManager;
|
||||||
private readonly RenderGraphNativePassBuilder _nativePassBuilder;
|
private readonly RenderGraphNativePassBuilder _nativePassBuilder;
|
||||||
@@ -19,12 +21,16 @@ internal sealed class RenderGraphCompiler
|
|||||||
|
|
||||||
public RenderGraphCompiler(
|
public RenderGraphCompiler(
|
||||||
IResourceManager resourceManager,
|
IResourceManager resourceManager,
|
||||||
|
IResourceDatabase resourceDatabase,
|
||||||
|
IResourceAllocator resourceAllocator,
|
||||||
RenderGraphResourceRegistry resources,
|
RenderGraphResourceRegistry resources,
|
||||||
ResourceAliasingManager aliasingManager,
|
ResourceAliasingManager aliasingManager,
|
||||||
RenderGraphNativePassBuilder nativePassBuilder,
|
RenderGraphNativePassBuilder nativePassBuilder,
|
||||||
RenderGraphCompilationCache compilationCache)
|
RenderGraphCompilationCache compilationCache)
|
||||||
{
|
{
|
||||||
_resourceManager = resourceManager;
|
_resourceManager = resourceManager;
|
||||||
|
_resourceDatabase = resourceDatabase;
|
||||||
|
_resourceAllocator = resourceAllocator;
|
||||||
_resources = resources;
|
_resources = resources;
|
||||||
_aliasingManager = aliasingManager;
|
_aliasingManager = aliasingManager;
|
||||||
_nativePassBuilder = nativePassBuilder;
|
_nativePassBuilder = nativePassBuilder;
|
||||||
@@ -208,10 +214,10 @@ internal sealed class RenderGraphCompiler
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_resourceManager.ResourceDatabase.ReleaseResource(res.backingResource);
|
_resourceDatabase.ReleaseResource(res.backingResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
_resourceManager.ResourceDatabase.ReleaseResource(_resourceHeap);
|
_resourceDatabase.ReleaseResource(_resourceHeap);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_aliasingManager.Heap.size == 0)
|
if (_aliasingManager.Heap.size == 0)
|
||||||
@@ -227,7 +233,7 @@ internal sealed class RenderGraphCompiler
|
|||||||
HeapType = HeapType.Default
|
HeapType = HeapType.Default
|
||||||
};
|
};
|
||||||
|
|
||||||
_resourceHeap = _resourceManager.ResourceAllocator.Allocate(in allocationDesc, "RenderGraphResourceHeap");
|
_resourceHeap = _resourceAllocator.Allocate(in allocationDesc, "RenderGraphResourceHeap");
|
||||||
if (_resourceHeap.IsInvalid)
|
if (_resourceHeap.IsInvalid)
|
||||||
{
|
{
|
||||||
return Error.InvalidState;
|
return Error.InvalidState;
|
||||||
@@ -253,11 +259,11 @@ internal sealed class RenderGraphCompiler
|
|||||||
if (res.type == RenderGraphResourceType.Texture)
|
if (res.type == RenderGraphResourceType.Texture)
|
||||||
{
|
{
|
||||||
var textureDesc = res.rgTextureDesc.ToTextureDesc(res.resolvedWidth, res.resolvedHeight);
|
var textureDesc = res.rgTextureDesc.ToTextureDesc(res.resolvedWidth, res.resolvedHeight);
|
||||||
res.backingResource = _resourceManager.ResourceAllocator.CreateTexture(in textureDesc, res.name, ops).AsResource();
|
res.backingResource = _resourceAllocator.CreateTexture(in textureDesc, res.name, ops).AsResource();
|
||||||
}
|
}
|
||||||
else if (res.type == RenderGraphResourceType.Buffer)
|
else if (res.type == RenderGraphResourceType.Buffer)
|
||||||
{
|
{
|
||||||
res.backingResource = _resourceManager.ResourceAllocator.CreateBuffer(in res.bufferDesc, res.name, ops).AsResource();
|
res.backingResource = _resourceAllocator.CreateBuffer(in res.bufferDesc, res.name, ops).AsResource();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -374,11 +380,11 @@ internal sealed class RenderGraphCompiler
|
|||||||
{
|
{
|
||||||
if (!res.isImported)
|
if (!res.isImported)
|
||||||
{
|
{
|
||||||
_resourceManager.ResourceDatabase.ReleaseResource(res.backingResource);
|
_resourceDatabase.ReleaseResource(res.backingResource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_resourceManager.ResourceDatabase.ReleaseResource(_resourceHeap);
|
_resourceDatabase.ReleaseResource(_resourceHeap);
|
||||||
_resourceHeap = Handle<GPUResource>.Invalid;
|
_resourceHeap = Handle<GPUResource>.Invalid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace Ghost.Graphics.RenderGraphModule;
|
|||||||
public interface IRenderGraphContext
|
public interface IRenderGraphContext
|
||||||
{
|
{
|
||||||
IResourceManager ResourceManager { get; }
|
IResourceManager ResourceManager { get; }
|
||||||
|
IResourceDatabase ResourceDatabase { get; }
|
||||||
|
|
||||||
Handle<GPUResource> GetActualResource(Identifier<RGResource> resource);
|
Handle<GPUResource> GetActualResource(Identifier<RGResource> resource);
|
||||||
Handle<Texture> GetActualTexture(Identifier<RGTexture> texture);
|
Handle<Texture> GetActualTexture(Identifier<RGTexture> texture);
|
||||||
@@ -58,6 +59,7 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
|||||||
private int _activeMeshIndexCount;
|
private int _activeMeshIndexCount;
|
||||||
|
|
||||||
public IResourceManager ResourceManager => _resourceManager;
|
public IResourceManager ResourceManager => _resourceManager;
|
||||||
|
public IResourceDatabase ResourceDatabase => _resourceDatabase;
|
||||||
|
|
||||||
public int ActiveMeshIndexCount => _activeMeshIndexCount;
|
public int ActiveMeshIndexCount => _activeMeshIndexCount;
|
||||||
|
|
||||||
|
|||||||
@@ -291,12 +291,12 @@ internal class MeshRenderPass : IRenderPass
|
|||||||
ref var matRef = ref r.Value;
|
ref var matRef = ref r.Value;
|
||||||
var blitProps = new ShaderProperties_Hidden_Blit
|
var blitProps = new ShaderProperties_Hidden_Blit
|
||||||
{
|
{
|
||||||
mainTex = ctx.ResourceManager.ResourceDatabase.GetBindlessIndex(ctx.GetActualResource(data.source.AsResource())),
|
mainTex = ctx.ResourceDatabase.GetBindlessIndex(ctx.GetActualResource(data.source.AsResource())),
|
||||||
sampler_mainTex = (uint)data.sampler.Value,
|
sampler_mainTex = (uint)data.sampler.Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
matRef.SetPropertyCache(in blitProps).ThrowIfFailed();
|
matRef.SetPropertyCache(in blitProps).ThrowIfFailed();
|
||||||
matRef.UploadData(ctx.CommandBuffer, ctx.ResourceManager.ResourceDatabase);
|
matRef.UploadData(ctx.CommandBuffer, ctx.ResourceDatabase);
|
||||||
|
|
||||||
ctx.CommandBuffer.SetRenderTargets([ctx.GetActualTexture(data.destination)], Handle<Texture>.Invalid);
|
ctx.CommandBuffer.SetRenderTargets([ctx.GetActualTexture(data.destination)], Handle<Texture>.Invalid);
|
||||||
|
|
||||||
@@ -307,20 +307,20 @@ internal class MeshRenderPass : IRenderPass
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cleanup(IResourceManager resourceManager)
|
public void Cleanup(IResourceManager resourceManager, IResourceDatabase resourceDatabase)
|
||||||
{
|
{
|
||||||
resourceManager.ReleaseMaterial(_blitMaterial);
|
resourceManager.ReleaseMaterial(_blitMaterial);
|
||||||
|
|
||||||
resourceManager.ReleaseMaterial(_material);
|
resourceManager.ReleaseMaterial(_material);
|
||||||
resourceManager.ReleaseShader(_shader);
|
resourceManager.ReleaseShader(_shader);
|
||||||
resourceManager.ReleaseMesh(_mesh);
|
resourceManager.ReleaseMesh(_mesh);
|
||||||
resourceManager.ResourceDatabase.ReleaseSampler(_sampler);
|
resourceDatabase.ReleaseSampler(_sampler);
|
||||||
|
|
||||||
if (_textures != null)
|
if (_textures != null)
|
||||||
{
|
{
|
||||||
foreach (var texture in _textures)
|
foreach (var texture in _textures)
|
||||||
{
|
{
|
||||||
resourceManager.ResourceDatabase.ReleaseResource(texture.AsResource());
|
resourceDatabase.ReleaseResource(texture.AsResource());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ public unsafe partial class GhostRenderPipeline : IRenderPipeline
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_renderGraph.Dispose();
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,38 @@ public interface IResourceManager
|
|||||||
/// <param name="handle">The handle of the material to release. Must refer to a material that has been previously acquired.</param>
|
/// <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);
|
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="paletteIndex">The palette index to validate.</param>
|
||||||
|
bool HasMaterialPalette(int paletteIndex);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets metadata for a material palette entry.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="paletteIndex">The palette index to query.</param>
|
||||||
|
MaterialPaletteInfo GetMaterialPaletteInfo(int paletteIndex);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a material handle from a palette entry by local material index.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="paletteIndex">The palette index to query.</param>
|
||||||
|
/// <param name="localMaterialIndex">The material slot inside the palette.</param>
|
||||||
|
Handle<Material> GetMaterialPaletteMaterial(int paletteIndex, int localMaterialIndex);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases a material palette reference previously returned by <see cref="GetOrCreateMaterialPalette(ReadOnlySpan{Handle{Material}})"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="paletteIndex">The palette index to release.</param>
|
||||||
|
void ReleaseMaterialPalette(int paletteIndex);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether a shader with the specified identifier exists in the collection.
|
/// Determines whether a shader with the specified identifier exists in the collection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -101,6 +133,8 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
|||||||
private UnsafeSlotMap<Material> _materials;
|
private UnsafeSlotMap<Material> _materials;
|
||||||
private UnsafeList<Shader> _shaders; // TODO: Use SlotMap?
|
private UnsafeList<Shader> _shaders; // TODO: Use SlotMap?
|
||||||
|
|
||||||
|
private readonly MaterialPaletteStore _materialPalettes;
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public ResourceManager(IResourceAllocator resourceAllocator, IResourceDatabase resourceDatabase)
|
public ResourceManager(IResourceAllocator resourceAllocator, IResourceDatabase resourceDatabase)
|
||||||
@@ -111,6 +145,7 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
|||||||
_meshes = new UnsafeSlotMap<Mesh>(64, Allocator.Persistent);
|
_meshes = new UnsafeSlotMap<Mesh>(64, Allocator.Persistent);
|
||||||
_materials = new UnsafeSlotMap<Material>(64, Allocator.Persistent);
|
_materials = new UnsafeSlotMap<Material>(64, Allocator.Persistent);
|
||||||
_shaders = new UnsafeList<Shader>(16, Allocator.Persistent);
|
_shaders = new UnsafeList<Shader>(16, Allocator.Persistent);
|
||||||
|
_materialPalettes = new MaterialPaletteStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
~ResourceManager()
|
~ResourceManager()
|
||||||
@@ -168,7 +203,7 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
|||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
var material = new Material();
|
var material = new Material();
|
||||||
if (material.SetShader(shader, this) != Error.None)
|
if (material.SetShader(shader, this, _resourceDatabase, _resourceAllocator) != Error.None)
|
||||||
{
|
{
|
||||||
return Handle<Material>.Invalid;
|
return Handle<Material>.Invalid;
|
||||||
}
|
}
|
||||||
@@ -249,6 +284,45 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
|||||||
material.ReleaseResource(_resourceDatabase);
|
material.ReleaseResource(_resourceDatabase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int GetOrCreateMaterialPalette(ReadOnlySpan<Handle<Material>> materials)
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
|
foreach (var material in materials)
|
||||||
|
{
|
||||||
|
if (material.IsInvalid || !HasMaterial(material))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _materialPalettes.InsertOrGet(materials);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasMaterialPalette(int paletteIndex)
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
return _materialPalettes.IsValid(paletteIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MaterialPaletteInfo GetMaterialPaletteInfo(int paletteIndex)
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
return _materialPalettes.GetInfo(paletteIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Handle<Material> GetMaterialPaletteMaterial(int paletteIndex, int localMaterialIndex)
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
return _materialPalettes.GetMaterial(paletteIndex, localMaterialIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReleaseMaterialPalette(int paletteIndex)
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
_materialPalettes.Release(paletteIndex);
|
||||||
|
}
|
||||||
|
|
||||||
public bool HasShader(Identifier<Shader> id)
|
public bool HasShader(Identifier<Shader> id)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
@@ -303,6 +377,7 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
|||||||
_meshes.Dispose();
|
_meshes.Dispose();
|
||||||
_materials.Dispose();
|
_materials.Dispose();
|
||||||
_shaders.Dispose();
|
_shaders.Dispose();
|
||||||
|
_materialPalettes.Dispose();
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace Ghost.Entities.Test;
|
|||||||
|
|
||||||
internal struct TestEntityQueryJob : IJobEntity<Transform>
|
internal struct TestEntityQueryJob : IJobEntity<Transform>
|
||||||
{
|
{
|
||||||
public readonly void Execute(Entity entity, ref Transform transform, int threadIndex)
|
public readonly void Execute(Entity entity, ref Transform transform, ref readonly JobExecutionContext ctx)
|
||||||
{
|
{
|
||||||
transform.position += new float3(5, 5, 5);
|
transform.position += new float3(5, 5, 5);
|
||||||
}
|
}
|
||||||
@@ -15,9 +15,9 @@ internal struct TestEntityQueryJob : IJobEntity<Transform>
|
|||||||
|
|
||||||
internal struct TestChunkQueryJob : IJobChunk
|
internal struct TestChunkQueryJob : IJobChunk
|
||||||
{
|
{
|
||||||
public readonly void Execute(ChunkView view, int threadIndex)
|
public readonly void Execute(ChunkView view, ref readonly JobExecutionContext ctx)
|
||||||
{
|
{
|
||||||
var random = new random((uint)threadIndex + 1u);
|
var random = new random((uint)ctx.ThreadIndex + 1u);
|
||||||
|
|
||||||
var transforms = view.GetComponentDataRW<Transform>();
|
var transforms = view.GetComponentDataRW<Transform>();
|
||||||
for (var i = 0; i < view.Count; i++)
|
for (var i = 0; i < view.Count; i++)
|
||||||
@@ -54,7 +54,7 @@ public partial class EntityQueryTest : ITest
|
|||||||
|
|
||||||
var testJob = new TestChunkQueryJob();
|
var testJob = new TestChunkQueryJob();
|
||||||
var handle = query.ScheduleChunkParallel(testJob, 1, JobHandle.Invalid);
|
var handle = query.ScheduleChunkParallel(testJob, 1, JobHandle.Invalid);
|
||||||
_jobScheduler.WaitComplete(handle);
|
_jobScheduler.Wait(handle);
|
||||||
|
|
||||||
query.ForEach<Transform>((e, ref t) =>
|
query.ForEach<Transform>((e, ref t) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ internal class SystemTest : ITest
|
|||||||
|
|
||||||
group.SortSystems();
|
group.SortSystems();
|
||||||
|
|
||||||
var api = new SystemAPI();
|
_world.SystemManager.InitializeAll(new TimeData());
|
||||||
_world.SystemManager.InitializeAll(in api);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cleanup()
|
public void Cleanup()
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
using Ghost.DSL.Generator;
|
||||||
using Misaki.HighPerformance.Mathematics;
|
using Misaki.HighPerformance.Mathematics;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
//ShaderStructGenerator.GenerateHLSL([typeof(TestStruct), typeof(TestEnum), typeof(TestEnumFlags)], PackingRules.Exact, "C:/Users/Misaki/Downloads/Archive/Test.cs.hlsl");
|
ShaderStructGenerator.GenerateHLSL([typeof(TestStruct), typeof(TestEnum), typeof(TestEnumFlags)], PackingRules.Exact, "C:/Users/Misaki/Downloads/Archive/Test.cs.hlsl");
|
||||||
|
|
||||||
//return;
|
//return;
|
||||||
#if false
|
#if false
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#if false
|
||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
|
|
||||||
namespace Ghost.UnitTest;
|
namespace Ghost.UnitTest;
|
||||||
@@ -432,3 +433,4 @@ public class AssetDatabaseIntegrationTest
|
|||||||
CheckInternalErrors();
|
CheckInternalErrors();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace Ghost.UnitTest;
|
|
||||||
|
|
||||||
[TestClass]
|
|
||||||
public class AssetMetaTest
|
|
||||||
{
|
|
||||||
[TestMethod]
|
|
||||||
public void TestMetaSerialization()
|
|
||||||
{
|
|
||||||
var meta = new AssetMeta
|
|
||||||
{
|
|
||||||
Guid = Guid.NewGuid(),
|
|
||||||
Version = 1,
|
|
||||||
Tags = new List<string> { "Test", "Asset" }
|
|
||||||
};
|
|
||||||
|
|
||||||
var json = JsonSerializer.Serialize(meta, new JsonSerializerOptions { WriteIndented = true });
|
|
||||||
|
|
||||||
Assert.IsNotNull(json);
|
|
||||||
Assert.Contains("Guid", json);
|
|
||||||
Assert.Contains("Version", json);
|
|
||||||
Assert.Contains("Tags", json);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TestMetaDeserialization()
|
|
||||||
{
|
|
||||||
var guid = Guid.NewGuid();
|
|
||||||
|
|
||||||
var json = $@"{{
|
|
||||||
""Guid"": ""{guid}"",
|
|
||||||
""Version"": 1,
|
|
||||||
""Tags"": [""Test"", ""Asset""]
|
|
||||||
}}";
|
|
||||||
|
|
||||||
var meta = JsonSerializer.Deserialize<AssetMeta>(json);
|
|
||||||
|
|
||||||
Assert.IsNotNull(meta);
|
|
||||||
Assert.AreEqual(guid, meta.Guid);
|
|
||||||
Assert.AreEqual(1, meta.Version);
|
|
||||||
Assert.HasCount(2, meta.Tags);
|
|
||||||
Assert.Contains("Test", meta.Tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TestMetaWithSettings()
|
|
||||||
{
|
|
||||||
var meta = new AssetMeta
|
|
||||||
{
|
|
||||||
Guid = Guid.NewGuid(),
|
|
||||||
Version = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add importer settings using the new API
|
|
||||||
var settings = new TextImporterSettings
|
|
||||||
{
|
|
||||||
Encoding = "UTF-8",
|
|
||||||
TrimWhitespace = true
|
|
||||||
};
|
|
||||||
|
|
||||||
meta.SetImporterSettings("TextImporter", settings);
|
|
||||||
|
|
||||||
var json = JsonSerializer.Serialize(meta, new JsonSerializerOptions { WriteIndented = true });
|
|
||||||
var deserialized = JsonSerializer.Deserialize<AssetMeta>(json);
|
|
||||||
|
|
||||||
Assert.IsNotNull(deserialized);
|
|
||||||
Assert.Contains("TextImporter", deserialized.ImporterSettings.Keys);
|
|
||||||
|
|
||||||
// Test retrieving the settings
|
|
||||||
var retrievedSettings = deserialized.GetImporterSettings<TextImporterSettings>("TextImporter");
|
|
||||||
Assert.IsNotNull(retrievedSettings);
|
|
||||||
Assert.AreEqual("UTF-8", retrievedSettings.Encoding);
|
|
||||||
Assert.IsTrue(retrievedSettings.TrimWhitespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TestFileHashAndDependenciesNotSerialized()
|
|
||||||
{
|
|
||||||
var meta = new AssetMeta
|
|
||||||
{
|
|
||||||
Guid = Guid.NewGuid(),
|
|
||||||
Version = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
var json = JsonSerializer.Serialize(meta, new JsonSerializerOptions { WriteIndented = true });
|
|
||||||
|
|
||||||
// FileHash and Dependencies should NOT be in the serialized JSON
|
|
||||||
Assert.DoesNotContain("FileHash", json);
|
|
||||||
Assert.DoesNotContain("Dependencies", json);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user