0

I am building a Windows MFC application. During some animations where objects collide at high speeds, my physics engine behaves unpredictably. I believe it has something to do with me dropping frames somehow. I was told that I'm not using double buffering. I thought I was, but I am still fairly new to this. Here is how I draw to the screen in OnPaint:

#include "pch.h"
#include "framework.h"
#include "ChildView.h"
#include "DoubleBufferDC.h"

void CChildView::OnPaint()
{
    CPaintDC paintDC(this);     // device context for painting
    CDoubleBufferDC dc(&paintDC); // device context for painting
    Graphics graphics(dc.m_hDC);    // Create GDI+ graphics context
    mGame.OnDraw(&graphics);

    if (mFirstDraw)
    {
        mFirstDraw = false;
        SetTimer(1, FrameDuration, nullptr);

        /*
         * Initialize the elapsed time system
         */
        LARGE_INTEGER time, freq;
        QueryPerformanceCounter(&time);
        QueryPerformanceFrequency(&freq);

        mLastTime = time.QuadPart;
        mTimeFreq = double(freq.QuadPart);
    }

    /*
     * Compute the elapsed time since the last draw
     */
    LARGE_INTEGER time;
    QueryPerformanceCounter(&time);
    long long diff = time.QuadPart - mLastTime;
    double elapsed = double(diff) / mTimeFreq;
    mLastTime = time.QuadPart;

    mGame.Update(elapsed);
}

void CChildView::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: Add your message handler code here and/or call default
    RedrawWindow(NULL, NULL, RDW_UPDATENOW);
    Invalidate();
    CWnd::OnTimer(nIDEvent);
}

When I create the Graphics object from the CDoubleBufferDC object, is this not creating a back buffer? I then pass this Graphics object to OnDraw where it is drawn on. If it is creating a back buffer, I'm confused about where the front buffer is created and when it is drawn on the screen.

Here are my current thoughts on how I think this works:

  1. The CPaintDC object is the front buffer
  2. The CDoubleBufferDC object is the back buffer
  3. A graphics object is created from the CDoubleBufferDC object which I draw the current state of the game on

If this is the case, when is the front buffer ever replaced with the new buffer created in the back? Can someone help me understand, and use double buffering if I'm not already?

Justin
  • 107
  • 1
  • 10
  • If you're using a naive collision detection method then at high speeds or low frame-rates you'll get all kinds of weirdness. Consider getting a good reference like [Real-Time Collision Detection](https://realtimecollisiondetection.net) to use the correct formulas that use linear interpolation to handle collisions instead of an AABB test. In any case, you've only shown drawing code, not the physics code that has the problem. – tadman May 15 '20 at 23:31
  • I appreciate you pointing me in the right direction. I will surely check it out. However, unless I'm doing the double buffering the correct way I don't think anything will work 100% correctly. I'm still wondering if that part is being done correctly. – Justin May 16 '20 at 00:38
  • Unless your drawing and physics are really tightly coupled, which they shouldn't be, this shouldn't be an issue. I'm also wondering why you're using the really old GDI approach when these days you'd use basic DirectX functions at the very least. – tadman May 16 '20 at 00:50
  • I don't know anything about DirectX functions, however, I'd bet I'm using the really old GDI approach you're referring to. The class I learned to make this kind of application in seemed pretty dated. I am using Gdiplus for drawing. If you could steer me into a better direction I'd greatly appreciate it, although this project has come a pretty long way and I have sunk tons of hours into it. – Justin May 16 '20 at 01:01
  • There's not nearly enough information to answer the question you asked. `CDoubleBufferDC` is not a standard MFC class and it's not clear, how, when, or even if the rendered image gets transferred to the `paintDC`. But, as noted above, an answer to the question asked isn't going to help you make forward progress. Regardless, if you want to switch to rendering system with potentially better performance characteristics, have a look at [Direct2D](https://learn.microsoft.com/en-us/windows/win32/direct2d/direct2d-quickstart). – IInspectable May 16 '20 at 07:57
  • "Really dated" in this case is a huge understatement considering DirectX has been around *since 1995*, and [WinG](https://en.wikipedia.org/wiki/WinG), the predecessor, has been around even longer. – tadman May 16 '20 at 16:54
  • The class didn't revolve around making MFC applications, it was just a software design class. To be honest, though, I did pride myself on making this physics engine from scratch and it works well at low speeds. I feel like that aspect of this project is really the only part that boosts my resume at all which I need as a new graduate. I will definitely look into DirectX though. Do they provide functions for collisions or something that makes this easier? Also, would it be worth it for me to rewrite this project using DirectX, or will it not make much of a difference? – Justin May 16 '20 at 19:13

1 Answers1

0

To answer your actual question, what is probably happening is that your CDoubleBufferDC() class has a destructor that swaps out the DC's - this is a common idiom (IIRC in 'modern' MFC versions CMemDC does that too). So yes I think you are using double buffering here, even if accidentally. You can do the drawing in GDI(+) for simple games, if this whole thing is a learning exercise, this way is much easier to understand than using DirectX.

However, you do need to decouple your collision detection from your drawing routines, so that any dropped frames don't mess up your timing (that is, if you're things so complex that they take that much time - in which case, you probably shouldn't be using GDI...). In other words, if your collision detection assumes that each OnDraw() call completes in less than 1/framerate seconds but that doesn't always happen, you'll run into problems at some point. There are many articles online about how to structure your game loop; I don't have enough information to link you to a specific one. Also it won't be easy to find one using the technology you're using, in 2020... But I do think that using a simple OnPaint()/GDI one is great for learning, as it will hide much of the complexity you need for more modern approaches.

Roel
  • 19,338
  • 6
  • 61
  • 90
  • Thanks for the response. I am starting to wonder now if I should drop the GDI approach. However, I'm not sure my collision detection system is too expensive - it's a billiards game, so there are only 16 objects that I need to check for in every frame. Do you think that's too many? – Justin May 25 '20 at 19:21
  • No that should be OK - even thousands of (simple) objects should be OK on modern CPU's. I don't know what's going wrong with your code, or why you think dropping frames is the source of your problem, but it's certainly possible to make your project (in asfar as you've described it here) work with the technology we're talking about and the approach you've shown in your original question. I think the first thing you should do is describe for yourself what exactly is going wrong; 'behaves unpredictably' is too vague to be actionable. – Roel May 26 '20 at 02:41
  • I actually made another post earlier today. I realized a big problem - The time between each draw is far too long (70 - 80ms). If I want 60fps, I need it to be drawn about 5 times as fast. As far as I can tell, my code is pretty optimized. If you're interested, you can check out the question I posted "Make Windows Game Loop Faster". I'm not sure why my draws are taking so long. – Justin May 26 '20 at 02:46