Am Samstag, 28. Februar 2009 20:42:00 schrieb Dennis Schridde:
> I played a bit with implementing a better memory pool for effects and this
> is what came out.
Updated, cleaned up, commented, final patch attached.
Will commit if no objections.

--DevU

diff --git a/src/effects.c b/src/effects.c
index 49183f7..d90b6ca 100644
--- a/src/effects.c
+++ b/src/effects.c
@@ -71,8 +71,6 @@
 #include "game.h"
 
 
-#define MAX_EFFECTS	2500
-
 #define	GRAVITON_GRAVITY	((float)-800)
 #define	EFFECT_X_FLIP		0x1
 #define	EFFECT_Y_FLIP		0x2
@@ -165,19 +163,44 @@
 #define	MAX_SHOCKWAVE_SIZE				500
 
 
-/* Our list of all game world effects */
-static EFFECT        asEffectsList[MAX_EFFECTS];
-static EFFECT_STATUS effectStatus[MAX_EFFECTS];
+/*! Number of effects in one chunk */
+#define EFFECT_CHUNKS_SIZE 10000
+
+
+/*! A memory chunk of effects */
+typedef struct _EffectChunk EffectChunk;
+
+struct _EffectChunk {
+	EFFECT effects[EFFECT_CHUNKS_SIZE]; //!< Chunk of effects
+	EffectChunk *next; //!< Next element in list
+};
+
+
+/*! List containing all allocated effect chunks */
+struct {
+	EffectChunk *first; //!< First element of list, used for iteration
+	EffectChunk *last; //!< Last element of list, used for appending
+} chunkList = {
+	NULL, NULL
+};
+
+
+/*! Our lists of all game world effects */
+struct {
+	size_t num; //!< Number of effects in this list, used when saving
+	EFFECT *first; //!< First element of list, used for iteration / finding free effects
+	EFFECT *last; //!< Last element of list, used for appending
+} activeList = { //!< List of all active effects
+	0, NULL, NULL
+}, inactiveList = { //!< List of unused effects
+	0, NULL, NULL
+};
+
 
 /* Tick counts for updates on a particular interval */
 static	UDWORD	lastUpdateDroids[EFFECT_DROID_DIVISION];
 static	UDWORD	lastUpdateStructures[EFFECT_STRUCTURE_DIVISION];
 
-static	UDWORD	freeEffect; /* Current next slot to use - cyclic */
-static	UDWORD	numEffects;
-static	UDWORD	activeEffects;
-static	UDWORD	missCount;
-static	UDWORD	skipped,skippedEffects,letThrough;
 static	UDWORD	auxVar; // dirty filthy hack - don't look for what this does.... //FIXME
 static	UDWORD	auxVarSec; // dirty filthy hack - don't look for what this does.... //FIXME
 static	UDWORD	specifiedSize;
@@ -233,6 +256,162 @@ static void effectDroidUpdates(void);
 static UDWORD effectGetNumFrames(EFFECT *psEffect);
 
 
