2012-06-14 09:06:06 -04:00
// LightingThread.h
// Interfaces to the cLightingThread class representing the thread that processes requests for lighting
/*
Lighting is done on whole chunks . For each chunk to be lighted , the whole 3 x3 chunk area around it is read ,
then it is processed , so that the middle chunk area has valid lighting , and the lighting is copied into the ChunkMap .
Lighting is calculated in full char arrays instead of nibbles , so that accessing the arrays is fast .
Lighting is calculated in a flood - fill fashion :
1. Generate seeds from where the light spreads ( full skylight / light - emitting blocks )
2. For each seed :
- Spread the light 1 block in each of the 6 cardinal directions , if the blocktype allows
- If the recipient block has had lower lighting value than that being spread , make it a new seed
3. Repeat step 2 , until there are no more seeds
The seeds need two fast operations :
- Check if a block at [ x , y , z ] is already a seed
- Get the next seed in the row
2014-07-17 10:33:09 -04:00
For that reason it is stored in two arrays , one stores a bool saying a seed is in that position ,
2012-06-14 09:06:06 -04:00
the other is an array of seed coords , encoded as a single int .
Step 2 needs two separate storages for old seeds and new seeds , so there are two actual storages for that purpose ,
their content is swapped after each full step - 2 - cycle .
The thread has two queues of chunks that are to be lighted .
The first queue , m_Queue , is the only one that is publicly visible , chunks get queued there by external requests .
The second one , m_PostponedQueue , is for chunks that have been taken out of m_Queue and didn ' t have neighbors ready .
Chunks from m_PostponedQueue are moved back into m_Queue when their neighbors get valid , using the ChunkReady callback .
*/
# pragma once
2012-09-23 17:23:33 -04:00
# include "OSSupport/IsThread.h"
2012-06-14 09:06:06 -04:00
# include "ChunkDef.h"
2014-02-08 15:55:21 -05:00
# include "ChunkStay.h"
2012-06-14 09:06:06 -04:00
// fwd: "cWorld.h"
class cWorld ;
class cLightingThread :
public cIsThread
{
typedef cIsThread super ;
public :
cLightingThread ( void ) ;
~ cLightingThread ( ) ;
bool Start ( cWorld * a_World ) ;
void Stop ( void ) ;
2014-02-08 15:55:21 -05:00
/** Queues the entire chunk for lighting */
2014-10-20 16:55:07 -04:00
void QueueChunk ( int a_ChunkX , int a_ChunkZ , cChunkCoordCallback * a_CallbackAfter = nullptr ) ;
2012-06-14 09:06:06 -04:00
2014-02-08 15:55:21 -05:00
/** Blocks until the queue is empty or the thread is terminated */
2012-06-14 09:06:06 -04:00
void WaitForQueueEmpty ( void ) ;
size_t GetQueueLength ( void ) ;
protected :
2014-02-08 15:55:21 -05:00
class cLightingChunkStay :
public cChunkStay
2012-06-14 09:06:06 -04:00
{
2014-02-08 15:55:21 -05:00
public :
cLightingThread & m_LightingThread ;
int m_ChunkX ;
int m_ChunkZ ;
cChunkCoordCallback * m_CallbackAfter ;
2012-06-14 09:06:06 -04:00
2014-02-08 15:55:21 -05:00
cLightingChunkStay ( cLightingThread & a_LightingThread , int a_ChunkX , int a_ChunkZ , cChunkCoordCallback * a_CallbackAfter ) ;
protected :
2014-07-17 10:33:09 -04:00
virtual void OnChunkAvailable ( int a_ChunkX , int a_ChunkZ ) override
2014-02-23 15:23:35 -05:00
{
UNUSED ( a_ChunkX ) ;
UNUSED ( a_ChunkZ ) ;
}
2014-02-10 16:47:10 -05:00
virtual bool OnAllChunksAvailable ( void ) override ;
virtual void OnDisabled ( void ) override ;
2012-06-14 09:06:06 -04:00
} ;
2014-02-08 15:55:21 -05:00
typedef std : : list < cChunkStay * > cChunkStays ;
2012-06-14 09:06:06 -04:00
2014-02-08 15:55:21 -05:00
cWorld * m_World ;
/** The mutex to protect m_Queue and m_PendingQueue */
2012-06-14 09:06:06 -04:00
cCriticalSection m_CS ;
2014-02-08 15:55:21 -05:00
/** The ChunkStays that are loaded and are waiting to be lit. */
cChunkStays m_Queue ;
/** The ChunkStays that are waiting for load. Used for stopping the thread. */
cChunkStays m_PendingQueue ;
cEvent m_evtItemAdded ; // Set when queue is appended, or to stop the thread
cEvent m_evtQueueEmpty ; // Set when the queue gets empty
2014-04-11 18:04:50 -04:00
/** The highest block in the current 3x3 chunk data */
HEIGHTTYPE m_MaxHeight ;
2012-06-14 09:06:06 -04:00
// Buffers for the 3x3 chunk data
// These buffers alone are 1.7 MiB in size, therefore they cannot be located on the stack safely - some architectures may have only 1 MiB for stack, or even less
// Placing the buffers into the object means that this object can light chunks only in one thread!
// The blobs are XZY organized as a whole, instead of 3x3 XZY-organized subarrays ->
// -> This means data has to be scatterred when reading and gathered when writing!
static const int BlocksPerYLayer = cChunkDef : : Width * cChunkDef : : Width * 3 * 3 ;
BLOCKTYPE m_BlockTypes [ BlocksPerYLayer * cChunkDef : : Height ] ;
NIBBLETYPE m_BlockLight [ BlocksPerYLayer * cChunkDef : : Height ] ;
NIBBLETYPE m_SkyLight [ BlocksPerYLayer * cChunkDef : : Height ] ;
HEIGHTTYPE m_HeightMap [ BlocksPerYLayer ] ;
// Seed management (5.7 MiB)
// Two buffers, in each calc step one is set as input and the other as output, then in the next step they're swapped
// Each seed is represented twice in this structure - both as a "list" and as a "position".
// "list" allows fast traversal from seed to seed
// "position" allows fast checking if a coord is already a seed
unsigned char m_IsSeed1 [ BlocksPerYLayer * cChunkDef : : Height ] ;
unsigned int m_SeedIdx1 [ BlocksPerYLayer * cChunkDef : : Height ] ;
unsigned char m_IsSeed2 [ BlocksPerYLayer * cChunkDef : : Height ] ;
unsigned int m_SeedIdx2 [ BlocksPerYLayer * cChunkDef : : Height ] ;
int m_NumSeeds ;
virtual void Execute ( void ) override ;
2014-02-08 15:55:21 -05:00
/** Lights the entire chunk. If neighbor chunks don't exist, touches them and re-queues the chunk */
void LightChunk ( cLightingChunkStay & a_Item ) ;
2012-06-14 09:06:06 -04:00
2014-04-11 18:04:50 -04:00
/** Prepares m_BlockTypes and m_HeightMap data; zeroes out the light arrays */
void ReadChunks ( int a_ChunkX , int a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
2014-02-08 15:55:21 -05:00
/** Uses m_HeightMap to initialize the m_SkyLight[] data; fills in seeds for the skylight */
2012-06-14 09:06:06 -04:00
void PrepareSkyLight ( void ) ;
2014-02-08 15:55:21 -05:00
/** Uses m_BlockTypes to initialize the m_BlockLight[] data; fills in seeds for the blocklight */
2012-06-14 09:06:06 -04:00
void PrepareBlockLight ( void ) ;
2014-04-11 18:04:50 -04:00
/** Same as PrepareBlockLight(), but uses a different traversal scheme; possibly better perf cache-wise.
To be compared in perf benchmarks . */
void PrepareBlockLight2 ( void ) ;
2014-02-08 15:55:21 -05:00
/** Calculates light in the light array specified, using stored seeds */
2012-06-14 09:06:06 -04:00
void CalcLight ( NIBBLETYPE * a_Light ) ;
2014-02-08 15:55:21 -05:00
/** Does one step in the light calculation - one seed propagation and seed recalculation */
2012-06-14 09:06:06 -04:00
void CalcLightStep (
2014-07-17 10:33:09 -04:00
NIBBLETYPE * a_Light ,
2012-06-14 09:06:06 -04:00
int a_NumSeedsIn , unsigned char * a_IsSeedIn , unsigned int * a_SeedIdxIn ,
int & a_NumSeedsOut , unsigned char * a_IsSeedOut , unsigned int * a_SeedIdxOut
) ;
2014-02-08 15:55:21 -05:00
/** Compresses from 1-block-per-byte (faster calc) into 2-blocks-per-byte (MC storage): */
2012-06-14 09:06:06 -04:00
void CompressLight ( NIBBLETYPE * a_LightArray , NIBBLETYPE * a_ChunkLight ) ;
inline void PropagateLight (
2014-07-17 10:33:09 -04:00
NIBBLETYPE * a_Light ,
2014-03-30 14:02:30 -04:00
unsigned int a_SrcIdx , unsigned int a_DstIdx ,
2012-06-14 09:06:06 -04:00
int & a_NumSeedsOut , unsigned char * a_IsSeedOut , unsigned int * a_SeedIdxOut
)
{
2014-03-30 14:02:30 -04:00
ASSERT ( a_SrcIdx < ARRAYCOUNT ( m_SkyLight ) ) ;
ASSERT ( a_DstIdx < ARRAYCOUNT ( m_BlockTypes ) ) ;
2012-11-16 04:40:15 -05:00
2014-03-01 14:34:19 -05:00
if ( a_Light [ a_SrcIdx ] < = a_Light [ a_DstIdx ] + cBlockInfo : : GetSpreadLightFalloff ( m_BlockTypes [ a_DstIdx ] ) )
2012-06-14 09:06:06 -04:00
{
// We're not offering more light than the dest block already has
return ;
}
2014-03-01 14:34:19 -05:00
a_Light [ a_DstIdx ] = a_Light [ a_SrcIdx ] - cBlockInfo : : GetSpreadLightFalloff ( m_BlockTypes [ a_DstIdx ] ) ;
2012-06-14 09:06:06 -04:00
if ( ! a_IsSeedOut [ a_DstIdx ] )
{
a_IsSeedOut [ a_DstIdx ] = true ;
a_SeedIdxOut [ a_NumSeedsOut + + ] = a_DstIdx ;
}
}
2014-02-08 15:55:21 -05:00
/** Queues a chunkstay that has all of its chunks loaded.
Called by cLightingChunkStay when all of its chunks are loaded . */
void QueueChunkStay ( cLightingChunkStay & a_ChunkStay ) ;
2012-06-14 09:06:06 -04:00
} ;