- another backend update from GZDoom.

This commit is contained in:
Christoph Oelckers 2022-06-06 15:28:41 +02:00
parent f599040212
commit 998def2487
4 changed files with 100 additions and 75 deletions

View file

@ -54,10 +54,7 @@
CVAR(Bool, inter_subtitles, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); CVAR(Bool, inter_subtitles, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
DObject* runner; CutsceneState cutscene;
PClass* runnerclass;
PType* runnerclasstype;
CompletionFunc completion;
static int ticks; static int ticks;
//============================================================================= //=============================================================================
@ -72,11 +69,11 @@ void Job_Init()
if (!done) if (!done)
{ {
done = true; done = true;
GC::AddMarkerFunc([] { GC::Mark(runner); }); GC::AddMarkerFunc([] { GC::Mark(cutscene.runner); });
} }
runnerclass = PClass::FindClass("ScreenJobRunner"); cutscene.runnerclass = PClass::FindClass("ScreenJobRunner");
if (!runnerclass) I_FatalError("ScreenJobRunner not defined"); if (!cutscene.runnerclass) I_FatalError("ScreenJobRunner not defined");
runnerclasstype = NewPointer(runnerclass); cutscene.runnerclasstype = NewPointer(cutscene.runnerclass);
} }
//============================================================================= //=============================================================================
@ -115,7 +112,7 @@ void CallCreateFunction(const char* qname, DObject* runner)
{ {
auto func = LookupFunction(qname); auto func = LookupFunction(qname);
if (func->Proto->ArgumentTypes.Size() != 1) I_Error("Bad cutscene function %s. Must receive precisely one argument.", qname); if (func->Proto->ArgumentTypes.Size() != 1) I_Error("Bad cutscene function %s. Must receive precisely one argument.", qname);
if (func->Proto->ArgumentTypes[0] != runnerclasstype) I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference.", qname); if (func->Proto->ArgumentTypes[0] != cutscene.runnerclasstype) I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference.", qname);
VMValue val = runner; VMValue val = runner;
VMCall(func, &val, 1, nullptr, 0); VMCall(func, &val, 1, nullptr, 0);
} }
@ -128,7 +125,7 @@ void CallCreateFunction(const char* qname, DObject* runner)
DObject* CreateRunner(bool clearbefore) DObject* CreateRunner(bool clearbefore)
{ {
auto obj = runnerclass->CreateNew(); auto obj = cutscene.runnerclass->CreateNew();
auto func = LookupFunction("ScreenJobRunner.Init", false); auto func = LookupFunction("ScreenJobRunner.Init", false);
VMValue val[3] = { obj, clearbefore, false }; VMValue val[3] = { obj, clearbefore, false };
VMCall(func, val, 3, nullptr, 0); VMCall(func, val, 3, nullptr, 0);
@ -182,15 +179,15 @@ void CutsceneDef::Create(DObject* runner)
void DeleteScreenJob() void DeleteScreenJob()
{ {
if (runner) runner->Destroy(); if (cutscene.runner) cutscene.runner->Destroy();
runner = nullptr; cutscene.runner = nullptr;
} }
void EndScreenJob() void EndScreenJob()
{ {
DeleteScreenJob(); DeleteScreenJob();
if (completion) completion(false); if (cutscene.completion) cutscene.completion(false);
completion = nullptr; cutscene.completion = nullptr;
} }
@ -213,12 +210,12 @@ bool ScreenJobResponder(event_t* ev)
} }
} }
FInputEvent evt = ev; FInputEvent evt = ev;
if (runner) if (cutscene.runner)
{ {
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnEvent) IFVIRTUALPTRNAME(cutscene.runner, NAME_ScreenJobRunner, OnEvent)
{ {
int result = 0; int result = 0;
VMValue parm[] = { runner, &evt }; VMValue parm[] = { cutscene.runner, &evt };
VMReturn ret(&result); VMReturn ret(&result);
VMCall(func, parm, 2, &ret, 1); VMCall(func, parm, 2, &ret, 1);
return result; return result;
@ -236,12 +233,12 @@ bool ScreenJobResponder(event_t* ev)
bool ScreenJobTick() bool ScreenJobTick()
{ {
ticks++; ticks++;
if (runner) if (cutscene.runner)
{ {
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnTick) IFVIRTUALPTRNAME(cutscene.runner, NAME_ScreenJobRunner, OnTick)
{ {
int result = 0; int result = 0;
VMValue parm[] = { runner }; VMValue parm[] = { cutscene.runner };
VMReturn ret(&result); VMReturn ret(&result);
VMCall(func, parm, 1, &ret, 1); VMCall(func, parm, 1, &ret, 1);
return result; return result;
@ -260,12 +257,12 @@ void ScreenJobDraw()
{ {
double smoothratio = I_GetTimeFrac(); double smoothratio = I_GetTimeFrac();
if (runner) if (cutscene.runner)
{ {
twod->ClearScreen(); twod->ClearScreen();
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, RunFrame) IFVIRTUALPTRNAME(cutscene.runner, NAME_ScreenJobRunner, RunFrame)
{ {
VMValue parm[] = { runner, smoothratio }; VMValue parm[] = { cutscene.runner, smoothratio };
VMCall(func, parm, 2, nullptr, 0); VMCall(func, parm, 2, nullptr, 0);
} }
} }
@ -279,12 +276,12 @@ void ScreenJobDraw()
bool ScreenJobValidate() bool ScreenJobValidate()
{ {
if (runner) if (cutscene.runner)
{ {
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, Validate) IFVIRTUALPTRNAME(cutscene.runner, NAME_ScreenJobRunner, Validate)
{ {
int res; int res;
VMValue parm[] = { runner }; VMValue parm[] = { cutscene.runner };
VMReturn ret(&res); VMReturn ret(&res);
VMCall(func, parm, 1, &ret, 1); VMCall(func, parm, 1, &ret, 1);
I_ResetFrameTime(); I_ResetFrameTime();
@ -304,12 +301,12 @@ bool StartCutscene(CutsceneDef& cs, int flags, const CompletionFunc& completion_
{ {
if ((cs.function.IsNotEmpty() || cs.video.IsNotEmpty()) && cs.function.CompareNoCase("none") != 0) if ((cs.function.IsNotEmpty() || cs.video.IsNotEmpty()) && cs.function.CompareNoCase("none") != 0)
{ {
completion = completion_; cutscene.completion = completion_;
runner = CreateRunner(); cutscene.runner = CreateRunner();
GC::WriteBarrier(runner); GC::WriteBarrier(cutscene.runner);
try try
{ {
cs.Create(runner); cs.Create(cutscene.runner);
if (!ScreenJobValidate()) if (!ScreenJobValidate())
{ {
DeleteScreenJob(); DeleteScreenJob();
@ -349,6 +346,26 @@ DEFINE_ACTION_FUNCTION(DScreenJobRunner, setTransition)
return 0; return 0;
} }
//=============================================================================
//
// to block wipes on cutscenes that cannot handle it
//
//=============================================================================
bool CanWipe()
{
if (cutscene.runner == nullptr) return true;
IFVM(ScreenJobRunner, CanWipe)
{
int can;
VMReturn ret(&can);
VMValue param = cutscene.runner;
VMCall(func, &param, 1, &ret, 1);
return can;
}
return true;
}
//============================================================================= //=============================================================================
// //
// //

View file

@ -43,14 +43,19 @@ bool ScreenJobValidate();
struct CutsceneDef; struct CutsceneDef;
bool StartCutscene(const char* s, int flags, const CompletionFunc& completion); bool StartCutscene(const char* s, int flags, const CompletionFunc& completion);
bool StartCutscene(CutsceneDef& cs, int flags, const CompletionFunc& completion_); bool StartCutscene(CutsceneDef& cs, int flags, const CompletionFunc& completion_);
bool CanWipe();
VMFunction* LookupFunction(const char* qname, bool validate = true); VMFunction* LookupFunction(const char* qname, bool validate = true);
void CallCreateFunction(const char* qname, DObject* runner); void CallCreateFunction(const char* qname, DObject* runner);
DObject* CreateRunner(bool clearbefore = true); DObject* CreateRunner(bool clearbefore = true);
void AddGenericVideo(DObject* runner, const FString& fn, int soundid, int fps); void AddGenericVideo(DObject* runner, const FString& fn, int soundid, int fps);
struct CutsceneState
{
DObject* runner;
PClass* runnerclass;
PType* runnerclasstype;
CompletionFunc completion;
};
extern DObject* runner; extern CutsceneState cutscene;
extern PClass* runnerclass;
extern PType* runnerclasstype;
extern CompletionFunc completion;

View file

@ -180,29 +180,21 @@ class TObjPtr
DObject *o; DObject *o;
}; };
public: public:
TObjPtr() = default;
TObjPtr(const TObjPtr<T> &q) = default;
#if 0 // Courtesy of GCC being stupid and not fully recognizing TobjPtr as trivial. GZDoom needs this constructor, but it breaks compilation in Raze, but only with GCC. constexpr TObjPtr<T>& operator=(T q) noexcept
TObjPtr(T q) noexcept
: pp(q)
{
}
#endif
T operator=(T q)
{ {
pp = q; pp = q;
return *this; return *this;
} }
T operator=(std::nullptr_t nul) constexpr TObjPtr<T>& operator=(std::nullptr_t nul) noexcept
{ {
o = nullptr; o = nullptr;
return *this; return *this;
} }
// To allow NULL, too. // To allow NULL, too.
T operator=(const int val) TObjPtr<T>& operator=(const int val) noexcept
{ {
assert(val == 0); assert(val == 0);
o = nullptr; o = nullptr;
@ -210,42 +202,42 @@ public:
} }
// To allow NULL, too. In Clang NULL is a long. // To allow NULL, too. In Clang NULL is a long.
T operator=(const long val) TObjPtr<T>& operator=(const long val) noexcept
{ {
assert(val == 0); assert(val == 0);
o = nullptr; o = nullptr;
return *this; return *this;
} }
T Get() noexcept constexpr T Get() noexcept
{ {
return GC::ReadBarrier(pp); return GC::ReadBarrier(pp);
} }
T ForceGet() noexcept //for situations where the read barrier needs to be skipped. constexpr T ForceGet() noexcept //for situations where the read barrier needs to be skipped.
{ {
return pp; return pp;
} }
operator T() noexcept constexpr operator T() noexcept
{ {
return GC::ReadBarrier(pp); return GC::ReadBarrier(pp);
} }
T &operator*() noexcept constexpr T &operator*() noexcept
{ {
T q = GC::ReadBarrier(pp); T q = GC::ReadBarrier(pp);
assert(q != NULL); assert(q != NULL);
return *q; return *q;
} }
T operator->() noexcept constexpr T operator->() noexcept
{ {
return GC::ReadBarrier(pp); return GC::ReadBarrier(pp);
} }
bool operator!=(T u) noexcept constexpr bool operator!=(T u) noexcept
{ {
return GC::ReadBarrier(o) != u; return GC::ReadBarrier(o) != u;
} }
bool operator==(T u) noexcept constexpr bool operator==(T u) noexcept
{ {
return GC::ReadBarrier(o) == u; return GC::ReadBarrier(o) == u;
} }
@ -257,6 +249,17 @@ public:
friend class DObject; friend class DObject;
}; };
// This is only needed because some parts of GCC do not treat a class with any constructor as trivial.
// TObjPtr needs to be fully trivial, though - some parts in the engine depend on it.
template<class T>
constexpr TObjPtr<T> MakeObjPtr(T t) noexcept
{
// since this exists to replace the constructor we cannot initialize in the declaration as this would require the constructor we want to avoid.
TObjPtr<T> tt;
tt = t;
return tt;
}
// Use barrier_cast instead of static_cast when you need to cast // Use barrier_cast instead of static_cast when you need to cast
// the contents of a TObjPtr to a related type. // the contents of a TObjPtr to a related type.
template<class T,class U> inline T barrier_cast(TObjPtr<U> &o) template<class T,class U> inline T barrier_cast(TObjPtr<U> &o)

View file

@ -77,7 +77,7 @@ static void CallCreateMapFunction(const char* qname, DObject* runner, MapRecord*
auto func = LookupFunction(qname); auto func = LookupFunction(qname);
if (func->Proto->ArgumentTypes.Size() == 1) return CallCreateFunction(qname, runner); // accept functions without map parameter as well here. if (func->Proto->ArgumentTypes.Size() == 1) return CallCreateFunction(qname, runner); // accept functions without map parameter as well here.
if (func->Proto->ArgumentTypes.Size() != 2) I_Error("Bad map-cutscene function %s. Must receive precisely two arguments.", qname); if (func->Proto->ArgumentTypes.Size() != 2) I_Error("Bad map-cutscene function %s. Must receive precisely two arguments.", qname);
if (func->Proto->ArgumentTypes[0] != runnerclasstype && func->Proto->ArgumentTypes[1] != maprecordtype) if (func->Proto->ArgumentTypes[0] != cutscene.runnerclasstype && func->Proto->ArgumentTypes[1] != maprecordtype)
I_Error("Bad cutscene function %s. Must receive ScreenJobRunner and MapRecord reference.", qname); I_Error("Bad cutscene function %s. Must receive ScreenJobRunner and MapRecord reference.", qname);
VMValue val[2] = { runner, map }; VMValue val[2] = { runner, map };
VMCall(func, val, 2, nullptr, 0); VMCall(func, val, 2, nullptr, 0);
@ -96,7 +96,7 @@ void CallCreateSummaryFunction(const char* qname, DObject* runner, MapRecord* ma
auto s = func->Proto->ArgumentTypes.Size(); auto s = func->Proto->ArgumentTypes.Size();
auto at = func->Proto->ArgumentTypes.Data(); auto at = func->Proto->ArgumentTypes.Data();
if (s != 3 && s != 4) I_Error("Bad map-cutscene function %s. Must receive precisely three or four arguments.", qname); if (s != 3 && s != 4) I_Error("Bad map-cutscene function %s. Must receive precisely three or four arguments.", qname);
if (at[0] != runnerclasstype && at[1] != maprecordtype && at[2] != summaryinfotype && (s == 3 || at[3] == maprecordtype)) if (at[0] != cutscene.runnerclasstype && at[1] != maprecordtype && at[2] != summaryinfotype && (s == 3 || at[3] == maprecordtype))
I_Error("Bad cutscene function %s. Must receive ScreenJobRunner, MapRecord and SummaryInfo reference,", qname); I_Error("Bad cutscene function %s. Must receive ScreenJobRunner, MapRecord and SummaryInfo reference,", qname);
if (info) summaryinfo = *info; // must be copied to a persistent location. if (info) summaryinfo = *info; // must be copied to a persistent location.
else summaryinfo = {}; else summaryinfo = {};
@ -158,24 +158,24 @@ void PlayLogos(gameaction_t complete_ga, gameaction_t def_ga, bool stopmusic)
void ShowScoreboard(int numplayers, const CompletionFunc& completion_) void ShowScoreboard(int numplayers, const CompletionFunc& completion_)
{ {
completion = completion_; cutscene.completion = completion_;
runner = CreateRunner(); cutscene.runner = CreateRunner();
Printf("Created runner at %p\n", runner); Printf("Created runner at %p\n", cutscene.runner);
GC::WriteBarrier(runner); GC::WriteBarrier(cutscene.runner);
const char* qname = globalCutscenes.MPSummaryScreen; const char* qname = globalCutscenes.MPSummaryScreen;
auto func = LookupFunction(qname); auto func = LookupFunction(qname);
if (func->Proto->ArgumentTypes.Size() != 2) I_Error("Bad map-cutscene function %s. Must receive precisely two arguments.", qname); if (func->Proto->ArgumentTypes.Size() != 2) I_Error("Bad map-cutscene function %s. Must receive precisely two arguments.", qname);
if (func->Proto->ArgumentTypes[0] != runnerclasstype && func->Proto->ArgumentTypes[1] != TypeSInt32) if (func->Proto->ArgumentTypes[0] != cutscene.runnerclasstype && func->Proto->ArgumentTypes[1] != TypeSInt32)
I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference and integer.", qname); I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference and integer.", qname);
VMValue val[2] = { runner, numplayers }; VMValue val[2] = { cutscene.runner, numplayers };
VMCall(func, val, 2, nullptr, 0); VMCall(func, val, 2, nullptr, 0);
if (!ScreenJobValidate()) if (!ScreenJobValidate())
{ {
runner->Destroy(); cutscene.runner->Destroy();
runner = nullptr; cutscene.runner = nullptr;
if (completion) completion(false); if (cutscene.completion) cutscene.completion(false);
completion = nullptr; cutscene.completion = nullptr;
return; return;
} }
gameaction = ga_intermission; gameaction = ga_intermission;
@ -189,7 +189,7 @@ void ShowScoreboard(int numplayers, const CompletionFunc& completion_)
void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, CompletionFunc completion_) void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, CompletionFunc completion_)
{ {
if (runner != nullptr) if (cutscene.runner != nullptr)
return; // protection against double exits. return; // protection against double exits.
if (fromMap == toMap) if (fromMap == toMap)
{ {
@ -200,17 +200,17 @@ void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, C
bool bossexit = g_bossexit; bool bossexit = g_bossexit;
g_bossexit = false; g_bossexit = false;
completion = completion_; cutscene.completion = completion_;
runner = CreateRunner(); cutscene.runner = CreateRunner();
GC::WriteBarrier(runner); GC::WriteBarrier(cutscene.runner);
// retrieve cluster relations for cluster-based cutscenes. // retrieve cluster relations for cluster-based cutscene.
ClusterDef* fromcluster = nullptr, *tocluster = nullptr; ClusterDef* fromcluster = nullptr, *tocluster = nullptr;
if (fromMap) fromcluster = FindCluster(fromMap->cluster); if (fromMap) fromcluster = FindCluster(fromMap->cluster);
if (toMap) tocluster = FindCluster(toMap->cluster); if (toMap) tocluster = FindCluster(toMap->cluster);
if (fromcluster == tocluster) fromcluster = tocluster = nullptr; if (fromcluster == tocluster) fromcluster = tocluster = nullptr;
auto runner = cutscene.runner;
try try
{ {
if (fromMap && (!(fromMap->gameflags & LEVEL_BOSSONLYCUTSCENE) || bossexit)) if (fromMap && (!(fromMap->gameflags & LEVEL_BOSSONLYCUTSCENE) || bossexit))
@ -245,9 +245,9 @@ void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, C
if (!ScreenJobValidate()) if (!ScreenJobValidate())
{ {
runner->Destroy(); runner->Destroy();
runner = nullptr; cutscene.runner = nullptr;
if (completion) completion(false); if (cutscene.completion) cutscene.completion(false);
completion = nullptr; cutscene.completion = nullptr;
return; return;
} }
gameaction = ga_intermission; gameaction = ga_intermission;
@ -255,7 +255,7 @@ void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, C
catch (...) catch (...)
{ {
if (runner) runner->Destroy(); if (runner) runner->Destroy();
runner = nullptr; cutscene.runner = nullptr;
throw; throw;
} }
} }