I've been asked many times recently to provide a tutorial for masm programmers wishing to write their own games.
As this tutorial grows, it will be cleaned up and posted online, and a URL will be provided in due course.

These days it seems like everyone has an interest in game development.
Many people daydream of working in this industry, but most have no idea where to begin. The simple answer is that you MUST produce a demo.
If you can't produce a demo, nobody is going to listen to you, much less employ you. Your demo is evidence of your ability.

Most game developers today write their sourcecode in C++ or some other object-oriented programming language.. It's not a requirement of the industry that you code strictly in C++, but it IS required that the code objects you create are compatible through the implementation of standard interfaces.

In case you've never heard of it, let me introduce you to ObjAsm32.
OA32 is a set of buildtime macros for MASM whose main purpose is to give the humble MASM programmer the ability to create and implement various kinds of standard interfaces, and to interact with existing object interfaces (regardless of the language they were written in).

Throughout this tutorial you will learn how to write and use 'oop classes' with respect to Game Development.
I choose to use the RadAsm IDE, but that is optional.

The attached zip file contains a "win32 skeleton" sourcecode which provides a basis for the rest of this tutorial.

If you haven't installed ObjAsm32, you should do so now.
If you haven't installed DebugCenter, you should do so now.
If you require assistance, ask and you shall receive.

Having installed both of these, you may attempt to build the provided sourcecode.
If you successfully build this sourcecode, run the executable you created and confirm that DebugCenter is working properly.
If you fail to build this sourcecode, or you see strange error messages,  please let me know, as your valuable feedback will become part of a FAQ aimed at resolving any 'teething problems'.

If you have any questions at all regarding the provided sourcecode, feel free to ask them, as there's no such thing as a stupid question (however there's a lot of stupid people!)

If you wish to critize at any stage, I welcome your perspective.

Posted on 2006-07-08 01:14:02 by Homer

The astute programmers among you might notice something odd in the sourcecode provided so far.. did you see it?
The variable called hInstance is referenced, but does not appear to have been declared... also, the usual call to obtain its value (GetModuleHandle,NULL) appears to be missing.. what's going on?

The answer is found in the SysInit macro, which you can see is being used at the Program EntryPoint.
Among other things, this macro declares the hInstance variable, performs the call to GetModuleHandle, and stores the result in hInstance for us.

"Ahhh.. I see !!" said the blind man.
Posted on 2006-07-08 01:31:33 by Homer

Now we've got a working Win32 Skeleton, it's time to decide which Graphics API (if any) we want to support.
In the attached sourcecode, I've added a simple 'buildtime switch' which lets you choose whether to use OpenGL, DirectX, or neither.

In the name of simplicity, this tutorial will be aimed at DirectX (and in particular Direct3D).. however I will endeavour to support OpenGL to some extent.

OpenGL and DirectX are both decent rendering engines, however DirectX supports other useful stuff like audio and networking, so for the purposes of writing a demo game, it's clearly the winner.
OpenGL is easier for beginners to use, because all its functionality is accessed through calls to procedures exported from a DLL.
DirectX on the other hand is implemented as a handful of procedures and a crapload of 'COM Interfaces'.
The words 'COM' and 'Interface' are enough to strike fear into the heart of the average MASM user, but as we shall see, thanks to OA32, there's nothing to be afraid of at all.

In any event, whether we decide to use OpenGL, DirectX, or even if we decide to provide our own rendering engine, we're going to need a Main Application Window, and we need to create it before we can do much at all.

OpenGL and DirectX BOTH have startup code that REQUIRES a handle to an existing window.

The next topic I'll be covering is the startup code for both Direct3D and OpenGL :)

Posted on 2006-07-08 02:27:03 by Homer

As indicated, our next step is to add some code to get either OpenGL or Direct3D started up (since we want to code modern 3D games, not old-fashioned 2D ones).

