Overcomplicated chunk loading

Implemented an overly complicated mechanism into LoadChunk to be able to passively wait for chunks to be loaded by another thread.
This commit is contained in:
Simon Lübeß
2021-03-03 19:00:34 +01:00
parent 4b032d2c7e
commit 08613b9e82
+85 -28
View File
@@ -8,6 +8,8 @@ using System.Collections;
using GlitchyEngine.ImGui;
using ImGui;
using System.IO;
using GlitchyEngine;
using GlitchyEngine.Threading;
namespace Sandbox.VoxelFun
{
@@ -30,6 +32,9 @@ namespace Sandbox.VoxelFun
Dictionary<Int32_3, Chunk> _chunks = new .() ~ DeleteDictionaryAndValues!(_);
private HashSet<Int32_3> _generatingChunks = new .() ~ delete _;
private Monitor _generatingChunksLock = new .() ~ delete _;
public Texture2D Texture ~ _?.ReleaseRef();
public Effect TextureEffect ~ _?.ReleaseRef();
@@ -48,6 +53,11 @@ namespace Sandbox.VoxelFun
public World World => _world;
public delegate void ChunkLoadedHandler(Int32_3 chunkCoordinate);
Event<ChunkLoadedHandler> _chunkLoaded ~ _.Dispose();
Monitor _chunkLoadedLock = new Monitor() ~ delete _;
public this(GraphicsContext context, VertexLayout vertexLayout, World world)
{
_context = context..AddRef();
@@ -101,12 +111,26 @@ namespace Sandbox.VoxelFun
/**
* Loads and returns the chunk with the given coordinate. If the chunk doesn't exist it will be generated.
* @param chunkCoordinate The coordinate of the chunk to load.
* @param noBlocking If set to true this method will not wait if the chunk cannot be loaded right now (because it is currently being loaded by another thread);
* otherwise the method will block until the chunk has been loaded.
* @returns The loaded chunk. If noBlocking is set to true and the chunk cannot be obtained right now the return value will be null.
*/
public Chunk LoadChunk(Int32_3 chunkCoordinate)
public Chunk LoadChunk(Int32_3 chunkCoordinate, bool noBlocking = false)
{
Chunk chunk = GetChunk(chunkCoordinate);
if(chunk == null)
{
// Add chunk coordinate to generating list
// If it could be added, we have to generate it ourselves, otherwise we have to wait for the other thread to finish.
bool alreadyInList = false;
using(_generatingChunksLock.Enter())
{
alreadyInList = !_generatingChunks.Add(chunkCoordinate);
}
if(!alreadyInList)
{
chunk = new Chunk();
chunk.Position = chunkCoordinate * .(VoxelChunk.SizeX, VoxelChunk.SizeY, VoxelChunk.SizeZ);
@@ -122,7 +146,56 @@ namespace Sandbox.VoxelFun
using(chunkListLock.Enter())
{
_chunks.Add(chunkCoordinate, chunk);
if(!_chunks.TryAdd(chunkCoordinate, chunk))
{
chunk = LoadChunk(chunkCoordinate);
}
}
using(_generatingChunksLock.Enter())
{
_generatingChunks.Remove(chunkCoordinate);
}
// Raise chunk loaded event (this will wake up all calls of LoadChunk waiting for our chunk)
using(_chunkLoadedLock.Enter())
{
_chunkLoaded.Invoke(chunkCoordinate);
}
}
else if(!noBlocking)
{
Semaphore semaphore = new Semaphore(0);
defer delete semaphore;
// event handler will increase semaphore as soon as our requested chunk is loaded.
ChunkLoadedHandler eventHandler = scope (coordinate) =>
{
if(coordinate == chunkCoordinate)
{
semaphore.Unlock();
}
};
// register event listener
using(_chunkLoadedLock.Enter())
{
_chunkLoaded.Add(eventHandler);
}
Log.ClientLogger.Trace($"Sleeping until chunk ({chunkCoordinate}) has been generated...");
// wait for semaphore to be released (in the event handler)
semaphore.Lock();
// unregister event handler
using(_chunkLoadedLock.Enter())
{
_chunkLoaded.Remove(eventHandler);
}
// retry loading the chunk
chunk = LoadChunk(chunkCoordinate);
}
}
@@ -195,46 +268,30 @@ namespace Sandbox.VoxelFun
}
}
int x = 0;
int z = 0;
Int32_3 chunkCoordinate = (Int32_3)chunkPos;
int x = 0, z = 0;
for(int r = 1; r < _viewDistance; r++)
{
for(; x < r; x++)
for(; x < r; x++, chunkCoordinate.X++)
{
Int32_3 chunkCoordinate = (Int32_3)chunkPos + .(x, 0, z);
LoadChunk(chunkCoordinate);
LoadChunk(chunkCoordinate, true);
}
for(; z < r; z++)
for(; z < r; z++, chunkCoordinate.Z++)
{
Int32_3 chunkCoordinate = (Int32_3)chunkPos + .(x, 0, z);
LoadChunk(chunkCoordinate);
LoadChunk(chunkCoordinate, true);
}
for(; x > -r; x--)
for(; x > -r; x--, chunkCoordinate.X--)
{
Int32_3 chunkCoordinate = (Int32_3)chunkPos + .(x, 0, z);
LoadChunk(chunkCoordinate);
LoadChunk(chunkCoordinate, true);
}
for(; z > -r; z--)
for(; z > -r; z--, chunkCoordinate.Z--)
{
Int32_3 chunkCoordinate = (Int32_3)chunkPos + .(x, 0, z);
LoadChunk(chunkCoordinate);
LoadChunk(chunkCoordinate, true);
}
}
/*
for(int x = -_viewDistance; x < _viewDistance; x++)
//for(int y = -_viewDistance; y < _viewDistance; y++)
for(int z = -_viewDistance; z < _viewDistance; z++)
{
int y = 0;
Int32_3 chunkCoordinate = (Int32_3)chunkPos + .(x, y, z);
LoadChunk(chunkCoordinate);
}
*/
}
Result<void> LoadChunkFromFile(Int32_3 chunkCoordinate, Chunk outChunk)