Skip to content

Commit

Permalink
Merge pull request #44 from StarCoreSE/advanced-blockculling
Browse files Browse the repository at this point in the history
optimizationsbymuzz
  • Loading branch information
InvalidArgument3 authored Aug 14, 2024
2 parents 524ebce + 0164cea commit 3508d42
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 46 deletions.
13 changes: 6 additions & 7 deletions BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Action> _taskQueue = new Queue<Action>();

Expand Down Expand Up @@ -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);
Expand All @@ -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());
}
Expand Down
132 changes: 93 additions & 39 deletions BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using VRage.ModAPI;
using VRage.Utils;
using VRageMath;
using VRageMath.Spatial;

namespace Scripts.BlockCulling
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand All @@ -218,6 +222,7 @@ private void UpdateGridCulling()
foreach (var block in _culledBlocks[grid])
{
if (!block.Visible) _queuedBlockUnculls.Add(block);

}
}
}
Expand Down Expand Up @@ -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)
Expand All @@ -256,7 +262,7 @@ private void OnEntityAdd(IMyEntity entity)
HashSet<IMyCubeBlock> gridBlocks = _culledBlocks.ContainsKey(grid)
? _culledBlocks[grid]
: (_culledBlocks[grid] = new HashSet<IMyCubeBlock>());

if (gridBlocks.Count > 0)
{
ThreadSafeLog.EnqueueMessageDebug($"Grid {grid.EntityId} already exists. Re-initializing...");
Expand Down Expand Up @@ -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<IMySlimBlock>();
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)
{
Expand All @@ -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<IMySlimBlock> blockSlimNeighbors)
{
if (blockSlimNeighbors == null) blockSlimNeighbors = new HashSet<IMySlimBlock>();
if (blockSlimNeighbors == null)
blockSlimNeighbors = new HashSet<IMySlimBlock>();

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<IMySlimBlock> slimNeighborsContributor = new List<IMySlimBlock>();
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;
}
Expand Down Expand Up @@ -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;
}
}
Expand Down

0 comments on commit 3508d42

Please sign in to comment.