The attached zip contains code to do exactly that.
Don't worry about the fact that nothing is being rendered in the application window yet..
Have a play with the buildtime switches, and read what DebugCenter has to say when you run the executable.

Above all, don't be put off by the rather complex-looking code required to start up Direct3D, as it's perhaps the most difficult thing we'll ever do.. and since I've done it for you, relax !!
(Note : my example D3D9 startup code can be considered as "intermediate level", since its not as simple as it CAN be, yet its nowhere CLOSE to as complex as it CAN be).

Our next mission will be to add a simple Render procedure so that we can actually see SOMETHING in our Application window.
I'll be rendering to the entire app window, however it's possible to render to one or more Child windows (for example, to show multiple views of a 3D object), and it's also possible to render in 'fullscreen mode' (I assume most gamers are familiar with the difference between Windowed and FullScreen modes).

Posted on 2006-07-08 04:23:13 by Homer

The attached zip contains code which renders BLACKNESS to the Rendering window (which just happens to be the Main App Window).
It's just proof that our OpenGL / Direct3D9 startup code is actually doing something :)

Well, I think that will be enough for one day.
Tomorrow we can begin with some very simple rendering of 'polygonal primitives', and after that, I'll begin to pick up the pace a little :)

Posted on 2006-07-08 05:06:39 by Homer
I'm trying to appeal to the widest possible audience.
Some of you will feel I am going too fast, some will feel I am moving too slowly.. I'm not going to please everyone.
Things will speed up considerably once I've covered the basics, so if you find that I am leaving you in the dust, feel free to ask questions, and/or do a little research yourself.

The attached update covers a LOT of ground.. and all it does is draw a colorful triangle.

In terms of Direct3D, this is not exactly exciting.. but in terms of OpenGL, it's a Big Deal - because everything we want to draw in OGL is built from triangles. Strictly speaking, that's true of D3D as well, but D3D does a lot more of the gruntwork for us when it comes to rendering more complex objects. We'll get to that fairly soon.

Even though I have included code to render a triangle for both OpenGL and Direct3D, I'm going to confine the discussion of the sourcecode to the D3D version, since its more complex.

The example D3D code defines a CUSTOM VERTEX. You can think of a Vertex as a Point in 3D space. Obviously, to make a Triangle, we need three of these.. I went on to describe a Triangle built from three such Vertices.
The Custom Vertex struct I described (MyVertex) is created from three REAL4 floating point values (Position XYZ) and one DWORD (Color ARGB).
In order for D3D to understand our Vertex Format, we have to also describe it in terms of some special D3D values OR'd together in a single DWORD called the "Vertex Descriptor" (D3DFVF_CUSTOMVERTEX).
So, we've described our Vertex Format in two ways : as a Struct, and as a combination of Flags.
We could just go ahead and Render the Triangle from user memory like the OpenGL code does, but I went even further.
In order to benefit from hardware acceleration, its necessary to allocate a "Vertex Buffer" and copy our actual Vertex data into it.
I've provided the CreateTriangle procedure for this purpose.
This might seem redundant since we already defined our vertex data as global data (TestTri), but we're not simply duplicating the vertex data in memory, we're uploading it to the video card.
In a 'real' demo, we would not create a VB just for one triangle - we'd create a VB for a few hundred or even a few thousand Triangles, and if that wasn't large enough for all the Triangles we wanted to draw, we'd treat it as a Page.. fill the VB, draw its contents, rinse and repeat.

Let's talk about some of the other new stuff :)

There's a few more new procedures worth mentioning.
'ResizeScene' sets up the Camera Perspective.
It's called once at the moment, but should be called whenever the render window is resized.
'Initialize' sets up the 'default Render States'.
We'll learn more about Render States as we go.
'Keyboard' checks for various keyboard keypresses.
It supports 'key combinations' (more than one simultaneous key).
I've supplied the macros 'CheckKey' and 'CheckKeyCombo' as helpers.
Note that I've chosen NOT to use DirectX's DirectInput to check the keyboard, the way I chose works for both DX and OGL.

