在先前的解決方案中新建一個新的Win32項目FirstD3D11Demo。在寫代碼之前,我們必須先添加dx11所需要的庫。為了鏈接dx庫,右鍵項目選擇屬性->vc++目錄,在包含目錄中添加你所安裝的SDK根目錄\Include,在庫目錄中添加 根目錄\lib\x86(或x64),在鏈接器->輸入的附加依賴項中添加d3d11.lib、d3dx11.lib、dxerr.lib。
第一次使用d3d,首先應該從初始化開始。
初始化d3d11的步驟主要有以下幾個:
1、定義我們要檢查的設備類型和特征級別
2、創建d3d設備,渲染環境和交換鏈
3、創建渲染對象
4、設置視口觀察區(ViewPort)
下面將對一些概念和用到的d3d對象和函數作具體說明。
數據格式
D3D應用程序中,無論是紋理圖片,還是創建的緩沖區,都有着特定的數據格式。D3D11支持有限的數據格式,以枚舉變量形式存在,如下幾種:
DXGI_FORMAT_R32G32B32_FLOAT: 3個32位單精度符點數組成,比如用於代表三維空間坐標,以及24位顏色;
DXGI_FORMAT_R16G16B16A16_UNORM: 4個16位數組成,每個成員位於[0,1.0f]之間,UNORM意指:unsigned normalized,即無符號,且歸一化的;
DXGI_FORMAT_R32G32_UINT:2個32位數組成,每個成員為無符號整型(unsigned int);
DXGI_FORMAT_R8G8B8A8_UNORM:4個8位數組成,每個成員為[0,1.f]之間;
DXGI_FORMAT_R8G8B8A8_SNORM:4個8位數組成,每個成員為[-1.0f, 1.0f]之間,SNORM意指:signed normalized;
DXGI_FORMAT_R8G8B8A8_SINT:4個8位數組成,每個成員為有符號整型;
特征級別(Feature Level)
特征級別定義了一系列支持不同d3d功能的相應等級,如果一個用戶的硬件不支持某一特征等級,程序可以選擇較低的等級來運行。
下面是d3d定義的幾個不同級別代表不同的d3d版本
typedef enum D3D_FEATURE_LEVEL { D3D_FEATURE_LEVEL_9_1 = 0x9100, D3D_FEATURE_LEVEL_9_2 = 0x9200, D3D_FEATURE_LEVEL_9_3 = 0x9300, D3D_FEATURE_LEVEL_10_0 = 0xa000, D3D_FEATURE_LEVEL_10_1 = 0xa100, D3D_FEATURE_LEVEL_11_0 = 0xb000, D3D_FEATURE_LEVEL_11_1 = 0xb100, D3D_FEATURE_LEVEL_12_0 = 0xc000, D3D_FEATURE_LEVEL_12_1 = 0xc100 } D3D_FEATURE_LEVEL;
在初始化過程中,我們可以提供一組不同的特征等級,程序會從第一個開始逐個檢測,碰到第一個合適的來創建設備。因此我們在數組中從高到低放置特征等級提供給初始化程序。
交換鏈(SwapChain)
為了實現平滑的動畫,至少需要兩個緩沖區,一個前緩沖區用於顯示,一個后緩沖區用於下一幀的繪制,每次繪制完一幀后通過交換前、后緩沖區對應的指針來顯示新一幀,並在之前的前緩沖區(當前的后緩沖區)上開始繼續繪制下一幀。交換鏈可以有3個或者更多緩沖區,但一般情況下兩個夠用了。通常在游戲中,我們有兩種顏色緩存,一個主緩存,一個輔助緩存,這就是所謂的前向和后向緩存。主緩存是顯示在屏幕上的,輔助緩存則是用於下一幀的繪制。在d3d11中交換鏈對應的接口為IDXGISwapChain。
深度/模板緩沖區:Depth/Stencil Buffer
深度緩沖區是與交換鏈緩沖區大小完全一樣的一塊顯存區域,即每個像素在深度緩沖區中對應相應的位置。在渲染管線的最終的混合階段(Output Merger Stage),每個片(Fragment)都有一個深度值z,與深度緩沖區對應位置上的深度相比較,如果該片段z更小,則繪制該片段,並覆蓋當前的嘗試值,否則拋棄該片段。該緩沖區主要用於實現投影在屏幕上同一位置、遠近不同的物體之間相同的遮擋效果。此外,靈活配置嘗試緩沖區,可以實現很多種高級特效。
多重采樣抗鋸齒:Multisampling Atialiasing
針對光柵化顯示器抗鋸齒的方法有多種,在d3d中采用的多重采樣方法。即在每個像素點內部,設置多個采樣點,繪制多邊形邊緣時,針對每個采樣點判斷是否被多邊形覆蓋,最終的顏色值從采樣點中取均值,以對多邊形的邊緣進行“模糊化",從而減輕鋸齒效果。如下圖所示,這是一個4重采樣的例子,該像素最終的顏色值是多邊形本身顏色值的3/4:
支持d3d11的硬件全部支持4重采樣,因此我們在后面的程序中將普遍使用4個采樣點。在d3d11中通過結構DXGI_SAMPLE_DESC來設置多重采樣,其定義如下:
typedef struct DXGI_SAMPLE_DESC { UINT Count; UINT Quality; } DXGI_SAMPLE_DESC;
D3D11_CREATE_DEVICE_FLAG 枚舉類型
typedef enum D3D11_CREATE_DEVICE_FLAG { D3D11_CREATE_DEVICE_SINGLETHREADED = 0x1, D3D11_CREATE_DEVICE_DEBUG = 0x2, D3D11_CREATE_DEVICE_SWITCH_TO_REF = 0x4, D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS = 0x8, D3D11_CREATE_DEVICE_BGRA_SUPPORT = 0x20, D3D11_CREATE_DEVICE_DEBUGGABLE = 0x40, D3D11_CREATE_DEVICE_PREVENT_ALTERING_LAYER_SETTINGS_FROM_REGISTRY = 0x80, D3D11_CREATE_DEVICE_DISABLE_GPU_TIMEOUT = 0x100, D3D11_CREATE_DEVICE_VIDEO_SUPPORT = 0x800 } D3D11_CREATE_DEVICE_FLAG;
D3D11_CREATE_DEVICE_SINGLETHREADED
如果使用該常量,你的應用程序將只可以在一個線程中的調用 Dierct3D 11接口。在默認情況下ID3D11Device
對象是一個安全線程。使用這個標志,你可以增強性能。然而,如果你使用這個標志並且你的應用程序使用
多線程調用Dierct3D 11接口,可能導致不可預期的結果。
D3D11_CREATE_DEVICE_DEBUG
創建一個設備支持調用層。
D3D11_CREATE_DEVICE_SWITCH_TO_REF
注意 這個標志不支持Direct3D 11.
D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
阻止被多線程創建。當使用WARP標志時,WARP和所有光柵不能夠被線程調用。這個標志不建議使用。
D3D11_CREATE_DEVICE_BGRA_SUPPORT
Dierct2D需要和Direct3D資源交互。
下面是每個步驟對應的代碼:
一、指明驅動設備等級和特征等級
1 HRESULT hResult = S_OK;//返回結果 2 3 RECT rc; 4 GetClientRect(g_hWnd, &rc);//獲取窗口客戶區大小 5 UINT width = rc.right - rc.left; 6 UINT height = rc.bottom - rc.top; 7 8 UINT createDeviceFlags = 0; 9 #ifdef _DEBUG 10 createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; 11 #endif 12 13 //驅動類型數組 14 D3D_DRIVER_TYPE driverTypes[] = 15 { 16 D3D_DRIVER_TYPE_HARDWARE, 17 D3D_DRIVER_TYPE_WARP, 18 D3D_DRIVER_TYPE_REFERENCE 19 }; 20 UINT numDriverTypes = ARRAYSIZE(driverTypes); 21 22 //特征級別數組 23 D3D_FEATURE_LEVEL featureLevels[] = 24 { 25 D3D_FEATURE_LEVEL_11_0, 26 D3D_FEATURE_LEVEL_10_1, 27 D3D_FEATURE_LEVEL_10_0 28 }; 29 UINT numFeatureLevels = ARRAYSIZE(featureLevels);
二、創建設備和交換鏈
交換鏈具體定義
typedef struct DXGI_SWAP_CHAIN_DESC {
DXGI_MODE_DESC BufferDesc;
DXGI_SAMPLE_DESC SampleDesc;
DXGI_USAGE BufferUsage;
UINT BufferCount;
HWND OutputWindow;
BOOL Windowed;
DXGI_SWAP_EFFECT SwapEffect;
UINT Flags;
} DXGI_SWAP_CHAIN_DESC;
BufferDesc指定后緩沖區有關特性;
SampleDesc指定多重采樣,前面說過;
BufferUsage,對於交換鏈,為DXGI_USAGE_RENDER_TARGET_OUTPUT;
BufferCount:我們只創建一個后緩沖區(雙緩沖),因此為1;
OutputWindow:指定窗口句柄,Win32程序初始化完創建的主窗口;
Windowed:是否全屏;
DXGI_SWAP_EFFECT:通常為DXGI_SWAP_EFFECT_DISCARD;
Flags:可選
其中DXGI_MODE_DESC定義如下
typedef struct DXGI_MODE_DESC {
UINT Width;
UINT Height;
DXGI_RATIONAL RefreshRate;
DXGI_FORMAT Format;
DXGI_MODE_SCANLINE_ORDER ScanlineOrdering;
DXGI_MODE_SCALING Scaling;
} DXGI_MODE_DESC, *LPDXGI_MODE_DESC;
Width、Height為緩沖區大小,一般設為主窗口大小;
Format為緩沖區類型,一般作為渲染對象緩沖區類型為DXGI_FORMAT_R8G8B8A8_UNORM;
其他參數一般為固定的。
交換鏈的設置
1 //交換鏈 2 DXGI_SWAP_CHAIN_DESC sd; 3 ZeroMemory(&sd, sizeof(DXGI_SWAP_CHAIN_DESC));//填充 4 sd.BufferCount = 1; //我們只創建一個后緩沖(雙緩沖)因此為1 5 sd.BufferDesc.Width = width; 6 sd.BufferDesc.Height = height; 7 sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 8 sd.BufferDesc.RefreshRate.Numerator = 60; 9 sd.BufferDesc.RefreshRate.Denominator = 1; 10 sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 11 sd.OutputWindow = g_hWnd; 12 sd.SampleDesc.Count = 1; //1重采樣 13 sd.SampleDesc.Quality = 0; //采樣等級 14 sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; //常用參數 15 sd.Windowed = TRUE; //是否全屏
創建設備及交換鏈,d3d設備一般都是設備本身和硬件之間的通信,而d3d上下文是一種描述設備如何繪制的渲染設備上下文,這也包含了渲染狀態和其他的繪圖信息。
而交換鏈是設備和上下文將要繪制的渲染目標。
D3D11CreateDeviceAndSwapChain函數原型:
HRESULT D3D11CreateDeviceAndSwapChain(
_In_opt_ IDXGIAdapter *pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags, _In_opt_ const D3D_FEATURE_LEVEL *pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, _In_opt_ const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, _Out_opt_ IDXGISwapChain **ppSwapChain, _Out_opt_ ID3D11Device **ppDevice, _Out_opt_ D3D_FEATURE_LEVEL *pFeatureLevel, _Out_opt_ ID3D11DeviceContext **ppImmediateContext );
pAdapter來選擇相應的圖形適配器,設為NULL以選擇默認的適配器;
DriverType設置驅動類型,一般毫無疑問選擇硬件加速,即D3D_DRIVER_TYPE_HARDWARE,此時下一個參數就是NULL;
Flags為可選參數,一般為NULL,可以設為D3D11_CREATE_DEVICE_DEBUG、D3D11_CREATE_DEVICE_SINGLETHREADED,或兩者一起,前者讓要用於調試時收集信息,后者在確定程序只在單線程下運行時設置為它,可以提高性能;
pFeatureLevels為我們提供給程序的特征等級的一個數組,下一個參數為數組中元素個數;
SDKVersion恆定為D3D11_SDK_VERSION;
ppDevice為設備指針的地址,注意設備是指針類型,這里傳遞的是指針的地址(二維指針,d3d程序中所有的接口都聲明為指針類型!);
pFeatureLevel為最后程序選中的特征等級,我們定義相應的變量,傳遞它的地址進來;
ppImmediateContext為設備上下文指針的地址,要求同設備指針。
創建設備和交換鏈的相關代碼
1 for (UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; ++driverTypeIndex) 2 { 3 g_driverType = driverTypes[driverTypeIndex]; 4 hResult = D3D11CreateDeviceAndSwapChain( 5 NULL, //默認圖形適配器 6 g_driverType, //驅動類型 7 NULL, //實現軟件渲染設備的動態庫句柄,如果使用的驅動設備類型是軟件設備則不能為NULL 8 createDeviceFlags, //創建標志,0用於游戲發布,一般D3D11_CREATE_DEVICE_DEBUG允許我們創建可供調試的設備,在開發中比較有用 9 featureLevels, //特征等級 10 numFeatureLevels, //特征等級數量 11 D3D11_SDK_VERSION, //sdk版本號 12 &sd, 13 &g_pSwapChain, 14 &g_pd3dDevice, 15 &g_featureLevel, 16 &g_pImmediateContext 17 ); 18 if (SUCCEEDED(hResult)) 19 break; 20 } 21 if (FAILED(hResult)) 22 return hResult;
三、創建和綁定渲染目標視圖
一個渲染目標視圖是一個由Output MergerStage讀取的D3D資源。交換鏈的主緩存和輔助緩存為彩色的圖像,通過調用交換鏈中的函數GetBuffer來得到它的指針。得到指針后,然后再通過CreateRenderTargetView函數來創建一個渲染目標視圖。創建完渲染目標后,就可以調用Release()釋放指針到交換鏈的后台緩存了。當想渲染一個特定的渲染目標的時,要在繪制函數調用前對它進行設置,這個工作是由OMSetRenderTarget函數完成的。
CreateRenderTargetView函數原型
HRESULT CreateRenderTargetView(
[in] ID3D11Resource *pResource, [in, optional] const D3D11_RENDER_TARGET_VIEW_DESC *pDesc, [out, optional] ID3D11RenderTargetView **ppRTView );
pResource為視圖對應資源
pDesc為視圖描述
ppRTView要創建的視圖,是一個指針的地址
這一部分的代碼
1 //創建渲染目標視圖 2 ID3D11Texture2D *pBackBuffer = NULL; 3 //獲取后緩沖區地址 4 hResult = g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer); 5 if (FAILED(hResult)) 6 return hResult; 7 8 //創建目標視圖 9 hResult = g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &g_pRenderTargetView); 10 //釋放后緩沖 11 pBackBuffer->Release(); 12 if (FAILED(hResult)) 13 return hResult; 14 15 //綁定到渲染管線 16 g_pImmediateContext->OMSetRenderTargets(1, &g_pRenderTargetView, NULL);
四、創建視口
視口定義了渲染到屏幕上的面積,對於單屏游戲來說一般為全屏的,這樣我們設置視口的width和height為交換鏈對應的width、height就好了;對於分屏游戲,可以創建兩個視口
放在屏幕不同位置,以不同玩家的角度來渲染。
D3D11_VIEWPORT定義
typedef struct D3D11_VIEWPORT {
FLOAT TopLeftX; //視口左上角x坐標,一般視口占滿屏幕的,所以為0
FLOAT TopLeftY; //y坐標
FLOAT Width; //視口寬度,一般與后緩沖區一致,以保持圖像不變形
FLOAT Height; //高度,同上
FLOAT MinDepth; //最小深度值:0.0f
FLOAT MaxDepth; //最大深度值:1.0f
} D3D11_VIEWPORT;
具體代碼
1 //設置viewport 2 D3D11_VIEWPORT vp; 3 vp.Height = (FLOAT)height; 4 vp.Width = (FLOAT)width; 5 vp.MinDepth = 0.0f; 6 vp.MaxDepth = 1.0f; 7 vp.TopLeftX = 0; 8 vp.TopLeftY = 0; 9 g_pImmediateContext->RSSetViewports(1, &vp);
接下來的Render函數中實現了清除顯示屏幕的功能
float ClearColor[4] = { 0.5f, 0.1f, 0.2f, 1.0f }; //red,green,blue,alpha g_pImmediateContext->ClearRenderTargetView(g_pRenderTargetView, ClearColor); g_pSwapChain->Present(0, 0);
下面給出整個工程的代碼:
#include <windows.h> #include <d3d11.h> #include <DxErr.h> #include <D3DX11.h> HINSTANCE g_hInstance = NULL; HWND g_hWnd = NULL; LPCWSTR g_name = L"FirstD3D11Demo"; D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL; //驅動類型 D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0; //特征等級 ID3D11Device *g_pd3dDevice = NULL; //設備 ID3D11DeviceContext *g_pImmediateContext = NULL; //設備上下文 IDXGISwapChain *g_pSwapChain = NULL; //交換鏈 ID3D11RenderTargetView *g_pRenderTargetView = NULL; //要創建的視圖 HRESULT InitWindow(HINSTANCE hInstance, int nCmdShow); HRESULT InitDevice(); void CleanupDevice(); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); void Render(); int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) { if (FAILED(InitWindow(hInstance, nShowCmd))) return 0; if (FAILED(InitDevice())) { CleanupDevice(); return 0; } MSG msg; ZeroMemory(&msg, sizeof(MSG)); while (msg.message != WM_QUIT) { if (PeekMessage(&msg, NULL, 0, 0,PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else//渲染 { Render(); } } CleanupDevice(); return static_cast<int>(msg.wParam); } HRESULT InitWindow(HINSTANCE hInstance, int nCmdShow) { WNDCLASSEX wcex; wcex.cbClsExtra = 0; wcex.cbSize = sizeof(WNDCLASSEX); wcex.cbWndExtra = 0; wcex.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); wcex.hCursor = LoadCursor(NULL,IDC_ARROW); wcex.hIcon = LoadIcon(NULL, IDI_WINLOGO); wcex.hIconSm = wcex.hIcon; wcex.hInstance = hInstance; wcex.lpfnWndProc = WndProc; wcex.lpszClassName = g_name; wcex.lpszMenuName = NULL; wcex.style = CS_HREDRAW | CS_VREDRAW; if (!RegisterClassEx(&wcex)) return E_FAIL; g_hInstance = hInstance; RECT rc{0,0,640,480}; AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE); g_hWnd = CreateWindowEx(WS_EX_APPWINDOW, g_name, g_name, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, g_hInstance, NULL); if (!g_hWnd) return E_FAIL; ShowWindow(g_hWnd, nCmdShow); return S_OK; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wPararm, LPARAM lParam) { switch (message) { case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wPararm, lParam); } return 0; } //創建設備及交換鏈 HRESULT InitDevice() { HRESULT hResult = S_OK;//返回結果 RECT rc; GetClientRect(g_hWnd, &rc);//獲取窗口客戶區大小 UINT width = rc.right - rc.left; UINT height = rc.bottom - rc.top; UINT createDeviceFlags = 0; #ifdef _DEBUG createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; #endif //驅動類型數組 D3D_DRIVER_TYPE driverTypes[] = { D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP, D3D_DRIVER_TYPE_REFERENCE }; UINT numDriverTypes = ARRAYSIZE(driverTypes); //特征級別數組 D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0 }; UINT numFeatureLevels = ARRAYSIZE(featureLevels); //交換鏈 DXGI_SWAP_CHAIN_DESC sd; ZeroMemory(&sd, sizeof(DXGI_SWAP_CHAIN_DESC));//填充 sd.BufferCount = 1; //我們只創建一個后緩沖(雙緩沖)因此為1 sd.BufferDesc.Width = width; sd.BufferDesc.Height = height; sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; sd.BufferDesc.RefreshRate.Numerator = 60; sd.BufferDesc.RefreshRate.Denominator = 1; sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; sd.OutputWindow = g_hWnd; sd.SampleDesc.Count = 1; //1重采樣 sd.SampleDesc.Quality = 0; //采樣等級 sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; //常用參數 sd.Windowed = TRUE; //是否全屏 for (UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; ++driverTypeIndex) { g_driverType = driverTypes[driverTypeIndex]; hResult = D3D11CreateDeviceAndSwapChain( NULL, //默認圖形適配器 g_driverType, //驅動類型 NULL, //實現軟件渲染設備的動態庫句柄,如果使用的驅動設備類型是軟件設備則不能為NULL createDeviceFlags, //創建標志,0用於游戲發布,一般D3D11_CREATE_DEVICE_DEBUG允許我們創建可供調試的設備,在開發中比較有用 featureLevels, //特征等級 numFeatureLevels, //特征等級數量 D3D11_SDK_VERSION, //sdk版本號 &sd, &g_pSwapChain, &g_pd3dDevice, &g_featureLevel, &g_pImmediateContext ); if (SUCCEEDED(hResult)) break; } if (FAILED(hResult)) return hResult; //創建渲染目標視圖 ID3D11Texture2D *pBackBuffer = NULL; //獲取后緩沖區地址 hResult = g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer); if (FAILED(hResult)) return hResult; //創建目標視圖 hResult = g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &g_pRenderTargetView); //釋放后緩沖 pBackBuffer->Release(); if (FAILED(hResult)) return hResult; //綁定到渲染管線 g_pImmediateContext->OMSetRenderTargets(1, &g_pRenderTargetView, NULL); //設置viewport D3D11_VIEWPORT vp; vp.Height = (FLOAT)height; vp.Width = (FLOAT)width; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = 0; vp.TopLeftY = 0; g_pImmediateContext->RSSetViewports(1, &vp); return S_OK; } void Render() { float ClearColor[4] = { 0.5f, 0.1f, 0.2f, 1.0f }; //red,green,blue,alpha g_pImmediateContext->ClearRenderTargetView(g_pRenderTargetView, ClearColor); g_pSwapChain->Present(0, 0); } void CleanupDevice() { if (g_pImmediateContext) g_pImmediateContext->ClearState(); if (g_pSwapChain) g_pSwapChain->Release(); if (g_pRenderTargetView) g_pRenderTargetView->Release(); if (g_pImmediateContext) g_pImmediateContext->Release(); if (g_pd3dDevice) g_pd3dDevice->Release(); }