diff --git a/BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Helpers.cs b/BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Helpers.cs index 9665058..67a356e 100644 --- a/BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Helpers.cs +++ b/BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Helpers.cs @@ -170,7 +170,7 @@ public static void Update() public class TaskScheduler { - private const int MAX_TASKS_PER_FRAME = 50; //at 10 it takes 5.5 minutes on a 33k reactor grid. we can speed that up + private const int MAX_TASKS_PER_FRAME = 10; //at 10 it takes 5.5 minutes on a 33k reactor grid. we can speed that up // I sped it up -Muzz private readonly object _lock = new object(); private readonly Queue _taskQueue = new Queue(); @@ -216,13 +216,13 @@ public bool TryGetEntity(out TEntity entity) public class PerformanceMonitor { - private const int REPORT_INTERVAL = 1000; + private const int REPORT_INTERVAL = 600; private int _tickCount; private long _syncTimeTotal; private long _asyncTimeTotal; private int _tasksProcessedTotal; - private long _blocksCulledTotal; // New! - private long _blocksUnculledTotal; // New! + private long _blocksCulledTotal; + private long _blocksUnculledTotal; public void RecordSyncOperation(long milliseconds) => Interlocked.Add(ref _syncTimeTotal, milliseconds); public void RecordAsyncOperation(long milliseconds) => Interlocked.Add(ref _asyncTimeTotal, milliseconds); @@ -242,14 +242,13 @@ public void Update() private void GenerateReport() { - // Using StringBuilder instead of string concatenation reduces memory allocations StringBuilder report = new StringBuilder(); report.AppendLine($"Performance Report (Last {REPORT_INTERVAL} ticks):") .AppendLine($"Avg Sync Time: {(double)_syncTimeTotal / REPORT_INTERVAL:F2}ms") .AppendLine($"Avg Async Time: {(double)_asyncTimeTotal / REPORT_INTERVAL:F2}ms") .AppendLine($"Tasks/Second: {(double)_tasksProcessedTotal / (REPORT_INTERVAL / 60.0):F2}") - .AppendLine($"Blocks Culled: {Interlocked.Read(ref _blocksCulledTotal):F2}") - .AppendLine($"Blocks Unculled: {Interlocked.Read(ref _blocksUnculledTotal):F2}"); + .AppendLine($"Blocks Culled: {_blocksCulledTotal:F2}") + .AppendLine($"Blocks Unculled: {_blocksUnculledTotal:F2}"); ThreadSafeLog.EnqueueMessage(report.ToString()); } diff --git a/BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Session.cs b/BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Session.cs index 9c4544d..715b38b 100644 --- a/BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Session.cs +++ b/BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Session.cs @@ -12,6 +12,7 @@ using VRage.ModAPI; using VRage.Utils; using VRageMath; +using VRageMath.Spatial; namespace Scripts.BlockCulling { @@ -97,28 +98,23 @@ protected override void UnloadData() public override void UpdateBeforeSimulation() { - - if (modConfig == null || !modConfig.ModEnabled) return; + if (modConfig == null || !modConfig.ModEnabled) + return; try { var stopwatch = Stopwatch.StartNew(); _taskScheduler?.ProcessTasks(); - if (ThreadSafeLog.EnableDebugLogging) - { - ThreadSafeLog.ProcessLogQueue(); - } + ThreadSafeLog.ProcessLogQueue(); stopwatch.Stop(); _performanceMonitor?.RecordSyncOperation(stopwatch.ElapsedMilliseconds); _performanceMonitor?.Update(); - if (ThreadSafeLog.EnableDebugLogging) + + _reportCounter++; + if (_reportCounter >= REPORT_INTERVAL) { - _reportCounter++; - if (_reportCounter >= REPORT_INTERVAL) - { - GenerateCulledBlocksReport(); - _reportCounter = 0; - } + GenerateCulledBlocksReport(); + _reportCounter = 0; } } catch (Exception e) @@ -185,18 +181,26 @@ public override void UpdateAfterSimulation() private void ProcessQueuedBlockCulls() { int maxProcessPerTick = MaxBlocksCulledPerTick; - int cullCount = Math.Min(_queuedBlockCulls.Count, maxProcessPerTick / 2); + int cullCount = Math.Min(_queuedBlockCulls.Count, maxProcessPerTick); + for (int i = 0; i < cullCount; i++) { _queuedBlockCulls[i].Visible = false; } _queuedBlockCulls.RemoveRange(0, cullCount); + int uncullCount = Math.Min(_queuedBlockUnculls.Count, maxProcessPerTick - cullCount); for (int i = 0; i < uncullCount; i++) { _queuedBlockUnculls[i].Visible = true; } _queuedBlockUnculls.RemoveRange(0, uncullCount); + + if (ThreadSafeLog.EnableDebugLogging) + { + _performanceMonitor.RecordBlocksCulled(cullCount); + _performanceMonitor.RecordBlocksUnculled(uncullCount); + } } private void UpdateGridCulling() @@ -218,6 +222,7 @@ private void UpdateGridCulling() foreach (var block in _culledBlocks[grid]) { if (!block.Visible) _queuedBlockUnculls.Add(block); + } } } @@ -245,7 +250,8 @@ private void GenerateCulledBlocksReport() } } report.AppendFormat("TOTAL: {0} blocks across {1} grids", totalCulledBlocks, totalGrids); - ThreadSafeLog.EnqueueMessageDebug(report.ToString()); + _performanceMonitor.RecordBlocksCulled(totalCulledBlocks); + ThreadSafeLog.EnqueueMessageDebug(report.ToString()); } private void OnEntityAdd(IMyEntity entity) @@ -256,7 +262,7 @@ private void OnEntityAdd(IMyEntity entity) HashSet gridBlocks = _culledBlocks.ContainsKey(grid) ? _culledBlocks[grid] : (_culledBlocks[grid] = new HashSet()); - + if (gridBlocks.Count > 0) { ThreadSafeLog.EnqueueMessageDebug($"Grid {grid.EntityId} already exists. Re-initializing..."); @@ -310,21 +316,31 @@ private void SetTransparencyAsync(SafeSlimBlockRef slimBlockRef, bool recursive try { IMySlimBlock slimBlock; - if (!slimBlockRef.TryGetSlimBlock(out slimBlock)) return; + if (!slimBlockRef.TryGetSlimBlock(out slimBlock)) + return; + IMyCubeBlock block = slimBlock.FatBlock; - if (!recursive && block == null) return; + if (!recursive && block == null) + return; + var blockSlimNeighbors = new HashSet(); bool shouldCullBlock = BlockEligibleForCulling(slimBlock, ref blockSlimNeighbors); + MainThreadDispatcher.Enqueue(() => { if (block?.CubeGrid != null) { - if (!_unCulledGrids.Contains(block.CubeGrid)) block.Visible = !shouldCullBlock; - if (shouldCullBlock) _culledBlocks[block.CubeGrid].Add(block); - else _culledBlocks[block.CubeGrid].Remove(block); + if (!_unCulledGrids.Contains(block.CubeGrid)) + block.Visible = !shouldCullBlock; + + if (shouldCullBlock) + _culledBlocks[block.CubeGrid].Add(block); + else + _culledBlocks[block.CubeGrid].Remove(block); } }); - if (recursive) + + if (recursive && !shouldCullBlock) { foreach (var slimBlockN in blockSlimNeighbors) { @@ -336,31 +352,55 @@ private void SetTransparencyAsync(SafeSlimBlockRef slimBlockRef, bool recursive { ThreadSafeLog.EnqueueMessage($"Exception in SetTransparencyAsync: {ex}"); } + stopwatch.Stop(); - _performanceMonitor.RecordAsyncOperation(stopwatch.ElapsedMilliseconds); + if (ThreadSafeLog.EnableDebugLogging) + { + _performanceMonitor.RecordAsyncOperation(stopwatch.ElapsedMilliseconds); + _performanceMonitor.RecordTasksProcessed(1); + } }); } private bool BlockEligibleForCulling(IMySlimBlock slimBlock, ref HashSet blockSlimNeighbors) { - if (blockSlimNeighbors == null) blockSlimNeighbors = new HashSet(); + if (blockSlimNeighbors == null) + blockSlimNeighbors = new HashSet(); + foreach (var blockPos in GetSurfacePositions(slimBlock)) { IMySlimBlock neighbor = slimBlock.CubeGrid.GetCubeBlock(blockPos); - if (neighbor != null) blockSlimNeighbors.Add(neighbor); + if (neighbor != null) + blockSlimNeighbors.Add(neighbor); } + IMyCubeBlock block = slimBlock?.FatBlock; - if (block == null) return false; + if (block == null) + return false; + List slimNeighborsContributor = new List(); - int blockFaceCount = GetBlockFaceCount(block); // Cache the result + int blockFaceCount = GetBlockFaceCount(block); + foreach (var slimNeighbor in blockSlimNeighbors) { int surroundingBlockCount = 0; foreach (Vector3I surfacePosition in GetSurfacePositions(slimNeighbor)) - if (slimNeighbor.CubeGrid.CubeExists(surfacePosition)) surroundingBlockCount++; - if (surroundingBlockCount != GetBlockFaceCount(slimNeighbor) && !ConnectsWithFullMountPoint(slimBlock, slimNeighbor)) return false; - if (slimNeighbor.FatBlock == null || !(slimNeighbor.FatBlock is IMyLightingBlock || slimNeighbor.BlockDefinition.Id.SubtypeName.Contains("Window"))) slimNeighborsContributor.Add(slimNeighbor); + if (slimNeighbor.CubeGrid.CubeExists(surfacePosition)) + surroundingBlockCount++; + + if (surroundingBlockCount != GetBlockFaceCount(slimNeighbor) && !ConnectsWithFullMountPoint(slimBlock, slimNeighbor)) + return false; + + if (slimNeighbor.FatBlock == null || + !(slimNeighbor.FatBlock is IMyDoor || + slimNeighbor.FatBlock is IMyAirtightHangarDoor || + slimNeighbor.FatBlock is IMyAirtightSlideDoor || + slimNeighbor.FatBlock is IMyAdvancedDoor || + slimNeighbor.FatBlock is IMyLightingBlock || + slimNeighbor.BlockDefinition.Id.SubtypeName.Contains("Window"))) + slimNeighborsContributor.Add(slimNeighbor); } + bool result = slimNeighborsContributor.Count == blockFaceCount; return result; } @@ -391,26 +431,40 @@ private int GetBlockFaceCount(IMyCubeBlock block) } private Vector3I[] _surfacePositions = new Vector3I[6]; - private Vector3I[] GetSurfacePositions(IMySlimBlock block) { Vector3I blockSize = Vector3I.Abs(block.Max - block.Min) + Vector3I.One; int faceCount = 2 * (blockSize.X * blockSize.Y + blockSize.Y * blockSize.Z + blockSize.Z * blockSize.X); - if (_surfacePositions.Length != faceCount) _surfacePositions = new Vector3I[faceCount]; + + if (_surfacePositions.Length != faceCount) + _surfacePositions = new Vector3I[faceCount]; + int idx = 0; - for (int x = -1; x <= blockSize.X; x++) + for (int dim = 0; dim < 3; dim++) { - for (int y = -1; y <= blockSize.Y; y++) + for (int i = 0; i < 2; i++) { - for (int z = -1; z <= blockSize.Z; z++) + Vector3I facePos = block.Min; + int faceDir = i == 0 ? -1 : blockSize[dim]; + facePos[dim] += faceDir; + + Vector3I faceSize = blockSize; + faceSize[dim] = 1; + + for (int x = 0; x < faceSize.X; x++) { - bool xLimit = (x == -1 || x == blockSize.X); - bool yLimit = (y == -1 || y == blockSize.Y); - bool zLimit = (z == -1 || z == blockSize.Z); - if ((!xLimit && yLimit ^ zLimit) || (xLimit && !(yLimit || zLimit))) _surfacePositions[idx++] = block.Min + new Vector3I(x, y, z); + for (int y = 0; y < faceSize.Y; y++) + { + for (int z = 0; z < faceSize.Z; z++) + { + Vector3I surfacePos = facePos + new Vector3I(x, y, z); + _surfacePositions[idx++] = surfacePos; + } + } } } } + return _surfacePositions; } }