The Render procedure was modified to include code to draw a Triangle. The D3D code draws one or more triangles in a VertexBuffer (as a TriangleList), while the OGL code draws one or more triangles from explicit values (as a TriangleList).

The only other feature worth mentioning is the Translation being applied during the Render procedure.
Our triangle is designed in the XY plane, at Z=0
Problem: our Camera is located at <0,0,0>
In order to SEE the Triangle, we either have to move the Camera backwards, or move the Triangle forwards.
The D3D code creates a Translation Matrix which contains the values <0,0,+5>, which is used to position our Triangle in the World at <0,0,-5> (I've explained in another thread how this works and why the sign appears to have flipped).
The OGL code achieves the same thing via a call to glTranslate, but takes the Camera-relative coordinates <0,0,-5> instead of the World-relative position <0,0,+5).
In actuality, the D3D translation matrix function internally flips the signs and so the way OGL works is more algorithmically efficient.

You might notice that the Main Loop is starting to take shape.
It's not just a MessagePump loop anymore.
Eventually the time will come when GetMessage doesn't cut it.
We'll either have to write a smarter MessagePump, or we can drive our Render proc from a separate thread.

In the next exciting episode, we'll learn how to construct a Quad from two Triangles (OpenGL supports Quads as Primitives, D3D does not).
After that we'll learn how to load and apply Textures from image files, how to render a 'Billboard', etc.
At that stage we'll be ready to create a static 'Loading' screen, add code to make our render function stateful (eg so we can render the Loading screen or the Menu screen or the actual Game etc), there's a lot of ground to cover, so I want to start introducing GameDev concepts as soon as possible.. lighting, mesh models and stuff can wait.

I can already see some interest in this thread.
People are downloading the zips, but I see no feedback from you.
Does that mean nobody experienced problems yet?
(Note: I'm using the D3D libs from the December 2005 SDK)
Posted on 2006-07-08 21:39:28 by Homer
right now adding a .obj filereader that directly converts it to customvertexformat from ascii
and now a designquestion arises:cant I have a list of pointers to direct VB to different data instead of all copying data to VB?
Posted on 2006-07-08 22:23:45 by daydreamer
Yes, you can render geometry directly from User memory as I stated earlier.. however, it's extremely slow in comparison to using a VertexBuffer, because your geometry is not stored in video memory, which means it gets sent to the video card each time you draw it.
Creating a VB means that you send the geometry to the video card ONCE, where the video card can access it much more quickly for rendering.
On the other hand, READING from video memory is EXTREMELY slow.
If you need to READ your vertices, you might choose to either render the geometry from user memory (which is slow), or you might decide to keep TWO copies of your geometry - one in user memory, and one copied to a VB. If you Lock a VB with "WRITE ACCESS ONLY", it's MUCH quicker than Locking it for "READ/WRITE ACCESS", so we try to treat a VB as "write only" in order to obtain maximum benefit from our video hardware whilst avoiding performance penalities.

Posted on 2006-07-08 22:34:21 by Homer

With only some very minor modifications, the attached zip shows how to render a Quad using two Triangles using D3D.
The OGL version has not been updated yet.

Note that I haven't changed the Vertex Format, but I HAVE changed from a 'TriangleList' to a 'TriangleStrip'.
In order to draw a Quad from a TriangleList, we would need to draw two separate Triangles, a total of six vertices.
By using a TriangleStrip, we can reduce that to four vertices.

I'll try to describe briefly how TriangleStrips work.
In a TriangleStrip, the first Triangle is defined normally, ie as three Vertices.. When we want to draw subsequent Triangles, its assumed that the last two Vertices of the previous Triangle are shared by next Triangle - that is to say, the last Edge of the previous triangle is Shared by the next Triangle.. since we can say that two Vertices of the next Triangle are already defined, we only need one Vertex for each subsequent Triangle in a TriangleStrip, and therefore, to draw a Strip containing two Triangles we need FOUR Vertices.

If we have multiple Triangles to draw which have shared Edges, then TriangleStrips are far more efficient (and faster) than TriangleLists.

I'll go away and add code to render a Quad in OGL.
Under OGL we can use TriangleStrips too, or if we want to, we can render Quads as a primitive in their own right.. D3D can't do that.

The next posting shall contain the updated OGL quad rendering code, as well as D3D code to Load a Texture from an Image File.

Whaddyaknow, it's lunch time :)
Posted on 2006-07-08 23:19:18 by Homer
Hi Homer. This be a great idea :) What I noticed is that you don't use a PURE DEVICE, which is 10-30 % faster. You can use PURE DEVICE if:
1) you use hardware vertex processing
2) the device supports rasterization, transform, lighting, and shading in hardware.
3) You don't plan to call any "get*" methods (getLight, getMaterial, etc.), which -in practice- are never called, because you always know what you set and/or you have it stored inside your own variables.

