mirror of
https://github.com/libretro/scummvm.git
synced 2024-11-30 21:00:39 +00:00
MTROPOLIS: Convert Miniscript program runs from tasks to coroutines
This commit is contained in:
parent
590844d4d4
commit
fdaa96daba
@ -79,7 +79,7 @@ VThreadState CoroutineStackFrame2::execute(VThread *thread) {
|
||||
return kVThreadError;
|
||||
else if (runtimeState._miniscriptOutcome == kMiniscriptInstructionOutcomeContinue)
|
||||
break;
|
||||
else if (runtimeState._miniscriptOutcome == kMiniscriptInstructionOutcomeYieldToVThreadNoRetry) {
|
||||
else if (runtimeState._miniscriptOutcome == kMiniscriptInstructionOutcomeYieldToVThread) {
|
||||
_nextInstr = ip;
|
||||
return kVThreadReturn;
|
||||
} else {
|
||||
|
@ -104,6 +104,27 @@ struct CoroutineParamsBase {
|
||||
Params() = delete; \
|
||||
}
|
||||
|
||||
#define CORO_DEFINE_PARAMS_4(type1, name1, type2, name2, type3, name3, type4, name4) \
|
||||
CORO_STUB \
|
||||
struct Params : public CoroutineParamsBase { \
|
||||
typedef type1 ParamType1_t; \
|
||||
typedef type2 ParamType2_t; \
|
||||
typedef type3 ParamType3_t; \
|
||||
typedef type4 ParamType4_t; \
|
||||
\
|
||||
ParamType1_t name1; \
|
||||
ParamType2_t name2; \
|
||||
ParamType3_t name3; \
|
||||
ParamType4_t name4; \
|
||||
\
|
||||
explicit Params(const ParamType1_t &p_##name1, const ParamType2_t &p_##name2, const ParamType3_t &p_##name3, const ParamType4_t &p_##name4) \
|
||||
: name1(p_##name1), name2(p_##name2), name3(p_##name3), name4(p_##name4) { \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
Params() = delete; \
|
||||
}
|
||||
|
||||
#define CORO_DEFINE_RETURN_TYPE(type) \
|
||||
typedef type ReturnValue_t
|
||||
|
||||
|
@ -129,7 +129,10 @@ struct ICoroutineCompiler {
|
||||
Locals *locals = &static_cast<CoroStackFrame *>(coroRuntime._frame)->_locals; \
|
||||
CoroutineReturnValueRef<ReturnValue_t> coroReturnValueRef = (static_cast<CoroStackFrame *>(coroRuntime._frame)->_rvRef);
|
||||
|
||||
#define CORO_DISUSE_CODE_BLOCK_VARS \
|
||||
#define CORO_AWAIT_PUSHED_TASK \
|
||||
return kVThreadReturn
|
||||
|
||||
#define CORO_DISUSE_CODE_BLOCK_VARS \
|
||||
(void)params; \
|
||||
(void)locals; \
|
||||
(void)coroReturnValueRef;
|
||||
@ -209,11 +212,11 @@ void type::compileCoroutine(ICoroutineCompiler *compiler) {
|
||||
#define CORO_WHILE(expr) \
|
||||
CORO_END_CODE_BLOCK \
|
||||
CORO_START_CODE_BLOCK(CoroOps::WhileCond) \
|
||||
coroCondition = !!(expr); \
|
||||
coroRuntime._condition = !!(expr); \
|
||||
CORO_END_CODE_BLOCK \
|
||||
CORO_START_CODE_BLOCK(CoroOps::WhileBody)
|
||||
|
||||
#define CORO_END_WHILE(expr) \
|
||||
#define CORO_END_WHILE \
|
||||
CORO_END_CODE_BLOCK \
|
||||
CORO_START_CODE_BLOCK(CoroOps::EndWhile)
|
||||
|
||||
|
@ -1057,7 +1057,7 @@ MiniscriptInstructionOutcome MovieElement::scriptSetTimestamp(MiniscriptThread *
|
||||
|
||||
if (asInteger != (int32)_currentTimestamp) {
|
||||
thread->getRuntime()->getVThread().pushCoroutine<MovieElement::SeekToTimeCoroutine>(this, getRuntime(), asInteger);
|
||||
return kMiniscriptInstructionOutcomeYieldToVThreadNoRetry;
|
||||
return kMiniscriptInstructionOutcomeYieldToVThread;
|
||||
}
|
||||
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
@ -1128,7 +1128,7 @@ MiniscriptInstructionOutcome MovieElement::scriptSetRangeTyped(MiniscriptThread
|
||||
|
||||
if (targetTS != _currentTimestamp) {
|
||||
thread->getRuntime()->getVThread().pushCoroutine<MovieElement::SeekToTimeCoroutine>(this, getRuntime(), targetTS);
|
||||
return kMiniscriptInstructionOutcomeYieldToVThreadNoRetry;
|
||||
return kMiniscriptInstructionOutcomeYieldToVThread;
|
||||
}
|
||||
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "common/random.h"
|
||||
#include "common/memstream.h"
|
||||
|
||||
#include "mtropolis/coroutines.h"
|
||||
#include "mtropolis/miniscript.h"
|
||||
|
||||
namespace MTropolis {
|
||||
@ -630,7 +631,7 @@ MiniscriptInstructionOutcome Send::execute(MiniscriptThread *thread) const {
|
||||
|
||||
if (_messageFlags.immediate) {
|
||||
thread->getRuntime()->sendMessageOnVThread(dispatch);
|
||||
return kMiniscriptInstructionOutcomeYieldToVThreadNoRetry;
|
||||
return kMiniscriptInstructionOutcomeYieldToVThread;
|
||||
} else {
|
||||
thread->getRuntime()->queueMessage(dispatch);
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
@ -1907,12 +1908,7 @@ MiniscriptInstructionOutcome Jump::execute(MiniscriptThread *thread) const {
|
||||
} // End of namespace MiniscriptInstructions
|
||||
|
||||
MiniscriptThread::MiniscriptThread(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msgProps, const Common::SharedPtr<MiniscriptProgram> &program, const Common::SharedPtr<MiniscriptReferences> &refs, Modifier *modifier)
|
||||
: _runtime(runtime), _msgProps(msgProps), _program(program), _refs(refs), _modifier(modifier), _currentInstruction(0), _failed(false) {
|
||||
}
|
||||
|
||||
void MiniscriptThread::runOnVThread(VThread &vthread, const Common::SharedPtr<MiniscriptThread> &thread) {
|
||||
ResumeTaskData *taskData = vthread.pushTask("MiniscriptThread::resumeTask", resumeTask);
|
||||
taskData->thread = thread;
|
||||
: _runtime(runtime), _msgProps(msgProps), _program(program), _instructions(program->getInstructions()), _refs(refs), _modifier(modifier), _currentInstruction(0), _failed(false) {
|
||||
}
|
||||
|
||||
void MiniscriptThread::error(const Common::String &message) {
|
||||
@ -2026,6 +2022,10 @@ void MiniscriptThread::createWriteIncomingDataProxy(DynamicValueWriteProxy &prox
|
||||
proxy.pod.ptrOrOffset = 0;
|
||||
}
|
||||
|
||||
void MiniscriptThread::retryInstruction() {
|
||||
_currentInstruction--;
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome MiniscriptThread::IncomingDataWriteInterface::write(MiniscriptThread *thread, const DynamicValue &value, void *objectRef, uintptr ptrOrOffset) {
|
||||
thread->_msgProps->setValue(value);
|
||||
|
||||
@ -2042,50 +2042,40 @@ MiniscriptInstructionOutcome MiniscriptThread::IncomingDataWriteInterface::refAt
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
|
||||
CORO_BEGIN_DEFINITION(MiniscriptThread::ResumeThreadCoroutine)
|
||||
struct Locals {
|
||||
MiniscriptThread *self = nullptr;
|
||||
size_t numInstrs = 0;
|
||||
size_t instrNum = 0;
|
||||
};
|
||||
|
||||
VThreadState MiniscriptThread::resumeTask(const ResumeTaskData &data) {
|
||||
return data.thread->resume(data);
|
||||
}
|
||||
CORO_BEGIN_FUNCTION
|
||||
locals->self = params->thread.get();
|
||||
locals->numInstrs = locals->self->_program->getInstructions().size();
|
||||
|
||||
VThreadState MiniscriptThread::resume(const ResumeTaskData &taskData) {
|
||||
const Common::Array<MiniscriptInstruction *> &instrsArray = _program->getInstructions();
|
||||
CORO_IF (locals->numInstrs == 0)
|
||||
CORO_RETURN;
|
||||
CORO_END_IF
|
||||
|
||||
if (instrsArray.size() == 0)
|
||||
return kVThreadReturn;
|
||||
CORO_WHILE (locals->self->_currentInstruction < locals->numInstrs && !locals->self->_failed)
|
||||
CORO_AWAIT_MINISCRIPT(locals->self->runNextInstruction());
|
||||
CORO_END_WHILE
|
||||
CORO_END_FUNCTION
|
||||
CORO_END_DEFINITION
|
||||
|
||||
MiniscriptInstruction *const *instrs = &instrsArray[0];
|
||||
size_t numInstrs = instrsArray.size();
|
||||
MiniscriptInstructionOutcome MiniscriptThread::runNextInstruction() {
|
||||
const MiniscriptInstruction *instr = _instructions[_currentInstruction++];
|
||||
|
||||
if (_currentInstruction >= numInstrs || _failed)
|
||||
return kVThreadReturn;
|
||||
MiniscriptInstructionOutcome outcome = instr->execute(this);
|
||||
|
||||
// Requeue now so that any VThread tasks queued by instructions run in front of the resume
|
||||
{
|
||||
ResumeTaskData *requeueData = _runtime->getVThread().pushTask("MiniscriptThread::resumeTask", resumeTask);
|
||||
requeueData->thread = taskData.thread;
|
||||
if (outcome == kMiniscriptInstructionOutcomeFailed) {
|
||||
// Treat this as non-fatal but bail out of the execution loop
|
||||
_failed = true;
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
|
||||
while (_currentInstruction < numInstrs && !_failed) {
|
||||
size_t instrNum = _currentInstruction++;
|
||||
MiniscriptInstruction *instr = instrs[instrNum];
|
||||
|
||||
MiniscriptInstructionOutcome outcome = instr->execute(this);
|
||||
if (outcome == kMiniscriptInstructionOutcomeFailed) {
|
||||
// Should this also interrupt the message dispatch?
|
||||
_failed = true;
|
||||
return kVThreadReturn;
|
||||
}
|
||||
|
||||
if (outcome == kMiniscriptInstructionOutcomeYieldToVThreadAndRetry) {
|
||||
_currentInstruction = instrNum;
|
||||
return kVThreadReturn;
|
||||
}
|
||||
|
||||
if (outcome == kMiniscriptInstructionOutcomeYieldToVThreadNoRetry)
|
||||
return kVThreadReturn;
|
||||
}
|
||||
|
||||
return kVThreadReturn;
|
||||
// Otherwise continue
|
||||
return outcome;
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome MiniscriptThread::tryLoadVariable(MiniscriptStackValue &stackValue) {
|
||||
|
@ -422,8 +422,6 @@ class MiniscriptThread {
|
||||
public:
|
||||
MiniscriptThread(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msgProps, const Common::SharedPtr<MiniscriptProgram> &program, const Common::SharedPtr<MiniscriptReferences> &refs, Modifier *modifier);
|
||||
|
||||
static void runOnVThread(VThread &vthread, const Common::SharedPtr<MiniscriptThread> &thread);
|
||||
|
||||
void error(const Common::String &message);
|
||||
|
||||
const Common::SharedPtr<MiniscriptProgram> &getProgram() const;
|
||||
@ -444,6 +442,13 @@ public:
|
||||
|
||||
void createWriteIncomingDataProxy(DynamicValueWriteProxy &proxy);
|
||||
|
||||
void retryInstruction();
|
||||
|
||||
struct ResumeThreadCoroutine {
|
||||
CORO_DEFINE_RETURN_TYPE(void);
|
||||
CORO_DEFINE_PARAMS_1(Common::SharedPtr<MiniscriptThread>, thread);
|
||||
};
|
||||
|
||||
private:
|
||||
struct IncomingDataWriteInterface {
|
||||
static MiniscriptInstructionOutcome write(MiniscriptThread *thread, const DynamicValue &dest, void *objectRef, uintptr ptrOrOffset);
|
||||
@ -451,18 +456,17 @@ private:
|
||||
static MiniscriptInstructionOutcome refAttribIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib, const DynamicValue &index);
|
||||
};
|
||||
|
||||
struct ResumeTaskData {
|
||||
Common::SharedPtr<MiniscriptThread> thread;
|
||||
};
|
||||
MiniscriptInstructionOutcome runNextInstruction();
|
||||
|
||||
static VThreadState resumeTask(const ResumeTaskData &data);
|
||||
VThreadState resume(const ResumeTaskData &data);
|
||||
VThreadState resume(MiniscriptThread *thread);
|
||||
|
||||
MiniscriptInstructionOutcome tryLoadVariable(MiniscriptStackValue &stackValue);
|
||||
|
||||
Common::SharedPtr<MiniscriptProgram> _program;
|
||||
Common::SharedPtr<MiniscriptReferences> _refs;
|
||||
Common::SharedPtr<MessageProperties> _msgProps;
|
||||
|
||||
const Common::Array<MiniscriptInstruction *> &_instructions;
|
||||
Modifier *_modifier;
|
||||
Runtime *_runtime;
|
||||
Common::Array<MiniscriptStackValue> _stack;
|
||||
|
@ -25,10 +25,9 @@
|
||||
namespace MTropolis {
|
||||
|
||||
enum MiniscriptInstructionOutcome {
|
||||
kMiniscriptInstructionOutcomeContinue, // Continue executing next instruction
|
||||
kMiniscriptInstructionOutcomeYieldToVThreadNoRetry, // Instruction pushed a VThread task and should be retried when the task completes
|
||||
kMiniscriptInstructionOutcomeYieldToVThreadAndRetry, // Instruction pushed a VThread task and completed
|
||||
kMiniscriptInstructionOutcomeFailed, // Instruction errored
|
||||
kMiniscriptInstructionOutcomeContinue, // Continue executing next instruction
|
||||
kMiniscriptInstructionOutcomeYieldToVThread, // Instruction pushed a VThread task
|
||||
kMiniscriptInstructionOutcomeFailed, // Instruction errored
|
||||
};
|
||||
|
||||
} // End of namespace MTropolis
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
#include "mtropolis/assets.h"
|
||||
#include "mtropolis/audio_player.h"
|
||||
#include "mtropolis/coroutines.h"
|
||||
#include "mtropolis/miniscript.h"
|
||||
#include "mtropolis/modifiers.h"
|
||||
#include "mtropolis/modifier_factory.h"
|
||||
@ -252,7 +253,7 @@ bool MiniscriptModifier::respondsToEvent(const Event &evt) const {
|
||||
VThreadState MiniscriptModifier::consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) {
|
||||
if (_enableWhen.respondsTo(msg->getEvent())) {
|
||||
Common::SharedPtr<MiniscriptThread> thread(new MiniscriptThread(runtime, msg, _program, _references, this));
|
||||
MiniscriptThread::runOnVThread(runtime->getVThread(), thread);
|
||||
runtime->getVThread().pushCoroutine<MiniscriptThread::ResumeThreadCoroutine>(thread);
|
||||
}
|
||||
|
||||
return kVThreadReturn;
|
||||
@ -1855,18 +1856,36 @@ bool IfMessengerModifier::respondsToEvent(const Event &evt) const {
|
||||
return _when.respondsTo(evt);
|
||||
}
|
||||
|
||||
CORO_BEGIN_DEFINITION(IfMessengerModifier::RunEvaluateAndSendCoroutine)
|
||||
struct Locals {
|
||||
Common::WeakPtr<RuntimeObject> triggerSource;
|
||||
DynamicValue incomingData;
|
||||
bool isTrue = false;
|
||||
Common::SharedPtr<MiniscriptThread> thread;
|
||||
};
|
||||
|
||||
CORO_BEGIN_FUNCTION
|
||||
// Is this the right place for this? Not sure if Miniscript can change incomingData
|
||||
locals->triggerSource = params->msg->getSource();
|
||||
locals->incomingData = params->msg->getValue();
|
||||
|
||||
locals->thread.reset(new MiniscriptThread(params->runtime, params->msg, params->self->_program, params->self->_references, params->self));
|
||||
|
||||
CORO_CALL(MiniscriptThread::ResumeThreadCoroutine, locals->thread);
|
||||
|
||||
CORO_IF (!locals->thread->evaluateTruthOfResult(locals->isTrue))
|
||||
CORO_ERROR;
|
||||
CORO_END_IF
|
||||
|
||||
CORO_IF(locals->isTrue)
|
||||
CORO_AWAIT(params->self->_sendSpec.sendFromMessenger(params->runtime, params->self, locals->triggerSource.lock().get(), locals->incomingData, nullptr));
|
||||
CORO_END_IF
|
||||
CORO_END_FUNCTION
|
||||
CORO_END_DEFINITION
|
||||
|
||||
VThreadState IfMessengerModifier::consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) {
|
||||
if (_when.respondsTo(msg->getEvent())) {
|
||||
Common::SharedPtr<MiniscriptThread> thread(new MiniscriptThread(runtime, msg, _program, _references, this));
|
||||
|
||||
EvaluateAndSendTaskData *evalAndSendData = runtime->getVThread().pushTask("IfMessengerModifier::evaluateAndSendTask", this, &IfMessengerModifier::evaluateAndSendTask);
|
||||
evalAndSendData->thread = thread;
|
||||
evalAndSendData->runtime = runtime;
|
||||
evalAndSendData->incomingData = msg->getValue();
|
||||
evalAndSendData->triggerSource = msg->getSource();
|
||||
|
||||
MiniscriptThread::runOnVThread(runtime->getVThread(), thread);
|
||||
}
|
||||
if (_when.respondsTo(msg->getEvent()))
|
||||
runtime->getVThread().pushCoroutine<IfMessengerModifier::RunEvaluateAndSendCoroutine>(this, runtime, msg);
|
||||
|
||||
return kVThreadReturn;
|
||||
}
|
||||
@ -1895,20 +1914,6 @@ void IfMessengerModifier::visitInternalReferences(IStructuralReferenceVisitor *v
|
||||
_references->visitInternalReferences(visitor);
|
||||
}
|
||||
|
||||
|
||||
VThreadState IfMessengerModifier::evaluateAndSendTask(const EvaluateAndSendTaskData &taskData) {
|
||||
MiniscriptThread *thread = taskData.thread.get();
|
||||
|
||||
bool isTrue = false;
|
||||
if (!thread->evaluateTruthOfResult(isTrue))
|
||||
return kVThreadError;
|
||||
|
||||
if (isTrue)
|
||||
_sendSpec.sendFromMessenger(taskData.runtime, this, taskData.triggerSource.lock().get(), taskData.incomingData, nullptr);
|
||||
|
||||
return kVThreadReturn;
|
||||
}
|
||||
|
||||
TimerMessengerModifier::TimerMessengerModifier() : _milliseconds(0), _looping(false) {
|
||||
}
|
||||
|
||||
|
@ -707,13 +707,9 @@ public:
|
||||
#endif
|
||||
|
||||
private:
|
||||
struct EvaluateAndSendTaskData {
|
||||
EvaluateAndSendTaskData() : runtime(nullptr) {}
|
||||
|
||||
Common::SharedPtr<MiniscriptThread> thread;
|
||||
Common::WeakPtr<RuntimeObject> triggerSource;
|
||||
Runtime *runtime;
|
||||
DynamicValue incomingData;
|
||||
struct RunEvaluateAndSendCoroutine {
|
||||
CORO_DEFINE_RETURN_TYPE(void);
|
||||
CORO_DEFINE_PARAMS_3(IfMessengerModifier *, self, Runtime *, runtime, Common::SharedPtr<MessageProperties>, msg);
|
||||
};
|
||||
|
||||
Common::SharedPtr<Modifier> shallowClone() const override;
|
||||
@ -721,8 +717,6 @@ private:
|
||||
void linkInternalReferences(ObjectLinkingScope *scope) override;
|
||||
void visitInternalReferences(IStructuralReferenceVisitor *visitor) override;
|
||||
|
||||
VThreadState evaluateAndSendTask(const EvaluateAndSendTaskData &taskData);
|
||||
|
||||
Event _when;
|
||||
MessengerSendSpec _sendSpec;
|
||||
|
||||
|
@ -4006,7 +4006,7 @@ MiniscriptInstructionOutcome Structural::scriptSetPaused(MiniscriptThread *threa
|
||||
thread->getRuntime()->sendMessageOnVThread(dispatch);
|
||||
}
|
||||
|
||||
return kMiniscriptInstructionOutcomeYieldToVThreadNoRetry;
|
||||
return kMiniscriptInstructionOutcomeYieldToVThread;
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome Structural::scriptSetLoop(MiniscriptThread *thread, const DynamicValue &value) {
|
||||
|
Loading…
Reference in New Issue
Block a user