mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-18 18:40:06 +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
|
// put another import in core.config. I should probably check the
|
||||||
// bug list and report it.
|
// bug list and report it.
|
||||||
updateMouseSensitivity();
|
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);
|
extern(C) int ois_isPressed(int keysym);
|
||||||
|
|
||||||
// Check if a key is currently down
|
// Check if a key is currently down
|
||||||
@ -247,8 +239,6 @@ bool isPressed(Keys key)
|
|||||||
|
|
||||||
extern(C) int d_frameStarted(float time)
|
extern(C) int d_frameStarted(float time)
|
||||||
{
|
{
|
||||||
(*frameCount)++;
|
|
||||||
|
|
||||||
if(doExit) return 0;
|
if(doExit) return 0;
|
||||||
|
|
||||||
// Run the Monster scheduler
|
// Run the Monster scheduler
|
||||||
|
@ -78,16 +78,32 @@ typedef extern(C) void function() c_callback;
|
|||||||
|
|
||||||
struct Function
|
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
|
Type type; // Return type
|
||||||
FuncType ftype; // Function type
|
FuncType ftype; // Function type
|
||||||
Token name;
|
|
||||||
Variable* params[]; // List of parameters
|
Variable* params[]; // List of parameters
|
||||||
MonsterClass owner;
|
|
||||||
int index; // Unique function identifier within its class
|
int index; // Unique function identifier within its class
|
||||||
|
|
||||||
int paramSize;
|
int paramSize;
|
||||||
|
|
||||||
|
/*
|
||||||
int imprint; // Stack imprint of this function. Equals
|
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)
|
// Is this function final? (can not be overridden in child classes)
|
||||||
bool isFinal;
|
bool isFinal;
|
||||||
@ -98,16 +114,6 @@ struct Function
|
|||||||
// What function we override (if any)
|
// What function we override (if any)
|
||||||
Function *overrides;
|
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 isNormal() { return ftype == FuncType.Normal; }
|
||||||
bool isNative()
|
bool isNative()
|
||||||
{
|
{
|
||||||
@ -138,7 +144,7 @@ struct Function
|
|||||||
// native code.
|
// native code.
|
||||||
void call(MonsterObject *obj)
|
void call(MonsterObject *obj)
|
||||||
{
|
{
|
||||||
assert(obj !is null);
|
assert(obj !is null || isStatic);
|
||||||
|
|
||||||
// Make sure there's a thread to use
|
// Make sure there's a thread to use
|
||||||
bool wasNew;
|
bool wasNew;
|
||||||
@ -187,9 +193,18 @@ struct Function
|
|||||||
// kill it.
|
// kill it.
|
||||||
if(cthread.isUnused)
|
if(cthread.isUnused)
|
||||||
cthread.kill();
|
cthread.kill();
|
||||||
|
else
|
||||||
|
// Otherwise, store the stack
|
||||||
|
cthread.acquireStack();
|
||||||
|
|
||||||
cthread = null;
|
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
|
// Call without an object. TODO: Only allowed for functions compiled
|
||||||
|
@ -38,6 +38,13 @@ import std.stdio;
|
|||||||
|
|
||||||
struct State
|
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;
|
Token name;
|
||||||
int index;
|
int index;
|
||||||
|
|
||||||
@ -45,16 +52,16 @@ struct State
|
|||||||
HashTable!(char[], StateLabel*) labels;
|
HashTable!(char[], StateLabel*) labels;
|
||||||
StateLabel* labelList[];
|
StateLabel* labelList[];
|
||||||
|
|
||||||
|
// Cache the begin label since it has special meaning and is looked
|
||||||
|
// up often.
|
||||||
|
StateLabel* begin;
|
||||||
|
|
||||||
StateScope sc; // Scope for this state
|
StateScope sc; // Scope for this state
|
||||||
MonsterClass owner; // Class where this state was defined
|
|
||||||
|
|
||||||
// State declaration - used to resolve forward references. Should
|
// State declaration - used to resolve forward references. Should
|
||||||
// not be kept around when compilation is finished.
|
// not be kept around when compilation is finished.
|
||||||
StateDeclaration stateDec;
|
StateDeclaration stateDec;
|
||||||
|
|
||||||
ubyte[] bcode;
|
|
||||||
LineSpec[] lines;
|
|
||||||
|
|
||||||
StateLabel* findLabel(char[] name)
|
StateLabel* findLabel(char[] name)
|
||||||
{
|
{
|
||||||
StateLabel *lb;
|
StateLabel *lb;
|
||||||
@ -209,6 +216,13 @@ class StateDeclaration : Statement
|
|||||||
assert(name == sl.name.str, "label name mismatch");
|
assert(name == sl.name.str, "label name mismatch");
|
||||||
sl.index = cnt++;
|
sl.index = cnt++;
|
||||||
st.labelList[sl.index] = sl;
|
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.io;
|
||||||
import monster.modules.timer;
|
import monster.modules.timer;
|
||||||
|
import monster.modules.frames;
|
||||||
|
|
||||||
void initAllModules()
|
void initAllModules()
|
||||||
{
|
{
|
||||||
initIOModule();
|
initIOModule();
|
||||||
initTimerModule();
|
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
|
// Initialize VM
|
||||||
scheduler.init();
|
scheduler.init();
|
||||||
initStack();
|
stack.init();
|
||||||
arrays.initialize();
|
arrays.initialize();
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@ module monster.vm.codestream;
|
|||||||
import std.string;
|
import std.string;
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
import monster.vm.error;
|
import monster.vm.error;
|
||||||
import monster.compiler.linespec;
|
|
||||||
|
|
||||||
// CodeStream is a simple utility structure for reading data
|
// CodeStream is a simple utility structure for reading data
|
||||||
// sequentially. It holds a piece of byte compiled code, and keeps
|
// sequentially. It holds a piece of byte compiled code, and keeps
|
||||||
@ -39,24 +38,11 @@ struct CodeStream
|
|||||||
int len;
|
int len;
|
||||||
ubyte *pos;
|
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:
|
public:
|
||||||
|
|
||||||
void setData(ubyte[] data,
|
void setData(ubyte[] data)
|
||||||
LineSpec[] lines)
|
|
||||||
{
|
{
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.lines = lines;
|
|
||||||
len = data.length;
|
len = data.length;
|
||||||
pos = data.ptr;
|
pos = data.ptr;
|
||||||
}
|
}
|
||||||
@ -64,34 +50,11 @@ struct CodeStream
|
|||||||
// Called when the end of the stream was unexpectedly encountered
|
// Called when the end of the stream was unexpectedly encountered
|
||||||
void eos(char[] func)
|
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);
|
func, -len);
|
||||||
|
|
||||||
res ~= debugString();
|
|
||||||
|
|
||||||
fail(res);
|
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
|
// Jump to given position
|
||||||
void jump(int newPos)
|
void jump(int newPos)
|
||||||
{
|
{
|
||||||
@ -107,27 +70,12 @@ struct CodeStream
|
|||||||
return pos-data.ptr;
|
return pos-data.ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the current line
|
|
||||||
int getLine()
|
|
||||||
{
|
|
||||||
// call shared.linespec.findLine
|
|
||||||
return findLine(lines, cmdPos-data.ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
ubyte get()
|
ubyte get()
|
||||||
{
|
{
|
||||||
if(len--) return *(pos++);
|
if(len--) return *(pos++);
|
||||||
eos("get");
|
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()
|
int getInt()
|
||||||
{
|
{
|
||||||
len -= 4;
|
len -= 4;
|
||||||
|
@ -30,21 +30,15 @@ import monster.vm.stack;
|
|||||||
import monster.vm.error;
|
import monster.vm.error;
|
||||||
import monster.compiler.states;
|
import monster.compiler.states;
|
||||||
import monster.compiler.functions;
|
import monster.compiler.functions;
|
||||||
|
import monster.compiler.linespec;
|
||||||
|
|
||||||
// "friendly" parameter and stack handling.
|
// "friendly" parameter and stack handling.
|
||||||
enum SPType
|
enum SPType
|
||||||
{
|
{
|
||||||
Function, // A function (script or native)
|
Function, // A function (script or native)
|
||||||
|
Idle, // Idle function
|
||||||
State, // State code
|
State, // State code
|
||||||
NConst, // Native constructor
|
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
|
// One entry in the function stack
|
||||||
@ -61,15 +55,46 @@ struct StackPoint
|
|||||||
SPType ftype;
|
SPType ftype;
|
||||||
|
|
||||||
MonsterObject *obj; // "this"-pointer for the function
|
MonsterObject *obj; // "this"-pointer for the function
|
||||||
MonsterClass cls; // class owning the function
|
|
||||||
|
|
||||||
int afterStack; // Where the stack should be when this function
|
// Could have an afterStack to check that the function has the
|
||||||
// returns
|
// correct imprint (corresponding to an imprint-var in Function.)
|
||||||
|
|
||||||
int *frame; // Stack frame, stored when entering the 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);
|
push(obj);
|
||||||
cur.ftype = SPType.Function;
|
cur.ftype = SPType.Function;
|
||||||
cur.func = func;
|
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.
|
// Point the code stream to the byte code, if any.
|
||||||
if(func.isNormal)
|
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");
|
assert(!func.isIdle, "don't use fstack.push() on idle functions");
|
||||||
}
|
}
|
||||||
@ -143,10 +169,9 @@ struct FunctionStack
|
|||||||
|
|
||||||
assert(obj !is null);
|
assert(obj !is null);
|
||||||
assert(st.owner.parentOf(obj.cls));
|
assert(st.owner.parentOf(obj.cls));
|
||||||
cur.cls = st.owner;
|
|
||||||
|
|
||||||
// Set up the byte code
|
// Set up the byte code
|
||||||
cur.code.setData(st.bcode, st.lines);
|
cur.code.setData(st.bcode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Native constructor
|
// Native constructor
|
||||||
@ -157,33 +182,16 @@ struct FunctionStack
|
|||||||
cur.ftype = SPType.NConst;
|
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);
|
push(obj);
|
||||||
cur.func = fn;
|
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");
|
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
|
// Pops one entry of the stack. Checks that the stack level has been
|
||||||
// returned to the correct position.
|
// returned to the correct position.
|
||||||
void pop()
|
void pop()
|
||||||
|
@ -314,10 +314,6 @@ struct MonsterObject
|
|||||||
// don't do anything.
|
// don't do anything.
|
||||||
else if(label is null) return;
|
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?
|
// Do we already have a thread?
|
||||||
if(sthread !is null)
|
if(sthread !is null)
|
||||||
{
|
{
|
||||||
@ -345,10 +341,9 @@ struct MonsterObject
|
|||||||
"' is not part of class " ~ cls.getName());
|
"' is not part of class " ~ cls.getName());
|
||||||
|
|
||||||
if(label is null)
|
if(label is null)
|
||||||
// findLabel will return null if the label is not found.
|
// Use the 'begin:' label, if any. It will be null there's
|
||||||
// TODO: The begin label should probably be cached within
|
// no begin label.
|
||||||
// State.
|
label = st.begin;
|
||||||
label = st.findLabel("begin");
|
|
||||||
|
|
||||||
if(label !is null)
|
if(label !is null)
|
||||||
{
|
{
|
||||||
@ -363,7 +358,7 @@ struct MonsterObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Don't leave an unused thread dangling - kill it instead.
|
// 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.kill();
|
||||||
sthread = null;
|
sthread = null;
|
||||||
|
@ -33,16 +33,13 @@ import monster.compiler.scopes;
|
|||||||
import monster.vm.mobject;
|
import monster.vm.mobject;
|
||||||
import monster.vm.mclass;
|
import monster.vm.mclass;
|
||||||
import monster.vm.arrays;
|
import monster.vm.arrays;
|
||||||
|
import monster.vm.fstack;
|
||||||
import monster.vm.error;
|
import monster.vm.error;
|
||||||
|
|
||||||
// Stack
|
// Stack. There's only one global instance, but threads will make
|
||||||
|
// copies when they need it.
|
||||||
CodeStack stack;
|
CodeStack stack;
|
||||||
|
|
||||||
void initStack()
|
|
||||||
{
|
|
||||||
stack.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
// A simple stack frame. All data are in chunks of 4 bytes
|
// A simple stack frame. All data are in chunks of 4 bytes
|
||||||
struct CodeStack
|
struct CodeStack
|
||||||
{
|
{
|
||||||
@ -71,8 +68,7 @@ struct CodeStack
|
|||||||
frame = null;
|
frame = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the current position index. Used mostly for debugging and
|
// Get the current position index.
|
||||||
// error checking.
|
|
||||||
int getPos()
|
int getPos()
|
||||||
{
|
{
|
||||||
return total-left;
|
return total-left;
|
||||||
@ -110,7 +106,11 @@ struct CodeStack
|
|||||||
left = total;
|
left = total;
|
||||||
pos = data.ptr;
|
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)
|
void pushInt(int i)
|
||||||
@ -174,6 +174,7 @@ struct CodeStack
|
|||||||
assert(len > 0);
|
assert(len > 0);
|
||||||
int[] r = getInts(len-1, len);
|
int[] r = getInts(len-1, len);
|
||||||
pop(len);
|
pop(len);
|
||||||
|
assert(r.length == len);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +81,21 @@ struct Thread
|
|||||||
// The contents of idleObj's extra data for the idle's owner class.
|
// The contents of idleObj's extra data for the idle's owner class.
|
||||||
SharedType extraData;
|
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
|
// 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
|
// 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
|
// need to push anything to reenter, since the function stack will
|
||||||
@ -92,12 +107,45 @@ struct Thread
|
|||||||
NodeList * list; // List owning this thread
|
NodeList * list; // List owning this thread
|
||||||
int retPos; // Return position in byte code.
|
int retPos; // Return position in byte code.
|
||||||
|
|
||||||
bool isActive; // Set to true whenever we are running from state
|
// Stored copy of the stack. Used when the thread is not running.
|
||||||
// code. If we are inside the state itself, this will
|
int[] sstack;
|
||||||
// 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
|
public:
|
||||||
// within a function in active code.
|
/*******************************************************
|
||||||
|
* *
|
||||||
|
* 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,
|
// Unschedule this node from the runlist or waitlist it belongs to,
|
||||||
// but don't kill it. Any idle function connected to this node is
|
// but don't kill it. Any idle function connected to this node is
|
||||||
@ -106,7 +154,7 @@ struct Thread
|
|||||||
{
|
{
|
||||||
if(idle !is null)
|
if(idle !is null)
|
||||||
{
|
{
|
||||||
fstack.pushIdleAbort(idle, idleObj);
|
fstack.pushIdle(idle, idleObj);
|
||||||
idle.idleFunc.abort(this);
|
idle.idleFunc.abort(this);
|
||||||
fstack.pop();
|
fstack.pop();
|
||||||
idle = null;
|
idle = null;
|
||||||
@ -117,40 +165,46 @@ struct Thread
|
|||||||
assert(!isScheduled);
|
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
|
// Remove the thread comletely
|
||||||
void kill()
|
void kill()
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
if(theObj is null)
|
||||||
|
writefln("Killing non-state thread");
|
||||||
|
else
|
||||||
|
writefln("Killing state thread");
|
||||||
|
*/
|
||||||
|
|
||||||
cancel();
|
cancel();
|
||||||
list.remove(this);
|
list.remove(this);
|
||||||
list = null;
|
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
|
// Schedule this thread to run next frame
|
||||||
void schedule(uint offs)
|
void schedule(int offs)
|
||||||
{
|
{
|
||||||
assert(!isScheduled,
|
assert(!isScheduled,
|
||||||
"cannot schedule an already scheduled thread");
|
"cannot schedule an already scheduled thread");
|
||||||
|
|
||||||
retPos = offs;
|
retPos = offs;
|
||||||
|
assert(offs >= 0);
|
||||||
moveTo(scheduler.runNext);
|
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?
|
// Are we currently scheduled?
|
||||||
bool isScheduled()
|
bool isScheduled()
|
||||||
{
|
{
|
||||||
@ -180,24 +234,6 @@ struct Thread
|
|||||||
( cast(NodeList.TList.Iterator)this ).getNext();
|
( 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.
|
// Reenter this thread to the point where it was previously stopped.
|
||||||
void reenter()
|
void reenter()
|
||||||
{
|
{
|
||||||
@ -210,7 +246,7 @@ struct Thread
|
|||||||
assert(!isActive,
|
assert(!isActive,
|
||||||
"reenter cannot be called when object is already active");
|
"reenter cannot be called when object is already active");
|
||||||
assert(fstack.isEmpty,
|
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);
|
assert(isScheduled);
|
||||||
|
|
||||||
if(isIdle)
|
if(isIdle)
|
||||||
@ -219,7 +255,7 @@ struct Thread
|
|||||||
assert(idleObj !is null || idle.isStatic);
|
assert(idleObj !is null || idle.isStatic);
|
||||||
|
|
||||||
// Tell the idle function that we we are reentering
|
// Tell the idle function that we we are reentering
|
||||||
fstack.pushIdleReentry(idle, idleObj);
|
fstack.pushIdle(idle, idleObj);
|
||||||
idle.idleFunc.reentry(this);
|
idle.idleFunc.reentry(this);
|
||||||
fstack.pop();
|
fstack.pop();
|
||||||
|
|
||||||
@ -227,9 +263,6 @@ struct Thread
|
|||||||
idle = null;
|
idle = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the current node from the run list
|
|
||||||
moveTo(&scheduler.unused);
|
|
||||||
|
|
||||||
// Set the active flat to indicate that we are now actively
|
// Set the active flat to indicate that we are now actively
|
||||||
// running. (Might not be needed in the future)
|
// running. (Might not be needed in the future)
|
||||||
isActive = true;
|
isActive = true;
|
||||||
@ -238,6 +271,12 @@ struct Thread
|
|||||||
assert(cthread is null);
|
assert(cthread is null);
|
||||||
cthread = this;
|
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.
|
// Set up the code stack for state code.
|
||||||
fstack.push(theObj.state, theObj);
|
fstack.push(theObj.state, theObj);
|
||||||
|
|
||||||
@ -251,6 +290,8 @@ struct Thread
|
|||||||
// Reset the thread
|
// Reset the thread
|
||||||
cthread = null;
|
cthread = null;
|
||||||
|
|
||||||
|
fstack.pop();
|
||||||
|
|
||||||
// We are no longer active
|
// We are no longer active
|
||||||
isActive = false;
|
isActive = false;
|
||||||
|
|
||||||
@ -258,8 +299,36 @@ struct Thread
|
|||||||
format("Stack not returned to zero after state code, __STACK__=",
|
format("Stack not returned to zero after state code, __STACK__=",
|
||||||
stack.getPos));
|
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:
|
private:
|
||||||
@ -269,21 +338,40 @@ struct Thread
|
|||||||
* *
|
* *
|
||||||
*******************************************************/
|
*******************************************************/
|
||||||
|
|
||||||
|
void restoreStack()
|
||||||
|
{
|
||||||
|
assert(stack.getPos() == 0,
|
||||||
|
"cannot restore into a non-empty stack");
|
||||||
|
|
||||||
|
if(sstack.length)
|
||||||
|
{
|
||||||
|
// Push the values back, and free the buffer
|
||||||
|
stack.pushInts(sstack);
|
||||||
|
Buffers.free(sstack);
|
||||||
|
assert(stack.getPos == sstack.length);
|
||||||
|
sstack = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move this node to another list.
|
||||||
|
void moveTo(NodeList *to)
|
||||||
|
{
|
||||||
|
assert(list !is null);
|
||||||
|
list.moveTo(*to, this);
|
||||||
|
list = to;
|
||||||
|
}
|
||||||
|
|
||||||
void fail(char[] msg)
|
void fail(char[] msg)
|
||||||
{
|
{
|
||||||
int line = -1;
|
Floc fl;
|
||||||
char[] file;
|
|
||||||
if(fstack.cur !is null)
|
if(fstack.cur !is null)
|
||||||
{
|
fl = fstack.cur.getFloc();
|
||||||
line = fstack.cur.code.getLine();
|
|
||||||
file = fstack.cur.cls.name.loc.fname;
|
.fail(msg, fl);
|
||||||
}
|
}
|
||||||
|
|
||||||
.fail(msg, file, line);
|
// Parse the BC.CallIdle instruction parameters and schedule the
|
||||||
}
|
// given idle function.
|
||||||
|
|
||||||
// Parse the BC.CallIdle instruction parameters and call schedule
|
|
||||||
// the given idle function.
|
|
||||||
void callIdle(MonsterObject *iObj)
|
void callIdle(MonsterObject *iObj)
|
||||||
{
|
{
|
||||||
assert(isActive && fstack.isStateCode,
|
assert(isActive && fstack.isStateCode,
|
||||||
@ -316,7 +404,7 @@ struct Thread
|
|||||||
extraData = *idleObj.getExtra(idle.owner);
|
extraData = *idleObj.getExtra(idle.owner);
|
||||||
|
|
||||||
// Notify the idle function
|
// Notify the idle function
|
||||||
fstack.pushIdleInit(idle, idleObj);
|
fstack.pushIdle(idle, idleObj);
|
||||||
if(idle.idleFunc.initiate(this))
|
if(idle.idleFunc.initiate(this))
|
||||||
moveTo(&scheduler.wait);
|
moveTo(&scheduler.wait);
|
||||||
fstack.pop();
|
fstack.pop();
|
||||||
@ -365,13 +453,11 @@ struct Thread
|
|||||||
// Get some values from the function stack
|
// Get some values from the function stack
|
||||||
CodeStream *code = &fstack.cur.code;
|
CodeStream *code = &fstack.cur.code;
|
||||||
MonsterObject *obj = fstack.cur.obj;
|
MonsterObject *obj = fstack.cur.obj;
|
||||||
MonsterClass cls = fstack.cur.cls;
|
MonsterClass cls = fstack.cur.getCls();
|
||||||
int clsInd = cls.getTreeIndex();
|
|
||||||
|
|
||||||
// Only an object belonging to this thread can be passed to
|
// Only an object belonging to this thread can be passed to
|
||||||
// execute() on the function stack.
|
// execute() on the function stack.
|
||||||
assert(obj is null || cls.parentOf(obj));
|
assert(obj is null || cls.parentOf(obj));
|
||||||
assert(obj is null || obj.cls.upcast(cls) == clsInd);
|
|
||||||
assert(obj !is null || fstack.cur.isStatic);
|
assert(obj !is null || fstack.cur.isStatic);
|
||||||
|
|
||||||
// Pops a pointer off the stack. Null pointers will throw an
|
// Pops a pointer off the stack. Null pointers will throw an
|
||||||
@ -392,7 +478,7 @@ struct Thread
|
|||||||
|
|
||||||
// Variable in this object
|
// Variable in this object
|
||||||
if(type == PT.DataOffs)
|
if(type == PT.DataOffs)
|
||||||
return obj.getDataInt(clsInd, index);
|
return obj.getDataInt(cls.treeIndex, index);
|
||||||
|
|
||||||
// This object, but another (parent) class
|
// This object, but another (parent) class
|
||||||
if(type == PT.DataOffsCls)
|
if(type == PT.DataOffsCls)
|
||||||
@ -449,7 +535,7 @@ struct Thread
|
|||||||
//for(long i=0;i<limit;i++)
|
//for(long i=0;i<limit;i++)
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
ubyte opCode = code.getCmd();
|
ubyte opCode = code.get();
|
||||||
|
|
||||||
//writefln("stack=", stack.getPos);
|
//writefln("stack=", stack.getPos);
|
||||||
//writefln("exec(%s): %s", code.getLine, bcToString[opCode]);
|
//writefln("exec(%s): %s", code.getLine, bcToString[opCode]);
|
||||||
@ -566,7 +652,7 @@ struct Thread
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case BC.PushClassVar:
|
case BC.PushClassVar:
|
||||||
stack.pushInt(*obj.getDataInt(clsInd, code.getInt()));
|
stack.pushInt(*obj.getDataInt(cls.treeIndex, code.getInt()));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BC.PushParentVar:
|
case BC.PushParentVar:
|
||||||
@ -1352,7 +1438,7 @@ struct Scheduler
|
|||||||
// possible.
|
// possible.
|
||||||
if(cn.isIdle)
|
if(cn.isIdle)
|
||||||
{
|
{
|
||||||
fstack.pushIdleCheck(cn.idle, cn.idleObj);
|
fstack.pushIdle(cn.idle, cn.idleObj);
|
||||||
if(cn.idle.idleFunc.hasFinished(cn))
|
if(cn.idle.idleFunc.hasFinished(cn))
|
||||||
// Schedule the code to start running again this round. We
|
// Schedule the code to start running again this round. We
|
||||||
// move it from the wait list to the run list.
|
// move it from the wait list to the run list.
|
||||||
|
@ -36,6 +36,7 @@ import monster.compiler.assembler;
|
|||||||
import monster.compiler.scopes;
|
import monster.compiler.scopes;
|
||||||
|
|
||||||
import monster.modules.timer;
|
import monster.modules.timer;
|
||||||
|
import monster.modules.frames;
|
||||||
|
|
||||||
import std.file;
|
import std.file;
|
||||||
import monster.util.string;
|
import monster.util.string;
|
||||||
@ -65,6 +66,8 @@ struct VM
|
|||||||
if(time != 0)
|
if(time != 0)
|
||||||
idleTime.add(time);
|
idleTime.add(time);
|
||||||
|
|
||||||
|
updateFrames(time);
|
||||||
|
|
||||||
scheduler.doFrame();
|
scheduler.doFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,8 +3,7 @@ singleton FPSTicker;
|
|||||||
|
|
||||||
import io, timer;
|
import io, timer;
|
||||||
|
|
||||||
// This is updated automatically by input/events.d
|
ulong lastFrame;
|
||||||
int frameCount;
|
|
||||||
|
|
||||||
float delay = 1.5;
|
float delay = 1.5;
|
||||||
|
|
||||||
@ -12,7 +11,7 @@ state tick
|
|||||||
{
|
{
|
||||||
begin:
|
begin:
|
||||||
sleep(delay);
|
sleep(delay);
|
||||||
print("fps: ", frameCount / delay);
|
print("fps:", (frames.counter-lastFrame) / delay);
|
||||||
frameCount = 0;
|
lastFrame = frames.counter;
|
||||||
goto begin;
|
goto begin;
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,10 @@ void initMonsterScripts()
|
|||||||
{ stack.pushInt(rnd.randInt
|
{ stack.pushInt(rnd.randInt
|
||||||
(stack.popInt,stack.popInt));});
|
(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
|
// Load and run the test script
|
||||||
mc = new MonsterClass("Test");
|
mc = new MonsterClass("Test");
|
||||||
mc.createObject().call("test");
|
mc.createObject().call("test");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user