+/*!
+ * Initialise memory between first and last as singly linked list
+ * \param first First element in memory chunk
+ * \param last Last element in memory chunk
+ */
+static void initEffectPool(EFFECT *first, EFFECT *last)
+{
+	for (EFFECT *it = first; it < last; it++)
+	{
+		 // We do not need a double-linked-list for inactiveeffects, since we always pick from the front:
+		it->prev = NULL;
+		it->next = it+1;
+	}
+
+	last->prev = NULL;
+	last->next = NULL;
+}
+
+
+/*!
+ * Allocate a new effect from memory pool
+ * FIXME: Does not deal with out-of-memory conditions (yet)
+ * \return New, uninitialised effect
+ */
+static EFFECT *Effect_malloc(void)
+{
+	/* Take the first item in inactiveList */
+	EFFECT *instance = inactiveList.first;
+
+	/* Remove from inactiveList */
+	inactiveList.first = instance->next; // Singly linked, so do not update prev
+	instance->next = NULL; // We are the last in activeList now
+
+	/* Append to activeList */
+	instance->prev = activeList.last;
+
+	if (instance->prev == NULL) {
+		activeList.first = instance; // activeList was empty, so fill it
+	}
+	else {
+		activeList.last->next = instance;
+	}
+
+	activeList.last = instance;
+
+	/* Adjust counts */
+	activeList.num++;
+	inactiveList.num--;
+
+	/* Ensure the next search will have something to feed its hunger with */
+	if (inactiveList.first == NULL)
+	{
+		/* Allocate new effect chunk */
+		EffectChunk *chunk = malloc(sizeof(EffectChunk));
+
+		/* Deal with out-of-memory conditions */
+		if (chunk == NULL) {
+			debug(LOG_ERROR, "Out of memory");
+			return NULL; // The next call relies on inactiveList being non-empty, and would thus segfault, so bail out early instead
+		}
+
+		/* Append to list */
+		chunk->next = NULL; // Last element
+		chunkList.last->next = chunk;  // chunkList is never empty, thus we can rely on last!=NULL
+
+		/* Update inactiveList */
+		inactiveList.num = EFFECT_CHUNKS_SIZE;
+		inactiveList.first = &chunk->effects[0];
+		inactiveList.last = &chunk->effects[EFFECT_CHUNKS_SIZE-1];
+
+		/* Initialise list links between inactive effects */
+		initEffectPool(inactiveList.first, inactiveList.last);
+	}
+
+	return instance;
+}
+
+
+/*!
+ * Return an effect into memory pool
+ * \param self Effect to be freed
+ */
+static void Effect_free(void *self)
+{
+	EFFECT *instance = self;
+
+	/* Remove from activeList and fixup endings necessary */
+	if (instance->prev != NULL) {
+		instance->prev->next = instance->next;
+	}
+	else {
+		activeList.first = instance->next; // We were the first in activeList
+	}
+	if (instance->next != NULL) {
+		instance->next->prev = instance->prev;
+	}
+	else {
+		activeList.last = instance->prev; // We were the last in activeList
+	}
+
+	/* Append to inactiveList */
+	instance->prev = NULL; // Singly linked
+	instance->next = NULL; // Last element
+
+	 // inactiveList is never empty (guaranteed in Effect_malloc), thus we can rely on last!=NULL
+	inactiveList.last->next = instance;
+	inactiveList.last = instance;
+
+	/* Adjust counts */
+	inactiveList.num++;
+	activeList.num--;
+}
+
+
+/*!
+ * Initialise effects system
+ * Cleans up old effects, allocates a first chunk of effects, initialises (in)active lists
+ */
+void initEffectsSystem(void)
+{
+	/* Clean up old chunks */
+	EffectChunk *chunk;
+	for (chunk = chunkList.first; chunk != NULL;)
+	{
+		EffectChunk *chunkNext = chunk->next;
+		free(chunk);
+		chunk = chunkNext;
+	}
+
+	/* Allocate new chunk */
+	chunk = malloc(sizeof(EffectChunk));
+
+	/* Deal with out-of-memory conditions */
+	if (chunk == NULL)
+		return; // FIXME We have no way to report an error here...
+
+	/* Create chunkList */
+	chunkList.first = chunk;
+	chunkList.last = chunk;
+	chunk->next = NULL; // Last element
+
+	/* activeList is empty at start */
+	activeList.num = 0;
+	activeList.first = NULL;
+	activeList.last = NULL;
+
+	/* inactiveList contains all elements */
+	inactiveList.num = EFFECT_CHUNKS_SIZE;
+	inactiveList.first = &chunk->effects[0];
+	inactiveList.last = &chunk->effects[EFFECT_CHUNKS_SIZE-1];
+
+	/* Initialise free-effects pool */
+	initEffectPool(inactiveList.first, inactiveList.last);
+}
+
+
 static void positionEffect(const EFFECT *psEffect)
 {
 	int rx, rz;
@@ -272,76 +451,9 @@ static void killEffect(EFFECT *e)
 			psTile->tileInfoBits &= ~BITS_ON_FIRE;	// clear fire bit
 		}
 	}
-	effectStatus[e-asEffectsList] = ES_INACTIVE;
-	e->control = (UBYTE) 0;
-}
 
