A surface controlled by the IDirectDrawSurface7 interface represents a rectangular area of display memory. You use this surface for items such as the primary display surface presented to the user on the screen or the textures applied to 3D surfaces. You can use either video memory or system memory for your surfaces. If you don't specify a particular memory type, DirectDraw places the DirectDrawSurface7 object into whatever memory allows for the best performance.
To create a surface, just call the IDirectDraw7::CreateSurface method, which is declared as shown here:
HRESULT CreateSurface( LPDDSURFACEDESC2 lpDDSurfaceDesc2, LPDIRECTDRAWSURFACE7 FAR *lplpDDSurface, IUnknown FAR *pUnkOuter ); |
Parameter | Description |
---|---|
lpDDSurfaceDesc2 | The address of a DDSURFACEDESC2 structure that describes the requested surface. Before calling this method, set any unused members of the DDSURFACEDESC2 structure to 0. A DDSCAPS2 structure is a member of DDSURFACEDESC2. |
lplpDDSurface | The address of a variable that will be set to a valid IDirectDrawSurface7 interface pointer if the call succeeds. |
pUnkOuter | Allows for compatibility with future COM aggregation features. The IDirectDraw7::CreateSurface currently returns an error if this parameter is set to anything other than NULL. |
You can use the IDirectDraw7::CreateSurface method to create a single surface object, a complex surface-flipping chain, or a three-dimensional surface. The IDirectDrawSurface7::Lock method lets you directly access display memory by locking the display memory and retrieving the address for that surface. This address can point to visible frame buffer memory, such as the primary surface, or to nonvisible buffers, such as overlay or off-screen surfaces.
The primary surface, also known as the front buffer, is the surface that is visible on the monitor. Each DirectDraw object can have only one primary surface, which is identified by the DDSCAPS_PRIMARYSURFACE flag.
When the primary surface is created, its dimensions and pixel format match the current display mode, so you don't need to declare them. Accidentally specifying them will cause the call to fail.
The following code shows the entire CreateFullScreenBuffers routine. This routine is used to create the front and back buffers. (You don't need a clipper object because you're not in windowed mode.) In the paragraphs following the code, each segment is described in detail, in the order in which it should be performed.
//------------------------------------------------------------------- // Name: CreateFullscreenBuffers // Desc: Creates the primary surface and (optional) back buffer for // rendering. Windowed mode and full-screen mode are handled // differently. //------------------------------------------------------------------- HRESULT CD3DFramework7::CreateFullscreenBuffers( DDSURFACEDESC2* pddsd ) { HRESULT hr; // STEP 1 - Full-Screen Mode // Get the dimensions of the screen bounds. // Store the rectangle that contains the renderer. SetRect( &m_rcScreenRect, 0, 0, pddsd->dwWidth, pddsd->dwHeight ); m_dwRenderWidth = m_rcScreenRect.right - m_rcScreenRect.left; m_dwRenderHeight = m_rcScreenRect.bottom - m_rcScreenRect.top; // STEP 2 - Full-Screen Mode // Set the display mode to the requested dimensions. Check for // 320 x 200 x 8 modes, and set the flag to avoid using Mode X. DWORD dwModeFlags = 0; if( (320==m_dwRenderWidth) && (200==m_dwRenderHeight) && (8==pddsd->ddpfPixelFormat.dwRGBBitCount) ) dwModeFlags |= DDSDM_STANDARDVGAMODE; if( FAILED( m_pDD->SetDisplayMode( m_dwRenderWidth, m_dwRenderHeight, pddsd->ddpfPixelFormat.dwRGBBitCount, pddsd->dwRefreshRate, dwModeFlags ) ) ) { DEBUG_MSG( _T("Can't set display mode") ); return D3DFWERR_BADDISPLAYMODE; } // STEP 3 - Full-Screen Mode // Set up to create the primary surface with a back buffer. DDSURFACEDESC2 ddsd; ZeroMemory( &ddsd, sizeof(ddsd) ); ddsd.dwSize = sizeof(ddsd); ddsd.dwFlags = DDSD_CAPS|DDSD_BACKBUFFERCOUNT; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE | DDSCAPS_FLIP | DDSCAPS_COMPLEX; ddsd.dwBackBufferCount = 1; // Support for stereoscopic viewing if( m_bIsStereo ) { ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; ddsd.ddsCaps.dwCaps2 |= DDSCAPS2_STEREOSURFACELEFT; } // STEP 4 - Full-Screen Mode // Create the primary surface. if( FAILED( hr = m_pDD->CreateSurface( &ddsd, &m_pddsFrontBuffer, NULL ) ) ) { DEBUG_MSG( _T("Error: Can't create primary surface") ); if( hr != DDERR_OUTOFVIDEOMEMORY ) return D3DFWERR_NOPRIMARY; DEBUG_MSG( _T("Error: Out of video memory") ); return DDERR_OUTOFVIDEOMEMORY; } // STEP 5 - Full-Screen Mode // Get the back buffer, which was created along with the primary // surface. DDSCAPS2 ddscaps = { DDSCAPS_BACKBUFFER, 0, 0, 0 }; if( FAILED( hr = m_pddsFrontBuffer->GetAttachedSurface( &ddscaps, &m_pddsBackBuffer ) ) ) { DEBUG_ERR( hr, _T("Error: Can't get the back buffer") ); return D3DFWERR_NOBACKBUFFER; } // Increment the back-buffer count (for consistency with // windowed mode). m_pddsBackBuffer->AddRef(); // STEP 6 - Full-Screen Mode // Support for stereoscopic viewing if( m_bIsStereo ) { // Get the left back buffer, which was created along with the // primary surface. DDSCAPS2 ddscaps = { 0, DDSCAPS2_STEREOSURFACELEFT, 0, 0 }; if( FAILED( hr = m_pddsBackBuffer->GetAttachedSurface( &ddscaps, &m_pddsBackBufferLeft ) ) ) { DEBUG_ERR( hr, _T("Error: Can't get the left back buffer") ); return D3DFWERR_NOBACKBUFFER; } m_pddsBackBufferLeft->AddRef(); } FILE *fplog = fopen("rrlogfile.txt","a"); ZeroMemory(&m_ddpfBackBufferPixelFormat, sizeof(DDPIXELFORMAT)); m_ddpfBackBufferPixelFormat.dwSize = sizeof(DDPIXELFORMAT); hr = m_pddsBackBuffer-> GetPixelFormat(&m_ddpfBackBufferPixelFormat); if(hr == DD_OK) { fprintf( fplog, "Backbuffer Pixelformat RGB bits = : "); fprintf( fplog, "%d\n", m_ddpfBackBufferPixelFormat.dwRGBBitCount); fprintf( fplog, "Backbuffer Pixelformat RGB masks = : "); fprintf( fplog, "%d %d %d\n\n", m_ddpfBackBufferPixelFormat.dwRBitMask, m_ddpfBackBufferPixelFormat.dwGBitMask, m_ddpfBackBufferPixelFormat.dwBBitMask); } else fprintf( fplog, "Check on back-buffer pixel format FAILED "); hr = m_pddsBackBuffer->GetSurfaceDesc( &ddsd ); fclose(fplog); return S_OK; } |
In STEP 1 of the CD3DFramework7::CreateFullscreenBuffers routine, m_rcScreenRect is filled with the rectangle defining the screen resolution information (for example, 640 × 480, 800 × 600, and so on). The m_dwRenderWidth and m_dwRenderHeight member variables are also set to the computed width and height of the screen.
In STEP 2, the code calls IDirectDraw7::SetDisplayMode, making sure to skip using the Mode X mode. The IDirectDraw7::SetDisplayMode method sets the mode of the display-device hardware. This method is defined as follows:
HRESULT SetDisplayMode ( DWORD dwWidth, DWORD dwHeight, DWORD dwBPP, DWORD dwRefreshRate, DWORD dwFlags ); |
Parameter | Description |
---|---|
dwWidth and dwHeight | Width and height of the new mode. |
dwBPP | Bits per pixel (bpp) of the new mode. |
dwRefreshRate | Refresh rate of the new mode. Set this value to 0 to request the default refresh rate for the driver. |
dwFlags | Flags describing additional options. Currently, the only valid flag is DDSDM_STANDARDVGAMODE, which causes the method to set Mode 13 instead of Mode X 320 × 200 × 8 mode. If you're setting another resolution, bit depth, or a Mode X mode, don't use this flag and set the parameter to 0. |
If the display mode is successfully set, you next create the primary surface and back buffer.
STEP 3 sets up a DDSURFACEDESC2 structure specifying the following flags:
If you're running in stereo video mode, you also need to specify these two flags:
Because we've specified that we want a complex flipping surface, we need to specify how many back buffers we want. To do this, we set the dwBackBufferCount member to 1 to indicate that we want just one back buffer.
In STEP 4, we use the IDirectDraw7::CreateSurface method to create the front buffer, using the flags set in the ddsd structure, and store the new surface in the m_pddsFrontBuffer variable.
In STEP 5, we call the IDirectDraw7::GetAttachedSurface method to get the back buffer. This method acquires the attached surface that has the requested capabilities (in this case, the back buffer). This method is defined as follows:
HRESULT GetAttachedSurface ( LPDDSCAPS2 lpDDSCaps, LPDIRECTDRAWSURFACE7 FAR *lplpDDAttachedSurface ); |
Parameter | Description |
---|---|
lpDDSCaps | Address of a DDSCAPS2 structure that contains the hardware capabilities of the surface. |
lplpDDAttachedSurface | Address of a variable that will contain a pointer to the retrieved surface's IDirectDrawSurface7 interface. The retrieved surface is the one that matches the description according to the lpDDSCaps parameter. |
Finally, in STEP 6, you get a pointer to the second back buffer if you're running in a stereo video mode. If you want this mode, call the IDirectDraw7::GetAttachedSurface method with the second back buffer (for the left image, since we'll use the first back buffer for the right image).
At this point, we're ready to render to these surfaces in full-screen mode.
If you want the program to run in windowed mode rather than full-screen mode, you need to modify some of the settings. Here's the code to handle running in windowed mode:
//------------------------------------------------------------------- // Name: CreateWindowedBuffers // Desc: Creates the primary surface and (optional) back buffer for // rendering. Windowed mode and full-screen mode are handled // differently. //------------------------------------------------------------------- HRESULT CD3DFramework7::CreateWindowedBuffers() { HRESULT hr; // STEP 1 - Windowed Mode // Get the dimensions of the viewport and screen bounds. GetClientRect( m_hWnd, &m_rcScreenRect ); ClientToScreen( m_hWnd, (POINT*)&m_rcScreenRect.left ); ClientToScreen( m_hWnd, (POINT*)&m_rcScreenRect.right ); m_dwRenderWidth = m_rcScreenRect.right - m_rcScreenRect.left; m_dwRenderHeight = m_rcScreenRect.bottom - m_rcScreenRect.top; // STEP 2 - Windowed Mode // Create the primary surface. DDSURFACEDESC2 ddsd; ZeroMemory( &ddsd, sizeof(ddsd) ); ddsd.dwSize = sizeof(ddsd); ddsd.dwFlags = DDSD_CAPS; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; // STEP 3 - Windowed Mode if( FAILED( hr = m_pDD->CreateSurface( &ddsd, &m_pddsFrontBuffer, NULL ) ) ) { DEBUG_MSG( _T("Error: Can't create primary surface") ); if( hr != DDERR_OUTOFVIDEOMEMORY ) return D3DFWERR_NOPRIMARY; DEBUG_MSG( _T("Error: Out of video memory") ); return DDERR_OUTOFVIDEOMEMORY; } // STEP 4 - Windowed Mode // If in windowed mode, create a clipper object. LPDIRECTDRAWCLIPPER pcClipper; if( FAILED( hr = m_pDD->CreateClipper( 0, &pcClipper, NULL ) ) ) { DEBUG_MSG( _T("Error: Couldn't create clipper") ); return D3DFWERR_NOCLIPPER; } // STEP 5 - Windowed Mode // Associate the clipper with the window. pcClipper->SetHWnd( 0, m_hWnd ); m_pddsFrontBuffer->SetClipper( pcClipper ); SAFE_RELEASE( pcClipper ); // STEP 6 - Windowed Mode // Create a back buffer. ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; ddsd.dwWidth = m_dwRenderWidth; ddsd.dwHeight = m_dwRenderHeight; ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE; if( FAILED( hr = m_pDD->CreateSurface( &ddsd, &m_pddsBackBuffer, NULL ) ) ) { DEBUG_ERR( hr, _T("Error: Couldn't create the backbuffer") ); if( hr != DDERR_OUTOFVIDEOMEMORY ) return D3DFWERR_NOBACKBUFFER; DEBUG_MSG( _T("Error: Out of video memory") ); return DDERR_OUTOFVIDEOMEMORY; } ZeroMemory(&m_ddpfBackBufferPixelFormat, sizeof(DDPIXELFORMAT)); m_ddpfBackBufferPixelFormat.dwSize = sizeof(DDPIXELFORMAT); hr = m_pddsBackBuffer-> GetPixelFormat(&m_ddpfBackBufferPixelFormat); hr = m_pddsBackBuffer->GetSurfaceDesc( &ddsd ); return S_OK; } |
In STEP 1, we acquire the bounding rectangle of the window using the window's GetClientRect routine. This routine returns the window's bounding rectangle in the m_rcScreenRect parameter. We then use the ClientToScreen routine to convert the display coordinates of the left and right back buffers to screen coordinates. This routine replaces the client coordinates in the m_rcScreenRect structure with the equivalent screen coordinates. These newly computed screen coordinates are relative to the upper left corner of the system display. In the last two lines, the screen width and height are computed.
In STEP 2, a DDSURFACEDESC2 structure is created and the DDSCAPS_PRIMARYSURFACE flag is specified to indicate that we want to create the primary surface.
In STEP 3, we call the IDirectDraw7::CreateSurface method to create the front buffer using video memory and place it in the m_pddsFrontBuffer variable.
In STEP 4, we create a clipper object. Although a clipper isn't needed when a program is running in full-screen mode, one is required in windowed mode to handle cases in which the window is partially obscured by other windows or partially outside the display area. This clipper prevents the accidental rendering outside a window and onto other windows or the desktop.
In STEP 5, we call IDirectDrawClipper::SetHWnd to set the window handle that the clipper object will use to obtain clipping information. This method is defined as follows:
HRESULT SetHWnd( DWORD dwFlags, HWND hWnd ); |
Parameter | Description |
---|---|
dwFlags | This parameter is currently not used and must be set to 0. |
hWnd | The window handle that obtains the clipping information. |
We then call IDirectDrawSurface7::SetClipper to attach the clipper object to the surface. This method is defined as follows:
HRESULT SetClipper ( LPDIRECTDRAWCLIPPER lpDDClipper ); |
The IDirectDrawSurface7::SetClipper has one parameter, lpDDClipper, which is the address of the IDirectDrawClipper interface for the DirectDrawClipper object that will be attached to the DirectDrawSurface object. If this parameter is NULL, the current DirectDrawClipper object will be detached.
Finally, in STEP 6, we use IDirectDraw7::CreateSurface to create the actual back buffer. At this point, the application is ready to run in either full-screen or windowed mode.