The Wipes Upgrade
* All those fugly main loop copies are GONE! Replaced by one powerful loop * Chat, menus and console can now be used during wipes * Wipes are now interpolation-enabled * Screenshotting now works consistently
This commit is contained in:
parent
0a893b5395
commit
b34a7cb66b
8 changed files with 199 additions and 176 deletions
224
src/d_main.cpp
224
src/d_main.cpp
|
|
@ -132,6 +132,8 @@ boolean devparm = false; // started game with -devparm
|
|||
boolean singletics = false; // timedemo
|
||||
boolean lastdraw = false;
|
||||
|
||||
static INT32 lastwipetic = 0;
|
||||
|
||||
INT32 postimgparam[MAXSPLITSCREENPLAYERS];
|
||||
|
||||
// These variables are in effect
|
||||
|
|
@ -734,6 +736,178 @@ static bool D_Display(void)
|
|||
return ranwipe;
|
||||
}
|
||||
|
||||
static void D_WipeTick(boolean menu)
|
||||
{
|
||||
I_OsPolling();
|
||||
D_ProcessEvents();
|
||||
|
||||
// Update the network so we don't cause timeouts
|
||||
NetKeepAlive();
|
||||
|
||||
HU_Ticker();
|
||||
if (menu)
|
||||
{
|
||||
#ifdef HAVE_THREADS
|
||||
I_lock_mutex(&m_menu_mutex);
|
||||
#endif
|
||||
M_Ticker();
|
||||
#ifdef HAVE_THREADS
|
||||
I_unlock_mutex(m_menu_mutex);
|
||||
#endif
|
||||
}
|
||||
CON_Ticker();
|
||||
}
|
||||
|
||||
static void D_WipeDraw(boolean menu)
|
||||
{
|
||||
if (rendermode == render_none)
|
||||
return;
|
||||
|
||||
I_UpdateNoBlit();
|
||||
|
||||
HU_Erase();
|
||||
HU_Drawer();
|
||||
if (menu)
|
||||
{
|
||||
#ifdef HAVE_THREADS
|
||||
I_lock_mutex(&m_menu_mutex);
|
||||
#endif
|
||||
M_Drawer();
|
||||
#ifdef HAVE_THREADS
|
||||
I_unlock_mutex(m_menu_mutex);
|
||||
#endif
|
||||
}
|
||||
CON_Drawer();
|
||||
|
||||
I_FinishUpdate(); // page flip or blit buffer
|
||||
}
|
||||
|
||||
static double D_EndFrame(precise_t enterprecise, int *frameskip)
|
||||
{
|
||||
// Fully completed frame made.
|
||||
precise_t finishprecise = I_GetPreciseTime();
|
||||
|
||||
// Use the time before sleep for frameskip calculations:
|
||||
// post-sleep time is literally being intentionally wasted
|
||||
double deltasecs = (double)((INT64)(finishprecise - enterprecise)) / I_GetPrecisePrecision();
|
||||
double deltatics = deltasecs * NEWTICRATE;
|
||||
|
||||
// If time spent this game loop exceeds a single tic,
|
||||
// it's probably because of rendering.
|
||||
//
|
||||
// Skip rendering the next frame, up to a limit of 3
|
||||
// frames before a frame is rendered no matter what.
|
||||
if (frameskip)
|
||||
{
|
||||
if (*frameskip < 3 && deltatics > 1.0)
|
||||
*frameskip += 1;
|
||||
else
|
||||
*frameskip = 0;
|
||||
}
|
||||
|
||||
if (!singletics)
|
||||
{
|
||||
precise_t elapsed = finishprecise - enterprecise;
|
||||
|
||||
// capbudget is the minimum precise_t duration of a single loop iteration
|
||||
precise_t capbudget = round((1.0 / R_GetFramerateCap()) * I_GetPrecisePrecision());
|
||||
|
||||
// in the case of "match refresh rate" + vsync, don't sleep at all
|
||||
const boolean vsync_with_match_refresh = cv_vidwait.value && cv_fpscap.value == 0;
|
||||
|
||||
if (elapsed > 0 && capbudget > elapsed && !vsync_with_match_refresh)
|
||||
{
|
||||
I_SleepDuration(capbudget - (finishprecise - enterprecise));
|
||||
}
|
||||
}
|
||||
|
||||
// Capture the time once more to get the real delta time.
|
||||
finishprecise = I_GetPreciseTime();
|
||||
deltasecs = (double)((INT64)(finishprecise - enterprecise)) / I_GetPrecisePrecision();
|
||||
return deltasecs * NEWTICRATE;
|
||||
}
|
||||
|
||||
static INT32 endtimes[] = {
|
||||
[WIPELOOP_RUNWIPE] = UINT8_MAX,
|
||||
[WIPELOOP_TITLECARD] = PRELEVELTIME*NEWTICRATERATIO,
|
||||
[WIPELOOP_ENCORE] = 3*TICRATE/2,
|
||||
[WIPELOOP_TITLEBLACK] = NEWTICRATE, // Shortened the quit time, used to be 2 seconds
|
||||
};
|
||||
|
||||
// one single function for all the extra main loops in this god-forsaken codebase
|
||||
// should make it easier to fold these back into D_SRB2Loop at some point...
|
||||
void D_WipeLoop(wipelooptype_t type, UINT8 wipetype, boolean drawMenu)
|
||||
{
|
||||
tic_t nowtime, endtime;
|
||||
UINT8 wipeframe = 0;
|
||||
fademask_t *fmask;
|
||||
double delta = 0.0;
|
||||
|
||||
nowtime = lastwipetic = I_GetTime();
|
||||
endtime = nowtime + endtimes[type];
|
||||
|
||||
if (gamestate != GS_TITLESCREEN && gamestate != GS_TIMEATTACK)
|
||||
drawMenu = true;
|
||||
|
||||
// lastwipetic should either be 0 or the tic we last wiped
|
||||
// on for fade-to-black
|
||||
while (nowtime < endtime)
|
||||
{
|
||||
precise_t enterprecise = I_GetPreciseTime();
|
||||
I_UpdateTime(cv_timescale.value);
|
||||
|
||||
nowtime = I_GetTime();
|
||||
|
||||
// wait loop
|
||||
if (nowtime - lastwipetic)
|
||||
{
|
||||
renderisnewtic = true;
|
||||
wipeframe++;
|
||||
D_WipeTick(drawMenu);
|
||||
if (type == WIPELOOP_TITLECARD)
|
||||
ST_runTitleCard();
|
||||
}
|
||||
else
|
||||
renderisnewtic = false;
|
||||
|
||||
// draw loop
|
||||
rendertimefrac = g_time.timefrac;
|
||||
renderdeltatics = FLOAT_TO_FIXED(delta);
|
||||
|
||||
#ifndef NOWIPE
|
||||
if (type == WIPELOOP_RUNWIPE)
|
||||
{
|
||||
// get fademask first so we can tell if it exists or not
|
||||
fmask = F_GetFadeMask(wipetype, wipeframe);
|
||||
if (!fmask)
|
||||
break;
|
||||
|
||||
#ifdef HWRENDER
|
||||
if (rendermode == render_opengl)
|
||||
HWR_DoWipe(wipetype, wipeframe); // send in the wipe type and wipeframe because we need to cache the graphic
|
||||
else
|
||||
#endif
|
||||
if (rendermode != render_none) //this allows F_RunWipe to be called in dedicated servers
|
||||
F_DoWipe(fmask);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (type == WIPELOOP_TITLECARD)
|
||||
ST_preLevelTitleCardDrawer();
|
||||
|
||||
D_WipeDraw(drawMenu);
|
||||
|
||||
// Only take screenshots after drawing.
|
||||
if (moviemode)
|
||||
M_SaveFrame();
|
||||
if (takescreenshot)
|
||||
M_DoScreenShot();
|
||||
|
||||
delta = D_EndFrame(enterprecise, NULL);
|
||||
lastwipetic = nowtime;
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// D_SRB2Loop
|
||||
// =========================================================================
|
||||
|
|
@ -744,7 +918,6 @@ void D_SRB2Loop(void)
|
|||
{
|
||||
tic_t entertic = 0, oldentertics = 0, realtics = 0, rendertimeout = INFTICS;
|
||||
double deltatics = 0.0;
|
||||
double deltasecs = 0.0;
|
||||
|
||||
boolean interp = false;
|
||||
boolean doDisplay = false;
|
||||
|
|
@ -797,20 +970,11 @@ void D_SRB2Loop(void)
|
|||
|
||||
for (;;)
|
||||
{
|
||||
// capbudget is the minimum precise_t duration of a single loop iteration
|
||||
precise_t capbudget;
|
||||
precise_t enterprecise = I_GetPreciseTime();
|
||||
precise_t finishprecise = enterprecise;
|
||||
|
||||
g_dc = {};
|
||||
Z_Frame_Reset();
|
||||
|
||||
{
|
||||
// Casting the return value of a function is bad practice (apparently)
|
||||
double budget = round((1.0 / R_GetFramerateCap()) * I_GetPrecisePrecision());
|
||||
capbudget = (precise_t) budget;
|
||||
}
|
||||
|
||||
bool ranwipe = false;
|
||||
|
||||
I_UpdateTime(cv_timescale.value);
|
||||
|
|
@ -965,47 +1129,11 @@ void D_SRB2Loop(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
// Fully completed frame made.
|
||||
finishprecise = I_GetPreciseTime();
|
||||
|
||||
// Use the time before sleep for frameskip calculations:
|
||||
// post-sleep time is literally being intentionally wasted
|
||||
deltasecs = (double)((INT64)(finishprecise - enterprecise)) / I_GetPrecisePrecision();
|
||||
deltatics = deltasecs * NEWTICRATE;
|
||||
|
||||
// If time spent this game loop exceeds a single tic,
|
||||
// it's probably because of rendering.
|
||||
//
|
||||
// Skip rendering the next frame, up to a limit of 3
|
||||
// frames before a frame is rendered no matter what.
|
||||
//
|
||||
// Wipes run an inner loop and artificially increase
|
||||
// the measured time.
|
||||
if (!ranwipe && frameskip < 3 && deltatics > 1.0)
|
||||
{
|
||||
frameskip++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ranwipe)
|
||||
frameskip = 0;
|
||||
}
|
||||
|
||||
if (!singletics)
|
||||
{
|
||||
INT64 elapsed = (INT64)(finishprecise - enterprecise);
|
||||
|
||||
// in the case of "match refresh rate" + vsync, don't sleep at all
|
||||
const boolean vsync_with_match_refresh = cv_vidwait.value && cv_fpscap.value == 0;
|
||||
|
||||
if (elapsed > 0 && (INT64)capbudget > elapsed && !vsync_with_match_refresh)
|
||||
{
|
||||
I_SleepDuration(capbudget - (finishprecise - enterprecise));
|
||||
}
|
||||
}
|
||||
// Capture the time once more to get the real delta time.
|
||||
finishprecise = I_GetPreciseTime();
|
||||
deltasecs = (double)((INT64)(finishprecise - enterprecise)) / I_GetPrecisePrecision();
|
||||
deltatics = deltasecs * NEWTICRATE;
|
||||
deltatics = D_EndFrame(enterprecise, !ranwipe ? &frameskip : NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
10
src/d_main.h
10
src/d_main.h
|
|
@ -61,6 +61,16 @@ extern char srb2path[256]; //Alam: SRB2's Home
|
|||
// the infinite loop of D_SRB2Loop() called from win_main for windows version
|
||||
void D_SRB2Loop(void) FUNCNORETURN;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
WIPELOOP_RUNWIPE,
|
||||
WIPELOOP_TITLECARD,
|
||||
WIPELOOP_ENCORE,
|
||||
WIPELOOP_TITLEBLACK,
|
||||
} wipelooptype_t;
|
||||
|
||||
void D_WipeLoop(wipelooptype_t type, UINT8 wipetype, boolean drawMenu);
|
||||
|
||||
//
|
||||
// D_SRB2Main()
|
||||
// Not a globally visible function, just included for source reference,
|
||||
|
|
|
|||
|
|
@ -431,34 +431,7 @@ void F_IntroTicker(void)
|
|||
}
|
||||
|
||||
// Stay on black for a bit. =)
|
||||
{
|
||||
tic_t nowtime, quittime, lasttime;
|
||||
nowtime = lasttime = I_GetTime();
|
||||
quittime = nowtime + NEWTICRATE; // Shortened the quit time, used to be 2 seconds
|
||||
while (quittime > nowtime)
|
||||
{
|
||||
while (!((nowtime = I_GetTime()) - lasttime))
|
||||
{
|
||||
I_Sleep(cv_sleep.value);
|
||||
I_UpdateTime(cv_timescale.value);
|
||||
}
|
||||
lasttime = nowtime;
|
||||
|
||||
I_OsPolling();
|
||||
I_UpdateNoBlit();
|
||||
#ifdef HAVE_THREADS
|
||||
I_lock_mutex(&m_menu_mutex);
|
||||
#endif
|
||||
M_Drawer(); // menu is drawn even on top of wipes
|
||||
#ifdef HAVE_THREADS
|
||||
I_unlock_mutex(m_menu_mutex);
|
||||
#endif
|
||||
I_FinishUpdate(); // Update the screen with the image Tails 06-19-2001
|
||||
|
||||
if (moviemode) // make sure we save frames for the white hold too
|
||||
M_SaveFrame();
|
||||
}
|
||||
}
|
||||
D_WipeLoop(WIPELOOP_TITLEBLACK, 0, false);
|
||||
|
||||
D_StartTitle();
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -145,12 +145,12 @@ void F_MenuPresTicker(boolean run);
|
|||
extern boolean WipeInAction;
|
||||
extern boolean WipeStageTitle;
|
||||
|
||||
extern INT32 lastwipetic;
|
||||
|
||||
// Don't know where else to place this constant
|
||||
// But this file seems appropriate
|
||||
#define PRELEVELTIME TICRATE // frames in tics
|
||||
|
||||
fademask_t *F_GetFadeMask(UINT8 masknum, UINT8 scrnnum);
|
||||
void F_DoWipe(fademask_t *fademask);
|
||||
void F_WipeStartScreen(void);
|
||||
void F_WipeEndScreen(void);
|
||||
void F_RunWipe(UINT8 wipetype, boolean drawMenu);
|
||||
|
|
|
|||
59
src/f_wipe.c
59
src/f_wipe.c
|
|
@ -43,12 +43,12 @@
|
|||
#define NOWIPE // do not enable wipe image post processing for ARM, SH and MIPS CPUs
|
||||
#endif
|
||||
|
||||
typedef struct fademask_s {
|
||||
struct fademask_t {
|
||||
UINT8* mask;
|
||||
UINT16 width, height;
|
||||
size_t size;
|
||||
fixed_t xscale, yscale;
|
||||
} fademask_t;
|
||||
};
|
||||
|
||||
UINT8 wipedefs[NUMWIPEDEFS] = {
|
||||
99, // wipe_credits_intermediate (0)
|
||||
|
|
@ -85,7 +85,6 @@ UINT8 wipedefs[NUMWIPEDEFS] = {
|
|||
|
||||
boolean WipeInAction = false;
|
||||
boolean WipeStageTitle = false;
|
||||
INT32 lastwipetic = 0;
|
||||
|
||||
#ifndef NOWIPE
|
||||
|
||||
|
|
@ -101,7 +100,7 @@ static fixed_t paldiv;
|
|||
* \param lump Lump name to get data from
|
||||
* \return fademask_t for lump
|
||||
*/
|
||||
static fademask_t *F_GetFadeMask(UINT8 masknum, UINT8 scrnnum) {
|
||||
fademask_t *F_GetFadeMask(UINT8 masknum, UINT8 scrnnum) {
|
||||
static char lumpname[9] = "FADEmmss";
|
||||
static fademask_t fm = {NULL,0,0,0,0,0};
|
||||
lumpnum_t lumpnum;
|
||||
|
|
@ -186,7 +185,7 @@ static fademask_t *F_GetFadeMask(UINT8 masknum, UINT8 scrnnum) {
|
|||
*
|
||||
* \param fademask pixels to change
|
||||
*/
|
||||
static void F_DoWipe(fademask_t *fademask)
|
||||
void F_DoWipe(fademask_t *fademask)
|
||||
{
|
||||
// Software mask wipe -- optimized; though it might not look like it!
|
||||
// Okay, to save you wondering *how* this is more optimized than the simpler
|
||||
|
|
@ -360,62 +359,14 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu)
|
|||
(void)wipetype;
|
||||
(void)drawMenu;
|
||||
#else
|
||||
tic_t nowtime;
|
||||
UINT8 wipeframe = 0;
|
||||
fademask_t *fmask;
|
||||
|
||||
paldiv = FixedDiv(257<<FRACBITS, 11<<FRACBITS);
|
||||
|
||||
// Init the wipe
|
||||
WipeInAction = true;
|
||||
wipe_scr = screens[0];
|
||||
|
||||
// lastwipetic should either be 0 or the tic we last wiped
|
||||
// on for fade-to-black
|
||||
for (;;)
|
||||
{
|
||||
// get fademask first so we can tell if it exists or not
|
||||
fmask = F_GetFadeMask(wipetype, wipeframe++);
|
||||
if (!fmask)
|
||||
break;
|
||||
D_WipeLoop(WIPELOOP_RUNWIPE, wipetype, drawMenu);
|
||||
|
||||
// wait loop
|
||||
while (!((nowtime = I_GetTime()) - lastwipetic))
|
||||
{
|
||||
I_Sleep(cv_sleep.value);
|
||||
I_UpdateTime(cv_timescale.value);
|
||||
}
|
||||
lastwipetic = nowtime;
|
||||
|
||||
#ifdef HWRENDER
|
||||
if (rendermode == render_opengl)
|
||||
HWR_DoWipe(wipetype, wipeframe-1); // send in the wipe type and wipeframe because we need to cache the graphic
|
||||
else
|
||||
#endif
|
||||
if (rendermode != render_none) //this allows F_RunWipe to be called in dedicated servers
|
||||
F_DoWipe(fmask);
|
||||
|
||||
I_OsPolling();
|
||||
I_UpdateNoBlit();
|
||||
|
||||
if (drawMenu && rendermode != render_none)
|
||||
{
|
||||
#ifdef HAVE_THREADS
|
||||
I_lock_mutex(&m_menu_mutex);
|
||||
#endif
|
||||
M_Drawer(); // menu is drawn even on top of wipes
|
||||
#ifdef HAVE_THREADS
|
||||
I_unlock_mutex(m_menu_mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
I_FinishUpdate(); // page flip or blit buffer
|
||||
|
||||
if (moviemode)
|
||||
M_SaveFrame();
|
||||
|
||||
NetKeepAlive(); // Update the network so we don't cause timeouts
|
||||
}
|
||||
WipeInAction = false;
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
25
src/g_game.c
25
src/g_game.c
|
|
@ -1507,30 +1507,7 @@ void G_StartTitleCard(void)
|
|||
void G_PreLevelTitleCard(void)
|
||||
{
|
||||
#ifndef NOWIPE
|
||||
tic_t strtime = I_GetTime();
|
||||
tic_t endtime = strtime + (PRELEVELTIME*NEWTICRATERATIO);
|
||||
tic_t nowtime = strtime;
|
||||
tic_t lasttime = strtime;
|
||||
while (nowtime < endtime)
|
||||
{
|
||||
// draw loop
|
||||
ST_runTitleCard();
|
||||
ST_preLevelTitleCardDrawer();
|
||||
I_FinishUpdate(); // page flip or blit buffer
|
||||
NetKeepAlive(); // Prevent timeouts
|
||||
|
||||
if (moviemode)
|
||||
M_SaveFrame();
|
||||
if (takescreenshot) // Only take screenshots after drawing.
|
||||
M_DoScreenShot();
|
||||
|
||||
while (!((nowtime = I_GetTime()) - lasttime))
|
||||
{
|
||||
I_Sleep(cv_sleep.value);
|
||||
I_UpdateTime(cv_timescale.value);
|
||||
}
|
||||
lasttime = nowtime;
|
||||
}
|
||||
D_WipeLoop(WIPELOOP_TITLECARD, 0, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8637,8 +8637,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
|||
// This is handled BEFORE sounds are stopped.
|
||||
else if (encoremode && !prevencoremode && !demo.rewinding)
|
||||
{
|
||||
tic_t locstarttime, endtime, nowtime;
|
||||
|
||||
if (rendermode != render_none)
|
||||
{
|
||||
S_StopMusic(); // er, about that...
|
||||
|
|
@ -8663,25 +8661,8 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
|||
F_RunWipe(wipedefs[wipe_level_final], false);
|
||||
}
|
||||
|
||||
locstarttime = nowtime = lastwipetic;
|
||||
endtime = locstarttime + (3*TICRATE)/2;
|
||||
|
||||
// Hold on white for extra effect.
|
||||
while (nowtime < endtime)
|
||||
{
|
||||
// wait loop
|
||||
while (!((nowtime = I_GetTime()) - lastwipetic))
|
||||
{
|
||||
I_Sleep(cv_sleep.value);
|
||||
I_UpdateTime(cv_timescale.value);
|
||||
}
|
||||
lastwipetic = nowtime;
|
||||
if (moviemode) // make sure we save frames for the white hold too
|
||||
M_SaveFrame();
|
||||
|
||||
// Keep the network alive
|
||||
NetKeepAlive();
|
||||
}
|
||||
D_WipeLoop(WIPELOOP_ENCORE, 0, false);
|
||||
|
||||
ranspecialwipe = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,6 +121,9 @@ TYPEDEF (cupheader_t);
|
|||
TYPEDEF (exitcondition_t);
|
||||
TYPEDEF (mapheader_lighting_t);
|
||||
|
||||
// f_finale.h
|
||||
TYPEDEF (fademask_t);
|
||||
|
||||
// font.h
|
||||
TYPEDEF (font_t);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue