Graphics synchronization patch
git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@47 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
@@ -711,6 +711,7 @@ LastOscillTime = LastTime;
|
|||||||
SleepThreadUntil (LastTime + 1);
|
SleepThreadUntil (LastTime + 1);
|
||||||
|
|
||||||
SetSemaphore (GraphicsSem);
|
SetSemaphore (GraphicsSem);
|
||||||
|
BatchGraphics ();
|
||||||
CurTime = GetTimeCounter ();
|
CurTime = GetTimeCounter ();
|
||||||
ElapsedTicks = CurTime - LastTime;
|
ElapsedTicks = CurTime - LastTime;
|
||||||
LastTime = CurTime;
|
LastTime = CurTime;
|
||||||
@@ -1031,6 +1032,7 @@ if (LastOscillTime + (ONE_SECOND / 32) < CurTime)
|
|||||||
LastOscillTime = CurTime;
|
LastOscillTime = CurTime;
|
||||||
UpdateSpeechGraphics (FALSE);
|
UpdateSpeechGraphics (FALSE);
|
||||||
}
|
}
|
||||||
|
UnbatchGraphics ();
|
||||||
ClearSemaphore (GraphicsSem);
|
ClearSemaphore (GraphicsSem);
|
||||||
}
|
}
|
||||||
(void) blah; /* Satisfying compiler (unused parameter) */
|
(void) blah; /* Satisfying compiler (unused parameter) */
|
||||||
|
|||||||
@@ -114,19 +114,26 @@ typedef struct tfb_drawcommandqueue
|
|||||||
{
|
{
|
||||||
int Front;
|
int Front;
|
||||||
int Back;
|
int Back;
|
||||||
|
int InsertionPoint;
|
||||||
|
int Batching;
|
||||||
|
volatile int FullSize;
|
||||||
volatile int Size;
|
volatile int Size;
|
||||||
} TFB_DrawCommandQueue;
|
} TFB_DrawCommandQueue;
|
||||||
|
|
||||||
TFB_DrawCommandQueue *TFB_DrawCommandQueue_Create ();
|
TFB_DrawCommandQueue *TFB_DrawCommandQueue_Create ();
|
||||||
|
|
||||||
|
void TFB_BatchGraphics ();
|
||||||
|
|
||||||
|
void TFB_UnbatchGraphics ();
|
||||||
|
|
||||||
|
void TFB_BatchReset ();
|
||||||
|
|
||||||
void TFB_DrawCommandQueue_Push (TFB_DrawCommandQueue* myQueue,
|
void TFB_DrawCommandQueue_Push (TFB_DrawCommandQueue* myQueue,
|
||||||
TFB_DrawCommand* Command);
|
TFB_DrawCommand* Command);
|
||||||
|
|
||||||
int TFB_DrawCommandQueue_Pop (TFB_DrawCommandQueue* myQueue,
|
int TFB_DrawCommandQueue_Pop (TFB_DrawCommandQueue* myQueue,
|
||||||
TFB_DrawCommand* Command);
|
TFB_DrawCommand* Command);
|
||||||
|
|
||||||
void TFB_DeallocateDrawCommand (TFB_DrawCommand* Command);
|
|
||||||
|
|
||||||
extern TFB_DrawCommandQueue *DrawCommandQueue;
|
extern TFB_DrawCommandQueue *DrawCommandQueue;
|
||||||
|
|
||||||
// The TFB_Enqueue* functions are necessary, because only the
|
// The TFB_Enqueue* functions are necessary, because only the
|
||||||
|
|||||||
@@ -43,27 +43,26 @@ blt (PRECT pClipRect, PRIMITIVEPTR PrimPtr)
|
|||||||
|
|
||||||
if (TYPE_GET (_CurFramePtr->TypeIndexAndFlags) == SCREEN_DRAWABLE)
|
if (TYPE_GET (_CurFramePtr->TypeIndexAndFlags) == SCREEN_DRAWABLE)
|
||||||
{
|
{
|
||||||
TFB_DrawCommand DC_on_stack;
|
TFB_DrawCommand DrawCommand;
|
||||||
TFB_DrawCommand *DrawCommand = &DC_on_stack;
|
|
||||||
|
|
||||||
// DrawCommand = HMalloc (sizeof (TFB_DrawCommand));
|
// DrawCommand = HMalloc (sizeof (TFB_DrawCommand));
|
||||||
|
|
||||||
DrawCommand->Type = TFB_DRAWCOMMANDTYPE_IMAGE;
|
DrawCommand.Type = TFB_DRAWCOMMANDTYPE_IMAGE;
|
||||||
DrawCommand->x = pClipRect->corner.x -
|
DrawCommand.x = pClipRect->corner.x -
|
||||||
GetFrameHotX (_CurFramePtr);
|
GetFrameHotX (_CurFramePtr);
|
||||||
DrawCommand->y = pClipRect->corner.y -
|
DrawCommand.y = pClipRect->corner.y -
|
||||||
GetFrameHotY (_CurFramePtr);
|
GetFrameHotY (_CurFramePtr);
|
||||||
DrawCommand->w = img->NormalImg->clip_rect.w;
|
DrawCommand.w = img->NormalImg->clip_rect.w;
|
||||||
DrawCommand->h = img->NormalImg->clip_rect.h;
|
DrawCommand.h = img->NormalImg->clip_rect.h;
|
||||||
|
|
||||||
if (gscale != 0 && gscale != 256)
|
if (gscale != 0 && gscale != 256)
|
||||||
{
|
{
|
||||||
DrawCommand->x += (GetFrameHotX (SrcFramePtr) *
|
DrawCommand.x += (GetFrameHotX (SrcFramePtr) *
|
||||||
((1 << 8) - gscale)) >> 8;
|
((1 << 8) - gscale)) >> 8;
|
||||||
DrawCommand->y += (GetFrameHotY (SrcFramePtr) *
|
DrawCommand.y += (GetFrameHotY (SrcFramePtr) *
|
||||||
((1 << 8) - gscale)) >> 8;
|
((1 << 8) - gscale)) >> 8;
|
||||||
DrawCommand->w = (DrawCommand->w * gscale) >> 8;
|
DrawCommand.w = (DrawCommand.w * gscale) >> 8;
|
||||||
DrawCommand->h = (DrawCommand->h * gscale) >> 8;
|
DrawCommand.h = (DrawCommand.h * gscale) >> 8;
|
||||||
|
|
||||||
if (img->ScaledImg)
|
if (img->ScaledImg)
|
||||||
{
|
{
|
||||||
@@ -120,8 +119,8 @@ blt (PRECT pClipRect, PRIMITIVEPTR PrimPtr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawCommand->image = (TFB_ImageStruct*) img;
|
DrawCommand.image = (TFB_ImageStruct*) img;
|
||||||
DrawCommand->UsePalette = FALSE;
|
DrawCommand.UsePalette = FALSE;
|
||||||
|
|
||||||
if (GetPrimType (PrimPtr) == STAMPFILL_PRIM)
|
if (GetPrimType (PrimPtr) == STAMPFILL_PRIM)
|
||||||
{
|
{
|
||||||
@@ -129,18 +128,18 @@ blt (PRECT pClipRect, PRIMITIVEPTR PrimPtr)
|
|||||||
DWORD c32k;
|
DWORD c32k;
|
||||||
|
|
||||||
c32k = GetPrimColor (PrimPtr) >> 8; // shift out color index
|
c32k = GetPrimColor (PrimPtr) >> 8; // shift out color index
|
||||||
DrawCommand->r = (c32k >> (10 - (8 - 5))) & 0xF8;
|
DrawCommand.r = (c32k >> (10 - (8 - 5))) & 0xF8;
|
||||||
DrawCommand->g = (c32k >> (5 - (8 - 5))) & 0xF8;
|
DrawCommand.g = (c32k >> (5 - (8 - 5))) & 0xF8;
|
||||||
DrawCommand->b = (c32k << (8 - 5)) & 0xF8;
|
DrawCommand.b = (c32k << (8 - 5)) & 0xF8;
|
||||||
|
|
||||||
for (i = 0; i < 256; ++i)
|
for (i = 0; i < 256; ++i)
|
||||||
{
|
{
|
||||||
DrawCommand->Palette[i].r = DrawCommand->r;
|
DrawCommand.Palette[i].r = DrawCommand.r;
|
||||||
DrawCommand->Palette[i].g = DrawCommand->g;
|
DrawCommand.Palette[i].g = DrawCommand.g;
|
||||||
DrawCommand->Palette[i].b = DrawCommand->b;
|
DrawCommand.Palette[i].b = DrawCommand.b;
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawCommand->UsePalette = TRUE;
|
DrawCommand.UsePalette = TRUE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -155,15 +154,15 @@ blt (PRECT pClipRect, PRIMITIVEPTR PrimPtr)
|
|||||||
(type == TFB_COLORMAP_PLANET && (img->Palette[255].r != 248 ||
|
(type == TFB_COLORMAP_PLANET && (img->Palette[255].r != 248 ||
|
||||||
img->Palette[255].g != 248 || img->Palette[255].b != 248)))
|
img->Palette[255].g != 248 || img->Palette[255].b != 248)))
|
||||||
{
|
{
|
||||||
if (TFB_CopyRGBColorMap(DrawCommand->Palette))
|
if (TFB_CopyRGBColorMap(DrawCommand.Palette))
|
||||||
{
|
{
|
||||||
DrawCommand->UsePalette = TRUE;
|
DrawCommand.UsePalette = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TFB_EnqueueDrawCommand(DrawCommand);
|
TFB_EnqueueDrawCommand(&DrawCommand);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -211,35 +210,31 @@ fillrect_blt (PRECT pClipRect, PRIMITIVEPTR PrimPtr)
|
|||||||
|
|
||||||
if (TYPE_GET (_CurFramePtr->TypeIndexAndFlags) == SCREEN_DRAWABLE)
|
if (TYPE_GET (_CurFramePtr->TypeIndexAndFlags) == SCREEN_DRAWABLE)
|
||||||
{
|
{
|
||||||
TFB_DrawCommand DC_on_stack;
|
TFB_DrawCommand DrawCommand;
|
||||||
TFB_DrawCommand *DrawCommand = &DC_on_stack;
|
|
||||||
|
|
||||||
if (DrawCommand)
|
DrawCommand.Type = TFB_DRAWCOMMANDTYPE_RECTANGLE;
|
||||||
{
|
DrawCommand.x = pClipRect->corner.x - GetFrameHotX (_CurFramePtr);
|
||||||
DrawCommand->Type = TFB_DRAWCOMMANDTYPE_RECTANGLE;
|
DrawCommand.y = pClipRect->corner.y - GetFrameHotY (_CurFramePtr);
|
||||||
DrawCommand->x = pClipRect->corner.x - GetFrameHotX (_CurFramePtr);
|
DrawCommand.w = pClipRect->extent.width;
|
||||||
DrawCommand->y = pClipRect->corner.y - GetFrameHotY (_CurFramePtr);
|
DrawCommand.h = pClipRect->extent.height;
|
||||||
DrawCommand->w = pClipRect->extent.width;
|
DrawCommand.r = r;
|
||||||
DrawCommand->h = pClipRect->extent.height;
|
DrawCommand.g = g;
|
||||||
DrawCommand->r = r;
|
DrawCommand.b = b;
|
||||||
DrawCommand->g = g;
|
|
||||||
DrawCommand->b = b;
|
|
||||||
|
|
||||||
if (gscale && GetPrimType (PrimPtr) != POINT_PRIM)
|
if (gscale && GetPrimType (PrimPtr) != POINT_PRIM)
|
||||||
{
|
{
|
||||||
DrawCommand->w = (DrawCommand->w * gscale) >> 8;
|
DrawCommand.w = (DrawCommand.w * gscale) >> 8;
|
||||||
DrawCommand->h = (DrawCommand->h * gscale) >> 8;
|
DrawCommand.h = (DrawCommand.h * gscale) >> 8;
|
||||||
DrawCommand->x += (pClipRect->extent.width -
|
DrawCommand.x += (pClipRect->extent.width -
|
||||||
DrawCommand->w) >> 1;
|
DrawCommand.w) >> 1;
|
||||||
DrawCommand->y += (pClipRect->extent.height -
|
DrawCommand.y += (pClipRect->extent.height -
|
||||||
DrawCommand->h) >> 1;
|
DrawCommand.h) >> 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawCommand->image = 0;
|
DrawCommand.image = 0;
|
||||||
DrawCommand->UsePalette = FALSE;
|
DrawCommand.UsePalette = FALSE;
|
||||||
|
|
||||||
TFB_EnqueueDrawCommand(DrawCommand);
|
TFB_EnqueueDrawCommand(&DrawCommand);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -322,18 +317,17 @@ read_screen (PRECT lpRect, FRAMEPTR DstFramePtr)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TFB_DrawCommand DC_auto;
|
TFB_DrawCommand DC;
|
||||||
TFB_DrawCommand* DC = &DC_auto;
|
|
||||||
|
|
||||||
DC->Type = TFB_DRAWCOMMANDTYPE_COPYBACKBUFFERTOOTHERBUFFER;
|
DC.Type = TFB_DRAWCOMMANDTYPE_COPYBACKBUFFERTOOTHERBUFFER;
|
||||||
DC->x = lpRect->corner.x;
|
DC.x = lpRect->corner.x;
|
||||||
DC->y = lpRect->corner.y;
|
DC.y = lpRect->corner.y;
|
||||||
DC->w = lpRect->extent.width;
|
DC.w = lpRect->extent.width;
|
||||||
DC->h = lpRect->extent.height;
|
DC.h = lpRect->extent.height;
|
||||||
DC->image = (TFB_ImageStruct *) ((BYTE *) DstFramePtr +
|
DC.image = (TFB_ImageStruct *) ((BYTE *) DstFramePtr +
|
||||||
DstFramePtr->DataOffs);
|
DstFramePtr->DataOffs);
|
||||||
|
|
||||||
TFB_EnqueueDrawCommand (DC);
|
TFB_EnqueueDrawCommand (&DC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#include "sdl_common.h"
|
#include "sdl_common.h"
|
||||||
|
|
||||||
|
int batch_depth = 0;
|
||||||
|
|
||||||
|
|
||||||
//Status: Not entirely unimplemented!
|
//Status: Not entirely unimplemented!
|
||||||
BOOLEAN
|
BOOLEAN
|
||||||
@@ -45,23 +47,32 @@ UninitGraphics () // Also probably empty
|
|||||||
{
|
{
|
||||||
HFree (DrawCommandQueue);
|
HFree (DrawCommandQueue);
|
||||||
|
|
||||||
HFree (ExtraScreen);
|
|
||||||
mem_uninit ();
|
mem_uninit ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Batch/UnbatchGraphics: These routines appear to be used to ensure
|
||||||
|
// that the screen doesn't update with a half-drawn region. This can
|
||||||
|
// be implemented in all sorts of ways - I've chosen to fold it into
|
||||||
|
// our DrawCommandQueue, for the most part. These just forward to
|
||||||
|
// the TFB_ versions in dcqueue.c. They may also make continuity_break
|
||||||
|
// redundant, but I haven't tested that yet. --Michael
|
||||||
void
|
void
|
||||||
BatchGraphics (void)
|
BatchGraphics (void)
|
||||||
{
|
{
|
||||||
|
TFB_BatchGraphics ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
UnbatchGraphics (void)
|
UnbatchGraphics (void)
|
||||||
{
|
{
|
||||||
|
TFB_UnbatchGraphics ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
FlushGraphics (void)
|
FlushGraphics (void)
|
||||||
{
|
{
|
||||||
|
TFB_BatchReset ();
|
||||||
continuity_break = 1;
|
continuity_break = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,16 +19,114 @@
|
|||||||
#ifdef GFXMODULE_SDL
|
#ifdef GFXMODULE_SDL
|
||||||
|
|
||||||
#include "sdl_common.h"
|
#include "sdl_common.h"
|
||||||
|
#include "libs/threadlib.h"
|
||||||
|
|
||||||
SDL_mutex *DCQ_mutex;
|
Semaphore DCQ_sem;
|
||||||
|
|
||||||
#define DCQ_MAX 4096
|
// variables for making the DCQ lock re-entrant
|
||||||
|
static int DCQ_locking_depth = 0;
|
||||||
|
static Uint32 DCQ_locking_thread = 0;
|
||||||
|
|
||||||
|
// Maximum size of the DCQ. The larger the DCQ, the larger frameskips
|
||||||
|
// become tolerable before initiating livelock deterrence and game
|
||||||
|
// slowdown. Other constants for controlling the frameskip/slowdown
|
||||||
|
// balance may be found in sdl_common.c near TFB_FlushGraphics.
|
||||||
|
#define DCQ_MAX 16384
|
||||||
TFB_DrawCommand DCQ[DCQ_MAX];
|
TFB_DrawCommand DCQ[DCQ_MAX];
|
||||||
|
|
||||||
TFB_DrawCommandQueue *DrawCommandQueue;
|
TFB_DrawCommandQueue *DrawCommandQueue;
|
||||||
|
|
||||||
|
// DCQ Synchronization: SDL-specific implementation of re-entrant
|
||||||
|
// locks to protect the Draw Command Queue. Lock is re-entrant to
|
||||||
|
// allow livelock deterrence to be written much more cleanly.
|
||||||
|
|
||||||
|
void
|
||||||
|
Lock_DCQ (void)
|
||||||
|
{
|
||||||
|
Uint32 current_thread = SDL_ThreadID ();
|
||||||
|
if (DCQ_locking_thread != current_thread)
|
||||||
|
{
|
||||||
|
SetSemaphore (DCQ_sem);
|
||||||
|
DCQ_locking_thread = current_thread;
|
||||||
|
}
|
||||||
|
++DCQ_locking_depth;
|
||||||
|
// printf("DCQ_sem locking depth: %i\n", DCQ_locking_depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Unlock_DCQ (void)
|
||||||
|
{
|
||||||
|
Uint32 current_thread = SDL_ThreadID ();
|
||||||
|
if (DCQ_locking_thread != current_thread)
|
||||||
|
{
|
||||||
|
printf("%8x attempted to unlock the DCQ when it didn't hold it!\n", current_thread);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
--DCQ_locking_depth;
|
||||||
|
// printf("DCQ_sem locking depth: %i\n", DCQ_locking_depth);
|
||||||
|
if (!DCQ_locking_depth)
|
||||||
|
{
|
||||||
|
DCQ_locking_thread = 0;
|
||||||
|
ClearSemaphore (DCQ_sem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always have the DCQ locked when calling this.
|
||||||
|
static void
|
||||||
|
Synchronize_DCQ (void)
|
||||||
|
{
|
||||||
|
if (!DrawCommandQueue->Batching)
|
||||||
|
{
|
||||||
|
int front = DrawCommandQueue->Front;
|
||||||
|
int back = DrawCommandQueue->InsertionPoint;
|
||||||
|
DrawCommandQueue->Back = DrawCommandQueue->InsertionPoint;
|
||||||
|
if (front <= back)
|
||||||
|
{
|
||||||
|
DrawCommandQueue->Size = (back - front);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DrawCommandQueue->Size = (back + DCQ_MAX - front);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TFB_BatchGraphics (void)
|
||||||
|
{
|
||||||
|
Lock_DCQ ();
|
||||||
|
DrawCommandQueue->Batching++;
|
||||||
|
Unlock_DCQ ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TFB_UnbatchGraphics (void)
|
||||||
|
{
|
||||||
|
Lock_DCQ ();
|
||||||
|
if (DrawCommandQueue->Batching)
|
||||||
|
{
|
||||||
|
DrawCommandQueue->Batching--;
|
||||||
|
}
|
||||||
|
Synchronize_DCQ ();
|
||||||
|
Unlock_DCQ ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel all pending batch operations, making them unbatched. This will
|
||||||
|
// cause a small amount of flicker when invoked, but prevents
|
||||||
|
// batching problems from freezing the game.
|
||||||
|
void
|
||||||
|
TFB_BatchReset (void)
|
||||||
|
{
|
||||||
|
Lock_DCQ ();
|
||||||
|
DrawCommandQueue->Batching = 0;
|
||||||
|
Synchronize_DCQ ();
|
||||||
|
Unlock_DCQ ();
|
||||||
|
}
|
||||||
|
|
||||||
// Draw Command Queue Stuff
|
// Draw Command Queue Stuff
|
||||||
|
// TODO: Make this be statically allocated, too. We only ever have one DCQ, after all.
|
||||||
|
|
||||||
TFB_DrawCommandQueue*
|
TFB_DrawCommandQueue*
|
||||||
TFB_DrawCommandQueue_Create()
|
TFB_DrawCommandQueue_Create()
|
||||||
@@ -40,9 +138,11 @@ TFB_DrawCommandQueue_Create()
|
|||||||
|
|
||||||
myQueue->Back = 0;
|
myQueue->Back = 0;
|
||||||
myQueue->Front = 0;
|
myQueue->Front = 0;
|
||||||
|
myQueue->InsertionPoint = 0;
|
||||||
|
myQueue->Batching = 0;
|
||||||
myQueue->Size = 0;
|
myQueue->Size = 0;
|
||||||
|
|
||||||
DCQ_mutex = SDL_CreateMutex();
|
DCQ_sem = CreateSemaphore(1);
|
||||||
|
|
||||||
return (myQueue);
|
return (myQueue);
|
||||||
}
|
}
|
||||||
@@ -51,29 +151,30 @@ void
|
|||||||
TFB_DrawCommandQueue_Push (TFB_DrawCommandQueue* myQueue,
|
TFB_DrawCommandQueue_Push (TFB_DrawCommandQueue* myQueue,
|
||||||
TFB_DrawCommand* Command)
|
TFB_DrawCommand* Command)
|
||||||
{
|
{
|
||||||
SDL_mutexP(DCQ_mutex);
|
Lock_DCQ ();
|
||||||
if (myQueue->Size < DCQ_MAX)
|
if (myQueue->Size < DCQ_MAX - 1)
|
||||||
{
|
{
|
||||||
DCQ[myQueue->Back] = *Command;
|
DCQ[myQueue->InsertionPoint] = *Command;
|
||||||
myQueue->Back = (myQueue->Back + 1) % DCQ_MAX;
|
myQueue->InsertionPoint = (myQueue->InsertionPoint + 1) % DCQ_MAX;
|
||||||
myQueue->Size++;
|
myQueue->FullSize++;
|
||||||
|
Synchronize_DCQ ();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TFB_DeallocateDrawCommand(Command);
|
printf("DCQ overload. Adjust your livelock deterrence constants!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_mutexV(DCQ_mutex);
|
Unlock_DCQ ();
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
TFB_DrawCommandQueue_Pop (TFB_DrawCommandQueue *myQueue, TFB_DrawCommand *target)
|
TFB_DrawCommandQueue_Pop (TFB_DrawCommandQueue *myQueue, TFB_DrawCommand *target)
|
||||||
{
|
{
|
||||||
SDL_mutexP(DCQ_mutex);
|
Lock_DCQ ();
|
||||||
|
|
||||||
if (myQueue->Size == 0)
|
if (myQueue->Size == 0)
|
||||||
{
|
{
|
||||||
SDL_mutexV(DCQ_mutex);
|
Unlock_DCQ ();
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +182,7 @@ TFB_DrawCommandQueue_Pop (TFB_DrawCommandQueue *myQueue, TFB_DrawCommand *target
|
|||||||
{
|
{
|
||||||
printf("Augh! Assertion failure in DCQ! Front == Back, Size != DCQ_MAX\n");
|
printf("Augh! Assertion failure in DCQ! Front == Back, Size != DCQ_MAX\n");
|
||||||
myQueue->Size = 0;
|
myQueue->Size = 0;
|
||||||
SDL_mutexV(DCQ_mutex);
|
Unlock_DCQ ();
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,17 +190,12 @@ TFB_DrawCommandQueue_Pop (TFB_DrawCommandQueue *myQueue, TFB_DrawCommand *target
|
|||||||
myQueue->Front = (myQueue->Front + 1) % DCQ_MAX;
|
myQueue->Front = (myQueue->Front + 1) % DCQ_MAX;
|
||||||
|
|
||||||
myQueue->Size--;
|
myQueue->Size--;
|
||||||
SDL_mutexV(DCQ_mutex);
|
myQueue->FullSize--;
|
||||||
|
Unlock_DCQ ();
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
TFB_DeallocateDrawCommand (TFB_DrawCommand* Command)
|
|
||||||
{
|
|
||||||
//HFree(Command);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TFB_EnqueueDrawCommand (TFB_DrawCommand* DrawCommand)
|
TFB_EnqueueDrawCommand (TFB_DrawCommand* DrawCommand)
|
||||||
{
|
{
|
||||||
@@ -122,23 +218,22 @@ TFB_EnqueueDrawCommand (TFB_DrawCommand* DrawCommand)
|
|||||||
_pCurContext->ClipRect.extent.height)
|
_pCurContext->ClipRect.extent.height)
|
||||||
{
|
{
|
||||||
// Enqueue command to set the glScissor spec
|
// Enqueue command to set the glScissor spec
|
||||||
TFB_DrawCommand DC_auto;
|
TFB_DrawCommand DC;
|
||||||
TFB_DrawCommand* DC = &DC_auto;
|
|
||||||
|
|
||||||
scissor_rect = _pCurContext->ClipRect;
|
scissor_rect = _pCurContext->ClipRect;
|
||||||
|
|
||||||
DC->Type = scissor_rect.extent.width
|
DC.Type = scissor_rect.extent.width
|
||||||
? (DC->x = scissor_rect.corner.x,
|
? (DC.x = scissor_rect.corner.x,
|
||||||
DC->y=scissor_rect.corner.y,
|
DC.y=scissor_rect.corner.y,
|
||||||
DC->w=scissor_rect.extent.width,
|
DC.w=scissor_rect.extent.width,
|
||||||
DC->h=scissor_rect.extent.height),
|
DC.h=scissor_rect.extent.height),
|
||||||
TFB_DRAWCOMMANDTYPE_SCISSORENABLE
|
TFB_DRAWCOMMANDTYPE_SCISSORENABLE
|
||||||
: TFB_DRAWCOMMANDTYPE_SCISSORDISABLE;
|
: TFB_DRAWCOMMANDTYPE_SCISSORDISABLE;
|
||||||
|
|
||||||
DC->image = 0;
|
DC.image = 0;
|
||||||
DC->UsePalette = FALSE;
|
DC.UsePalette = FALSE;
|
||||||
|
|
||||||
TFB_EnqueueDrawCommand(DC);
|
TFB_EnqueueDrawCommand(&DC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -287,11 +287,33 @@ TFB_ComputeFPS ()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Livelock deterrance constants. Because the entire screen is rarely
|
||||||
|
// refreshed, we may not drop draw commands on the floor with abandon.
|
||||||
|
// Furthermore, if the main program is queuing commands at a speed
|
||||||
|
// comparable to our processing of the commands, we never finish and
|
||||||
|
// the game freezes. Thus, if the queue starts out larger than
|
||||||
|
// DCQ_FORCE_SLOWDOWN_SIZE, or DCQ_LIVELOCK_MAX commands find
|
||||||
|
// themselves being processed in one go, livelock deterrence is
|
||||||
|
// enabled, and TFB_FlushGraphics locks the DCQ until it has processed
|
||||||
|
// all entries. If batched but pending commands exceed DCQ_FORCE_BREAK_SIZE,
|
||||||
|
// a continuity break is performed. This will effectively slow down the
|
||||||
|
// game logic, a fate we seek to avoid - however, it seems to be unavoidable
|
||||||
|
// on slower machines. Even there, it's seems nonexistent outside of
|
||||||
|
// communications screens. --Michael
|
||||||
|
|
||||||
|
#define DCQ_FORCE_SLOWDOWN_SIZE 1024
|
||||||
|
#define DCQ_FORCE_BREAK_SIZE 4096
|
||||||
|
#define DCQ_LIVELOCK_MAX 2048
|
||||||
|
|
||||||
void
|
void
|
||||||
TFB_FlushGraphics () // Only call from main thread!!
|
TFB_FlushGraphics () // Only call from main thread!!
|
||||||
{
|
{
|
||||||
int semval;
|
int semval;
|
||||||
|
int commands_handled;
|
||||||
|
BOOLEAN livelock_deterrence;
|
||||||
|
|
||||||
|
// This is technically a locking violation on DrawCommandQueue->Size,
|
||||||
|
// but it is likely to not be very destructive.
|
||||||
if (DrawCommandQueue == 0 || DrawCommandQueue->Size == 0)
|
if (DrawCommandQueue == 0 || DrawCommandQueue->Size == 0)
|
||||||
{
|
{
|
||||||
SDL_Delay(5);
|
SDL_Delay(5);
|
||||||
@@ -302,41 +324,58 @@ TFB_FlushGraphics () // Only call from main thread!!
|
|||||||
// TODO: a more optimal way of getting the lock on GraphicsSem..
|
// TODO: a more optimal way of getting the lock on GraphicsSem..
|
||||||
// cannot currently use SDL_SemWait because it would break
|
// cannot currently use SDL_SemWait because it would break
|
||||||
// the usage of continuity_break
|
// the usage of continuity_break
|
||||||
|
// Michael asks: Why are we locking, then releasing, the GraphicsSem?
|
||||||
|
|
||||||
semval = TimeoutSetSemaphore (GraphicsSem, ONE_SECOND / 10);
|
semval = TimeoutSetSemaphore (GraphicsSem, ONE_SECOND / 10);
|
||||||
if (semval != 0 && !continuity_break)
|
if (semval != 0)
|
||||||
return;
|
return;
|
||||||
continuity_break = 0;
|
else
|
||||||
|
|
||||||
if (semval == 0)
|
|
||||||
SDL_SemPost (GraphicsSem);
|
SDL_SemPost (GraphicsSem);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
continuity_break = 0;
|
continuity_break = 0;
|
||||||
}
|
|
||||||
|
|
||||||
if (ShowFPS)
|
if (ShowFPS)
|
||||||
TFB_ComputeFPS ();
|
TFB_ComputeFPS ();
|
||||||
|
|
||||||
while (DrawCommandQueue->Size > 0)
|
commands_handled = 0;
|
||||||
{
|
livelock_deterrence = FALSE;
|
||||||
TFB_DrawCommand DC_real;
|
|
||||||
TFB_Image *DC_image;
|
|
||||||
|
|
||||||
TFB_DrawCommand* DC = &DC_real;
|
if (DrawCommandQueue->FullSize > DCQ_FORCE_BREAK_SIZE)
|
||||||
|
|
||||||
if (!TFB_DrawCommandQueue_Pop (DrawCommandQueue, DC))
|
|
||||||
{
|
{
|
||||||
printf ("Woah there coyboy! Trouble with TFB_Queues...\n");
|
TFB_BatchReset ();
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DC_image = (TFB_Image*) DC->image;
|
if (DrawCommandQueue->Size > DCQ_FORCE_SLOWDOWN_SIZE)
|
||||||
|
{
|
||||||
|
Lock_DCQ ();
|
||||||
|
livelock_deterrence = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
TFB_DrawCommand DC;
|
||||||
|
TFB_Image *DC_image;
|
||||||
|
|
||||||
|
if (!TFB_DrawCommandQueue_Pop (DrawCommandQueue, &DC))
|
||||||
|
{
|
||||||
|
// the Queue is now empty.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++commands_handled;
|
||||||
|
if (!livelock_deterrence && commands_handled + DrawCommandQueue->Size > DCQ_LIVELOCK_MAX)
|
||||||
|
{
|
||||||
|
// printf("Initiating livelock deterrence!\n");
|
||||||
|
livelock_deterrence = TRUE;
|
||||||
|
|
||||||
|
Lock_DCQ ();
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_image = (TFB_Image*) DC.image;
|
||||||
if (DC_image)
|
if (DC_image)
|
||||||
SDL_mutexP (DC_image->mutex);
|
SDL_mutexP (DC_image->mutex);
|
||||||
|
|
||||||
switch (DC->Type)
|
switch (DC.Type)
|
||||||
{
|
{
|
||||||
case TFB_DRAWCOMMANDTYPE_IMAGE:
|
case TFB_DRAWCOMMANDTYPE_IMAGE:
|
||||||
{
|
{
|
||||||
@@ -349,10 +388,10 @@ TFB_FlushGraphics () // Only call from main thread!!
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
targetRect.x = DC->x;
|
targetRect.x = DC.x;
|
||||||
targetRect.y = DC->y;
|
targetRect.y = DC.y;
|
||||||
|
|
||||||
if ((DC->w != DC_image->NormalImg->w || DC->h != DC_image->NormalImg->h) &&
|
if ((DC.w != DC_image->NormalImg->w || DC.h != DC_image->NormalImg->h) &&
|
||||||
DC_image->ScaledImg)
|
DC_image->ScaledImg)
|
||||||
surf = DC_image->ScaledImg;
|
surf = DC_image->ScaledImg;
|
||||||
else
|
else
|
||||||
@@ -360,9 +399,9 @@ TFB_FlushGraphics () // Only call from main thread!!
|
|||||||
|
|
||||||
if (surf->format->BytesPerPixel == 1)
|
if (surf->format->BytesPerPixel == 1)
|
||||||
{
|
{
|
||||||
if (DC->UsePalette)
|
if (DC.UsePalette)
|
||||||
{
|
{
|
||||||
SDL_SetColors (surf, (SDL_Color*)DC->Palette, 0, 256);
|
SDL_SetColors (surf, (SDL_Color*)DC.Palette, 0, 256);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -382,12 +421,12 @@ TFB_FlushGraphics () // Only call from main thread!!
|
|||||||
PutPixelFn screen_plot;
|
PutPixelFn screen_plot;
|
||||||
|
|
||||||
screen_plot = putpixel_for (SDL_Screen);
|
screen_plot = putpixel_for (SDL_Screen);
|
||||||
color = SDL_MapRGB (SDL_Screen->format, DC->r, DC->g, DC->b);
|
color = SDL_MapRGB (SDL_Screen->format, DC.r, DC.g, DC.b);
|
||||||
|
|
||||||
x1 = DC->x;
|
x1 = DC.x;
|
||||||
x2 = DC->w;
|
x2 = DC.w;
|
||||||
y1 = DC->y;
|
y1 = DC.y;
|
||||||
y2 = DC->h;
|
y2 = DC.h;
|
||||||
|
|
||||||
SDL_GetClipRect(SDL_Screen, &r);
|
SDL_GetClipRect(SDL_Screen, &r);
|
||||||
|
|
||||||
@@ -420,21 +459,21 @@ TFB_FlushGraphics () // Only call from main thread!!
|
|||||||
case TFB_DRAWCOMMANDTYPE_RECTANGLE:
|
case TFB_DRAWCOMMANDTYPE_RECTANGLE:
|
||||||
{
|
{
|
||||||
SDL_Rect r;
|
SDL_Rect r;
|
||||||
r.x = DC->x;
|
r.x = DC.x;
|
||||||
r.y = DC->y;
|
r.y = DC.y;
|
||||||
r.w = DC->w;
|
r.w = DC.w;
|
||||||
r.h = DC->h;
|
r.h = DC.h;
|
||||||
|
|
||||||
SDL_FillRect(SDL_Screen, &r, SDL_MapRGB(SDL_Screen->format, DC->r, DC->g, DC->b));
|
SDL_FillRect(SDL_Screen, &r, SDL_MapRGB(SDL_Screen->format, DC.r, DC.g, DC.b));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TFB_DRAWCOMMANDTYPE_SCISSORENABLE:
|
case TFB_DRAWCOMMANDTYPE_SCISSORENABLE:
|
||||||
{
|
{
|
||||||
SDL_Rect r;
|
SDL_Rect r;
|
||||||
r.x = DC->x;
|
r.x = DC.x;
|
||||||
r.y = DC->y;
|
r.y = DC.y;
|
||||||
r.w = DC->w;
|
r.w = DC.w;
|
||||||
r.h = DC->h;
|
r.h = DC.h;
|
||||||
|
|
||||||
SDL_SetClipRect(SDL_Screen, &r);
|
SDL_SetClipRect(SDL_Screen, &r);
|
||||||
break;
|
break;
|
||||||
@@ -445,10 +484,10 @@ TFB_FlushGraphics () // Only call from main thread!!
|
|||||||
case TFB_DRAWCOMMANDTYPE_COPYBACKBUFFERTOOTHERBUFFER:
|
case TFB_DRAWCOMMANDTYPE_COPYBACKBUFFERTOOTHERBUFFER:
|
||||||
{
|
{
|
||||||
SDL_Rect src, dest;
|
SDL_Rect src, dest;
|
||||||
src.x = dest.x = DC->x;
|
src.x = dest.x = DC.x;
|
||||||
src.y = dest.y = DC->y;
|
src.y = dest.y = DC.y;
|
||||||
src.w = DC->w;
|
src.w = DC.w;
|
||||||
src.h = DC->h;
|
src.h = DC.h;
|
||||||
|
|
||||||
if (DC_image == 0)
|
if (DC_image == 0)
|
||||||
{
|
{
|
||||||
@@ -465,10 +504,10 @@ TFB_FlushGraphics () // Only call from main thread!!
|
|||||||
case TFB_DRAWCOMMANDTYPE_COPYFROMOTHERBUFFER:
|
case TFB_DRAWCOMMANDTYPE_COPYFROMOTHERBUFFER:
|
||||||
{
|
{
|
||||||
SDL_Rect src, dest;
|
SDL_Rect src, dest;
|
||||||
src.x = dest.x = DC->x;
|
src.x = dest.x = DC.x;
|
||||||
src.y = dest.y = DC->y;
|
src.y = dest.y = DC.y;
|
||||||
src.w = DC->w;
|
src.w = DC.w;
|
||||||
src.h = DC->h;
|
src.h = DC.h;
|
||||||
SDL_BlitSurface(ExtraScreen, &src, SDL_Screen, &dest);
|
SDL_BlitSurface(ExtraScreen, &src, SDL_Screen, &dest);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -493,8 +532,11 @@ TFB_FlushGraphics () // Only call from main thread!!
|
|||||||
|
|
||||||
if (DC_image)
|
if (DC_image)
|
||||||
SDL_mutexV (DC_image->mutex);
|
SDL_mutexV (DC_image->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
TFB_DeallocateDrawCommand (DC);
|
if (livelock_deterrence)
|
||||||
|
{
|
||||||
|
Unlock_DCQ ();
|
||||||
}
|
}
|
||||||
|
|
||||||
TFB_SwapBuffers();
|
TFB_SwapBuffers();
|
||||||
|
|||||||
@@ -218,9 +218,15 @@ CreateThreadAux (ThreadFunction func, void *data, SDWORD stackSize
|
|||||||
* from being removed from the queue and from displaying the
|
* from being removed from the queue and from displaying the
|
||||||
* 'killed' debug message.
|
* 'killed' debug message.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* 17 Sep: Added a TFB_BatchReset call. If a thread is killed while
|
||||||
|
* batching stuff, we don't want this to freeze the game.
|
||||||
|
* Better safe than sorry! --Michael
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
KillThread (Thread thread)
|
KillThread (Thread thread)
|
||||||
{
|
{
|
||||||
|
TFB_BatchReset ();
|
||||||
NativeKillThread (thread->native);
|
NativeKillThread (thread->native);
|
||||||
#ifdef DEBUG_THREADS
|
#ifdef DEBUG_THREADS
|
||||||
fprintf (stderr, "Thread '%s' killed.\n", ThreadName (thread));
|
fprintf (stderr, "Thread '%s' killed.\n", ThreadName (thread));
|
||||||
@@ -240,7 +246,7 @@ WaitThread (Thread thread, int *status)
|
|||||||
void
|
void
|
||||||
SleepThread (TimePeriod timePeriod)
|
SleepThread (TimePeriod timePeriod)
|
||||||
{
|
{
|
||||||
return NativeSleepThread (timePeriod);
|
NativeSleepThread (timePeriod);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -300,7 +306,9 @@ DestroySemaphore (Semaphore sem)
|
|||||||
int
|
int
|
||||||
SetSemaphore (Semaphore sem)
|
SetSemaphore (Semaphore sem)
|
||||||
{
|
{
|
||||||
return NativeSetSemaphore ((NativeSemaphore) sem);
|
int result;
|
||||||
|
result = NativeSetSemaphore ((NativeSemaphore) sem);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|||||||
Reference in New Issue
Block a user