Refactor material palette system with GPU indirection

Major overhaul of material palette management:
- Added two-buffer indirection (PaletteOffsetBuffer, MaterialIndexBuffer) for GPU material lookup, with incremental upload and resizing.
- MaterialPaletteStore now tracks dirty ranges, supports deferred slot reclamation, and exposes CPU-side arrays for upload.
- ResourceManager manages persistent GPU buffers and uploads only dirty subranges per frame.
- Updated HLSL and C# structs to use palette indices.
- Refactored systems/components to use new palette index and release logic.
- Added RenderContext.UploadBufferRange for partial uploads.
Minor: Fixed StbIApi interop signatures, updated test namespaces, and performed code cleanups.
This commit is contained in:
2026-04-28 18:22:09 +09:00
parent 631638f3fb
commit 0eaf7cd51d
19 changed files with 390 additions and 30 deletions

View File

@@ -1,8 +1,11 @@
using Ghost.Core;
using Ghost.Entities;
using Ghost.Graphics.Services;
namespace Ghost.Engine.Components;
public struct GPUInstanceRef : IComponent
{
public uint gpuSceneIndex;
public uint gpuInstanceIndex;
public Identifier<MaterialPalette> materialPalette;
}

View File

@@ -27,7 +27,7 @@ internal partial class GhostRenderPipeline
public float4x4 localToWorld;
public uint instanceID;
public uint meshBuffer;
public uint materialPalette;
public uint materialPaletteIndex;
public uint renderingLayerMask;
public uint shadowCastingMode;
}
@@ -71,7 +71,7 @@ internal partial class GhostRenderPipeline
localToWorld = addRequest.localToWorld,
instanceID = addRequest.instanceId,
meshBuffer = resourceDatabase.GetBindlessIndex(mesh.Get().MeshDataBuffer.AsResource()),
materialPalette = (uint)addRequest.meshInstance.materialPalette.Value,
materialPaletteIndex = (uint)addRequest.meshInstance.materialPalette.Value,
renderingLayerMask = addRequest.meshInstance.renderingLayerMask,
shadowCastingMode = (uint)addRequest.meshInstance.shadowCastingMode
};

View File

@@ -46,7 +46,9 @@ internal class AddGPUInstanceSystem : SystemBase
var entity = entities.GetElementUnsafe(i);
var index = payload.AddInstance(localToWorld.matrix, in meshInstance);
systemAPI.World.EntityCommandBuffer.AddComponent(entity, new GPUInstanceRef { gpuSceneIndex = index });
var materialPalette = meshInstance.materialPalette;
systemAPI.World.EntityCommandBuffer.AddComponent(entity, new GPUInstanceRef { gpuInstanceIndex = index, materialPalette = materialPalette });
}
}

View File

@@ -43,7 +43,9 @@ internal class RemoveGPUInstanceSystem : SystemBase
var gpuInstance = gpuInstanceRefs.GetElementUnsafe(i);
var entity = entities.GetElementUnsafe(i);
payload.RemoveInstance(gpuInstance.gpuSceneIndex);
payload.RemoveInstance(gpuInstance.gpuInstanceIndex);
_renderSystem.ResourceManager.ReleaseMaterialPalette(gpuInstance.materialPalette);
systemAPI.World.EntityCommandBuffer.RemoveComponent<GPUInstanceRef>(entity);
}
}

View File

@@ -49,7 +49,7 @@ internal class UpdateGPUInstanceSystem : SystemBase
ref readonly var mesh = ref meshs.GetElementUnsafe(i);
ref readonly var instance = ref gpuInstances.GetElementUnsafe(i);
playload.UpdateInstance(instance.gpuSceneIndex, ltw.matrix, in mesh);
playload.UpdateInstance(instance.gpuInstanceIndex, ltw.matrix, in mesh);
}
}
}