cwinapibufferdirectdrawwindowed

Creating a simple, single buffer application (IDirectDraw)


I am attempting to author a single buffered, windowed application using the original DirectDraw interface.

This is purely for educational purposes and the just because attitude. I'm playing around with the IDirectDraw interface (I stress this is the original version, as in DirectX 1.0).

Now, the documentation contains group of tutorials on creating a back-buffer and flipping between the primary surface and the back-buffer surface.

However, it gives no description about writing a windowed, single buffer application. In fact I can find little reference to the idea at all. There's nothing that indicates its not possible.

By setting the cooperation level with to DDSCL_NORMAL the application behaves within a window.

hr = IDirectDraw_SetCooperativeLevel(lpDirectDraw, hWnd_, DDSCL_NORMAL);
if (hr != DD_OK)
    return -1;

Followed by creating the primary surface after success.

ZeroMemory(&ddSurfaceDesc, sizeof ddSurfaceDesc);
ddSurfaceDesc.dwSize            = sizeof ddSurfaceDesc;
ddSurfaceDesc.dwFlags           = DDSD_CAPS;
ddSurfaceDesc.ddsCaps.dwCaps    = DDSCAPS_PRIMARYSURFACE;

hr = IDirectDraw_CreateSurface(lpDirectDraw, &ddSurfaceDesc, &lpDirectDrawPrimarySurface, (IUnknown *) NULL);
if (hr != DD_OK)
    return -1;

This function also succeeds. Note that I do not create a back-buffer.

However, in my main loop:

while (fIterateLoop)
{
    for (ZeroMemory(&msg, sizeof msg); PeekMessage(&msg, (HWND) NULL, 0, 0, PM_REMOVE); DispatchMessage(&msg))
        fIterateLoop = msg.message == WM_QUIT ? FALSE : TRUE;

    if (fDraw && lpDirectDrawPrimarySurface != (LPDIRECTDRAWSURFACE) NULL)
    {
        HDC hdc;

        hr = IDirectDrawSurface_GetDC(lpDirectDrawPrimarySurface, &hdc);
        if (hr == DD_OK)
        {
            SetBkColor(hdc, RGB(0, 0, 0));
            SetTextColor(hdc, RGB(255, 255, 255));
            TextOut(hdc, 15, 15, "hello, world!", sizeof "hello, world!" - 1);

            hr = IDirectDrawSurface_ReleaseDC(lpDirectDrawPrimarySurface, hdc);
        }
    }
}

Nothing is drawn to the window at all. My original code (a fullscreen buffered application) correctly shows "hello, world!" in the top left-hand side of the screen and the only modifications are the tiny edits to stop the back-buffer creation, and to write directly to the primary surface.

If I call CreateSurface with the options for a back-buffer after calling SetCooperativeLevel with DDSCL_NORMAL then it returns and error. This I understand because the documentation states:

If you were using IDirectDraw::SetCooperativeLevel to set the mode to DDSCL_NORMAL, you could create only surfaces that blit between the surfaces.

Have I missed something? Have I fundamentally misunderstood DirectDraw? I was under the impression that using buffers was for:

I care about neither of these right not. What can I do?


Solution

  • This is perfectly achievable. What is required is a primary surface to write to and a clipper to trim the surface to the window.

    Create a clipper of type IDirectDrawClipper and use IDirectDrawClipper_SetHWnd to match the clipper to the window. Next and then use IDirectDrawSurface_SetClipper to attach the clipper to the memory controlled by the primary surface.

    Make sure you re-call IDirectDraw_SetDisplayMode and then restore the surface each time the window changes size if you aren't locking the window size.

    Also remember that you are required to blit to the surface and not flip as if you had a backbuffer.

    Several good points are made in this blog post.