mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-10 03:39:55 +00:00
- improved the fps ticker
- latest Monster source git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@81 ea6a568a-9f4f-0410-981a-c910a81bb256
This commit is contained in:
parent
a0a95927c4
commit
879cc132d5
@ -224,16 +224,8 @@ void initializeInput()
|
||||
// put another import in core.config. I should probably check the
|
||||
// bug list and report it.
|
||||
updateMouseSensitivity();
|
||||
|
||||
// Set up the FPS ticker
|
||||
auto mo = (new MonsterClass("FPSTicker")).getSing();
|
||||
frameCount = mo.getIntPtr("frameCount");
|
||||
mo.setState("tick");
|
||||
}
|
||||
|
||||
// Points directly to FPSTicker.frameCounter in Monster
|
||||
int *frameCount;
|
||||
|
||||
extern(C) int ois_isPressed(int keysym);
|
||||
|
||||
// Check if a key is currently down
|
||||
@ -247,8 +239,6 @@ bool isPressed(Keys key)
|
||||
|
||||
extern(C) int d_frameStarted(float time)
|
||||
{
|
||||
(*frameCount)++;
|
||||
|
||||
if(doExit) return 0;
|
||||
|
||||
// Run the Monster scheduler
|
||||
|
@ -78,16 +78,32 @@ typedef extern(C) void function() c_callback;
|
||||
|
||||
struct Function
|
||||
{
|
||||
// These three variables (owner, lines and bcode) are common between
|
||||
// Function and State. They MUST be placed and ordered equally in
|
||||
// both structs because we're use some unsafe pointer trickery.
|
||||
MonsterClass owner;
|
||||
LineSpec[] lines; // Line specifications for byte code
|
||||
union
|
||||
{
|
||||
ubyte[] bcode; // Final compiled code (normal functions)
|
||||
dg_callback natFunc_dg; // Various types of native functions
|
||||
fn_callback natFunc_fn;
|
||||
c_callback natFunc_c;
|
||||
IdleFunction idleFunc; // Idle function callback
|
||||
}
|
||||
|
||||
Token name;
|
||||
Type type; // Return type
|
||||
FuncType ftype; // Function type
|
||||
Token name;
|
||||
Variable* params[]; // List of parameters
|
||||
MonsterClass owner;
|
||||
int index; // Unique function identifier within its class
|
||||
|
||||
int paramSize;
|
||||
|
||||
/*
|
||||
int imprint; // Stack imprint of this function. Equals
|
||||
// (type.getSize() - paramSize) (NOT USED YET)
|
||||
// (type.getSize() - paramSize) (not implemented yet)
|
||||
*/
|
||||
|
||||
// Is this function final? (can not be overridden in child classes)
|
||||
bool isFinal;
|
||||
@ -98,16 +114,6 @@ struct Function
|
||||
// What function we override (if any)
|
||||
Function *overrides;
|
||||
|
||||
union
|
||||
{
|
||||
ubyte[] bcode; // Final compiled code (normal functions)
|
||||
dg_callback natFunc_dg; // Various types of native functions
|
||||
fn_callback natFunc_fn;
|
||||
c_callback natFunc_c;
|
||||
IdleFunction idleFunc; // Idle function callback
|
||||
}
|
||||
LineSpec[] lines; // Line specifications for byte code
|
||||
|
||||
bool isNormal() { return ftype == FuncType.Normal; }
|
||||
bool isNative()
|
||||
{
|
||||
@ -138,7 +144,7 @@ struct Function
|
||||
// native code.
|
||||
void call(MonsterObject *obj)
|
||||
{
|
||||
assert(obj !is null);
|
||||
assert(obj !is null || isStatic);
|
||||
|
||||
// Make sure there's a thread to use
|
||||
bool wasNew;
|
||||
@ -187,9 +193,18 @@ struct Function
|
||||
// kill it.
|
||||
if(cthread.isUnused)
|
||||
cthread.kill();
|
||||
else
|
||||
// Otherwise, store the stack
|
||||
cthread.acquireStack();
|
||||
|
||||
cthread = null;
|
||||
|
||||
assert(fstack.isEmpty);
|
||||
}
|
||||
// I think we could also check fstack if it's empty instead of
|
||||
// using wasNew. Leave these checks here to see if this assumtion
|
||||
// is correct.
|
||||
else assert(!fstack.isEmpty);
|
||||
}
|
||||
|
||||
// Call without an object. TODO: Only allowed for functions compiled
|
||||
|
@ -38,6 +38,13 @@ import std.stdio;
|
||||
|
||||
struct State
|
||||
{
|
||||
// These three variables (owner, lines and bcode) are common between
|
||||
// Function and State. They MUST be placed and ordered equally in
|
||||
// both structs because we're use some unsafe pointer trickery.
|
||||
MonsterClass owner;
|
||||
LineSpec[] lines; // Line specifications for byte code
|
||||
ubyte[] bcode; // Final compiled code
|
||||
|
||||
Token name;
|
||||
int index;
|
||||
|
||||
@ -45,16 +52,16 @@ struct State
|
||||
HashTable!(char[], StateLabel*) labels;
|
||||
StateLabel* labelList[];
|
||||
|
||||
// Cache the begin label since it has special meaning and is looked
|
||||
// up often.
|
||||
StateLabel* begin;
|
||||
|
||||
StateScope sc; // Scope for this state
|
||||
MonsterClass owner; // Class where this state was defined
|
||||
|
||||
// State declaration - used to resolve forward references. Should
|
||||
// not be kept around when compilation is finished.
|
||||
StateDeclaration stateDec;
|
||||
|
||||
ubyte[] bcode;
|
||||
LineSpec[] lines;
|
||||
|
||||
StateLabel* findLabel(char[] name)
|
||||
{
|
||||
StateLabel *lb;
|
||||
@ -209,6 +216,13 @@ class StateDeclaration : Statement
|
||||
assert(name == sl.name.str, "label name mismatch");
|
||||
sl.index = cnt++;
|
||||
st.labelList[sl.index] = sl;
|
||||
|
||||
// Cache the 'begin:' label
|
||||
if(name == "begin")
|
||||
{
|
||||
assert(st.begin is null);
|
||||
st.begin = sl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,9 +2,11 @@ module monster.modules.all;
|
||||
|
||||
import monster.modules.io;
|
||||
import monster.modules.timer;
|
||||
import monster.modules.frames;
|
||||
|
||||
void initAllModules()
|
||||
{
|
||||
initIOModule();
|
||||
initTimerModule();
|
||||
initFramesModule();
|
||||
}
|
||||
|
88
monster/modules/frames.d
Normal file
88
monster/modules/frames.d
Normal file
@ -0,0 +1,88 @@
|
||||
|
||||
// Provides some simple numbers and functions regarding the rendering
|
||||
// frames of the application. It's up to the user to some degree to
|
||||
// provide this information, though. We rely on vm.frame to be called
|
||||
// each frame.
|
||||
module monster.modules.frames;
|
||||
|
||||
import monster.monster;
|
||||
import monster.vm.mclass;
|
||||
import monster.vm.idlefunction;
|
||||
import monster.vm.thread;
|
||||
|
||||
const char[] moduleDef =
|
||||
"module frames;
|
||||
|
||||
float time; // Time since last frame
|
||||
float totalTime; // Time since rendering started
|
||||
|
||||
ulong counter; // Number of frames since program startup
|
||||
|
||||
// Sleep a given number of frames
|
||||
idle sleep(int frameNum);
|
||||
"; //"
|
||||
|
||||
// Keep local copies of these, since we don't want Monster code to
|
||||
// overwrite them (we'll be able to explicitly forbid this later.)
|
||||
ulong frames = 0;
|
||||
float totTime = 0;
|
||||
|
||||
ulong *counter_ptr;
|
||||
float *time_ptr;
|
||||
float *totalTime_ptr;
|
||||
|
||||
// Add the given time and number of frames to the counters
|
||||
void updateFrames(float time, int frmCount = 1)
|
||||
{
|
||||
// Add up to the totals
|
||||
frames += frmCount;
|
||||
totTime += time;
|
||||
|
||||
// Set the Monster variables
|
||||
*counter_ptr = frames;
|
||||
*time_ptr = time;
|
||||
*totalTime_ptr = totTime;
|
||||
|
||||
// TODO: A similar priority queue like we're planning for timer
|
||||
// would also be applicable here. However I'm guessing frameSleep()
|
||||
// will be used a lot less than sleep() though, so this is really
|
||||
// not high up on the priority list.
|
||||
}
|
||||
|
||||
// Idle function that sleeps a given number of frames before
|
||||
// returning.
|
||||
class IdleFrameSleep : IdleFunction
|
||||
{
|
||||
override:
|
||||
bool initiate(Thread* cn)
|
||||
{
|
||||
// Calculate the return frame
|
||||
cn.idleData.l = frames + stack.popInt;
|
||||
|
||||
// Schedule us
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hasFinished(Thread* cn)
|
||||
{
|
||||
// Are we at (or past) the correct frame?
|
||||
return frames >= cn.idleData.l;
|
||||
}
|
||||
}
|
||||
|
||||
void initFramesModule()
|
||||
{
|
||||
static MonsterClass mc;
|
||||
if(mc !is null) return;
|
||||
|
||||
mc = new MonsterClass(MC.String, moduleDef, "frames");
|
||||
|
||||
// Bind the idle
|
||||
mc.bind("sleep", new IdleFrameSleep);
|
||||
|
||||
// Get pointers to the variables so we can write to them easily.
|
||||
auto mo = mc.getSing();
|
||||
counter_ptr = mo.getUlongPtr("counter");
|
||||
time_ptr = mo.getFloatPtr("time");
|
||||
totalTime_ptr = mo.getFloatPtr("totalTime");
|
||||
}
|
25
monster/modules/threads.d
Normal file
25
monster/modules/threads.d
Normal file
@ -0,0 +1,25 @@
|
||||
// This module provides an interface to the virtual threading API in
|
||||
// Monster. Not done.
|
||||
|
||||
module monster.modules.threads;
|
||||
|
||||
/*
|
||||
import monster.monster;
|
||||
|
||||
const char[] moduleDef =
|
||||
"singleton thread;
|
||||
native cancel();
|
||||
native schedule();
|
||||
idle pause();
|
||||
"; //"
|
||||
|
||||
void initThreadModule()
|
||||
{
|
||||
static MonsterClass mc;
|
||||
|
||||
if(mc !is null)
|
||||
return;
|
||||
|
||||
mc = new MonsterClass(MC.String, moduleDef, "thread");
|
||||
}
|
||||
*/
|
@ -56,6 +56,6 @@ static this()
|
||||
|
||||
// Initialize VM
|
||||
scheduler.init();
|
||||
initStack();
|
||||
stack.init();
|
||||
arrays.initialize();
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ module monster.vm.codestream;
|
||||
import std.string;
|
||||
import std.stdio;
|
||||
import monster.vm.error;
|
||||
import monster.compiler.linespec;
|
||||
|
||||
// CodeStream is a simple utility structure for reading data
|
||||
// sequentially. It holds a piece of byte compiled code, and keeps
|
||||
@ -39,24 +38,11 @@ struct CodeStream
|
||||
int len;
|
||||
ubyte *pos;
|
||||
|
||||
// Position of the last instruction
|
||||
ubyte *cmdPos;
|
||||
|
||||
// Used to convert position to the corresponding source code line,
|
||||
// for error messages.
|
||||
LineSpec[] lines;
|
||||
|
||||
// Size of debug output
|
||||
const int preView = 50;
|
||||
const int perLine = 16;
|
||||
|
||||
public:
|
||||
|
||||
void setData(ubyte[] data,
|
||||
LineSpec[] lines)
|
||||
void setData(ubyte[] data)
|
||||
{
|
||||
this.data = data;
|
||||
this.lines = lines;
|
||||
len = data.length;
|
||||
pos = data.ptr;
|
||||
}
|
||||
@ -64,34 +50,11 @@ struct CodeStream
|
||||
// Called when the end of the stream was unexpectedly encountered
|
||||
void eos(char[] func)
|
||||
{
|
||||
char[] res = format("Premature end of input:\nCodeStream.%s() missing %s byte(s)\n",
|
||||
char[] res = format("Premature end of input: %s() missing %s byte(s)",
|
||||
func, -len);
|
||||
|
||||
res ~= debugString();
|
||||
|
||||
fail(res);
|
||||
}
|
||||
|
||||
char[] debugString()
|
||||
{
|
||||
int start = data.length - preView;
|
||||
if(start < 0) start = 0;
|
||||
|
||||
char[] res = format("\nLast %s bytes of byte code:\n", data.length-start);
|
||||
foreach(int i, ubyte val; data[start..$])
|
||||
{
|
||||
if(i%perLine == 0)
|
||||
res ~= format("\n 0x%-4x: ", i+start);
|
||||
res ~= format("%-4x", val);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void debugPrint()
|
||||
{
|
||||
writefln(debugString());
|
||||
}
|
||||
|
||||
// Jump to given position
|
||||
void jump(int newPos)
|
||||
{
|
||||
@ -107,27 +70,12 @@ struct CodeStream
|
||||
return pos-data.ptr;
|
||||
}
|
||||
|
||||
// Get the current line
|
||||
int getLine()
|
||||
{
|
||||
// call shared.linespec.findLine
|
||||
return findLine(lines, cmdPos-data.ptr);
|
||||
}
|
||||
|
||||
ubyte get()
|
||||
{
|
||||
if(len--) return *(pos++);
|
||||
eos("get");
|
||||
}
|
||||
|
||||
// Used for getting an instruction. It stores the offset which can
|
||||
// be used to infer the line number later.
|
||||
ubyte getCmd()
|
||||
{
|
||||
cmdPos = pos;
|
||||
return get();
|
||||
}
|
||||
|
||||
int getInt()
|
||||
{
|
||||
len -= 4;
|
||||
|
@ -30,21 +30,15 @@ import monster.vm.stack;
|
||||
import monster.vm.error;
|
||||
import monster.compiler.states;
|
||||
import monster.compiler.functions;
|
||||
import monster.compiler.linespec;
|
||||
|
||||
// "friendly" parameter and stack handling.
|
||||
enum SPType
|
||||
{
|
||||
Function, // A function (script or native)
|
||||
Idle, // Idle function
|
||||
State, // State code
|
||||
NConst, // Native constructor
|
||||
|
||||
// The idle function callbacks are split because they handle the
|
||||
// stack differently. We probably don't need to have one type for
|
||||
// each though.
|
||||
Idle_Initiate, // IdleFunction.initiate()
|
||||
Idle_Reentry, // IdleFunction.reentry()
|
||||
Idle_Abort, // IdleFunction.abort()
|
||||
Idle_Check // IdleFunction.hasFinished()
|
||||
}
|
||||
|
||||
// One entry in the function stack
|
||||
@ -61,15 +55,46 @@ struct StackPoint
|
||||
SPType ftype;
|
||||
|
||||
MonsterObject *obj; // "this"-pointer for the function
|
||||
MonsterClass cls; // class owning the function
|
||||
|
||||
int afterStack; // Where the stack should be when this function
|
||||
// returns
|
||||
// Could have an afterStack to check that the function has the
|
||||
// correct imprint (corresponding to an imprint-var in Function.)
|
||||
|
||||
int *frame; // Stack frame, stored when entering the function
|
||||
|
||||
bool isStatic()
|
||||
// Get the class owning the function
|
||||
MonsterClass getCls()
|
||||
{
|
||||
return (ftype == SPType.Function) && func.isStatic;
|
||||
assert(isFunc || isState);
|
||||
assert(func !is null);
|
||||
return func.owner;
|
||||
}
|
||||
|
||||
bool isStatic()
|
||||
{ return isFunc() && func.isStatic; }
|
||||
|
||||
bool isFunc()
|
||||
{ return (ftype == SPType.Function) || (ftype == SPType.Idle); }
|
||||
|
||||
bool isState()
|
||||
{ return ftype == SPType.State; }
|
||||
|
||||
// Get the current source position (file name and line
|
||||
// number). Mostly used for error messages.
|
||||
Floc getFloc()
|
||||
{
|
||||
assert(isFunc || isState);
|
||||
|
||||
Floc fl;
|
||||
fl.fname = getCls().name.loc.fname;
|
||||
|
||||
// Subtract one to make sure we get the last instruction executed,
|
||||
// not the next.
|
||||
int pos = code.getPos() - 1;
|
||||
if(pos < 0) pos = 0;
|
||||
|
||||
fl.line = findLine(func.lines, pos);
|
||||
|
||||
return fl;
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,11 +148,12 @@ struct FunctionStack
|
||||
push(obj);
|
||||
cur.ftype = SPType.Function;
|
||||
cur.func = func;
|
||||
cur.cls = func.owner;
|
||||
|
||||
assert(obj is null || func.owner.parentOf(obj.cls));
|
||||
|
||||
// Point the code stream to the byte code, if any.
|
||||
if(func.isNormal)
|
||||
cur.code.setData(func.bcode, func.lines);
|
||||
cur.code.setData(func.bcode);
|
||||
|
||||
assert(!func.isIdle, "don't use fstack.push() on idle functions");
|
||||
}
|
||||
@ -143,10 +169,9 @@ struct FunctionStack
|
||||
|
||||
assert(obj !is null);
|
||||
assert(st.owner.parentOf(obj.cls));
|
||||
cur.cls = st.owner;
|
||||
|
||||
// Set up the byte code
|
||||
cur.code.setData(st.bcode, st.lines);
|
||||
cur.code.setData(st.bcode);
|
||||
}
|
||||
|
||||
// Native constructor
|
||||
@ -157,33 +182,16 @@ struct FunctionStack
|
||||
cur.ftype = SPType.NConst;
|
||||
}
|
||||
|
||||
private void pushIdleCommon(Function *fn, MonsterObject *obj, SPType tp)
|
||||
void pushIdle(Function *fn, MonsterObject *obj)
|
||||
{
|
||||
// Not really needed - we will allow static idle functions later
|
||||
// on.
|
||||
assert(obj !is null);
|
||||
|
||||
push(obj);
|
||||
cur.func = fn;
|
||||
cur.cls = fn.owner;
|
||||
cur.ftype = SPType.Idle;
|
||||
|
||||
assert(obj is null || fn.owner.parentOf(obj.cls));
|
||||
assert(fn.isIdle, fn.name.str ~ "() is not an idle function");
|
||||
cur.ftype = tp;
|
||||
}
|
||||
|
||||
// These are used for the various idle callbacks. TODO: Probably
|
||||
// overkill to have one for each, but leave it until you're sure.
|
||||
void pushIdleInit(Function *fn, MonsterObject *obj)
|
||||
{ pushIdleCommon(fn, obj, SPType.Idle_Initiate); }
|
||||
|
||||
void pushIdleReentry(Function *fn, MonsterObject *obj)
|
||||
{ pushIdleCommon(fn, obj, SPType.Idle_Reentry); }
|
||||
|
||||
void pushIdleAbort(Function *fn, MonsterObject *obj)
|
||||
{ pushIdleCommon(fn, obj, SPType.Idle_Abort); }
|
||||
|
||||
void pushIdleCheck(Function *fn, MonsterObject *obj)
|
||||
{ pushIdleCommon(fn, obj, SPType.Idle_Check); }
|
||||
|
||||
// Pops one entry of the stack. Checks that the stack level has been
|
||||
// returned to the correct position.
|
||||
void pop()
|
||||
|
@ -314,10 +314,6 @@ struct MonsterObject
|
||||
// don't do anything.
|
||||
else if(label is null) return;
|
||||
|
||||
// TODO: We can reorganize the entire function to deal with one
|
||||
// sthread !is null test. Just do the label-checking first, and
|
||||
// store the label offset
|
||||
|
||||
// Do we already have a thread?
|
||||
if(sthread !is null)
|
||||
{
|
||||
@ -345,10 +341,9 @@ struct MonsterObject
|
||||
"' is not part of class " ~ cls.getName());
|
||||
|
||||
if(label is null)
|
||||
// findLabel will return null if the label is not found.
|
||||
// TODO: The begin label should probably be cached within
|
||||
// State.
|
||||
label = st.findLabel("begin");
|
||||
// Use the 'begin:' label, if any. It will be null there's
|
||||
// no begin label.
|
||||
label = st.begin;
|
||||
|
||||
if(label !is null)
|
||||
{
|
||||
@ -363,7 +358,7 @@ struct MonsterObject
|
||||
}
|
||||
|
||||
// Don't leave an unused thread dangling - kill it instead.
|
||||
if(sthread !is null && !sthread.isScheduled)
|
||||
if(sthread !is null && sthread.isUnused)
|
||||
{
|
||||
sthread.kill();
|
||||
sthread = null;
|
||||
|
@ -33,16 +33,13 @@ import monster.compiler.scopes;
|
||||
import monster.vm.mobject;
|
||||
import monster.vm.mclass;
|
||||
import monster.vm.arrays;
|
||||
import monster.vm.fstack;
|
||||
import monster.vm.error;
|
||||
|
||||
// Stack
|
||||
// Stack. There's only one global instance, but threads will make
|
||||
// copies when they need it.
|
||||
CodeStack stack;
|
||||
|
||||
void initStack()
|
||||
{
|
||||
stack.init();
|
||||
}
|
||||
|
||||
// A simple stack frame. All data are in chunks of 4 bytes
|
||||
struct CodeStack
|
||||
{
|
||||
@ -71,8 +68,7 @@ struct CodeStack
|
||||
frame = null;
|
||||
}
|
||||
|
||||
// Get the current position index. Used mostly for debugging and
|
||||
// error checking.
|
||||
// Get the current position index.
|
||||
int getPos()
|
||||
{
|
||||
return total-left;
|
||||
@ -110,7 +106,11 @@ struct CodeStack
|
||||
left = total;
|
||||
pos = data.ptr;
|
||||
|
||||
assert(fleft == left);
|
||||
if(fleft != 0)
|
||||
writefln("left=%s total=%s fleft=%s", left, total, fleft);
|
||||
assert(frame is null);
|
||||
assert(fleft == 0);
|
||||
assert(fstack.isEmpty);
|
||||
}
|
||||
|
||||
void pushInt(int i)
|
||||
@ -174,6 +174,7 @@ struct CodeStack
|
||||
assert(len > 0);
|
||||
int[] r = getInts(len-1, len);
|
||||
pop(len);
|
||||
assert(r.length == len);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -81,6 +81,21 @@ struct Thread
|
||||
// The contents of idleObj's extra data for the idle's owner class.
|
||||
SharedType extraData;
|
||||
|
||||
// Set to true whenever we are running from state code. If we are
|
||||
// inside the state itself, this will be true and 'next' will be 1.
|
||||
bool isActive;
|
||||
|
||||
// Set to true when a state change is in progress. Only used when
|
||||
// state is changed from within a function in active code.
|
||||
bool stateChange;
|
||||
|
||||
/*******************************************************
|
||||
* *
|
||||
* Private variables *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
private:
|
||||
// Temporarily needed since we need a state and an object to push on
|
||||
// the stack to return to state code. This'll change soon (we won't
|
||||
// need to push anything to reenter, since the function stack will
|
||||
@ -92,12 +107,45 @@ struct Thread
|
||||
NodeList * list; // List owning this thread
|
||||
int retPos; // Return position in byte code.
|
||||
|
||||
bool isActive; // Set to true whenever we are running from state
|
||||
// code. If we are inside the state itself, this will
|
||||
// be true and 'next' will be 1.
|
||||
bool stateChange; // Set to true when a state change is in
|
||||
// progress. Only used when state is changed from
|
||||
// within a function in active code.
|
||||
// Stored copy of the stack. Used when the thread is not running.
|
||||
int[] sstack;
|
||||
|
||||
|
||||
public:
|
||||
/*******************************************************
|
||||
* *
|
||||
* Public functions *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
// Get a new thread. It starts in the 'unused' list.
|
||||
static Thread* getNew(MonsterObject *obj = null)
|
||||
{
|
||||
auto cn = scheduler.unused.getNew();
|
||||
cn.list = &scheduler.unused;
|
||||
|
||||
with(*cn)
|
||||
{
|
||||
theObj = obj;
|
||||
|
||||
// Initialize other variables
|
||||
idle = null;
|
||||
idleObj = null;
|
||||
isActive = false;
|
||||
stateChange = false;
|
||||
retPos = -1;
|
||||
sstack = null;
|
||||
}
|
||||
|
||||
/*
|
||||
if(obj !is null)
|
||||
writefln("Got a new state thread");
|
||||
else
|
||||
writefln("Got a new non-state thread");
|
||||
*/
|
||||
|
||||
return cn;
|
||||
}
|
||||
|
||||
// Unschedule this node from the runlist or waitlist it belongs to,
|
||||
// but don't kill it. Any idle function connected to this node is
|
||||
@ -106,7 +154,7 @@ struct Thread
|
||||
{
|
||||
if(idle !is null)
|
||||
{
|
||||
fstack.pushIdleAbort(idle, idleObj);
|
||||
fstack.pushIdle(idle, idleObj);
|
||||
idle.idleFunc.abort(this);
|
||||
fstack.pop();
|
||||
idle = null;
|
||||
@ -117,40 +165,46 @@ struct Thread
|
||||
assert(!isScheduled);
|
||||
}
|
||||
|
||||
static Thread* getNew(MonsterObject *obj = null)
|
||||
{
|
||||
auto cn = scheduler.unused.getNew();
|
||||
cn.list = &scheduler.unused;
|
||||
cn.initialize(obj);
|
||||
return cn;
|
||||
}
|
||||
|
||||
// Remove the thread comletely
|
||||
void kill()
|
||||
{
|
||||
/*
|
||||
if(theObj is null)
|
||||
writefln("Killing non-state thread");
|
||||
else
|
||||
writefln("Killing state thread");
|
||||
*/
|
||||
|
||||
cancel();
|
||||
list.remove(this);
|
||||
list = null;
|
||||
|
||||
if(sstack.length)
|
||||
Buffers.free(sstack);
|
||||
sstack = null;
|
||||
|
||||
/*
|
||||
writefln("Thread lists:");
|
||||
writefln(" run: ", scheduler.run.length);
|
||||
writefln(" runNext: ", scheduler.runNext.length);
|
||||
writefln(" wait: ", scheduler.wait.length);
|
||||
writefln(" unused: ", scheduler.unused.length);
|
||||
*/
|
||||
}
|
||||
|
||||
bool isDead() { return list is null; }
|
||||
|
||||
// Schedule this thread to run next frame
|
||||
void schedule(uint offs)
|
||||
void schedule(int offs)
|
||||
{
|
||||
assert(!isScheduled,
|
||||
"cannot schedule an already scheduled thread");
|
||||
|
||||
retPos = offs;
|
||||
assert(offs >= 0);
|
||||
moveTo(scheduler.runNext);
|
||||
}
|
||||
|
||||
// Move this node to another list.
|
||||
void moveTo(NodeList *to)
|
||||
{
|
||||
assert(list !is null);
|
||||
list.moveTo(*to, this);
|
||||
list = to;
|
||||
}
|
||||
|
||||
// Are we currently scheduled?
|
||||
bool isScheduled()
|
||||
{
|
||||
@ -180,24 +234,6 @@ struct Thread
|
||||
( cast(NodeList.TList.Iterator)this ).getNext();
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* *
|
||||
* Public functions *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
void initialize(MonsterObject *obj)
|
||||
{
|
||||
theObj = obj;
|
||||
|
||||
// Initialize other variables
|
||||
idle = null;
|
||||
idleObj = null;
|
||||
isActive = false;
|
||||
stateChange = false;
|
||||
retPos = -1;
|
||||
}
|
||||
|
||||
// Reenter this thread to the point where it was previously stopped.
|
||||
void reenter()
|
||||
{
|
||||
@ -210,7 +246,7 @@ struct Thread
|
||||
assert(!isActive,
|
||||
"reenter cannot be called when object is already active");
|
||||
assert(fstack.isEmpty,
|
||||
"state code can only run at the bottom of the function stack");
|
||||
"can only reenter at the bottom of the function stack");
|
||||
assert(isScheduled);
|
||||
|
||||
if(isIdle)
|
||||
@ -219,7 +255,7 @@ struct Thread
|
||||
assert(idleObj !is null || idle.isStatic);
|
||||
|
||||
// Tell the idle function that we we are reentering
|
||||
fstack.pushIdleReentry(idle, idleObj);
|
||||
fstack.pushIdle(idle, idleObj);
|
||||
idle.idleFunc.reentry(this);
|
||||
fstack.pop();
|
||||
|
||||
@ -227,9 +263,6 @@ struct Thread
|
||||
idle = null;
|
||||
}
|
||||
|
||||
// Remove the current node from the run list
|
||||
moveTo(&scheduler.unused);
|
||||
|
||||
// Set the active flat to indicate that we are now actively
|
||||
// running. (Might not be needed in the future)
|
||||
isActive = true;
|
||||
@ -238,6 +271,12 @@ struct Thread
|
||||
assert(cthread is null);
|
||||
cthread = this;
|
||||
|
||||
// Remove the current thread from the run list
|
||||
moveTo(&scheduler.unused);
|
||||
|
||||
// Restore the stack
|
||||
restoreStack();
|
||||
|
||||
// Set up the code stack for state code.
|
||||
fstack.push(theObj.state, theObj);
|
||||
|
||||
@ -251,6 +290,8 @@ struct Thread
|
||||
// Reset the thread
|
||||
cthread = null;
|
||||
|
||||
fstack.pop();
|
||||
|
||||
// We are no longer active
|
||||
isActive = false;
|
||||
|
||||
@ -258,8 +299,36 @@ struct Thread
|
||||
format("Stack not returned to zero after state code, __STACK__=",
|
||||
stack.getPos));
|
||||
|
||||
fstack.pop();
|
||||
if(!isUnused)
|
||||
// Store the stack
|
||||
acquireStack();
|
||||
else
|
||||
// If the thread is not used for anything, might as well kill it
|
||||
kill();
|
||||
}
|
||||
|
||||
// Make a copy of the stack and store it for later. Reset the global
|
||||
// stack.
|
||||
void acquireStack()
|
||||
{
|
||||
assert(!isUnused(),
|
||||
"unused threads should never need to aquire the stack");
|
||||
assert(sstack.length == 0,
|
||||
"Thread already has a stack");
|
||||
assert(fstack.isEmpty);
|
||||
|
||||
// This can be optimized later
|
||||
int len = stack.getPos();
|
||||
if(len)
|
||||
{
|
||||
writefln("acquiring %s ints", len);
|
||||
|
||||
// Get a new buffer, and copy the stack
|
||||
sstack = Buffers.getInt(len);
|
||||
sstack[] = stack.popInts(len);
|
||||
}
|
||||
|
||||
stack.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
@ -269,21 +338,40 @@ struct Thread
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
void fail(char[] msg)
|
||||
void restoreStack()
|
||||
{
|
||||
int line = -1;
|
||||
char[] file;
|
||||
if(fstack.cur !is null)
|
||||
{
|
||||
line = fstack.cur.code.getLine();
|
||||
file = fstack.cur.cls.name.loc.fname;
|
||||
}
|
||||
assert(stack.getPos() == 0,
|
||||
"cannot restore into a non-empty stack");
|
||||
|
||||
.fail(msg, file, line);
|
||||
if(sstack.length)
|
||||
{
|
||||
// Push the values back, and free the buffer
|
||||
stack.pushInts(sstack);
|
||||
Buffers.free(sstack);
|
||||
assert(stack.getPos == sstack.length);
|
||||
sstack = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the BC.CallIdle instruction parameters and call schedule
|
||||
// the given idle function.
|
||||
// Move this node to another list.
|
||||
void moveTo(NodeList *to)
|
||||
{
|
||||
assert(list !is null);
|
||||
list.moveTo(*to, this);
|
||||
list = to;
|
||||
}
|
||||
|
||||
void fail(char[] msg)
|
||||
{
|
||||
Floc fl;
|
||||
if(fstack.cur !is null)
|
||||
fl = fstack.cur.getFloc();
|
||||
|
||||
.fail(msg, fl);
|
||||
}
|
||||
|
||||
// Parse the BC.CallIdle instruction parameters and schedule the
|
||||
// given idle function.
|
||||
void callIdle(MonsterObject *iObj)
|
||||
{
|
||||
assert(isActive && fstack.isStateCode,
|
||||
@ -316,7 +404,7 @@ struct Thread
|
||||
extraData = *idleObj.getExtra(idle.owner);
|
||||
|
||||
// Notify the idle function
|
||||
fstack.pushIdleInit(idle, idleObj);
|
||||
fstack.pushIdle(idle, idleObj);
|
||||
if(idle.idleFunc.initiate(this))
|
||||
moveTo(&scheduler.wait);
|
||||
fstack.pop();
|
||||
@ -365,13 +453,11 @@ struct Thread
|
||||
// Get some values from the function stack
|
||||
CodeStream *code = &fstack.cur.code;
|
||||
MonsterObject *obj = fstack.cur.obj;
|
||||
MonsterClass cls = fstack.cur.cls;
|
||||
int clsInd = cls.getTreeIndex();
|
||||
MonsterClass cls = fstack.cur.getCls();
|
||||
|
||||
// Only an object belonging to this thread can be passed to
|
||||
// execute() on the function stack.
|
||||
assert(obj is null || cls.parentOf(obj));
|
||||
assert(obj is null || obj.cls.upcast(cls) == clsInd);
|
||||
assert(obj !is null || fstack.cur.isStatic);
|
||||
|
||||
// Pops a pointer off the stack. Null pointers will throw an
|
||||
@ -392,7 +478,7 @@ struct Thread
|
||||
|
||||
// Variable in this object
|
||||
if(type == PT.DataOffs)
|
||||
return obj.getDataInt(clsInd, index);
|
||||
return obj.getDataInt(cls.treeIndex, index);
|
||||
|
||||
// This object, but another (parent) class
|
||||
if(type == PT.DataOffsCls)
|
||||
@ -449,7 +535,7 @@ struct Thread
|
||||
//for(long i=0;i<limit;i++)
|
||||
for(;;)
|
||||
{
|
||||
ubyte opCode = code.getCmd();
|
||||
ubyte opCode = code.get();
|
||||
|
||||
//writefln("stack=", stack.getPos);
|
||||
//writefln("exec(%s): %s", code.getLine, bcToString[opCode]);
|
||||
@ -566,7 +652,7 @@ struct Thread
|
||||
break;
|
||||
|
||||
case BC.PushClassVar:
|
||||
stack.pushInt(*obj.getDataInt(clsInd, code.getInt()));
|
||||
stack.pushInt(*obj.getDataInt(cls.treeIndex, code.getInt()));
|
||||
break;
|
||||
|
||||
case BC.PushParentVar:
|
||||
@ -1352,7 +1438,7 @@ struct Scheduler
|
||||
// possible.
|
||||
if(cn.isIdle)
|
||||
{
|
||||
fstack.pushIdleCheck(cn.idle, cn.idleObj);
|
||||
fstack.pushIdle(cn.idle, cn.idleObj);
|
||||
if(cn.idle.idleFunc.hasFinished(cn))
|
||||
// Schedule the code to start running again this round. We
|
||||
// move it from the wait list to the run list.
|
||||
|
@ -36,6 +36,7 @@ import monster.compiler.assembler;
|
||||
import monster.compiler.scopes;
|
||||
|
||||
import monster.modules.timer;
|
||||
import monster.modules.frames;
|
||||
|
||||
import std.file;
|
||||
import monster.util.string;
|
||||
@ -65,6 +66,8 @@ struct VM
|
||||
if(time != 0)
|
||||
idleTime.add(time);
|
||||
|
||||
updateFrames(time);
|
||||
|
||||
scheduler.doFrame();
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,7 @@ singleton FPSTicker;
|
||||
|
||||
import io, timer;
|
||||
|
||||
// This is updated automatically by input/events.d
|
||||
int frameCount;
|
||||
ulong lastFrame;
|
||||
|
||||
float delay = 1.5;
|
||||
|
||||
@ -12,7 +11,7 @@ state tick
|
||||
{
|
||||
begin:
|
||||
sleep(delay);
|
||||
print("fps: ", frameCount / delay);
|
||||
frameCount = 0;
|
||||
print("fps:", (frames.counter-lastFrame) / delay);
|
||||
lastFrame = frames.counter;
|
||||
goto begin;
|
||||
}
|
||||
|
@ -54,7 +54,11 @@ void initMonsterScripts()
|
||||
mc.bind("randInt",
|
||||
{ stack.pushInt(rnd.randInt
|
||||
(stack.popInt,stack.popInt));});
|
||||
|
||||
|
||||
// Set up and run the fps ticker
|
||||
auto mo = (new MonsterClass("FPSTicker")).getSing();
|
||||
mo.setState("tick");
|
||||
|
||||
// Load and run the test script
|
||||
mc = new MonsterClass("Test");
|
||||
mc.createObject().call("test");
|
||||
|
Loading…
x
Reference in New Issue
Block a user