It's as simple as checking a few flags and then adding D3DCREATE_PUREDEVICE flag to device's creation parameters. It's also smart to add D3DCREATE_DISABLE_DRIVER_MANAGEMENT. Direct3D9 has the ability to overcome some problems that stupid drivers have.
Posted on 2006-07-09 02:51:05 by ti_mo_n
ti_mo_n -
Well spotted :)
The next incarnation of the Framework will begin to address such issues.

Everyone -
Well, I did intend to forge ahead into the land of Textures and Solid Models... however, now might be a good time to "harden" our D3D Framework, and to begin talking about OOP.

So far we've only seen how to use COM Objects.
We've learned how to use the ICall (InterfaceCall) macro to call the Methods of a COM interface, and if we've been looking closely, we've also learned that COM interfaces always have a Method called Release, which is used to Destroy them. We may have also noticed that we called some Procedure to Create them.

It's time we learned about another kind of Object.
We're going to define our first OA32 standard object.
These objects are totally compatible with C++, and are quite similar to COM interface objects in many respects.

Once we've defined an OA32 Class Object, we can call its Methods using the OCall (ObjectCall) macro, whose syntax is exactly the same as for ICall.
We can create any number of Instances of the Object using the New macro (we'll see it soon enough), but there's no Release method.. we use the Destroy macro to destroy instances of OA32 objects.

The typical use of an OA32 class is to allow us to create MANY instances of some kind of object.. for example, we might create a Class which represents a 3D Cube.. we could then create any number of Instances of the Cube, each having its own Position and Orientation in the World.

However, our first OA32 class will instead describe something we actually only want one Instance of.
You see, making multiple instances of a class object isn't the only good reason for putting code in its own class.
Another very good reason to do this is to create a Code Module which is easily able to be reused in different projects without ANY modification.

You might like to think of a Code Module as a Brick, and an Application as a House.. Bricks are useful, easy to recycle, and can be put together in many ways.

With that in mind, our first OA32 Class will be called D3DApp.
Sorry OpenGL people, I'm going to leave you now, although the OOP principles are equally applicable to OGL, I'm just not interested in writing a cross-api engine for you.. you've seen that it's not hard, and you've seen that D3D and OGL are not all THAT different.

This new D3DApp class will be the new home for 90 percent of the code I've shared with you so far, and will build on that code to provide a robust and reusable code module.
This is very similar to the way that the DX SDK samples are presented - all this 'base framework' code is wrapped into a reusable Class.
Another advantage of doing this is that we've effectively swept all this code under the rug.. by removing it from the main sourcecode, we've tidied up the main sourcecode, making it highly readable, and hiding away a lot of stuff that we've already learned about.

Our new D3DApp class will contain a bunch of bonus goodies :)
For example, it can enumerate all the possible Video Modes that your hardware can produce and allow you to select from them at runtime.
Also, it can remember the previously used settings in a datafile, and ask you whether you want to use the previous settings at startup.
It contains code to switch between fullscreen and windowed mode, and other stuff too, so what are we waiting for?

