mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 20:56:39 +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
					
				
					 15 changed files with 389 additions and 211 deletions
				
			
		|  | @ -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…
	
		Reference in a new issue