void TFB_DrawCommandQueue_Push (TFB_DrawCommand* Command) { Lock_DCQ (1); DCQ[DrawCommandQueue.InsertionPoint] = *Command; DrawCommandQueue.InsertionPoint = (DrawCommandQueue.InsertionPoint + 1) % DCQ_MAX; DrawCommandQueue.FullSize++; Synchronize_DCQ (); Unlock_DCQ (); }
void TFB_DrawScreen_WaitForSignal (void) { TFB_DrawCommand DrawCommand; Semaphore s; s = GetMyThreadLocal ()->flushSem; DrawCommand.Type = TFB_DRAWCOMMANDTYPE_SENDSIGNAL; DrawCommand.data.sendsignal.sem = s; Lock_DCQ (1); TFB_BatchReset (); TFB_EnqueueDrawCommand (&DrawCommand); Unlock_DCQ(); SetSemaphore (s); }
// Only call from main() thread!! void TFB_FlushGraphics (void) { 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.Size == 0) { static int last_fade = 255; static int last_transition = 255; int current_fade = GetFadeAmount (); int current_transition = TransitionAmount; if ((current_fade != 255 && current_fade != last_fade) || (current_transition != 255 && current_transition != last_transition) || (current_fade == 255 && last_fade != 255) || (current_transition == 255 && last_transition != 255)) { TFB_SwapBuffers (TFB_REDRAW_FADING); // if fading, redraw every frame } else { TaskSwitch (); } last_fade = current_fade; last_transition = current_transition; BroadcastCondVar (RenderingCond); return; } if (GfxFlags & TFB_GFXFLAGS_SHOWFPS) computeFPS (); commands_handled = 0; livelock_deterrence = FALSE; if (DrawCommandQueue.FullSize > DCQ_FORCE_BREAK_SIZE) { TFB_BatchReset (); } if (DrawCommandQueue.Size > DCQ_FORCE_SLOWDOWN_SIZE) { Lock_DCQ (-1); livelock_deterrence = TRUE; } TFB_BBox_Reset (); for (;;) { TFB_DrawCommand DC; if (!TFB_DrawCommandQueue_Pop (&DC)) { // the Queue is now empty. break; } ++commands_handled; if (!livelock_deterrence && commands_handled + DrawCommandQueue.Size > DCQ_LIVELOCK_MAX) { // log_add (log_Debug, "Initiating livelock deterrence!"); livelock_deterrence = TRUE; Lock_DCQ (-1); } switch (DC.Type) { case TFB_DRAWCOMMANDTYPE_SETMIPMAP: { TFB_DrawCommand_SetMipmap *cmd = &DC.data.setmipmap; TFB_DrawImage_SetMipmap (cmd->image, cmd->mipmap, cmd->hotx, cmd->hoty); break; } case TFB_DRAWCOMMANDTYPE_IMAGE: { TFB_DrawCommand_Image *cmd = &DC.data.image; TFB_Image *DC_image = cmd->image; const int x = cmd->x; const int y = cmd->y; TFB_DrawCanvas_Image (DC_image, x, y, cmd->scale, cmd->scaleMode, cmd->colormap, cmd->drawMode, TFB_GetScreenCanvas (cmd->destBuffer)); if (cmd->destBuffer == TFB_SCREEN_MAIN) { LockMutex (DC_image->mutex); if (cmd->scale) TFB_BBox_RegisterCanvas (DC_image->ScaledImg, x - DC_image->last_scale_hs.x, y - DC_image->last_scale_hs.y); else TFB_BBox_RegisterCanvas (DC_image->NormalImg, x - DC_image->NormalHs.x, y - DC_image->NormalHs.y); UnlockMutex (DC_image->mutex); } break; } case TFB_DRAWCOMMANDTYPE_FILLEDIMAGE: { TFB_DrawCommand_FilledImage *cmd = &DC.data.filledimage; TFB_Image *DC_image = cmd->image; const int x = cmd->x; const int y = cmd->y; TFB_DrawCanvas_FilledImage (DC_image, x, y, cmd->scale, cmd->scaleMode, cmd->color, cmd->drawMode, TFB_GetScreenCanvas (cmd->destBuffer)); if (cmd->destBuffer == TFB_SCREEN_MAIN) { LockMutex (DC_image->mutex); if (cmd->scale) TFB_BBox_RegisterCanvas (DC_image->ScaledImg, x - DC_image->last_scale_hs.x, y - DC_image->last_scale_hs.y); else TFB_BBox_RegisterCanvas (DC_image->NormalImg, x - DC_image->NormalHs.x, y - DC_image->NormalHs.y); UnlockMutex (DC_image->mutex); } break; } case TFB_DRAWCOMMANDTYPE_FONTCHAR: { TFB_DrawCommand_FontChar *cmd = &DC.data.fontchar; TFB_Char *DC_char = cmd->fontchar; const int x = cmd->x; const int y = cmd->y; TFB_DrawCanvas_FontChar (DC_char, cmd->backing, x, y, cmd->drawMode, TFB_GetScreenCanvas (cmd->destBuffer)); if (cmd->destBuffer == TFB_SCREEN_MAIN) { RECT r; r.corner.x = x - DC_char->HotSpot.x; r.corner.y = y - DC_char->HotSpot.y; r.extent.width = DC_char->extent.width; r.extent.height = DC_char->extent.height; TFB_BBox_RegisterRect (&r); } break; } case TFB_DRAWCOMMANDTYPE_LINE: { TFB_DrawCommand_Line *cmd = &DC.data.line; if (cmd->destBuffer == TFB_SCREEN_MAIN) { TFB_BBox_RegisterPoint (cmd->x1, cmd->y1); TFB_BBox_RegisterPoint (cmd->x2, cmd->y2); } TFB_DrawCanvas_Line (cmd->x1, cmd->y1, cmd->x2, cmd->y2, cmd->color, cmd->drawMode, TFB_GetScreenCanvas (cmd->destBuffer)); break; } case TFB_DRAWCOMMANDTYPE_RECTANGLE: { TFB_DrawCommand_Rect *cmd = &DC.data.rect; if (cmd->destBuffer == TFB_SCREEN_MAIN) TFB_BBox_RegisterRect (&cmd->rect); TFB_DrawCanvas_Rect (&cmd->rect, cmd->color, cmd->drawMode, TFB_GetScreenCanvas (cmd->destBuffer)); break; } case TFB_DRAWCOMMANDTYPE_SCISSORENABLE: { TFB_DrawCommand_Scissor *cmd = &DC.data.scissor; TFB_DrawCanvas_SetClipRect ( TFB_GetScreenCanvas (TFB_SCREEN_MAIN), &cmd->rect); TFB_BBox_SetClipRect (&DC.data.scissor.rect); break; } case TFB_DRAWCOMMANDTYPE_SCISSORDISABLE: TFB_DrawCanvas_SetClipRect ( TFB_GetScreenCanvas (TFB_SCREEN_MAIN), NULL); TFB_BBox_SetClipRect (NULL); break; case TFB_DRAWCOMMANDTYPE_COPYTOIMAGE: { TFB_DrawCommand_CopyToImage *cmd = &DC.data.copytoimage; TFB_Image *DC_image = cmd->image; const POINT dstPt = {0, 0}; if (DC_image == 0) { log_add (log_Debug, "DCQ ERROR: COPYTOIMAGE passed null " "image ptr"); break; } LockMutex (DC_image->mutex); TFB_DrawCanvas_CopyRect ( TFB_GetScreenCanvas (cmd->srcBuffer), &cmd->rect, DC_image->NormalImg, dstPt); UnlockMutex (DC_image->mutex); break; } case TFB_DRAWCOMMANDTYPE_COPY: { TFB_DrawCommand_Copy *cmd = &DC.data.copy; const RECT r = cmd->rect; if (cmd->destBuffer == TFB_SCREEN_MAIN) TFB_BBox_RegisterRect (&cmd->rect); TFB_DrawCanvas_CopyRect ( TFB_GetScreenCanvas (cmd->srcBuffer), &r, TFB_GetScreenCanvas (cmd->destBuffer), r.corner); break; } case TFB_DRAWCOMMANDTYPE_DELETEIMAGE: { TFB_Image *DC_image = DC.data.deleteimage.image; TFB_DrawImage_Delete (DC_image); break; } case TFB_DRAWCOMMANDTYPE_DELETEDATA: { void *data = DC.data.deletedata.data; HFree (data); break; } case TFB_DRAWCOMMANDTYPE_SENDSIGNAL: ClearSemaphore (DC.data.sendsignal.sem); break; case TFB_DRAWCOMMANDTYPE_REINITVIDEO: { TFB_DrawCommand_ReinitVideo *cmd = &DC.data.reinitvideo; int oldDriver = GraphicsDriver; int oldFlags = GfxFlags; int oldWidth = ScreenWidthActual; int oldHeight = ScreenHeightActual; // JMS_GFX: Added resolutionFactor if (TFB_ReInitGraphics (cmd->driver, cmd->flags, cmd->width, cmd->height, resolutionFactor)) { log_add (log_Error, "Could not provide requested mode: " "reverting to last known driver."); // We don't know what exactly failed, so roll it all back if (TFB_ReInitGraphics (oldDriver, oldFlags, oldWidth, oldHeight, resolutionFactor)) { log_add (log_Fatal, "Couldn't reinit at that point either. " "Your video has been somehow tied in knots."); exit (EXIT_FAILURE); } } TFB_SwapBuffers (TFB_REDRAW_YES); break; } case TFB_DRAWCOMMANDTYPE_CALLBACK: { DC.data.callback.callback (DC.data.callback.arg); break; } } } if (livelock_deterrence) Unlock_DCQ (); TFB_SwapBuffers (TFB_REDRAW_NO); RenderedFrames++; BroadcastCondVar (RenderingCond); }