Direct3D 9 Framerate Limited...

Started by
12 comments, last by Adam Hamilton 14 years, 5 months ago
Hey all I've got a really simple program here (C++, DX9) - just rendering a quad. I've opened up FRAPS, and it tells me that it's only running at about 30-40fps... and about 0-1% CPU. Is there something I need to do to prevent it from limiting the framerate? If it was stuck at about 60fps, I'd assume v-sync was on... but 40fps? So maybe I need to tell it "hey, actually use some CPU time to render as many frame as you can"? Render function is just this:
    p_dx_Device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255, 255, 255), 1.0f, 0);
    p_dx_Device->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    p_dx_Device->BeginScene();

    p_dx_Device->SetStreamSource(0, p_dx_VertexBuffer, 0, sizeof(QUADVERTEX));
    p_dx_Device->SetFVF(QUADVERTEXFVF);

    p_dx_Device->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);

    p_dx_Device->EndScene();
    p_dx_Device->Present(NULL, NULL, NULL, NULL);
And message loop is just this:
    while(appRunning)
    {
        while(PeekMessage(&msg_Message, han_Window, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg_Message);
            DispatchMessage(&msg_Message);
        }
        DrawScene(p_Device, p_dx_VB);
    }
Advertisement
It would be nice to also see how you create your device.

Also you might want to just process one windows message per drawScene call rather than emptying the queue of windows messages.

Quote:Original post by Steve_Segreto
It would be nice to also see how you create your device.

Good point :)

    LPDIRECT3D9 p_dx_Object;    LPDIRECT3DDEVICE9 p_dx_Device;    p_dx_Object = Direct3DCreate9(D3D_SDK_VERSION);    D3DPRESENT_PARAMETERS dx_PresParams;    ZeroMemory(&dx_PresParams, sizeof(dx_PresParams));    dx_PresParams.Windowed = TRUE;    dx_PresParams.SwapEffect = D3DSWAPEFFECT_DISCARD;    dx_PresParams.BackBufferFormat = D3DFMT_X8R8G8B8;    dx_PresParams.EnableAutoDepthStencil = TRUE;    dx_PresParams.AutoDepthStencilFormat = D3DFMT_D16;


Quote:Also you might want to just process one windows message per drawScene call rather than emptying the queue of windows messages.

Oh, I thought that this would mean that each frame would only get one message?
And thus, if the user presses more than one key, or moves the mouse, it will take many frames for the game to catch up?

I ran into this problem a while ago:
http://www.gamedev.net/community/forums/topic.asp?topic_id=530473
Message queue code should handle all the messages available, not one per-frame (meaning your code is right on that part.) However, you don't handle seem to handle WM_QUIT, which is not good.

p_dx_Device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255, 255, 255), 1.0f, 0);p_dx_Device->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);


This however, should just be:
p_dx_Device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255, 255, 255), 1.0f, 0);


And vertical-sync is most definitely on (unless you have some other app disabling it.) To disable it, you set PresentParameters::PresentationInterval to D3DPRESENT_INTERVAL_IMMEDIATE.

Beyond that, you haven't shown much code, so I can't really say.

If you need a tutorial to help you with the back-side of things, I'd recommend these articles. His articles cover a lot of the not-so-well-covered stuff (like message queues, lost devices, etc.)

HTH!
Quote:Original post by Programmer16
However, you don't handle seem to handle WM_QUIT, which is not good.

I deal with the particular messages in my WndProc function, is that ok?

Quote:D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER

Cool, thanks for the tip!

Quote:And vertical-sync is most definitely on (unless you have some other app disabling it.) To disable it, you set PresentParameters::PresentationInterval to D3DPRESENT_INTERVAL_IMMEDIATE.

Perfect, this fixed it :)

Though I find it interesting that it was limiting it to about 30-40fps, and not 60!

Thanks!
Quote:Original post by Steve_SegretoAlso you might want to just process one windows message per drawScene call rather than emptying the queue of windows messages.


That would severely limit the application. Worst idea ever.
With some mouse movement and keyboard interaction you're easily looking at 20 messages that needs to be processed per frame in order to get the correct/intended result.