-static BOOL	essentialEffect(EFFECT_GROUP group, EFFECT_TYPE type)
-{
-	switch(group)
-	{
-	case	EFFECT_FIRE:
-	case	EFFECT_WAYPOINT:
-	case	EFFECT_DESTRUCTION:
-	case	EFFECT_SAT_LASER:
-	case	EFFECT_STRUCTURE:
-		return(true);
-		break;
-	case	EFFECT_EXPLOSION:
-		if(type == EXPLOSION_TYPE_LAND_LIGHT)
-		{
-			return(true);
-		}
-	default:
-		return(false);
-	}
-}
-
-static BOOL utterlyReject( EFFECT_GROUP group )
-{
-	switch(group)
-	{
-	case EFFECT_BLOOD:
-	case EFFECT_DUST_BALL:
-	case EFFECT_CONSTRUCTION:
-		return(true);
-	default:
-		return(false);
-	}
-}
-
-/**	Simply sets the free pointer to the start - actually this isn't necessary
-	as it will work just fine anyway. This WOULD be necessary were we to change
-	the system so that it seeks FREE slots rather than the oldest one. This is because
-	different effects last for different times and the oldest effect may have
-	just got going (if it was a long effect).
-*/
-void	initEffectsSystem( void )
-{
-	UDWORD	i;
-	EFFECT	*psEffect;
-
-	/* Set position to first */
-	freeEffect = 0;
-
-	/* None are active */
-	numEffects = 0;
-
-	activeEffects = 0;
-
-	missCount=0;
-
-	skipped = letThrough = 0;
-
-	for(i=0; i<MAX_EFFECTS; i++)
-	{
-		/* Get a pointer - just cos our macro requires it, speeds not an issue here */
-		psEffect = &asEffectsList[i];
-		/* Clear all the control bits */
-		psEffect->control = (UBYTE)0;
-		/* All effects are initially inactive */
-		effectStatus[i] = ES_INACTIVE;
-	}
+	/* Put effect back into pool */
+	Effect_free(e);
 }
 
 void	effectSetLandLightSpec(LAND_LIGHT_SPEC spec)
@@ -392,96 +504,27 @@ void addMultiEffect(const Vector3i *basePos, Vector3i *scatter, EFFECT_GROUP gro
 	}
 }
 