I've already written and tested this Class some time ago (here's one I prepared earlier lol) - however I'll walk you through the process of building the class from scratch, rather than just hand it over.

In the next update, we'll begin to create our D3DApp class, and we'll be stripping the main sourcecode down to almost nothing.

I repeat, wrapping our existing code is NOT the 'most valid use' for OOP.. but it's as good a place to begin as any.
We'll sure be using a lot more OOP as we make more progress !!
Posted on 2006-07-09 03:09:10 by Homer
Attached is Tutorial #7.
I've taken the project skeleton from the original zip, and added skeleton code for our new D3DApp class.
I've also added some debugging lines so that if you execute the program and examine the debug spew, you can compare it to the sourcecode and make sense of what's happening (although I don't think it's too difficult, this stuff will seem alien to a few people).

Our D3DApp class has a method called Run.
This will be the equivalent of our old WinMain procedure, ie we won't return from there until its time to Die.
That means that the .ASM file as you see it in Tutorial #7 is pretty much 100% complete as it stands.. so what about all the old code?

What happens from here is that we start shovelling all the code from the previous Tutorial #6 into this empty bag we named the D3Dapp class :P

So ask me now - why didn't I damn well design it that way in the first place? For a very good reason.. Well actually, I DID, but I want you to see me moving the existing code into an oop framework, so you understand more about OA32 through practical example.

Posted on 2006-07-09 06:51:43 by Homer

D3DApp is basically complete.
It has the following features:
-Window management code (Creation,MessagePump,Destruction)
-Video Settings Dialog
-User CallBacks for Rendering and/or critical events
-Lost Device recovery
-Windowing mode toggle
-ConfigFile for last known Working video settings
and other stuff.

See attached zipfile.

Please let me know which parts of this sourcecode require explanation.

Posted on 2006-07-10 06:22:43 by Homer
Now we've got Rendering back again.
Here's the D3DApp version of the Triangle demo, with a twist.
A texture is loaded from a bmp file.
The vertex format specifies both color AND texturecoords.
The result is a multicolored triangle with a texture.
Visually, it looks like we're blending the texture.

Note: You can use alt-enter to toggle fullscreen, and the Escape key to quit.

Posted on 2006-07-10 09:09:48 by Homer

For those of you who are new to OA32, I'd like to explain about Collections.

We've already seen how to use MemAlloc/MemFree to allocate and release chunks of memory (aka 'heap objects').
We've also seen how to use New/Destroy to create and destroy OA32 objects.

Collection is one of the standard OA32 objects.
In essence, a Collection is an array of DWORD, plus some Methods that we can use to mess around with the contents of the array.
More specifically, Collection assumes that the dwords its array contains are in fact pointers to other OA32 objects.
If we destroy the Collection, it will automagically destroy all the objects it contains, which makes life easy for us (otherwise we'd have to issue a Destroy for each and every object in the array).

DataCollections are very similar to Collections - in fact DataCollection is implemented as an Ancestor of Collection with a few methods of Collection being overridden (replaced).
DataCollections are designed to store Heap Objects (MemAlloc'd), and if you destroy a DataCollection, it behaves similarly to a Collection.
All the objects it contains are MemFree'd for you.

That's enough theory for now, let's see a practical example.

D3D does not perform management of textures for you.
For example, if you load the same texture twice, D3D will assume that you intended this, and create two IDirect3DTexture9 objects.
What if we have textures which are shared by more than one entity in our 3D world? How can we make use of shared resources and eliminate the duplication of such resources in memory?

The attached file contains two OA32 object definitions.
They are called Texture and TextureManager.
The former is a glorified struct - a container for an IDirect3DTexture9 object and the name of the file from which it was loaded.
The latter is a simple class which uses a Collection to keep a list of loaded Textures, which it searches whenever an attempt to load a texture is performed.
If we find that a given texture is already loaded, we just return its IDirect3DTexture9 object pointer.
However, if the texture is not yet loaded, we load it, then we create a Texture container object, fill its fields, and add it to the Collection.

One really nice reason for doing this is that when we the User wants to quit the game (or other app), we just destroy our TextureManager, and it will automatically release all the textures for us (because the Texture.Done method contains code to release the IDirect3DTexture object that was stored in the Texture.pTexture field).

I'll be adding TextureManager support to the D3DApp class.
If you have questions, ask them :)
Posted on 2006-07-11 02:07:50 by Homer

As for a demo game, I think I'd prefer to keep it simple, so this thread doesn't become excessive - after all, we want to make games, not learn the latest in special effects or whatever.

We have a fairly functional application framework for writing our games in now (ok, its crap, but we have enough to begin with).
So let's decide on what game we're making, and how we're making it.

I think a 3D 'breakout'-style game is pretty easy and an OK place to start. We'll have a view from somewhere up in the air looking down.
The game will basically be a 2D game but drawn in 3D.
Let's say that the world as seen from above is a 2D chessboard of tiles.
What kind of Scale are we talking about?
Let's say the Tiles are 10 x 10 Units in size.

Most games try to 'partition' the gamespace in some way.
You can think of this as 'cutting up the world into subspaces'.
Other common names for subspaces: cells, sectors, rooms
Typically, we define a Struct which describes the properties of and/or the contents of a subspace. The data for each subspace is stored in some structured way, usually as an array or tree. Thus the array or tree represents the entire World, and everything in it.
Basically we're working with a 2D array.

WorldCell struct
cbContents db ?
WorldCell ends

Our WorldCell is nothing but a single byte.
We want to plan for the future, so we design it properly.

OA32 has an excellent object we can use to manage our array.
Array is a Standard OA32 object which implements an n-dimensional array of structs of arbitrary fixed size.
You tell it how large the array elements are, and then you start adding Dimensions to it (telling it how large each Dimension is).

I've implemented an Array in the attached demo.
To be precise, I've defined a new object called World which inherits from Array (and so has all its variables and methods in addition to its own).

Next we must begin the most important part of the tutorial, and it has nothing to do with programming.. we have to discuss the nature of the game in order to determine the code requirements. In doing so, we'll be planning the remainder of this tutorial.
Posted on 2006-07-11 05:23:59 by Homer
In between talking about the proposed demo game, I'll continue to advance the Framework and post updates.

I've added a DrawGrid method to D3DApp.
It draws a Grid in the XZ plane at Y=0, however you can specify the granularity of the grid (ie distance between the lines), and you can specify the color.

The grid looks something like what you'd see in a 3D modelling package, and basically just gives us a sense of Scale when our World is mostly empty space.

The grid is implemented using LINE primitives and drawing from User-memory (no VB). This is very inefficient, but since the Grid is only for Developers and will not be rendered in Release versions, we can live with the performance hit.

Posted on 2006-07-11 10:43:36 by Homer

I've written a very simple Beginner's level Camera object, and added support for it to D3DApp.

It's got four methods:
Position will set the Camera's Position in 3D space.
LookAt will make the Camera face a particular Direction.
Update will build and apply a ViewMatrix based on the above.
Move_Camera will move the Camera a given amount forwards or backwards in the direction the Camera is currently facing.

I've changed the size of the Grid to 10, and I've created the camera up in the air at<0,50,0>, and facing our Triangle at <0,0,5>.
You can use the up and down cursors to move the camera, but you can't rotate or strafe yet.
Try moving backwards to see exactly what our 500x500 grid looks like when drawn at a granularity of 10 (ie 50 lines in X and Z)
That's a lot of little squares huh?

Posted on 2006-07-12 10:55:28 by Homer

I've added a new method called Strafe_Camera to the D3DCamera class.
You can use the Left and Right cursors to strafe relative to the direction the Camera is facing.

Basically I simply modified a copy of the existing Move_Camera method..
Instead of moving forwards and backwards in 'Camera Z', we now move left and right in 'Camera X' (I simply switched the X and Z axes in regards to the output, and effectively ignored the Y axis).
The conventional solution is often to calculate a 'Right' vector from the View and Up vectors, which is imho over-engineering the solution to the problem at hand, at least for a Camera that never 'Rolls'.

Posted on 2006-07-12 13:02:54 by Homer
I mentioned previously that eventually our MessagePump would need to be improved (or the GameLoop moved into a separate thread).
For various reasons, I would prefer not to run the GameLoop in another thread.

We need a way to desynchronize the Rendering from the processing of WMs, but without requiring a new thread.
In order to solve any problem, it usually helps to describe it in detail - once you've done that, the solution is often very obvious.
So let's describe what's happening :)