Quote:Original post by NightCabbage
Though I find it interesting that it was limiting it to about 30-40fps, and not 60!


D3DPRESENT_INTERVAL_ONE will limit it at 60 fps (assuming 60Hz monitor)
D3DPRESENT_INTERVAL_DEFAULT will limit it at the 30-40 you're seeing.

Quote:MSDN
D3DPRESENT_INTERVAL_DEFAULT uses the default system timer resolution whereas the D3DPRESENT_INTERVAL_ONE calls timeBeginPeriod to enhance system timer resolution. This improves the quality of vertical sync, but consumes slightly more processing time. Both parameters attempt to synchronize vertically.
Quote:Original post by Programmer16
If you need a tutorial to help you with the back-side of things, I'd recommend these articles. His articles cover a lot of the not-so-well-covered stuff (like message queues, lost devices, etc.)

Best tutorial I've seen. Highly recommended!

Thanks for the link!
Can anyone tell me why you would want to render as many frames as possible with spare CPU time. I have always believed that you should try and limit the FPS of the rendering to the refresh rate of the monitor, sending the extra bandwidth to update calls

I guess the only benefit I can see is if the rendering is done to off-screen render targets and shader programs running through algorithms that don't necessarily write to the screen. I am not sure if this is right - I am just starting to get into Direct3D programming and it would be good to know in any case.

I guess the only other use I can come up with is benchmarking the card / CPU


Cheers
Adam Hamilton
There's no particular reason - some people like to run with vsync turned off.

Also you bring up another point... "sending the extra bandwidth to update calls"

What's the best way to do this?

eg. isn't the update call done before the draw call (one after the other - once per frame)?

and would there be any point in updating more than once per frame?
Regarding the absurdly high framerates, yes, they're a bit useless, aside from benchmarks and bragging rights. If DirectX supported the useful method of triple buffering, the extra fast rendering could reduce input lag and show no tearing, however DirectX doesn't support the useful method of triple buffering.

The other benefit of not being vertical synced is when you can't guarantee 60Hz. Most people would rather get a smoothly fluctuating frame rate with some mild tearing rather than sudden jumps between 30Hz and 60Hz (and/or 20Hz).

-

I know you're going to ask about that useful vs. useless triple buffering, as it's not common knowledge... I only read about it earlier this year, and it makes so much sense.

Under DirectX, the buffers are always shown in the order they were rendered. A third buffer just mean everything is delayed by 1 scene draw without vsync, or 1 frame with vsync. In the useful triple buffer method, when the front and backbuffers are swapped, the old front buffer swaps with the backbuffer that is not currently drawing. If no new frame is ready at vsync time, the existing buffer stays the front buffer.

Lets assume you can render 3 times faster than 60Hz, or 5-6ms per frame.

What DirectX does for triple buffering without vsync.
Render to backbuffer 1.
Render to backbuffer 2.
DirectX will show backbuffer 1 (5-6ms old, may have tearing)
Render to backbuffer 1.
DirectX will show backbuffer 2 (5-6ms old, may have tearing)
Render to backbuffer 2.
DirectX will show backbuffer 1 (5-6ms old, may have tearing)

What DirectX does for triple buffering with vsync enabled.
Render to backbuffer 1.
Render to backbuffer 2.
DirectX will show backbuffer 1 (5-6ms old)
Render to backbuffer 1.
DirectX will show backbuffer 2 (10.6-11.6ms old (16.666ms - 5-6ms to render))
Render to backbuffer 2.
DirectX will show backbuffer 1 (10.6-11.6ms old (16.666ms - 5-6ms to render))
These frame times may get even longer as we stall, waiting for the buffers in the render queue to be presented.

The useful triple buffering system is as follows... vsync enabled.
Render to backbuffer 1.
Render to backbuffer 2.
Render to backbuffer 1.
Something other than DirectX takes the latest ready frame.
Render to backbuffer 2.
Render to backbuffer 1.
Render to backbuffer 2.
Something other than DirectX takes the latest ready frame.
In this case the latest frame is always between 0 and 5-6ms behind, as we might have just finished drawing it, or we might be just about to finish, and so at worst, the last fully drawn frame was 5-6ms ago.

This topic is closed to new replies.

Advertisement