After you create a DirectDraw object and set the cooperative level, you can create the surfaces that your application will use to render and display a scene. Exactly how you create your surfaces depends largely on whether or not your application will run in a window or in full-screen mode.
Full-screen Application Note Applications that will run in full-screen mode can create surfaces as shown in the preceding code examples. More often, these applications should take advantage of page-flipping, a feature only available in full-screen, exclusive mode. In this case, instead of explicitly creating two surfaces, you can create a flipping chain of surfaces with a single call. For more information, see Creating Complex Surfaces and Flipping Chains.
The Triangle sample, designed to run in a window, starts by creating a primary surface, which represents the display:
// Prepare a surface description for the primary surface.
DDSURFACEDESC2 ddsd;
ZeroMemory( &ddsd, sizeof(DDSURFACEDESC2) );
ddsd.dwSize = sizeof(DDSURFACEDESC2);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
// Create the primary surface.
hr = g_pDD4->CreateSurface( &ddsd, &g_pddsPrimary, NULL );
if( FAILED( hr ) )
return hr;
The description for the primary surface doesn't contain information about dimensions or pixel format, as these traits are assumed to be the same as the display mode. If the current display mode is 800x600, 16-bit color, DirectDraw ensures that the primary surface matches. After creating the primary surface, you can create the render target surface. In the case of Triangle, this is a separate off-screen surface created as follows:
ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE;
// Set the dimensions of the back buffer. Note that if our window changes
// size, we need to destroy this surface and create a new one.
GetClientRect( hWnd, &g_rcScreenRect );
GetClientRect( hWnd, &g_rcViewportRect );
ClientToScreen( hWnd, (POINT*)&g_rcScreenRect.left );
ClientToScreen( hWnd, (POINT*)&g_rcScreenRect.right );
ddsd.dwWidth = g_rcScreenRect.right - g_rcScreenRect.left;
ddsd.dwHeight = g_rcScreenRect.bottom - g_rcScreenRect.top;
// Create the back buffer. The most likely reason for failure is running
// out of video memory. (A more sophisticated app should handle this.)
hr = g_pDD4->CreateSurface( &ddsd, &g_pddsBackBuffer, NULL );
if( FAILED( hr ) )
return hr;
The preceding code creates an off-screen surface that is equal to the dimensions of the program window. There is no need to create a larger surface, because the dimensions of the window dictate what is visible to the user. (This code also initializes two global variables that are later used to set up the viewport and track the application window size and position.) As the preceding excerpt shows, you must include the DDSCAPS_3DDEVICE capability for any surface that will be used as a render target. This capability causes the system to allocate additional internal data structures that are used only for 3-D rendering. As when creating the primary surface, the pixel format for the off-screen surface is assumed to be the same as the display mode when it isn't provided in the surface description.
Note Applications that will use a depth buffer should create one and attach it to the render target surface at this point. For simplicity, this tutorial doesn't employ a depth buffer, but they are covered in Tutorial 2: Adding a Depth Buffer and in Depth Buffers.
After creating the primary and render target surface, you can create and attach a DirectDrawClipper object to the display surface. Using a clipper frees you from attempting to handle cases when the window is partially obscured by other windows, or when the window is partially outside the display area. Clippers aren't needed for applications that run in full-screen mode. The Triangle sample uses the following code to create a clipper and associate it with the display window:
LPDIRECTDRAWCLIPPER pcClipper;
// Create the clipper.
hr = g_pDD4->CreateClipper( 0, &pcClipper, NULL );
if( FAILED( hr ) )
return hr;
// Assign it to the window handle, then set
// the clipper to the desired surface.
pcClipper->SetHWnd( 0, hWnd );
g_pddsPrimary->SetClipper( pcClipper );
pcClipper->Release();
Having created the basic DirectDraw objects, you can move on to setting up the essential Direct3D objects that will render the scene. The Triangle sample performs this task in Step 2.3: Initialize Direct3D.