Refactor resource management and update project configs

- Use `using` for MeshNode disposal in MeshAssetHandler
- Switch to `ref` UnsafeList in meshlet hierarchy methods for perf
- Ensure proper disposal of UnsafeList<int> and TempBinaryNode
- Add launchSettings.json for Ghost.Editor.Core debugging
- Update GhostEngine.slnx with platform mappings for Editor.Core
- Remove MHP_ENABLE_SAFETY_CHECKS from Debug|AnyCPU in csproj
This commit is contained in:
2026-05-07 00:23:51 +09:00
parent 744b058e6a
commit d052ca848f
5 changed files with 134 additions and 115 deletions

View File

@@ -261,17 +261,16 @@ internal class MeshAssetHandler : IImportableAssetHandler, IPackableAssetHandler
return Result.Failure<ImportedSubAsset[]>("Source file does not exist."); return Result.Failure<ImportedSubAsset[]>("Source file does not exist.");
} }
var meshSettings = ResolveSettings(sourcePath, settings);
var root = new MeshNode();
try try
{ {
var heapSize1 = AllocationManager.TotalAllocatedMemory; var meshSettings = ResolveSettings(sourcePath, settings);
using var root = new MeshNode();
var parseJob = new MeshParsingJob(root, sourcePath, AllocationHandle.Persistent, meshSettings); var parseJob = new MeshParsingJob(root, sourcePath, AllocationHandle.Persistent, meshSettings);
var context = default(Misaki.HighPerformance.Jobs.JobExecutionContext); var context = default(Misaki.HighPerformance.Jobs.JobExecutionContext);
parseJob.Execute(in context); parseJob.Execute(in context);
var heapSize2 = AllocationManager.TotalAllocatedMemory;
var manifest = new ModelManifest var manifest = new ModelManifest
{ {
AssetId = id, AssetId = id,
@@ -291,10 +290,6 @@ internal class MeshAssetHandler : IImportableAssetHandler, IPackableAssetHandler
{ {
return Result.Failure<ImportedSubAsset[]>($"Failed to import mesh asset: {ex.Message}"); return Result.Failure<ImportedSubAsset[]>($"Failed to import mesh asset: {ex.Message}");
} }
finally
{
root.Dispose();
}
} }
public ValueTask<Result> ExportAsync(string assetPath, string targetPath, IAssetExportOptions? options, CancellationToken token = default) public ValueTask<Result> ExportAsync(string assetPath, string targetPath, IAssetExportOptions? options, CancellationToken token = default)

View File

@@ -864,7 +864,7 @@ internal static unsafe partial class MeshProcessor
public int meshletIndex; public int meshletIndex;
} }
private static int BuildBinaryTree(UnsafeList<TempBinaryNode> nodes, UnsafeArray<int> meshletIndices, int start, int end, ReadOnlySpan<Meshlet> meshlets) private static int BuildBinaryTree(ref UnsafeList<TempBinaryNode> nodes, UnsafeArray<int> meshletIndices, int start, int end, ReadOnlySpan<Meshlet> meshlets)
{ {
if (start == end - 1) if (start == end - 1)
{ {
@@ -921,8 +921,8 @@ internal static unsafe partial class MeshProcessor
mid = start + (end - start) / 2; mid = start + (end - start) / 2;
} }
var left = BuildBinaryTree(nodes, meshletIndices, start, mid, meshlets); var left = BuildBinaryTree(ref nodes, meshletIndices, start, mid, meshlets);
var right = BuildBinaryTree(nodes, meshletIndices, mid, end, meshlets); var right = BuildBinaryTree(ref nodes, meshletIndices, mid, end, meshlets);
var leftNode = nodes[left]; var leftNode = nodes[left];
var rightNode = nodes[right]; var rightNode = nodes[right];
@@ -945,7 +945,7 @@ internal static unsafe partial class MeshProcessor
return internalNodeIndex; return internalNodeIndex;
} }
private static void GatherChildren(UnsafeList<TempBinaryNode> binaryNodes, int nodeIndex, UnsafeList<int> gathered) private static void GatherChildren(UnsafeList<TempBinaryNode> binaryNodes, int nodeIndex, ref UnsafeList<int> gathered)
{ {
gathered.Clear(); gathered.Clear();
var node = binaryNodes[nodeIndex]; var node = binaryNodes[nodeIndex];
@@ -992,80 +992,88 @@ internal static unsafe partial class MeshProcessor
return -1; return -1;
} }
using var gathered = new UnsafeList<int>(4, AllocationHandle.Persistent); var gathered = new UnsafeList<int>(4, AllocationHandle.Persistent);
GatherChildren(binaryNodes, binaryNodeIndex, gathered);
var bvhNode = new MeshletHierarchyNode(); try
var minX = new float4(float.PositiveInfinity);
var minY = new float4(float.PositiveInfinity);
var minZ = new float4(float.PositiveInfinity);
var maxX = new float4(float.NegativeInfinity);
var maxY = new float4(float.NegativeInfinity);
var maxZ = new float4(float.NegativeInfinity);
var maxParentError = new float4(0);
var nodeData = new uint4(0xFFFFFFFF);
var outNodeIndex = hierarchyNodes.Count;
hierarchyNodes.Add(bvhNode); // Reserve slot
for (var i = 0; i < gathered.Count; i++)
{ {
var childIdx = gathered[i]; GatherChildren(binaryNodes, binaryNodeIndex, ref gathered);
var childNode = binaryNodes[childIdx];
uint data = 0; var bvhNode = new MeshletHierarchyNode();
if (childNode.leftChild == -1)
var minX = new float4(float.PositiveInfinity);
var minY = new float4(float.PositiveInfinity);
var minZ = new float4(float.PositiveInfinity);
var maxX = new float4(float.NegativeInfinity);
var maxY = new float4(float.NegativeInfinity);
var maxZ = new float4(float.NegativeInfinity);
var maxParentError = new float4(0);
var nodeData = new uint4(0xFFFFFFFF);
var outNodeIndex = hierarchyNodes.Count;
hierarchyNodes.Add(bvhNode); // Reserve slot
for (var i = 0; i < gathered.Count; i++)
{ {
data = (uint)childNode.meshletIndex; var childIdx = gathered[i];
} var childNode = binaryNodes[childIdx];
else
{ uint data = 0;
var child4AryIndex = CollapseTo4Ary(binaryNodes, childIdx, hierarchyNodes); if (childNode.leftChild == -1)
data = (1u << 31) | (uint)child4AryIndex; {
data = (uint)childNode.meshletIndex;
}
else
{
var child4AryIndex = CollapseTo4Ary(binaryNodes, childIdx, hierarchyNodes);
data = (1u << 31) | (uint)child4AryIndex;
}
if (i == 0)
{
minX.x = childNode.bounds.Min.x; minY.x = childNode.bounds.Min.y; minZ.x = childNode.bounds.Min.z;
maxX.x = childNode.bounds.Max.x; maxY.x = childNode.bounds.Max.y; maxZ.x = childNode.bounds.Max.z;
maxParentError.x = childNode.maxParentError;
nodeData.x = data;
}
else if (i == 1)
{
minX.y = childNode.bounds.Min.x; minY.y = childNode.bounds.Min.y; minZ.y = childNode.bounds.Min.z;
maxX.y = childNode.bounds.Max.x; maxY.y = childNode.bounds.Max.y; maxZ.y = childNode.bounds.Max.z;
maxParentError.y = childNode.maxParentError;
nodeData.y = data;
}
else if (i == 2)
{
minX.z = childNode.bounds.Min.x; minY.z = childNode.bounds.Min.y; minZ.z = childNode.bounds.Min.z;
maxX.z = childNode.bounds.Max.x; maxY.z = childNode.bounds.Max.y; maxZ.z = childNode.bounds.Max.z;
maxParentError.z = childNode.maxParentError;
nodeData.z = data;
}
else if (i == 3)
{
minX.w = childNode.bounds.Min.x; minY.w = childNode.bounds.Min.y; minZ.w = childNode.bounds.Min.z;
maxX.w = childNode.bounds.Max.x; maxY.w = childNode.bounds.Max.y; maxZ.w = childNode.bounds.Max.z;
maxParentError.w = childNode.maxParentError;
nodeData.w = data;
}
} }
if (i == 0) bvhNode.minX = minX;
{ bvhNode.minY = minY;
minX.x = childNode.bounds.Min.x; minY.x = childNode.bounds.Min.y; minZ.x = childNode.bounds.Min.z; bvhNode.minZ = minZ;
maxX.x = childNode.bounds.Max.x; maxY.x = childNode.bounds.Max.y; maxZ.x = childNode.bounds.Max.z; bvhNode.maxX = maxX;
maxParentError.x = childNode.maxParentError; bvhNode.maxY = maxY;
nodeData.x = data; bvhNode.maxZ = maxZ;
} bvhNode.maxParentError = maxParentError;
else if (i == 1) bvhNode.nodeData = nodeData;
{
minX.y = childNode.bounds.Min.x; minY.y = childNode.bounds.Min.y; minZ.y = childNode.bounds.Min.z; hierarchyNodes[outNodeIndex] = bvhNode;
maxX.y = childNode.bounds.Max.x; maxY.y = childNode.bounds.Max.y; maxZ.y = childNode.bounds.Max.z; return outNodeIndex;
maxParentError.y = childNode.maxParentError; }
nodeData.y = data; finally
} {
else if (i == 2) gathered.Dispose();
{
minX.z = childNode.bounds.Min.x; minY.z = childNode.bounds.Min.y; minZ.z = childNode.bounds.Min.z;
maxX.z = childNode.bounds.Max.x; maxY.z = childNode.bounds.Max.y; maxZ.z = childNode.bounds.Max.z;
maxParentError.z = childNode.maxParentError;
nodeData.z = data;
}
else if (i == 3)
{
minX.w = childNode.bounds.Min.x; minY.w = childNode.bounds.Min.y; minZ.w = childNode.bounds.Min.z;
maxX.w = childNode.bounds.Max.x; maxY.w = childNode.bounds.Max.y; maxZ.w = childNode.bounds.Max.z;
maxParentError.w = childNode.maxParentError;
nodeData.w = data;
}
} }
bvhNode.minX = minX;
bvhNode.minY = minY;
bvhNode.minZ = minZ;
bvhNode.maxX = maxX;
bvhNode.maxY = maxY;
bvhNode.maxZ = maxZ;
bvhNode.maxParentError = maxParentError;
bvhNode.nodeData = nodeData;
hierarchyNodes[outNodeIndex] = bvhNode;
return outNodeIndex;
} }
/// <summary> /// <summary>
@@ -1082,43 +1090,49 @@ internal static unsafe partial class MeshProcessor
meshletIndices[i] = i; meshletIndices[i] = i;
} }
var meshletsSpan = new ReadOnlySpan<Meshlet>(meshletData.meshlets.GetUnsafePtr(), meshletData.meshlets.Count); var binaryNodes = new UnsafeList<TempBinaryNode>(meshletData.meshletCount * 2, AllocationHandle.Persistent);
using var binaryNodes = new UnsafeList<TempBinaryNode>(meshletData.meshletCount * 2, AllocationHandle.Persistent); try
var rootIndex = BuildBinaryTree(binaryNodes, meshletIndices, 0, meshletIndices.Length, meshletsSpan);
if (!meshletData.hierarchyNodes.IsCreated)
{ {
meshletData.hierarchyNodes = new UnsafeList<MeshletHierarchyNode>(meshletData.meshletCount, allocationHandle); var rootIndex = BuildBinaryTree(ref binaryNodes, meshletIndices, 0, meshletIndices.Length, meshletData.meshlets);
if (!meshletData.hierarchyNodes.IsCreated)
{
meshletData.hierarchyNodes = new UnsafeList<MeshletHierarchyNode>(meshletData.meshletCount, allocationHandle);
}
if (binaryNodes[rootIndex].leftChild == -1)
{
var bvhNode = new MeshletHierarchyNode();
bvhNode.minX = new float4(float.PositiveInfinity);
bvhNode.minY = new float4(float.PositiveInfinity);
bvhNode.minZ = new float4(float.PositiveInfinity);
bvhNode.maxX = new float4(float.NegativeInfinity);
bvhNode.maxY = new float4(float.NegativeInfinity);
bvhNode.maxZ = new float4(float.NegativeInfinity);
bvhNode.maxParentError = new float4(0);
bvhNode.nodeData = new uint4(0xFFFFFFFF);
var childNode = binaryNodes[rootIndex];
bvhNode.minX.x = childNode.bounds.Min.x;
bvhNode.minY.x = childNode.bounds.Min.y;
bvhNode.minZ.x = childNode.bounds.Min.z;
bvhNode.maxX.x = childNode.bounds.Max.x;
bvhNode.maxY.x = childNode.bounds.Max.y;
bvhNode.maxZ.x = childNode.bounds.Max.z;
bvhNode.maxParentError.x = childNode.maxParentError;
bvhNode.nodeData.x = (uint)childNode.meshletIndex;
meshletData.hierarchyNodes.Add(bvhNode);
}
else
{
CollapseTo4Ary(binaryNodes, rootIndex, meshletData.hierarchyNodes);
}
} }
finally
if (binaryNodes[rootIndex].leftChild == -1)
{ {
var bvhNode = new MeshletHierarchyNode(); binaryNodes.Dispose();
bvhNode.minX = new float4(float.PositiveInfinity);
bvhNode.minY = new float4(float.PositiveInfinity);
bvhNode.minZ = new float4(float.PositiveInfinity);
bvhNode.maxX = new float4(float.NegativeInfinity);
bvhNode.maxY = new float4(float.NegativeInfinity);
bvhNode.maxZ = new float4(float.NegativeInfinity);
bvhNode.maxParentError = new float4(0);
bvhNode.nodeData = new uint4(0xFFFFFFFF);
var childNode = binaryNodes[rootIndex];
bvhNode.minX.x = childNode.bounds.Min.x;
bvhNode.minY.x = childNode.bounds.Min.y;
bvhNode.minZ.x = childNode.bounds.Min.z;
bvhNode.maxX.x = childNode.bounds.Max.x;
bvhNode.maxY.x = childNode.bounds.Max.y;
bvhNode.maxZ.x = childNode.bounds.Max.z;
bvhNode.maxParentError.x = childNode.maxParentError;
bvhNode.nodeData.x = (uint)childNode.meshletIndex;
meshletData.hierarchyNodes.Add(bvhNode);
}
else
{
CollapseTo4Ary(binaryNodes, rootIndex, meshletData.hierarchyNodes);
} }
} }
} }

View File

@@ -0,0 +1,8 @@
{
"profiles": {
"Ghost.Editor.Core": {
"commandName": "Project",
"debugEngines": "managed"
}
}
}

View File

@@ -7,7 +7,9 @@
<Folder Name="/Editor/"> <Folder Name="/Editor/">
<Project Path="Editor/Ghost.DSL/Ghost.DSL.csproj" /> <Project Path="Editor/Ghost.DSL/Ghost.DSL.csproj" />
<Project Path="Editor/Ghost.Editor.Core/Ghost.Editor.Core.csproj"> <Project Path="Editor/Ghost.Editor.Core/Ghost.Editor.Core.csproj">
<Platform Solution="Debug|x64" Project="x64" /> <Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
<Platform Solution="*|x86" Project="x86" />
</Project> </Project>
<Project Path="Editor/Ghost.Editor/Ghost.Editor.csproj"> <Project Path="Editor/Ghost.Editor/Ghost.Editor.csproj">
<Platform Solution="*|ARM64" Project="ARM64" /> <Platform Solution="*|ARM64" Project="ARM64" />

View File

@@ -8,7 +8,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>$(DefineConstants);MHP_ENABLE_SAFETY_CHECKS;MHP_ENABLE_MIMALLOC;MHP_FASTMATH</DefineConstants> <DefineConstants>$(DefineConstants);MHP_ENABLE_MIMALLOC;MHP_FASTMATH</DefineConstants>
<IsAotCompatible>True</IsAotCompatible> <IsAotCompatible>True</IsAotCompatible>
<IsTrimmable>True</IsTrimmable> <IsTrimmable>True</IsTrimmable>
</PropertyGroup> </PropertyGroup>