在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 方法将交换链内容渲染到指定的窗口中。
下图为本例代码执行效果: