Sign in to follow this  
Beginner_Joe

Frame Rates for beginners!

Recommended Posts

Hi everyone! I recently decided to start learning C++, specifically for game programming, and have had some great fun working through various tutorials, and reading posts on this forum (you all seem very knowledgeable and helpful!!). I have a question which is probably really stupid, but I hope you don't mind me asking: It's a bit tricky to explain, but I'll do my best.... I've been using the tutorials from http://www.directxtutorial.com/ which seem to be quite well used, and everything is working as expected. However my quesiton is to do with frame rates; let's say the main code is as follows (I won't put the whole thing here): // include the basic windows header files and the Direct3D header file #include <windows.h> #include <windowsx.h> #include <d3d9.h> #include <d3dx9.h> // define the screen resolution and keyboard macros #define SCREEN_WIDTH 640 #define SCREEN_HEIGHT 480 #define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0) #define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1) // include the Direct3D Library files #pragma comment (lib, "d3d9.lib") #pragma comment (lib, "d3dx9.lib") // global declarations LPDIRECT3D9 d3d; // the pointer to our Direct3D interface LPDIRECT3DDEVICE9 d3ddev; // the pointer to the device class LPDIRECT3DVERTEXBUFFER9 t_buffer = NULL; // the pointer to the vertex buffer // function prototypes void initD3D(HWND hWnd); // sets up and initializes Direct3D void render_frame(void); // renders a single frame void cleanD3D(void); // closes Direct3D and releases memory void init_graphics(void); // 3D declarations struct CUSTOMVERTEX {FLOAT X, Y, Z; DWORD COLOR;}; #define CUSTOMFVF (D3DFVF_XYZ | D3DFVF_DIFFUSE) // the WindowProc function prototype LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); // the entry point for any Windows program int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; WNDCLASSEX wc; ZeroMemory(&wc, sizeof(WNDCLASSEX)); wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC)WindowProc; wc.hInstance = hInstance; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.lpszClassName = L"WindowClass"; RegisterClassEx(&wc); hWnd = CreateWindowEx(NULL, L"WindowClass", L"Our Direct3D Program", WS_EX_TOPMOST | WS_POPUP, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, nCmdShow); // set up and initialize Direct3D initD3D(hWnd); // enter the main loop: MSG msg; while(TRUE) { DWORD starting_point = GetTickCount(); if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } render_frame(); // check the 'escape' key if(KEY_DOWN(VK_ESCAPE)) PostMessage(hWnd, WM_DESTROY, 0, 0); while ((GetTickCount() - starting_point) < 25); } // clean up DirectX and COM cleanD3D(); return msg.wParam; } OK, so this bit: while ((GetTickCount() - starting_point) < 25); makes sure it only updates the frame once every 25ms or therebouts. So taking it out should mean that it updates as fast as the computer can handle. To this code I've added a basic framerate counter. So when I take out the 'limiter', it still only runs at about 100 frames per second. Now I know my PC plays some older games waaayyy faster than that, so the question I have is what in this code limits the frame rate? My guess is perhaps something to do with the way windows prioritises applications, or perhaps that's just the nature of DirectX? I'm sorry for such a silly quesiton, but any thoughts would be gratefully received. I guess 100fps is pretty much fast enough for anything, but I'm working on a pinball game with a bit of 'real world physics', and the faster the 'framerate' (or I guess you'd say main looprate) the more accurate my physics will be. Just to say I'm convinced it's not the limit of my PC, because a) the frame rate of some older games I have, and b) I've expanded the code to display multiple objects, with lodas of key inputs, text boxes etc., running at 1600x1200 and it still runs at the same framerate as when it is completely basic like above- ???!! Hope this makes sense? cheers, Joe

Share this post


Link to post
Share on other sites
Thanks for that ToohrVyk - that's really quite interesting.

If that is what's limiting my 'frame rate', I guess I need to introduce something to my code that does this:

1) Turns off 'Vsync'
2) Executes the main loop as often as it possibly can
but
3) Only uses render_frame() at the correct time

i.e. the main loop runs v fast (thus the physics becomes more accurate), but the rendering only happens say 85 times per second (if that's my refresh rate) to ensure there's no tearing.

This sounds really good - I'll research how to do this;

Out of interest does anyone else think it's a different issue?

thanks again!

Joe

Share this post


Link to post
Share on other sites
Also one other thing...

This line here:

PostMessage(hWnd, WM_DESTROY, 0, 0);


It's not really a big deal for a simple program with one window, but this isn't how you destroy a window. You use the DestroyWindow function. That function actually cleans up resources created for the window, which won't happen if you just send yourself a WM_DESTROY message.

Share this post


Link to post
Share on other sites
Thanks MJP,

yes, I measure the 'loop rate' using QueryPerformanceCounter/QueryPerformanceFrequency , and (I should have posted that code here, but I'm at work at the moment!) just before the render_frame() I use counter++, and work out the fps (or lps!) by comparing the counter int to the results of the 'realtime' counter above.

In fact, I have gone as far as displaying how many microseconds each loop takes - its about 13,333 - and as mentioned doesn't change significantly no matter how many polys & objects I throw at it; this is why I reckon something is 'holding up' each loop?

perhaps I'm just being obsessive!

thanks for all the help so far,

cheers,

joe

Share this post


Link to post
Share on other sites
Quote:
Original post by Beginner_Joe
1) Turns off 'Vsync'
2) Executes the main loop as often as it possibly can
but
3) Only uses render_frame() at the correct time


This is a possibility. However, what's wrong with:

1) Gets the current time
2) Computes the world state for the current time
3) Renders the frame using the world state and with VSync

Regardless of VSync, you'll still get a framerate that's reasonable enough for collecting input, and you can avoid VSync problems entirely by using triple buffering if it really annoys you.

The physics loop doesn't have to run in real-time—it only has to run as if it happened in real-time.

Share this post


Link to post
Share on other sites
Quote:
Original post by Beginner_Joe
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

Is this still how C++ is taught? C++ has had const and inline functions since 1983 or something.

Share this post


Link to post
Share on other sites
Thanks ToohrVyk that's of course a good point. Actually that's how my prog works at the moment (e.g. for the ball position it works out what the accel would be at the point in time the function is called, from that the velocity and from that the world position).

And in 99.9% of circumstances this is fine (although for collisions I 'back-engineer' to the time of collision, and update before and after etc.)

however in some circumstances I want to figure out changes smaller than the current delta ( approx 13ms),

Plus I'm thinking there must be a way to do it cos other progs I've used don't seem to have this 'limit' (i.e. played severance the other day and it runs at 400fps...!)

DevFred - I'm sure there are 100 better ways to do things than how I'm learning! let's just say the tutorials that I'm doing are about the right level for me (I did used to program the speccy in hex dumps, but I was 10 at the time, and for some reason things seemed easier then...?!!)

Share this post


Link to post
Share on other sites
Quote:
Original post by Beginner_Joe
however in some circumstances I want to figure out changes smaller than the current delta ( approx 13ms),
You're limiting yourself to thinking in terms of "one update step per frame". Suppose for a minute that you want your physics (very simple and easy to compute) to update every 2 milliseconds, but your frames are only rendered every 14 milliseconds. What do you do? You just run seven updates before every frame!

In short:
int updates = 0;
float start_time = time();
while (running)
{
int expected_updates = (time() - start_time) * UPDATES_PER_SECOND;

while (updates < expected_updates)
{ run_update(); ++updates; }

render_frame();
}


This guarantees time-independence, a great update frequency for your physics, and is still simple.

Quote:
Plus I'm thinking there must be a way to do it cos other progs I've used don't seem to have this 'limit' (i.e. played severance the other day and it runs at 400fps...!)
To do what? Display a very high value followed by "FPS" ? Unless it has a very clear impact on the gameplay (and at 85FPS, chances are that it won't), don't bother with it.

Quote:
DevFred - I'm sure there are 100 better ways to do things than how I'm learning! let's just say the tutorials that I'm doing are about the right level for me
The issue is with them being outdated. The correct version of those lines would be:

const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
inline bool KEY_DOWN(int vk_code) { return GetAsyncKeyState(vk_code) & 0x8000; }
inline bool KEY_UP(int vk_code) { return GetAsyncKeyState(vk_code) & 0x8000; }


Share this post


Link to post
Share on other sites
hahaha - genius, yes of course ToohrVyk that makes perfect sense! I have to get used to not thinking 'step-by-step'.

Comment noted re fps of >100 fps etc. - you're right, what impact does it have?! The solution you suggest is exactly what I need; the fps doesn't matter, but I want the time delta for calculating motion to be as small as possible.

And thanks for the corrections re #define / const. I'm getting my head round what the difference is (with a little assistance from 'C++ for dummies'). I've only been learning C++ for 3 weeks, so I've had a lot to take in! I only understood what the point of pointers was about 5 days ago.... ;)

Thanks again for your time & help, I'm really grateful for you assisting this rookie!

Now I'd better get back to my day job (which is recruiting IT people!).

And one of the reasons why I'm learning C++? I've been asked by games companies to recruit coders & artists for them, been out to meet a few studios, and thought 'that is a job that I want'. I don't care what the entry level pays, I'm imagining working in a job that I really enjoy.

One day, one day!






Share this post


Link to post
Share on other sites
Excellent -

Of course, once I disabled VSync in the code (and after a bit of puzzlement, in my graphics card driver global settings as well!) the app runs at 2000fps. Which I don't need obviously.

This means that you guys were right it's the Vsync.

So the aim is now to follow the advice, and run a loop until the monitor is ready to refresh. I don't want to guestimate the number of loops, and I want to squeeze as many loops as I can between frames

Bit of research has shown up D3DRASTER_STATUS and GETRASTERSTATUS as possible ways of timing this right.

Would any one be kind enough to show me a bit of code that would allow me to check the raster status each loop so I can call the frame rendering at exactly the right time? I've looked at the MSDN but I'm getting confused, and can't understand how to get anything meaningful!

Hope you don't mind me asking this,

cheers,

Joe

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this