- rewrote the ScreenJob player as a class that can be called by an asynchronous dispatcher.

Works, except for timing issues with ANMs.
This commit is contained in:
Christoph Oelckers 2020-07-19 11:57:00 +02:00
parent 2ce612e8ab
commit 31b9995406
9 changed files with 263 additions and 114 deletions

View file

@ -67,95 +67,6 @@ int DImageScreen::Frame(uint64_t clock, bool skiprequest)
return skiprequest ? -1 : 1;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void RunScreenJob(JobDesc *jobs, int count, CompletionFunc completion, bool clearbefore)
{
// Release all jobs from the garbage collector - the code as it is cannot deal with them getting collected.
for (int i = 0; i < count; i++)
{
jobs[i].job->Release();
}
bool skipped = false;
for (int i = 0; i < count; i++)
{
auto job = jobs[i];
if (job.job != nullptr && (!skipped || !job.ignoreifskipped))
{
skipped = false;
if (clearbefore)
{
twod->ClearScreen();
videoNextPage();
}
auto startTime = I_nsTime();
// Input later needs to be event based so that these screens can do more than be skipped.
inputState.ClearAllInput();
float screenfade = job.job->fadestyle & DScreenJob::fadein ? 0.f : 1.f;
while (true)
{
auto now = I_nsTime();
handleevents();
bool skiprequest = inputState.CheckAllInput();
auto clock = now - startTime;
if (screenfade < 1.f)
{
float ms = (clock / 1'000'000) / job.job->fadetime;
screenfade = clamp(ms, 0.f, 1.f);
twod->SetScreenFade(screenfade);
job.job->fadestate = DScreenJob::fadein;
}
else job.job->fadestate = DScreenJob::visible;
job.job->SetClock(clock);
int state = job.job->Frame(clock, skiprequest);
startTime -= job.job->GetClock() - clock;
// Must lock before displaying.
if (state < 1 && job.job->fadestyle & DScreenJob::fadeout)
twod->Lock();
videoNextPage();
if (state < 1)
{
if (job.job->fadestyle & DScreenJob::fadeout)
{
job.job->fadestate = DScreenJob::fadeout;
startTime = now;
float screenfade2;
do
{
now = I_nsTime();
auto clock = now - startTime;
float ms = (clock / 1'000'000) / job.job->fadetime;
screenfade2 = clamp(screenfade - ms, 0.f, 1.f);
twod->SetScreenFade(screenfade2);
if (screenfade2 <= 0.f) twod->Unlock(); // must unlock before displaying.
videoNextPage();
} while (screenfade2 > 0.f);
}
skipped = state < 0;
job.job->Destroy();
job.job->ObjectFlags |= OF_YesReallyDelete;
delete job.job;
twod->SetScreenFade(1);
break;
}
}
}
if (job.postAction) job.postAction();
}
if (completion) completion(false);
}
//---------------------------------------------------------------------------
//
//
@ -305,3 +216,235 @@ DScreenJob *PlayVideo(const char* filename, const AnimSound* ans, const int* fra
return nothing();
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class ScreenJobRunner
{
enum
{
State_Clear,
State_Run,
State_Fadeout
};
TArray<JobDesc> jobs;
CompletionFunc completion;
int index = -1;
float screenfade;
bool clearbefore;
bool skipped = false;
uint64_t startTime;
int actionState;
int terminateState;
public:
ScreenJobRunner(JobDesc* jobs_, int count, CompletionFunc completion_, bool clearbefore_)
: completion(std::move(completion_)), clearbefore(clearbefore_)
{
jobs.Resize(count);
memcpy(jobs.Data(), jobs_, count * sizeof(JobDesc));
// Release all jobs from the garbage collector - the code as it is cannot deal with them getting collected. This should be removed later once the GC is working.
for (int i = 0; i < count; i++)
{
jobs[i].job->Release();
}
AdvanceJob(false);
}
void AdvanceJob(bool skip)
{
index++;
while (index < jobs.Size() && (jobs[index].job == nullptr || (skip && jobs[index].ignoreifskipped))) index++;
actionState = clearbefore ? State_Clear : State_Run;
if (index < jobs.Size()) screenfade = jobs[index].job->fadestyle & DScreenJob::fadein ? 0.f : 1.f;
}
int DisplayFrame()
{
auto& job = jobs[index];
auto now = I_nsTime();
bool skiprequest = inputState.CheckAllInput();
auto clock = now - startTime;
if (screenfade < 1.f)
{
float ms = (clock / 1'000'000) / job.job->fadetime;
screenfade = clamp(ms, 0.f, 1.f);
twod->SetScreenFade(screenfade);
job.job->fadestate = DScreenJob::fadein;
}
else job.job->fadestate = DScreenJob::visible;
job.job->SetClock(clock);
int state = job.job->Frame(clock, skiprequest);
startTime -= job.job->GetClock() - clock;
return state;
}
int FadeoutFrame()
{
auto now = I_nsTime();
auto clock = now - startTime;
float ms = (clock / 1'000'000) / jobs[index].job->fadetime;
float screenfade2 = clamp(screenfade - ms, 0.f, 1.f);
twod->SetScreenFade(screenfade2);
if (screenfade2 <= 0.f)
{
twod->Unlock(); // must unlock before displaying.
return 0;
}
return 1;
}
bool RunFrame()
{
if (index >= jobs.Size())
{
for (auto& job : jobs)
{
job.job->Destroy();
job.job->ObjectFlags |= OF_YesReallyDelete;
delete job.job;
}
twod->SetScreenFade(1);
if (completion) completion(false);
return false;
}
handleevents();
if (actionState == State_Clear)
{
actionState = State_Run;
twod->ClearScreen();
}
else if (actionState == State_Run)
{
terminateState = DisplayFrame();
if (terminateState < 1)
{
// Must lock before displaying.
if (jobs[index].job->fadestyle & DScreenJob::fadeout)
{
twod->Lock();
startTime = I_nsTime();
jobs[index].job->fadestate = DScreenJob::fadeout;
actionState = State_Fadeout;
}
else
{
AdvanceJob(terminateState < 0);
}
}
}
else if (actionState == State_Fadeout)
{
int ended = FadeoutFrame();
if (ended < 1)
{
AdvanceJob(terminateState < 0);
}
}
return true;
}
};
void RunScreenJob(JobDesc* jobs, int count, CompletionFunc completion, bool clearbefore)
{
ScreenJobRunner runner(jobs, count, completion, clearbefore);
while (runner.RunFrame())
{
videoNextPage();
}
}
#if 0
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void RunScreenJobSync(JobDesc* jobs, int count, CompletionFunc completion, bool clearbefore)
{
// Release all jobs from the garbage collector - the code as it is cannot deal with them getting collected.
for (int i = 0; i < count; i++)
{
jobs[i].job->Release();
}
bool skipped = false;
for (int i = 0; i < count; i++)
{
auto job = jobs[i];
if (job.job != nullptr && (!skipped || !job.ignoreifskipped))
{
skipped = false;
if (clearbefore)
{
twod->ClearScreen();
videoNextPage();
}
auto startTime = I_nsTime();
// Input later needs to be event based so that these screens can do more than be skipped.
inputState.ClearAllInput();
float screenfade = job.job->fadestyle & DScreenJob::fadein ? 0.f : 1.f;
while (true)
{
auto now = I_nsTime();
handleevents();
bool skiprequest = inputState.CheckAllInput();
auto clock = now - startTime;
if (screenfade < 1.f)
{
float ms = (clock / 1'000'000) / job.job->fadetime;
screenfade = clamp(ms, 0.f, 1.f);
twod->SetScreenFade(screenfade);
job.job->fadestate = DScreenJob::fadein;
}
else job.job->fadestate = DScreenJob::visible;
job.job->SetClock(clock);
int state = job.job->Frame(clock, skiprequest);
startTime -= job.job->GetClock() - clock;
// Must lock before displaying.
if (state < 1 && job.job->fadestyle & DScreenJob::fadeout)
twod->Lock();
videoNextPage();
if (state < 1)
{
if (job.job->fadestyle & DScreenJob::fadeout)
{
job.job->fadestate = DScreenJob::fadeout;
startTime = now;
float screenfade2;
do
{
now = I_nsTime();
auto clock = now - startTime;
float ms = (clock / 1'000'000) / job.job->fadetime;
screenfade2 = clamp(screenfade - ms, 0.f, 1.f);
twod->SetScreenFade(screenfade2);
if (screenfade2 <= 0.f) twod->Unlock(); // must unlock before displaying.
videoNextPage();
} while (screenfade2 > 0.f);
}
skipped = state < 0;
job.job->Destroy();
job.job->ObjectFlags |= OF_YesReallyDelete;
delete job.job;
twod->SetScreenFade(1);
break;
}
}
}
if (job.postAction) job.postAction();
}
if (completion) completion(false);
}
#endif

View file

@ -4,6 +4,7 @@
using CompletionFunc = std::function<void(bool)>;
struct JobDesc;
class ScreenJobRunner;
class DScreenJob : public DObject
{
@ -14,6 +15,7 @@ class DScreenJob : public DObject
int fadestate = fadein;
friend void RunScreenJob(JobDesc* jobs, int count, CompletionFunc completion, bool clearbefore);
friend class ScreenJobRunner;
public:
enum
@ -79,6 +81,7 @@ struct JobDesc
void RunScreenJob(JobDesc *jobs, int count, CompletionFunc completion, bool clearbefore = true);
void RunScreenJobSync(JobDesc* jobs, int count, CompletionFunc completion, bool clearbefore = true);
struct AnimSound
{

View file

@ -253,7 +253,7 @@ public:
//
//---------------------------------------------------------------------------
void Logo_d(CompletionFunc completion)
void Logo_d(const CompletionFunc &completion)
{
Mus_Stop();
FX_StopAllSounds(); // JBF 20031228
@ -636,7 +636,7 @@ static void bonussequence_d(int num, JobDesc *jobs, int &job)
//
//---------------------------------------------------------------------------
void showtwoscreens(CompletionFunc completion)
void showtwoscreens(const CompletionFunc& completion)
{
JobDesc jobs[2];
int job = 0;
@ -646,7 +646,7 @@ void showtwoscreens(CompletionFunc completion)
RunScreenJob(jobs, job, completion);
}
void doorders(CompletionFunc completion)
void doorders(const CompletionFunc& completion)
{
JobDesc jobs[4];
int job = 0;
@ -956,7 +956,7 @@ public:
//
//---------------------------------------------------------------------------
void dobonus_d(bool bonusonly, CompletionFunc completion)
void dobonus_d(bool bonusonly, const CompletionFunc& completion)
{
JobDesc jobs[20];
int job = 0;
@ -988,7 +988,7 @@ void dobonus_d(bool bonusonly, CompletionFunc completion)
//
//---------------------------------------------------------------------------
void e4intro(CompletionFunc completion)
void e4intro(const CompletionFunc& completion)
{
JobDesc jobs[5];
int job = 0;

View file

@ -164,7 +164,7 @@ static void MiniText(double x, double y, const char* t, int shade, int align = -
//
//---------------------------------------------------------------------------
void Logo_r(CompletionFunc completion)
void Logo_r(const CompletionFunc& completion)
{
Mus_Stop();
FX_StopAllSounds(); // JBF 20031228
@ -552,7 +552,7 @@ public:
//
//---------------------------------------------------------------------------
void dobonus_r(bool bonusonly, CompletionFunc completion)
void dobonus_r(bool bonusonly, const CompletionFunc& completion)
{
JobDesc jobs[20];
int job = 0;

View file

@ -114,8 +114,8 @@ void think_r();
void animatesprites_d(int x, int y, int a, int smoothratio);
void animatesprites_r(int x, int y, int a, int smoothratio);
void Logo_d(CompletionFunc);
void Logo_r(CompletionFunc);
void Logo_d(const CompletionFunc&);
void Logo_r(const CompletionFunc&);
void InitFonts_d();
void InitFonts_r();
void PrintPaused_d();

View file

@ -69,7 +69,7 @@ struct GameInterface : ::GameInterface
struct Dispatcher
{
// global stuff
void (*ShowLogo)(CompletionFunc completion);
void (*ShowLogo)(const CompletionFunc& completion);
void (*InitFonts)();
void (*PrintPaused)();

View file

@ -156,8 +156,8 @@ void setsectinterpolate(int sprnum);
int LocateTheLocator(int const tag, int const sectnum);
void clearcamera(player_struct* ps);
void showtwoscreens(CompletionFunc func);
void doorders(CompletionFunc func);
void showtwoscreens(const CompletionFunc& func);
void doorders(const CompletionFunc& func);
void execute(int s, int p, int d);
void makeitfall(int s);
@ -208,9 +208,9 @@ void drawstatusbar_d(int snum);
void drawstatusbar_r(int snum);
void drawoverheadmap(int cposx, int cposy, int czoom, int cang);
void cameratext(int i);
void dobonus(int bonusonly, CompletionFunc completion);
void dobonus_d(bool bonusonly, CompletionFunc completion);
void dobonus_r(bool bonusonly, CompletionFunc completion);
void dobonus(int bonusonly, const CompletionFunc& completion);
void dobonus_d(bool bonusonly, const CompletionFunc& completion);
void dobonus_r(bool bonusonly, const CompletionFunc& completion);
void displayrest(int32_t smoothratio);
void drawbackground(void);
@ -221,11 +221,10 @@ void resettimevars();
bool setnextmap(bool checksecretexit);
void prelevel_d(int g);
void prelevel_r(int g);
void e4intro(CompletionFunc completion);
void e4intro(const CompletionFunc& completion);
void clearfrags(void);
void exitlevel();
int enterlevel(MapRecord* mi, int gm);
void newgame(MapRecord* mi, int sk, CompletionFunc completion);
void donewgame(MapRecord* map, int sk);
void startnewgame(MapRecord* map, int skill);
void setlocalplayerinput(player_struct *pp);

View file

@ -109,22 +109,25 @@ void setmapfog(int fogtype)
//
//---------------------------------------------------------------------------
static void runbonus(CompletionFunc completion)
template<class func>
void runbonus(func completion)
{
// MP scoreboard
if (playerswhenstarted > 1 && ps[myconnectindex].gm & MODE_GAME && !ud.coop)
{
dobonus(1, completion);
}
else if (completion) completion(false);
else completion(false);
}
static void runtwoscreens(CompletionFunc completion)
template <class func>
void runtwoscreens(func completion)
{
// shareware and TEN screens
if (!VOLUMEALL && !isRR())
showtwoscreens(completion);
else if (completion) completion(false);
else completion(false);
}
static void endthegame(bool)
@ -722,7 +725,7 @@ void cameratext(int i)
//
//---------------------------------------------------------------------------
void dobonus(int bonusonly, CompletionFunc completion)
void dobonus(int bonusonly, const CompletionFunc& completion)
{
if (isRRRA()) { if (completion) completion(false); }
else if (isRR()) dobonus_r(bonusonly, completion);

View file

@ -837,7 +837,8 @@ void donewgame(MapRecord* map, int sk)
}
}
void newgame(MapRecord* map, int sk, CompletionFunc completion)
template<class func>
void newgame(MapRecord* map, int sk, func completion)
{
handleevents();
ready2send = 0;
@ -846,12 +847,12 @@ void newgame(MapRecord* map, int sk, CompletionFunc completion)
{
if (!isRR() && map->levelNumber == levelnum(3, 0) && (ud.multimode < 2))
{
e4intro([=](bool) { donewgame(map, sk); if (completion) completion(res); });
e4intro([=](bool) { donewgame(map, sk); completion(res); });
}
else
{
donewgame(map, sk);
if (completion) completion(res);
completion(res);
}
};