+ Reply to Thread
Page 1 of 7 1 2 3 ... LastLast
Results 1 to 10 of 61

Thread: Where to render

  1. #1
    Join Date
    May 2005
    Posts
    390

    Default Where to render

    The D3D sample code in the SDK (not the samples themselves, but what's in the SDK docs) mostly render in WM_PAINT. I spotted one example that moves it to the message pump. Ron, what do you do?

    I'm interested in the perspective of the type of game where you need to try to pump out as many FPS as possible and redraw more or less constantly. This is specifically distinct from GDI (and WM_PAINT, which is why this came up) where you don't redraw unless something causes you to.

    I guess the answer is going to be something based on "don't render in WM_PAINT", but how do you generally handle the case where you want to be more or less redrawing constantly (or "whenever your scene changes") ? I guess if you wanted to render in WM_PAINT, you could start a timer and then constantly invalidate the window, but that seems inelegant.

    Let's say I want to, I dunno, just draw a small object moving in a circle, like a planet orbiting a star.

  2. #2
    Join Date
    Mar 2005
    Location
    California
    Posts
    2,099

    Default

    No, you don't want to rely on the Message Pump at all. Rendering in WM_PAINT is a terrible idea.

    What most applications do (and also what I do) is to render in the message pump's idle time. Now, that CAN cause issues if the pump gets really busy (like if you are in windowed mode and the user grabs the title bar and moves the window). So you have to be careful about that.

    The thing is, if you are going to do animation, then you can't simply +1 your animation position once per message loop and redraw. Well, you can, but then you will be CPU bound (meaning your app will run at different speeds on different computers based on the CPU speed). Instead, you want to know how much time has elapsed since the last frame draw, and base you change in positions on that time. And for that you need a high precision timer.
    "They laughed when I said I was going to be a comedian ... They're not laughing now." - Bob Monkhouse

  3. #3
    Join Date
    May 2005
    Posts
    390

    Default

    By "message pump" I meant the translatemessage/dispatchmessage loop, not wndproc.

    At any rate, so let's say I wanted, like i said, a simple planet orbiting a sun, and let's say that 60hz updates are good enough: I could set up a 60hz wm_timer, and then update the position of the planet, and then i render frames in, what, wm_enteridle? Or am I barking up the wrong tree here?

    I mentioned wm_timer because I don't know how to use the high precision timers yet.

  4. #4
    Join Date
    Mar 2005
    Location
    California
    Posts
    2,099

    Default

    Quote Originally Posted by Pix View Post
    By "message pump" I meant the translatemessage/dispatchmessage loop, not wndproc.
    So did I.

    This is a fairly standard game loop.

    Code:
    //the message pump
    while (true)
    {
    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
    if (msg.message == WM_QUIT)
    {
    break;
    }
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }
    else
    {
    Sleep(1); //do not max out processor
    Renderer.Render();
    }
    }
    
    Where the render function would handle updating your position and timer, and drawing to the screen. It will throw up a new frame once per loop, presuming there are no pending messages going through (which, 99% of the time, there won't be).

    At any rate, so let's say I wanted, like i said, a simple planet orbiting a sun, and let's say that 60hz updates are good enough: I could set up a 60hz wm_timer, and then update the position of the planet, and then i render frames in, what, wm_enteridle? Or am I barking up the wrong tree here?
    Is there such a message? I hadn't heard of that one, heh. Nah, use the "else" part of the message pump to catch your idle time. Safer that way (once you get into D3D, messages are something you should stay away from for most things. The WndProc part of typical game programs is very short).

    I mentioned wm_timer because I don't know how to use the high precision timers yet.
    Check into the QueryPerformanceCounter and QueryPerformanceFrequency functions. They are not really very difficult to use. They do have their own set of bugs on some CPUs, but you can probably ignore them (most people do).

    You can use another timer (like GetTickCount()) easier, I suppose, but I think you will probably have to check for 0 time passed and make sure you don't get any divide by zero errors from them. I'd imagine (because of their imprecision) you might get a little bit of jerkyness/jitter in your animations from them. But you'd have to try it out to see what happens.
    "They laughed when I said I was going to be a comedian ... They're not laughing now." - Bob Monkhouse

  5. #5
    Join Date
    May 2005
    Posts
    390

    Default

    Quote Originally Posted by RonHiler View Post
    So did I.

    This is a fairly standard game loop.

    Code:
    //the message pump
    while (true)
    {
    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
    if (msg.message == WM_QUIT)
    {
    break;
    }
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }
    else
    {
    Sleep(1); //do not max out processor
    Renderer.Render();
    }
    }
    
    Where the render function would handle updating your position and timer, and drawing to the screen. It will throw up a new frame once per loop, presuming there are no pending messages going through (which, 99% of the time, there won't be).
    That example is exactly what I was talking about, but I couldn't find it in the DX SDK docs when I went looking for it. That's as opposed, as we have discussed, to putting Render() in OnPaint().

    Is there such a message? I hadn't heard of that one, heh. Nah, use the "else" part of the message pump to catch your idle time. Safer that way (once you get into D3D, messages are something you should stay away from for most things. The WndProc part of typical game programs is very short).
    Of course it's a real message! I looked it up in windef.h. More seriously it's documented in MSDN but is probably not useful here: "Sent to the owner window of a modal dialog box or menu that is entering an idle state. A modal dialog box or menu enters an idle state when no messages are waiting in its queue after it has processed one or more previous messages."

    Check into the QueryPerformanceCounter and QueryPerformanceFrequency functions. They are not really very difficult to use. They do have their own set of bugs on some CPUs, but you can probably ignore them (most people do).

    You can use another timer (like GetTickCount()) easier, I suppose, but I think you will probably have to check for 0 time passed and make sure you don't get any divide by zero errors from them. I'd imagine (because of their imprecision) you might get a little bit of jerkyness/jitter in your animations from them. But you'd have to try it out to see what happens.

  6. #6
    Join Date
    Mar 2005
    Location
    California
    Posts
    2,099

    Default

    Quote Originally Posted by Pix View Post
    That example is exactly what I was talking about, but I couldn't find it in the DX SDK docs when I went looking for it. That's as opposed, as we have discussed, to putting Render() in OnPaint().
    Aye. If they are throwing the render in WM_PAINT, then they are either showing a static scene that doesn't need updating, or they are somehow invalidating the window (either through a timer as you mentioned before, or if the animation is hooked into an key input (e.g. press an arrow key to rotate a mesh), through that message, WM_KEYDOWN or the like).

    That's probably fine for quick and dirty example programs, where you just want to throw something up on the screen. For actual applications though, that's going to cause problems (like stuttering when the message pump gets hit with a lot of messages).
    "They laughed when I said I was going to be a comedian ... They're not laughing now." - Bob Monkhouse

  7. #7
    Join Date
    May 2005
    Posts
    390

    Default

    Ok, let me check something: I just looked into QueryPerformance*. It looks like I would want to do something like this, assuming I wanted a nominal 100fps?

    Code:
    LARGE_INTEGER tps, tpf, LastRenderedAt;
    
    void Setup()
    {
        QueryPerformanceFrequeny(&tps);
        tpf = tps / 100;
    }
    
    void Render()
    {
        LARGE_INTEGER CurTick;
        QueryPerformanceCounter(&CurTick);
        if (CurTick > LastRenderedAt + tpf)
        {
            DrawScene();
            LastRenderedAt = CurTick;
        }
    }

  8. #8
    Join Date
    Mar 2005
    Location
    California
    Posts
    2,099

    Default

    Yeah, that'd work. Though you are still CPU bound for anything below 100 FPS, and you are limiting the render rate to 100 FPS. I prefer not to put limits on the frame rate like that, if the loop is ready to render, I like to let it go ahead and render.

    If it were me, I'd do it like this:

    QueryPerformanceCounter(&CurTick);
    DrawScene(CurTick - LastRenderedAt);
    LastRenderedAt = CurTick;
    Sending the elapsed time since the last draw lets up update your animations based on time elapsed (and if you know the rate at which your objects should move, you can do a simple d=rt to update their positions) rather than some constant based on number of frames drawn, (it gets you away from being CPU bound, and you don't have to worry about the Message Pump taking over too many frames and causing weird slowdowns in the animation). Plus, you are no longer limited to 100 FPS, it will fire as fast as possible.

    But that's just me. What you have will work.
    "They laughed when I said I was going to be a comedian ... They're not laughing now." - Bob Monkhouse

  9. #9
    Join Date
    May 2005
    Posts
    390

    Default

    Fair 'nough. I mentioned 100fps because it's a nice round number, not because I didn't want to draw faster than that.

    Altho I will point out that all my current systems have LCDs and are limited to 60hz so anything more than that is actually a waste.

  10. #10
    Join Date
    May 2005
    Posts
    390

    Default

    Ron,

    do you feel up to looking at some short code to try to figure out what I'm doing wrong?

    I swiped Ray Chen's new scratch program for a basic framework, then split it into a few files, and added a small amount of code based on what we've talked about here. The program is just trying to draw an X that moves in a circle centered on the middle of a window, completing one revolution per second. I'm just using plain old GDI for now, to get the looping right. I'm not sure what I'm doing wrong, but it's flickering badly (which I'm not worried about at the moment) and I can't tell if it's actually animating at the right rate or not.

+ Reply to Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts