在DirectX程序中我们可以通过创建交换链将共享的资源绘制在不同的窗口里。

我们先看以下创建交换链的代码:

//********************************************************************
//      Filename: Main.cpp
//        Author: Chinafish
//      Modifier: Chinafish
//       Created: 2010-06-25 14:36
//       Updated: 2010-06-25 14:36
//            QQ: 149200849
//           MSN: china_fish@msn.com
//       Purpose: Render to multiple windows using swap chains.
//====================================================================
//  Copyright(C) 2004-2010 by Chinafish. All Rights Reserved.
//********************************************************************

#define STRICT
#define WIN32_LEAN_AND_MEAN
#include "windows.h"

// Link D3D
#include "d3d9.h"
#pragma comment(lib, "d3d9.lib")

#define Release_Safe(p){if(p!=NULL) p->Release();}

#define WINDOW_TITLE_0    "www.csinx.org - Swap Chains #0 (DX9+VC7)"
#define WINDOW_TITLE_1    "www.csinx.org - Swap Chains #1 (DX9+VC7)"

#define WINDOW_WIDTH	800
#define WINDOW_HEIGHT	600

//-----------------------------------------------------------------------------
// GLOBALS
//-----------------------------------------------------------------------------
HWND					g_hWnd_0			= NULL; // Handle to the first window
HWND					g_hWnd_1			= NULL; // Handle to the second window
LPDIRECT3D9				g_pD3D				= NULL;
LPDIRECT3DDEVICE9		g_pd3dDevice		= NULL;
LPDIRECT3DSWAPCHAIN9	g_swapChain_0		= NULL; // Direct3D device, which supports
LPDIRECT3DSWAPCHAIN9	g_swapChain_1		= NULL; // two swap chains.
D3DPRESENT_PARAMETERS	g_d3dpp;
//-----------------------------------------------------------------------------
// PROTOTYPES
//-----------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void InitD3D();
void ShutDown(void);
void Render_0(void);
void Render_1(void);
//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX winClass;
	MSG        uMsg;
	memset(&uMsg,0,sizeof(uMsg));

	winClass.lpszClassName = "CLASS_SWAPCHAINS";
	winClass.cbSize        = sizeof(WNDCLASSEX);
	winClass.style         = CS_HREDRAW | CS_VREDRAW;
	winClass.lpfnWndProc   = WindowProc;
	winClass.hInstance     = hInstance;
	winClass.hIcon         = LoadIcon(hInstance, (LPCTSTR)NULL);
	winClass.hIconSm       = LoadIcon(hInstance, (LPCTSTR)NULL);
	winClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
	winClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	winClass.lpszMenuName  = NULL;
	winClass.cbClsExtra    = 0;
	winClass.cbWndExtra    = 0;

	if(!RegisterClassEx(&winClass))
		return E_FAIL;

	//
    // Create window #0...
    //

	g_hWnd_0 = CreateWindowEx(NULL, "CLASS_SWAPCHAINS",
		WINDOW_TITLE_0,
		WS_EX_TOPMOST, 0, 0,
		WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL,
		hInstance, NULL);

	if(g_hWnd_0 == NULL)
		return E_FAIL;

	// Adjust window
	RECT rect_0 = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT };
	AdjustWindowRect(&rect_0, GetWindowLong(g_hWnd_0, GWL_STYLE), FALSE);
	SetWindowPos(g_hWnd_0, 0, 0, 0, rect_0.right - rect_0.left, rect_0.bottom - rect_0.top,
		SWP_NOZORDER | SWP_NOMOVE);

	ShowWindow(g_hWnd_0, nCmdShow);
	UpdateWindow(g_hWnd_0);

	//
	// Create window #1...
	//

	g_hWnd_1 = CreateWindowEx(NULL, "CLASS_SWAPCHAINS",
		WINDOW_TITLE_1,
		WS_EX_TOPMOST, 300, 200,
		WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL,
		hInstance, NULL);

	if(g_hWnd_1 == NULL)
		return E_FAIL;

	// Adjust window
	RECT rect_1 = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT };
	AdjustWindowRect(&rect_1, GetWindowLong(g_hWnd_1, GWL_STYLE), FALSE);
	SetWindowPos(g_hWnd_1, 0, 0, 0, rect_1.right - rect_1.left, rect_1.bottom - rect_1.top,
		SWP_NOZORDER | SWP_NOMOVE);

	ShowWindow(g_hWnd_1, nCmdShow);
	UpdateWindow(g_hWnd_1);

	// Init D3D
	InitD3D();

	while(uMsg.message != WM_QUIT)
	{
		if(PeekMessage(&uMsg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&uMsg);
			DispatchMessage(&uMsg);
		}
		else
		{
			// Render a frame
			Render_0();
			Render_1();
		}
	}

	ShutDown();

	UnregisterClass("CLASS_SWAPCHAINS", winClass.hInstance);

	return uMsg.wParam;
}

