Render extraction system & ECS/graphics refactor
Introduced RenderExtractionSystem for entity-based render data extraction. Added MeshInstance and MeshPalette components with shadow casting support. Refactored QueryBuilder API, SharedComponentStore, and component registration for clarity and flexibility. Updated SystemManager and SystemGroup to use SystemAPI. Replaced RenderingConfig with GraphicsEngineDesc/RenderSystemDesc. RenderFrame now uses CPU/GPU fence values for sync. Removed Camera.cs in favor of ECS-based rendering. Improved Material, RenderingLayerMask, Mesh, and RenderList APIs. Updated package references and fixed naming, error handling, and disposal issues.
This commit is contained in:
@@ -1,56 +1,67 @@
|
||||
#if false
|
||||
using Ghost.Core;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ghost.Entities;
|
||||
|
||||
public interface ISharedComponent
|
||||
public interface ISharedComponent;
|
||||
public interface ISharedWarper
|
||||
{
|
||||
public int Index
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
|
||||
public struct Shared<T> : IComponent, ISharedWarper
|
||||
where T : unmanaged, ISharedComponent
|
||||
{
|
||||
public int Index
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe sealed class SharedComponentStore : IDisposable
|
||||
{
|
||||
private struct EntryInfo
|
||||
{
|
||||
public int RefCount;
|
||||
public int HashCode;
|
||||
public int Version;
|
||||
public int NextFree; // free-list linkage (index)
|
||||
public int refCount;
|
||||
public int hashCode;
|
||||
public int version;
|
||||
public int nextFree; // free-list linkage (index)
|
||||
}
|
||||
|
||||
private struct TypeStore : IDisposable
|
||||
{
|
||||
public int TypeSize;
|
||||
public UnsafeList<byte> Data; // raw bytes, stride = TypeSize
|
||||
public UnsafeList<EntryInfo> Infos; // parallel to Data entries (Entry 0 reserved)
|
||||
public UnsafeHashMap<long, int> HashLookup; // (hashKey) -> entryIndex
|
||||
public int FreeListHead; // head index, 0 means none (we'll use Infos[0].NextFree too if you prefer)
|
||||
public int VersionCounter;
|
||||
public int typeSize;
|
||||
public UnsafeList<byte> data; // raw bytes, stride = TypeSize
|
||||
public UnsafeList<EntryInfo> infos; // parallel to Data entries (Entry 0 reserved)
|
||||
public UnsafeHashMap<long, int> hashLookup; // (hashKey) -> entryIndex
|
||||
public int freeListHead; // head index, 0 means none (we'll use Infos[0].NextFree too if you prefer)
|
||||
public int versionCounter;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Data.Dispose();
|
||||
Infos.Dispose();
|
||||
HashLookup.Dispose();
|
||||
data.Dispose();
|
||||
infos.Dispose();
|
||||
hashLookup.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly UnsafeHashMap<int, TypeStore> _perType; // componentTypeId -> TypeStore
|
||||
private UnsafeHashMap<int, TypeStore> _perType; // componentTypeId -> TypeStore
|
||||
private bool _disposed;
|
||||
|
||||
public SharedComponentStore(int initialCapacity = 16)
|
||||
{
|
||||
_perType = new UnsafeHashMap<int, TypeStore>(initialCapacity, Allocator.Persistent);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
~SharedComponentStore()
|
||||
{
|
||||
foreach (var kvp in _perType)
|
||||
{
|
||||
kvp.Value.Dispose();
|
||||
}
|
||||
|
||||
_perType.Dispose();
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public int InsertOrGet(int componentTypeId, int typeSize, void* data, int hashCode)
|
||||
@@ -66,12 +77,12 @@ internal unsafe sealed class SharedComponentStore : IDisposable
|
||||
// Combine (typeId, hash) into a single key; collisions handled by memcmp below.
|
||||
var key = ((long)componentTypeId << 32) ^ (uint)hashCode;
|
||||
|
||||
if (store.HashLookup.TryGetValue(key, out var existingIndex))
|
||||
if (store.hashLookup.TryGetValue(key, out var existingIndex))
|
||||
{
|
||||
var existingPtr = (byte*)store.Data.GetUnsafePtr() + (existingIndex * store.TypeSize);
|
||||
if (new Span<byte>(existingPtr, store.TypeSize).SequenceEqual(new Span<byte>(data, store.TypeSize)))
|
||||
var existingPtr = (byte*)store.data.GetUnsafePtr() + (existingIndex * store.typeSize);
|
||||
if (new Span<byte>(existingPtr, store.typeSize).SequenceEqual(new Span<byte>(data, store.typeSize)))
|
||||
{
|
||||
((EntryInfo*)store.Infos.GetUnsafePtr())[existingIndex].RefCount++;
|
||||
((EntryInfo*)store.infos.GetUnsafePtr())[existingIndex].refCount++;
|
||||
return existingIndex;
|
||||
}
|
||||
// If collision: fall through to insert (you may want a secondary structure).
|
||||
@@ -79,98 +90,167 @@ internal unsafe sealed class SharedComponentStore : IDisposable
|
||||
|
||||
int index = AllocateEntry(ref store);
|
||||
|
||||
var dst = (byte*)store.Data.GetUnsafePtr() + (index * store.TypeSize);
|
||||
MemoryUtility.MemCpy(dst, data, (nuint)store.TypeSize);
|
||||
var dst = (byte*)store.data.GetUnsafePtr() + (index * store.typeSize);
|
||||
MemoryUtility.MemCpy(dst, data, (nuint)store.typeSize);
|
||||
|
||||
store.Infos[index] = new EntryInfo
|
||||
store.infos[index] = new EntryInfo
|
||||
{
|
||||
RefCount = 1,
|
||||
HashCode = hashCode,
|
||||
Version = ++store.VersionCounter,
|
||||
NextFree = -1
|
||||
refCount = 1,
|
||||
hashCode = hashCode,
|
||||
version = ++store.versionCounter,
|
||||
nextFree = -1
|
||||
};
|
||||
|
||||
store.HashLookup[key] = index;
|
||||
store.hashLookup[key] = index;
|
||||
return index;
|
||||
}
|
||||
|
||||
public void AddRef(int componentTypeId, int index)
|
||||
public RefResult<T, Error> GetComponentData<T>(int componentTypeId, int index)
|
||||
where T : unmanaged
|
||||
{
|
||||
if (index == 0) return;
|
||||
ref var store = ref _perType[componentTypeId];
|
||||
store.Infos[index].RefCount++;
|
||||
if (index == 0)
|
||||
{
|
||||
return Error.InvalidArgument;
|
||||
}
|
||||
|
||||
ref var store = ref _perType.GetValueRef(componentTypeId, out var exist);
|
||||
if (!exist || index >= store.infos.Count)
|
||||
{
|
||||
return Error.NotFound;
|
||||
}
|
||||
|
||||
var info = store.infos[index];
|
||||
if (info.refCount <= 0)
|
||||
{
|
||||
return Error.NotFound;
|
||||
}
|
||||
|
||||
var dataPtr = (byte*)store.data.GetUnsafePtr() + (index * store.typeSize);
|
||||
return new RefResult<T, Error>(ref *(T*)dataPtr, Error.Success);
|
||||
}
|
||||
|
||||
public Error AddRef(int componentTypeId, int index)
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
return Error.Success;
|
||||
}
|
||||
|
||||
ref var store = ref _perType.GetValueRef(componentTypeId, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return Error.NotFound;
|
||||
}
|
||||
|
||||
store.infos[index].refCount++;
|
||||
|
||||
return Error.Success;
|
||||
}
|
||||
|
||||
public void Release(int componentTypeId, int index)
|
||||
{
|
||||
if (index == 0) return;
|
||||
ref var store = ref _perType.GetValueByKey(componentTypeId);
|
||||
if (index == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref var info = ref store.Infos.Ptr[index];
|
||||
info.RefCount--;
|
||||
if (info.RefCount > 0) return;
|
||||
ref var store = ref _perType.GetValueRef(componentTypeId, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref var info = ref store.infos[index];
|
||||
info.refCount--;
|
||||
if (info.refCount > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove from hash lookup (best-effort; collisions require more robust handling)
|
||||
long key = ((long)componentTypeId << 32) ^ (uint)info.HashCode;
|
||||
store.HashLookup.Remove(key);
|
||||
long key = ((long)componentTypeId << 32) ^ (uint)info.hashCode;
|
||||
store.hashLookup.Remove(key);
|
||||
|
||||
// Push to free-list
|
||||
info.NextFree = store.FreeListHead;
|
||||
store.FreeListHead = index;
|
||||
info.nextFree = store.freeListHead;
|
||||
store.freeListHead = index;
|
||||
}
|
||||
|
||||
public void* GetDataPtr(int componentTypeId, int index)
|
||||
{
|
||||
if (index == 0) return null;
|
||||
ref var store = ref _perType.GetValueByKey(componentTypeId);
|
||||
return (byte*)store.Data.Ptr + (index * store.TypeSize);
|
||||
if (index == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ref var store = ref _perType.GetValueRef(componentTypeId, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (byte*)store.data.GetUnsafePtr() + (index * store.typeSize);
|
||||
}
|
||||
|
||||
private ref TypeStore GetOrCreateTypeStore(int componentTypeId, int typeSize)
|
||||
{
|
||||
if (_perType.TryGetValue(componentTypeId, out var existing))
|
||||
ref var existing = ref _perType.GetValueRef(componentTypeId, out var exist);
|
||||
if (exist)
|
||||
{
|
||||
// UnsafeHashMap returns by value in some implementations; you may need a different pattern here.
|
||||
// Adjust to your container API (e.g., TryGetValueRef).
|
||||
return ref existing;
|
||||
}
|
||||
|
||||
var store = new TypeStore
|
||||
{
|
||||
TypeSize = typeSize,
|
||||
Data = new UnsafeList<byte>(typeSize * 16, Allocator.Persistent),
|
||||
Infos = new UnsafeList<EntryInfo>(16, Allocator.Persistent),
|
||||
HashLookup = new UnsafeHashMap<long, int>(16, Allocator.Persistent),
|
||||
FreeListHead = 0,
|
||||
VersionCounter = 0
|
||||
typeSize = typeSize,
|
||||
data = new UnsafeList<byte>(typeSize * 16, Allocator.Persistent),
|
||||
infos = new UnsafeList<EntryInfo>(16, Allocator.Persistent),
|
||||
hashLookup = new UnsafeHashMap<long, int>(16, Allocator.Persistent),
|
||||
freeListHead = 0,
|
||||
versionCounter = 0
|
||||
};
|
||||
|
||||
// Create reserved default entry at index 0
|
||||
store.Data.Resize(typeSize); // one element worth of bytes
|
||||
store.Infos.Add(new EntryInfo { RefCount = int.MaxValue, HashCode = 0, Version = 0, NextFree = -1 });
|
||||
store.data.Resize(typeSize); // one element worth of bytes
|
||||
store.infos.Add(new EntryInfo { refCount = int.MaxValue, hashCode = 0, version = 0, nextFree = -1 });
|
||||
|
||||
_perType.Add(componentTypeId, store);
|
||||
// NOTE: returning a ref requires a "get ref" API; adjust to your UnsafeHashMap capabilities.
|
||||
return ref _perType.GetValueByKey(componentTypeId);
|
||||
|
||||
existing = ref _perType.GetValueRef(componentTypeId, out exist);
|
||||
Debug.Assert(exist);
|
||||
|
||||
return ref existing;
|
||||
}
|
||||
|
||||
private static int AllocateEntry(ref TypeStore store)
|
||||
{
|
||||
if (store.FreeListHead != 0)
|
||||
if (store.freeListHead != 0)
|
||||
{
|
||||
int idx = store.FreeListHead;
|
||||
store.FreeListHead = store.Infos[idx].NextFree;
|
||||
store.Infos[idx].NextFree = -1;
|
||||
int idx = store.freeListHead;
|
||||
store.freeListHead = store.infos[idx].nextFree;
|
||||
store.infos[idx].nextFree = -1;
|
||||
return idx;
|
||||
}
|
||||
|
||||
int newIndex = store.Infos.Count;
|
||||
store.Infos.Add(default);
|
||||
int newIndex = store.infos.Count;
|
||||
store.infos.Add(default);
|
||||
|
||||
int newByteCount = (newIndex + 1) * store.TypeSize;
|
||||
store.Data.Resize(newByteCount);
|
||||
int newByteCount = (newIndex + 1) * store.typeSize;
|
||||
store.data.Resize(newByteCount);
|
||||
|
||||
return newIndex;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var kvp in _perType)
|
||||
{
|
||||
kvp.Value.Dispose();
|
||||
}
|
||||
|
||||
_perType.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user