-UDWORD	getNumActiveEffects( void )
-{
-	return(activeEffects);
-}
-
-UDWORD	getMissCount( void )
-{
-	return(missCount);
-}
-
-UDWORD	getNumSkippedEffects(void)
-{
-	return(skippedEffects);
-}
-
-UDWORD	getNumEvenEffects(void)
-{
-	return(letThrough);
-}
-
 
 void addEffect(const Vector3i *pos, EFFECT_GROUP group, EFFECT_TYPE type, bool specified, iIMDShape *imd, bool lit)
 {
-	static unsigned int aeCalls = 0;
-	UDWORD	essentialCount;
-	UDWORD	i;
-	EFFECT	*psEffect = NULL;
-
-	aeCalls++;
+	EFFECT *psEffect = NULL;
 
 	if(gamePaused())
 	{
 		return;
 	}
 
-	/* Quick optimsation to reject every second non-essential effect if it's off grid */
-	if(clipXY(pos->x, pos->z) == false)
-	{
-		/* 	If effect is essentail - then let it through */
-	  	if(!essentialEffect(group,type) )
-		{
-			bool bSmoke = false;
+	/* Retrieve a new effect from pool */
+	psEffect = Effect_malloc();
 
-			/* Some we can get rid of right away */
-			if ( utterlyReject( group ) )
-			{
-				skipped++;
-				return;
-			}
-			/* Smoke gets culled more than most off grid effects */
-			if(group == EFFECT_SMOKE)
-			{
-				bSmoke = true;
-			}
-
-			/* Others intermittently (50/50 for most and 25/100 for smoke */
-			if(bSmoke ? (aeCalls & 0x03) : (aeCalls & 0x01) )
-			{
-				/* Do one */
-				skipped++;
-				return;
-			}
-			letThrough++;
-		}
+	/* Deal with out-of-memory conditions */
+	if (psEffect == NULL) {
+		debug(LOG_ERROR, "Out of memory");
+		return; // FIXME We have no way to report an error here...
 	}
 
-
-	for (i = freeEffect, essentialCount = 0; (asEffectsList[i].control & EFFECT_ESSENTIAL)
-		&& essentialCount < MAX_EFFECTS; i++)
-	{
-		/* Check for wrap around */
-		if (i >= (MAX_EFFECTS-1))
-		{
-			/* Go back to the first one */
-			i = 0;
-		}
-		essentialCount++;
-		missCount++;
-	}
-
-	/* Check the list isn't just full of essential effects */
-	if (essentialCount >= MAX_EFFECTS)
-	{
-		/* All of the effects are essential!?!? */
-		return;
-	}
-
-	freeEffect = i;
-
-	psEffect = &asEffectsList[freeEffect];
+	/* Reset control bits */
+	psEffect->control = 0;
 
 	/* Store away it's position - into FRACTS */
 	psEffect->position.x = pos->x;
@@ -499,7 +542,6 @@ void addEffect(const Vector3i *pos, EFFECT_GROUP group, EFFECT_TYPE type, bool s
 	{
 		psEffect->frameNumber = lit;
 	}
-
 	else
 	{
 		/* Starts off on frame zero */
@@ -562,9 +604,6 @@ void addEffect(const Vector3i *pos, EFFECT_GROUP group, EFFECT_TYPE type, bool s
 			break;
 	}
 
-	/* Make the effect active */
-	effectStatus[freeEffect] = ES_ACTIVE;
-
 	/* As of yet, it hasn't bounced (or whatever)... */
 	if(type!=EXPLOSION_TYPE_LAND_LIGHT)
 	{
@@ -572,43 +611,28 @@ void addEffect(const Vector3i *pos, EFFECT_GROUP group, EFFECT_TYPE type, bool s
 	}
 
 	ASSERT(psEffect->imd != NULL || group == EFFECT_DESTRUCTION || group == EFFECT_FIRE || group == EFFECT_SAT_LASER, "null effect imd");
-
-	/* No more slots available? */
-	if(freeEffect++ >= (MAX_EFFECTS-1))
-	{
-		/* Go back to the first one */
-		freeEffect = 0;
-	}
 }
 
 
 /* Calls all the update functions for each different currently active effect */
 void processEffects(void)
 {
-	unsigned int i;
-
-	/* Establish how long the last game frame took */
-	missCount = 0;
-
-	/* Reset counter */
-	numEffects = 0;
-
 	/* Traverse the list */
-	for (i = 0; i < MAX_EFFECTS; i++)
+	for (EFFECT *it = activeList.first; it != NULL;)
 	{
-		/* Don't bother unless it's active */
-		if(effectStatus[i] == ES_ACTIVE)
+		EFFECT *itNext = it->next; // If updateEffect deletes something, we would be screwed...
+
+		/* Run updates, effect may be deleted here */
+		updateEffect(it);
+
+		/* Is it on the grid */
+		if (clipXY(it->position.x, it->position.z))
 		{
-			updateEffect(&asEffectsList[i]);
-			/* One more is active */
-			numEffects++;
-			/* Is it on the grid */
-			if (clipXY(asEffectsList[i].position.x, asEffectsList[i].position.z))
-			{
-				/* Add it to the bucket */
-				bucketAddTypeToList(RENDER_EFFECT,&asEffectsList[i]);
-			}
+			/* Add it to the bucket */
+			bucketAddTypeToList(RENDER_EFFECT, it);
 		}
+
+		it = itNext;
 	}
 
 	/* Add any droid effects */
@@ -616,9 +640,6 @@ void processEffects(void)
 
 	/* Add any structure effects */
 	effectStructureUpdates();
-
-	activeEffects = numEffects;
-	skippedEffects = skipped;
 }
 
 
@@ -2552,8 +2573,6 @@ static const char FXData_file_identifier[] = "FXData";
 /** This will save out the effects data */
 bool writeFXData(const char* fileName)
 {
-	unsigned int count, i;
-
 	if (!tagOpenWrite(FXData_tag_definition, fileName))
 	{
 		ASSERT(false, "writeFXData: error while opening file (%s)", fileName);
@@ -2562,45 +2581,30 @@ bool writeFXData(const char* fileName)
 
 	tagWriteString(0x01, FXData_file_identifier);
 
-	// Count all the active EFFECTs
-	for (i = 0, count = 0; i < MAX_EFFECTS; ++i)
+	// Enter effects group and dump all active EFFECTs
+	tagWriteEnter(0x02, activeList.num);
+	for (EFFECT *it = activeList.first; it != NULL; it = it->next)
 	{
-		if(effectStatus[i] == ES_ACTIVE)
-		{
-			++count;
-		}
-	}
-
-	// Enter effects group and dump all EFFECTs
-	tagWriteEnter(0x02, count);
-	for (i = 0; i < MAX_EFFECTS; ++i)
-	{
-		const EFFECT* curEffect = &asEffectsList[i];
-
-		// Don't save inactive effects
-		if (effectStatus[i] != ES_ACTIVE)
-			continue;
+		tagWrite(0x01, it->control);
+		tagWrite(0x02, it->group);
+		tagWrite(0x03, it->type);
+		tagWrite(0x04, it->frameNumber);
+		tagWrite(0x05, it->size);
+		tagWrite(0x06, it->baseScale);
+		tagWrite(0x07, it->specific);
 
-		tagWrite(0x01, curEffect->control);
-		tagWrite(0x02, curEffect->group);
-		tagWrite(0x03, curEffect->type);
-		tagWrite(0x04, curEffect->frameNumber);
-		tagWrite(0x05, curEffect->size);
-		tagWrite(0x06, curEffect->baseScale);
-		tagWrite(0x07, curEffect->specific);
+		tagWritefv   (0x08, 3, &it->position.x);
+		tagWritefv   (0x09, 3, &it->velocity.x);
+		tagWrites32v (0x0A, 3, &it->rotation.x);
+		tagWrites32v (0x0B, 3, &it->spin.x);
 
-		tagWritefv   (0x08, 3, &curEffect->position.x);
-		tagWritefv   (0x09, 3, &curEffect->velocity.x);
-		tagWrites32v (0x0A, 3, &curEffect->rotation.x);
-		tagWrites32v (0x0B, 3, &curEffect->spin.x);
+		tagWrite(0x0C, it->birthTime);
+		tagWrite(0x0D, it->lastFrame);
+		tagWrite(0x0E, it->frameDelay);
+		tagWrite(0x0F, it->lifeSpan);
+		tagWrite(0x10, it->radius);
 
-		tagWrite(0x0C, curEffect->birthTime);
-		tagWrite(0x0D, curEffect->lastFrame);
-		tagWrite(0x0E, curEffect->frameDelay);
-		tagWrite(0x0F, curEffect->lifeSpan);
-		tagWrite(0x10, curEffect->radius);
-
-		tagWriteString(0x11, resGetNamefromData("IMD", curEffect->imd));
+		tagWriteString(0x11, resGetNamefromData("IMD", it->imd));
 
 		// Move on to reading the next effect group
 		tagWriteNext();
@@ -2642,10 +2646,14 @@ bool readFXData(const char* fileName)
 	count = tagReadEnter(0x02);
 	for(i = 0; i < count; ++i)
 	{
-		EFFECT* curEffect = &asEffectsList[i];
 		char imd_name[PATH_MAX];
+		EFFECT *curEffect = Effect_malloc();
 
-		ASSERT(i < MAX_EFFECTS, "readFXData: more effects in this file than our array can contain");
+		/* Deal with out-of-memory conditions */
+		if (curEffect == NULL) {
+			debug(LOG_ERROR, "Out of memory");
+			return false;
+		}
 
 		curEffect->control      = tagRead(0x01);
 		curEffect->group        = tagRead(0x02);
@@ -2676,17 +2684,12 @@ bool readFXData(const char* fileName)
 			curEffect->imd = NULL;
 		}
 
-		effectStatus[i] = ES_ACTIVE;
-
 		// Move on to reading the next effect group
 		tagReadNext();
 	}
 	// Leave the effects group again...
 	tagReadLeave(0x02);
 
-	/* Ensure free effects kept up to date */
-	freeEffect = i;
-
 	// Close the file
 	tagClose();
 
diff --git a/src/effects.h b/src/effects.h
index b0ddb6e..e678775 100644
--- a/src/effects.h
+++ b/src/effects.h
@@ -113,14 +113,6 @@ typedef enum
 } EFFECT_TYPE;
 
 
-/* Is the slot currently being used and is it active? */
-typedef enum
-{
-	ES_INACTIVE,
-	ES_ACTIVE
-} EFFECT_STATUS;
-
-
 typedef enum
 {
 	LL_MIDDLE,
@@ -149,6 +141,7 @@ struct _effect_def
 	uint16_t          lifeSpan;    // what is it's life expectancy?
 	uint16_t          radius;      // Used for area effects
 	iIMDShape         *imd;        // pointer to the imd the effect uses.
+	EFFECT *prev, *next; // Previous and next element in linked list
 };
 
 

Attachment: signature.asc
Description: This is a digitally signed message part.

_______________________________________________
Warzone-dev mailing list
Warzone-dev@gna.org
https://mail.gna.org/listinfo/warzone-dev

Reply via email to