//-----------------------------------------------------------------------------
// Name: WindowProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
	case WM_KEYDOWN:
		{
			switch(wParam)
			{
			case VK_ESCAPE:
				PostQuitMessage(0);
				break;
			}
		}
		break;
	case WM_CLOSE:
		{
			PostQuitMessage(0);
		}
	case WM_DESTROY:
		{
			PostQuitMessage(0);
		}
		break;
	default:
	{
		return DefWindowProc(hWnd, msg, wParam, lParam);
	}
	break;
	}

	return 0;
}

//-----------------------------------------------------------------------------
// Name: InitD3D()
// Desc: This function will only be called once during the application's
//       initialization phase. Therefore, it can't contain any resources that
//       need to be restored every time the Direct3D device is lost or the
//       window is resized.
//-----------------------------------------------------------------------------
void InitD3D()
{
	// Create D3D
	g_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
	if(!g_pD3D)
	{
		MessageBox(NULL, "Direct3DCreate9() - Failed!", "Error", NULL);
		return;
	}

	D3DDISPLAYMODE d3ddm;
	g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);

	ZeroMemory(&g_d3dpp, sizeof(g_d3dpp));
	g_d3dpp.Windowed               = TRUE;
	g_d3dpp.SwapEffect             = D3DSWAPEFFECT_DISCARD;
	g_d3dpp.BackBufferFormat       = d3ddm.Format;
	g_d3dpp.EnableAutoDepthStencil = TRUE;
	g_d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	g_d3dpp.PresentationInterval   = D3DPRESENT_INTERVAL_IMMEDIATE;

	g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
		g_hWnd_0, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
		&g_d3dpp, &g_pd3dDevice);

	if(!g_pd3dDevice)
	{
		return;
	}

	//
	// Create additional swap chains for use by multiple windows...
	//

	// After CreateDevice, the first swap chain already exists, so just get it...
	g_pd3dDevice->GetSwapChain(0, &g_swapChain_0);

	// But the second one will need to be explicitly created...
	g_pd3dDevice->CreateAdditionalSwapChain(&g_d3dpp, &g_swapChain_1);
}

//-----------------------------------------------------------------------------
// Name: ShutDown()
// Desc: Release all
//-----------------------------------------------------------------------------
void ShutDown(void)
{
	Release_Safe(g_swapChain_0);
	Release_Safe(g_swapChain_1);
	Release_Safe(g_pd3dDevice);
	Release_Safe(g_pD3D);
}

//-----------------------------------------------------------------------------
// Name: Render_0()
// Desc: Render something you want
//-----------------------------------------------------------------------------
void Render_0(void)
{
	// Tell the Direct3D device to render to the first swap chain抯 back buffer
	LPDIRECT3DSURFACE9 pBackBuffer = NULL;
	g_swapChain_0->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
	g_pd3dDevice->SetRenderTarget(0, pBackBuffer);

	// Clear screen with D3DCOLOR_XRGB(255, 0, 255)
	g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255, 0, 255), 1.0f, 0);

	g_pd3dDevice->BeginScene();

	// Render geometry here...

	g_pd3dDevice->EndScene();

	// Present swap chain #0 to window #0
	g_swapChain_0->Present(NULL, NULL, g_hWnd_0, NULL, 0);

	pBackBuffer->Release();
}

//-----------------------------------------------------------------------------
// Name: Render_1()
// Desc: Render something you want
//-----------------------------------------------------------------------------
void Render_1(void)
{
	// Tell the Direct3D device to render to the second swap chain抯 back buffer
	LPDIRECT3DSURFACE9 pBackBuffer = NULL;
	g_swapChain_1->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
	g_pd3dDevice->SetRenderTarget(0, pBackBuffer);

	// Clear screen with D3DCOLOR_XRGB(0, 255, 0)
	g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 255, 0), 1.0f, 0);

	g_pd3dDevice->BeginScene();

	// Render geometry here...

	g_pd3dDevice->EndScene();

	// Present swap chain #1 to window #1
	g_swapChain_1->Present(NULL, NULL, g_hWnd_1, NULL, 0);

	pBackBuffer->Release();
}
			

框架代码我们在前面已经有说明,这里需要说明的是在创建设备后D3D已默认存在一个交换链,可以通过调用 IDirect3DDevice9::GetSwapChain 方法得到第一个交换链。 再通过调用 IDirect3DDevice9::CreateAdditionalSwapChain 方法创建第二个交换链。

在渲染之前通过调用 IDirect3DSwapChain9::GetBackBuffer 方法取得交换链后台缓冲,通过 IDirect3DDevice9::SetRenderTarget 方法设置新的缓冲区,最后通过调用 IDirect3DSwapChain9::Present 方法将交换链内容渲染到指定的窗口中。

下图为本例代码执行效果:

[返回目录][我有话说]