Some of you with low-end hardware may have noticed that the application's performance isn't really that great in Windowed mode.
The main reason is that we are driving the Render function from within the MessagePump, meaning that unless WM's are being processed, no rendering occurs (underdriving the Render function) - and conversely, when MANY WM's are being processed, we are OVERDRIVING the Render function.
Think of it this way: if you stop moving the Mouse around, and don't press any keys, there's very few WM's being processed and thus rendering effectively ceases altogether.. but if you wiggle the mouse and/or go crazy on the keyboard, we try to Render too often.

I've improved the Run method in two ways.
1 - Code to calculate the FrameRate (fps) was added, it's used to cap the FrameRate to a maximum of 30fps. If we find that we've rendered 30 frames in less than a second, we don't render any more frames until one second has elapsed.. this is brutal, we can do better.
2 - The MessagePump loop's GetMessage call was replaced with PeekMessage, so that the MessagePump does not 'Hang' when there's no WM's to process. This drives your cpu MUCH harder (somewhere near 100%). Note that this is NOT HARMFUL - your cpu ALWAYS operates at 100%, what's new is that our Application is trying to hog the entire 100% all for itself.
If my code was better, we could estimate how much time we can Sleep without affecting the framerate, and not consume quite so much cpu time.. although it is acceptable for fullscreen games to consume 100% cpu time, it's unreasonable for a Windowed application.
Perhaps I'll revise the 'framerate capping' mechanism to use 'instantaneous values' rather than counting samples over time.
Right now (at least as far as Windowed mode goes), we've eliminated some problems and introduced new ones.

In regards to 'instantaneous FPS':
We've decided what our maximum fps should be.
We want 30 frames per second.
1/30 of a second is .033333 recurring.
In a perfect world,we know that each frame requires 1/30th of a second to render.
Any more and our framerate can't be achieved - our hardware isn't fast enough to meet our demands.
Any less and we have 'spare time' inbetween each Frame.
If we find that the 'instantaneous' time taken to render one frame is less than 1/30 of a second, we can Sleep for the remaining time.
For example, if we find that a frame took .013333 seconds to render, we know that we can Sleep for .02 seconds, ie until the end of the current 'timeslice'.
This idea can be improved even further by keeping track of the Average of our per-frame timer values, and applying an Average Sleep for each Frame.
This way the Sleep times gracefully ramp up and down on demand instead of being 'jittery'.

Some of the advantages of using 'instantaneous fps' are obvious (for example, we're distributing the rendering events over time, if our hardware was really fast we'd detect the pause when we apply the cap once per second).
Other are less obvious (we can hand 'spare time per frame' to another thread, for example an asynchronous loader thread that performs disk io without interfering with rendering, and only perform a Sleep if theres absolutely NOTHING that needs to be serviced).

Your thoughts?
Posted on 2006-07-12 18:53:26 by Homer