解析 Command-line 参数
当应用程序执行的时候,ParseCommandLineArguments函数可以使用命令行参数来重载一些全局定义的变量。
void ParseCommandLineArguments()
{
int argc;
wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
for (size_t i = 0; i < argc; ++i)
{
if (::wcscmp(argv[i], L"-w") == 0 || ::wcscmp(argv[i], L"--width") == 0)
{
g_ClientWidth = ::wcstol(argv[++i], nullptr, 10);
}
if (::wcscmp(argv[i], L"-h") == 0 || ::wcscmp(argv[i], L"--height") == 0)
{
g_ClientHeight = ::wcstol(argv[++i], nullptr, 10);
}
if (::wcscmp(argv[i], L"-warp") == 0 || ::wcscmp(argv[i], L"--warp") == 0)
{
g_UseWarp = true;
}
}
// Free memory allocated by CommandLineToArgvW
::LocalFree(argv);
}
你可能注意到有些函数的前缀是::操作符,这是用于定义全局中的系统函数。
下面这个表描述了应用程序支持的命令行参数。
参数 | 描述 |
---|---|
-w , --width | 渲染窗口的宽(像素) |
-h , --height | 渲染窗口的高(像素) |
-warp , --warp | 为创建device使用 Windows Advanced Rasterization Platform (WARP) |
启用 Direct3D 12 调试层
在使用DXGI或D3D API之前,调试层应当首先被启用。在创建ID3D12Deviece后启用调试层会让device被移除。
void EnableDebugLayer()
{
#if defined(_DEBUG)
// Always enable the debug layer before doing anything DX12 related
// so all possible errors generated while creating DX12 objects
// are caught by the debug layer.
ComPtr<ID3D12Debug> debugInterface;
ThrowIfFailed(D3D12GetDebugInterface(IID_PPV_ARGS(&debugInterface)));
debugInterface->EnableDebugLayer();
#endif
}
IID_PPV_ARGS宏用于接收接口指针,supplying the IID value of the requested interface automatically based on the type of the interface pointer used.
常见的接收接口指针的语法包括下面两个参数。
- 一个 [in] 参数,类型通常为
REFIID,用于指定接收接口的IID
- 一个 [out] 参数, 类型通常为
void**,用于接收接口指针
.
这个宏将会基于接口类型来计算IID,用于防止接口指针与IID不匹配。
注册窗口类
在创建窗口实例之前,必须先注册窗口类。当应用程序停止后,窗口类会被自动注销。
void RegisterWindowClass( HINSTANCE hInst, const wchar_t* windowClassName )
{
// Register a window class for creating our render window with.
WNDCLASSEXW windowClass = {};
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = &WndProc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = hInst;
windowClass.hIcon = ::LoadIcon(hInst, NULL);
windowClass.hCursor = ::LoadCursor(NULL, IDC_ARROW);
windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
windowClass.lpszMenuName = NULL;
windowClass.lpszClassName = windowClassName;
windowClass.hIconSm = ::LoadIcon(hInst, NULL);
static ATOM atom = ::RegisterClassExW(&windowClass);
assert(atom > 0);
}
The RegisterClassEx
function takes a pointer to a WNDCLASSEX
structure as its only argument.
The WNDCLASSEX
structure has the following definition [17]:
typedef struct tagWNDCLASSEXW {
UINT cbSize;
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCWSTR lpszMenuName;
LPCWSTR lpszClassName;
HICON hIconSm;
} WNDCLASSEXW, *PWNDCLASSEXW;
每个成员的定义如下
UINT cbSize
: 这个结构体的字节大小,设置为sizeof(WNDCLASSEXW)
.UINT style
: 类样式。比如CS_HREDRAW
指定了当窗口的宽度改变时需要重新绘制整个窗口,CS_VREDRAW
则是当高度改变时重新绘制整个窗口。WNDPROC lpfnWndProc
: 窗口消息处理程序,用于处理在这个类下实例化出来的窗口所接收到的消息。可默认也可自定义。int cbClsExtra
: 给这个窗口类结构体额外分配的字节。我们暂时不需要所以设置为0.int cpWndExtra
: 给这个窗口实例额外分配的字节。我们暂时不需要所以设置为0.HINSTANCE hInstance
: A handle to the instance that contains the window procedure for the class. This module instance handle is passed to theWinMain
function which will be shown later.HICON hIcon
: 类图标的handle。你可以使用LoadIcon来加载自定义图标,或者是
NULL
(ornullptr
) 来使用默认图标。HCURSOR hCursor
: 类鼠标的handle。我们将使用默认鼠标,也就是使用函数LoadCursor( nullptr, IDC_ARROW )
.HBRUSH hbrBackground
: 背景笔刷的颜色,包括以下几种。COLOR_ACTIVEBORDER
COLOR_ACTIVECAPTION
COLOR_APPWORKSPACE
COLOR_BACKGROUND
COLOR_BTNFACE
COLOR_BTNSHADOW
COLOR_BTNTEXT
COLOR_CAPTIONTEXT
COLOR_GRAYTEXT
COLOR_HIGHLIGHT
COLOR_HIGHLIGHTTEXT
COLOR_INACTIVEBORDER
COLOR_INACTIVECAPTION
COLOR_MENU
COLOR_MENUTEXT
COLOR_SCROLLBAR
COLOR_WINDOW
COLOR_WINDOWFRAME
COLOR_WINDOWTEXT
LPCWSTR lpszMenuName
: Pointer to a null-terminated character string that specifies the resource name of the class menu, as the name appears in the resource file. If this member isNULL
, windows belonging to this class have no default menu.LPCWSTR lpszClassName
: 实例化窗口要使用一个窗口类,这就是那个类的名字。HICON hIconSm
: 窗口类使用的小图标。
创建窗口实例
窗口将被创建在显示器的中心。注意不要让窗口的一部分创建到显示器外面去了。
HWND CreateWindow(const wchar_t* windowClassName, HINSTANCE hInst,
const wchar_t* windowTitle, uint32_t width, uint32_t height)
{
int screenWidth = ::GetSystemMetrics(SM_CXSCREEN);
int screenHeight = ::GetSystemMetrics(SM_CYSCREEN);
RECT windowRect = { 0, 0, static_cast<LONG>(width), static_cast<LONG>(height) };
::AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE);
int windowWidth = windowRect.right - windowRect.left;
int windowHeight = windowRect.bottom - windowRect.top;
// Center the window within the screen. Clamp to 0, 0 for the top-left corner.
int windowX = std::max<int>(0, (screenWidth - windowWidth) / 2);
int windowY = std::max<int>(0, (screenHeight - windowHeight) / 2);
}
GetSystemMetrics很简单,返回屏幕的宽高。为了计算期望的窗口大小,使用函数AdjustWindowRect。WS_OVERLAPPEDWINDOW说明这个窗口可以被最小化,最大化,以及边框很薄。
The top-left corner point of the window is computed on lines 158-159 so that the window appears in the center of the screen. The window position should be clamped to (0,0)(0,0) to prevent the window from being positioned offscreen.
当知道了窗口的维度,就可以创建窗口了。
HWND hWnd = ::CreateWindowExW(
NULL,
windowClassName,
windowTitle,
WS_OVERLAPPEDWINDOW,
windowX,
windowY,
windowWidth,
windowHeight,
NULL,
NULL,
hInst,
nullptr
);
assert(hWnd && "Failed to create window");
return hWnd;
}
CreateWindowExW结构体如下
HWND WINAPI CreateWindowExW(
_In_ DWORD dwExStyle,
_In_opt_ LPCWSTR lpClassName,
_In_opt_ LPCWSTR lpWindowName,
_In_ DWORD dwStyle,
_In_ int X,
_In_ int Y,
_In_ int nWidth,
_In_ int nHeight,
_In_opt_ HWND hWndParent,
_In_opt_ HMENU hMenu,
_In_opt_ HINSTANCE hInstance,
_In_opt_ LPVOID lpParam
);
每个成员变量分别是
DWORD dwExStyle
:窗口的扩展样式 Extended Window Styles。LPCWSTR lpClassName
: 使用RegisterClass
orRegisterClassEx
注册的窗口内。LPCWSTR lpWindowName
: 窗口的名字,如果有标题框的话,窗口的名字将显示出来。DWORD dwStyle
: 窗口的标准样式 window style values.- 接下来四个参数是窗口的位置和大小,可以设置为
CW_USEDEFAULT。
HWND hWndParent
:父窗口的handleHMENU hMenu
:目录的handle,取决于窗口的样式,可以为NULL。HINSTANCE hInstance
: 窗口模组的handle,LPVOID lpParam
: 将要使用CREATESTRUCT
结构来通过窗口传递的数据的指针 (lpCreateParams
member) 。
窗口被创建成功但是没有展示出来。因为还要创建和初始化Device和指令队列。
查询DirectX 12 Adapter
在创建Device之前,必须现在用户的电脑上找到兼容的adapter,因此使用GetAdapter函数。
ComPtr<IDXGIAdapter4> GetAdapter(bool useWarp)
{
ComPtr<IDXGIFactory4> dxgiFactory;
UINT createFactoryFlags = 0;
#if defined(_DEBUG)
createFactoryFlags = DXGI_CREATE_FACTORY_DEBUG;
#endif
ThrowIfFailed(CreateDXGIFactory2(createFactoryFlags, IID_PPV_ARGS(&dxgiFactory)));
}
但在查询可用的适配器之前,还需要创建DXGI工厂。启用DXGI_CREATE_FACTORY_DEBUG以便捕获错误。但在要发售的产品上不能用这个标签。
ComPtr<IDXGIAdapter1> dxgiAdapter1;
ComPtr<IDXGIAdapter4> dxgiAdapter4;
if (useWarp)
{
ThrowIfFailed(dxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(&dxgiAdapter1)));
ThrowIfFailed(dxgiAdapter1.As(&dxgiAdapter4));
}
In the case that a WARP device should be used, the IDXGIFactory4::EnumWarpAdapter
method can be used to directly create the WARP adapter.
这种情况下应该使用WARP device,因此使用IDXGIFactory4::EnumWarpAdapter来创建WARP适配器,这个方法需要一个IDXGIAdapter1接口的指针,但GetAdapter函数返回的是IDXGIAdapter4的指针,因此要使用ComPtr::As来将其cast到正确的类型。当不使用ComPtr的时候,应该使用QueryInterface方法来查询正确的COM物体。
在COM物体上使用 static_cast
既不安全也不可靠。
else
{
SIZE_T maxDedicatedVideoMemory = 0;
for (UINT i = 0; dxgiFactory->EnumAdapters1(i, &dxgiAdapter1) != DXGI_ERROR_NOT_FOUND; ++i)
{
DXGI_ADAPTER_DESC1 dxgiAdapterDesc1;
dxgiAdapter1->GetDesc1(&dxgiAdapterDesc1);
// Check to see if the adapter can create a D3D12 device without actually
// creating it. The adapter with the largest dedicated video memory
// is favored.
if ((dxgiAdapterDesc1.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) == 0 &&
SUCCEEDED(D3D12CreateDevice(dxgiAdapter1.Get(),
D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), nullptr)) &&
dxgiAdapterDesc1.DedicatedVideoMemory > maxDedicatedVideoMemory )
{
maxDedicatedVideoMemory = dxgiAdapterDesc1.DedicatedVideoMemory;
ThrowIfFailed(dxgiAdapter1.As(&dxgiAdapter4));
}
}
}
return dxgiAdapter4;
}
当不使用WARP适配器时,就会使用DXGI工厂来查询硬件适配器。IDXGIFactory1::EnumAdapters1将会查询可用的GPU。如果适配器ID大于或等于可用的适配器数量时就会返回DXGI_ERROR_NOT_FOUND。
如果只考虑硬件适配器,那么就不应该使用 DXGI_ADAPTER_FLAG_SOFTWARE
标签。
为了确保IDXGIFactory1::EnumAdapters1
返回的适配器与DX12兼容,会使用D3D12CreateDevice创造一个device,如果这个函数返回S_OK,说明这个适配器确实和DX12兼容。通常来说,DX12喜欢更大显存的适配器。
如果找到了合适的GPU适配器,那么将会创建真正的DX12 device。
创建 DirectX 12 Device
DX12 device用于创建资源,比如纹理和缓存,指令列,指令队列,fence,heaps等。DX12 device不会直接用于发布绘制或dispatch指令。DX12 device可用被认为是一个memory context用于追踪GPU显存上的分配。摧毁DX12 device将会让所有通过这个device分配下的资源都变得无效,然后调试层就会发出警示信息。
ComPtr<ID3D12Device2> CreateDevice(ComPtr<IDXGIAdapter4> adapter)
{
ComPtr<ID3D12Device2> d3d12Device2;
ThrowIfFailed(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&d3d12Device2)));
你在之前的GetAdapter函数哪里已经看见D3D12CreateDevice了。这里,真正的device被创建好后将被存储在d3d12Device2参数里。
HRESULT WINAPI D3D12CreateDevice(
_In_opt_ IUnknown *pAdapter,
D3D_FEATURE_LEVEL MinimumFeatureLevel,
_In_ REFIID riid,
_Out_opt_ void **ppDevice
);
这个结构体有如下参数
IUnknown *pAdapter
:创建device时的适配器的指针。使用NULL来使用默认适配器,which is the first adapter that is enumerated byIDXGIFactory1::EnumAdapters
.D3D_FEATURE_LEVEL MinimumFeatureLevel
: The minimumD3D_FEATURE_LEVEL
required for successful device creation.REFIID riid
: Device接口的globally unique identifier (GUID
) for the device interface. This parameter, andppDevice
, can be addressed with the single macroIID_PPV_ARGS
.void **ppDevice
: A pointer to a memory block that receives a pointer to the device.
为了让开发者能够修复由调试层抛出的错误,dx12 device提供了ID3D12InfoQueue接口,用于断点和过滤需要的信息。
// Enable debug messages in debug mode.
#if defined(_DEBUG)
ComPtr<ID3D12InfoQueue> pInfoQueue;
if (SUCCEEDED(d3d12Device2.As(&pInfoQueue)))
{
pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE);
pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE);
pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE);
ID3D12InfoQueue接口使用ID3D12Device使用ComPtr::As查询获得
The ID3D12InfoQueue::SetBreakOnSeverity
method sets a message severity level to break on (while the application is attached to a debugger) when a message with that severity level passes through the storage filter. The D3D12_MESSAGE_SEVERITY_ERROR
and the D3D12_MESSAGE_SEVERITY_WARNING
messages are generated if an error or warning is generated by the debug layer. The D3D12_MESSAGE_SEVERITY_CORRUPTION
message is generated if a memory corruption occurs.
由于要消除所有的警告问题不切实际,因此可用用过滤器来筛选重要的信息。
// Suppress whole categories of messages
//D3D12_MESSAGE_CATEGORY Categories[] = {};
// Suppress messages based on their severity level
D3D12_MESSAGE_SEVERITY Severities[] =
{
D3D12_MESSAGE_SEVERITY_INFO
};
// Suppress individual messages by their ID
D3D12_MESSAGE_ID DenyIds[] = {
D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE, // I'm really not sure how to avoid this message.
D3D12_MESSAGE_ID_MAP_INVALID_NULLRANGE, // This warning occurs when using capture frame while graphics debugging.
D3D12_MESSAGE_ID_UNMAP_INVALID_NULLRANGE, // This warning occurs when using capture frame while graphics debugging.
};
D3D12_INFO_QUEUE_FILTER NewFilter = {};
//NewFilter.DenyList.NumCategories = _countof(Categories);
//NewFilter.DenyList.pCategoryList = Categories;
NewFilter.DenyList.NumSeverities = _countof(Severities);
NewFilter.DenyList.pSeverityList = Severities;
NewFilter.DenyList.NumIDs = _countof(DenyIds);
NewFilter.DenyList.pIDList = DenyIds;
ThrowIfFailed(pInfoQueue->PushStorageFilter(&NewFilter));
}
#endif
return d3d12Device2;
}
Since D3D12_MESSAGE_SEVERITY_INFO
message severity is for information only, info messages are supressed.
以下的警告信息由于 message ID而被忽视
CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE
:当你要使用ClearColor来清楚RenderTargetView的时候会发出此警告。MAP_INVALID_NULLRANGE
andUNMAP_INVALID_NULLRANGE
: 当使用Visual Studio中集成的图形调试器捕获帧时,会出现这些警告。由于我认为此错误永远不会在调试器中修复,因此最好只是忽略此警告。
创建指令队列
使用 CreateCommandQueue 来创建指令队列
ComPtr<ID3D12CommandQueue> CreateCommandQueue(ComPtr<ID3D12Device2> device, D3D12_COMMAND_LIST_TYPE type )
{
ComPtr<ID3D12CommandQueue> d3d12CommandQueue;
D3D12_COMMAND_QUEUE_DESC desc = {};
desc.Type = type;
desc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
desc.NodeMask = 0;
ThrowIfFailed(device->CreateCommandQueue(&desc, IID_PPV_ARGS(&d3d12CommandQueue)));
return d3d12CommandQueue;
}
其中用到了以下结构体
typedef struct D3D12_COMMAND_QUEUE_DESC {
D3D12_COMMAND_LIST_TYPE Type;
INT Priority;
D3D12_COMMAND_QUEUE_FLAGS Flags;
UINT NodeMask;
} D3D12_COMMAND_QUEUE_DESC;
D3D12_COMMAND_LIST_TYPE Type
: 指定指令队列的类型D3D12_COMMAND_LIST_TYPE_DIRECT
: 可用执行绘制,计算以及复制指令,这是最常用的。D3D12_COMMAND_LIST_TYPE_COMPUTE
: 可用执行计算以及复制指令。D3D12_COMMAND_LIST_TYPE_COPY
:可用于执行复制指令。
INT Priority
: 指令队列的优先级D3D12_COMMAND_QUEUE_FLAGS Flags
: 额外标签。一般来说是D3D12_COMMAND_QUEUE_FLAG_DISABLE_GPU_TIMEOUT
which indicates that the GPU timeout should be disabled for this command queue. Be careful when using this flag. If you encounter errors with GPU timeouts, you should probably address the error instead of using this flag.UINT NodeMask
: 如果只有一个GPU,则这个设置为0。如果有多个,就需要设置为相应的需要用来执行这个消息队列的GPU。参见 Multi-Adapter.
检查防撕裂
显示器刷新速率变量 (NVidia's G-Sync and AMD's FreeSync) 需要被正确设置以用于防撕裂,即 "vsync-off" [19].
为了创建可用支持不同显示器刷新速率的应用程序,必须支持DXGI_FEATURE_PRESENT_ALLOW_TEARING和DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING,而且当交换链的同步间隔为0时必须设置DXGI_PRESENT_ALLOW_TEARING。
Windows Display Driver Model (WDDM) 目前的版本是 2.1. 为了检查用户电脑是否支持防撕裂,所以要用 IDXGIFactory5::CheckFeatureSupport
方法。
bool CheckTearingSupport()
{
BOOL allowTearing = FALSE;
// Rather than create the DXGI 1.5 factory interface directly, we create the
// DXGI 1.4 interface and query for the 1.5 interface. This is to enable the
// graphics debugging tools which will not support the 1.5 factory interface
// until a future update.
ComPtr<IDXGIFactory4> factory4;
if (SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(&factory4))))
{
ComPtr<IDXGIFactory5> factory5;
if (SUCCEEDED(factory4.As(&factory5)))
{
if (FAILED(factory5->CheckFeatureSupport(
DXGI_FEATURE_PRESENT_ALLOW_TEARING,
&allowTearing, sizeof(allowTearing))))
{
allowTearing = FALSE;
}
}
}
return allowTearing == TRUE;
}
结构体如下:
HRESULT CheckFeatureSupport(
DXGI_FEATURE Feature,
[in, out] void *pFeatureSupportData,
UINT FeatureSupportDataSize
);
DXGI_FEATURE Feature
: 用户电脑是否支持某些特性的查询。包括DXGI_FEATURE_PRESENT_ALLOW_TEARING
: 用户电脑是否支持不同刷新速率。
void *pFeatureSupportData
: 支持特性的数据的缓存的指针UINT FeatureSupportDataSize
:pFeatureSupportData的大小,字节。
创建交换链
交换链至少储存两个buffer,一个用于渲染的backbuffer,一个用于显示到屏幕上的frontbuffer。Windows8和DXGI 1.2引入的flip 展示方法为此提供了性能加速。
交换链可以创建两种效果。
DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
: 使用 flip presentation model 并且要求 DXGI 在IDXGISwapChain1::Present1
. 继续保持back buffer。这个标签不能用于多重采样。DXGI_SWAP_EFFECT_FLIP_DISCARD
: 使用 flip presentation model 并且要求 DXGI 在IDXGISwapChain1::Present1
. 丢弃back buffer。这个标签不能用于多重采样。and partial presentation.
为了使用vsync-off时到达最高帧率,必须使用第二个标签。而第一个标签可能会导致延迟,因为没有多余的缓存空间可让下一次back buffer渲染能继续进行。
创建交换链使用如下代码:
ComPtr<IDXGISwapChain4> CreateSwapChain(HWND hWnd,
ComPtr<ID3D12CommandQueue> commandQueue,
uint32_t width, uint32_t height, uint32_t bufferCount )
{
ComPtr<IDXGISwapChain4> dxgiSwapChain4;
ComPtr<IDXGIFactory4> dxgiFactory4;
UINT createFactoryFlags = 0;
#if defined(_DEBUG)
createFactoryFlags = DXGI_CREATE_FACTORY_DEBUG;
#endif
ThrowIfFailed(CreateDXGIFactory2(createFactoryFlags, IID_PPV_ARGS(&dxgiFactory4)));
我们首先会创建DXGI factory,这与GetAdapter函数中相似。
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
swapChainDesc.Width = width;
swapChainDesc.Height = height;
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.Stereo = FALSE;
swapChainDesc.SampleDesc = { 1, 0 };
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = bufferCount;
swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
// It is recommended to always allow tearing if tearing support is available.
swapChainDesc.Flags = CheckTearingSupport() ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0;
DXGI_SWAP_CHAIN_DESC1
结构体如下:
typedef struct _DXGI_SWAP_CHAIN_DESC1 {
UINT Width;
UINT Height;
DXGI_FORMAT Format;
BOOL Stereo;
DXGI_SAMPLE_DESC SampleDesc;
DXGI_USAGE BufferUsage;
UINT BufferCount;
DXGI_SCALING Scaling;
DXGI_SWAP_EFFECT SwapEffect;
DXGI_ALPHA_MODE AlphaMode;
UINT Flags;
} DXGI_SWAP_CHAIN_DESC1;
UINT Width
: 分辨率的宽度。如果设置为0,那么就会从输出窗口来获取宽度。然后你可以用IDXGISwapChain1::GetDesc1
方法来获取宽度值。UINT Height
: 同上。DXGI_FORMAT Format
:DXGI_FORMAT
结构体用于描述显示格式BOOL Stereo
: Specifies whether the full-screen display mode or the swap-chain back buffer is stereo.TRUE
if stereo; otherwise,FALSE
. If you specify stereo, you must also specify a flip-model swap chain.DXGI_SAMPLE_DESC SampleDesc
:DXGI_SAMPLE_DESC
多采样参数,This member is valid only with bit-block transfer (bitblt) model swap chains. When using flip model swap chain, this member must be specified as{1, 0}
.DXGI_USAGE BufferUsage
:DXGI_USAGE值,用于描述usage和backbuffer的CPU选项。此backbuffer可用于Shader输入
(DXGI_USAGE_SHADER_INPUT
) or render-target output (DXGI_USAGE_RENDER_TARGET_OUTPUT
).UINT BufferCount
: 交换链中缓存的数量。当使用全屏模式的交互链时,一般会把front buffer也包括进来。最小数量是2。DXGI_SCALING Scaling
: 当back buffer的大小和render output的大小不一致时的缩放行为,包括:DXGI_SCALING_STRETCH
: 拉伸。这是使用IDXGIFactory::CreateSwapChain
方法的默认行为。DXGI_SCALING_NONE
: 不拉伸,其他地方的背景颜色由IDXGISwapChain1::SetBackgroundColor指定。
DXGI_SCALING_ASPECT_RATIO_STRETCH
: 拉伸但是保持宽高比,不能用于IDXGIFactory2::CreateSwapChainForHwnd
.
DXGI_SWAP_EFFECT SwapEffect
: 交换效果,包括DXGI_ALPHA_MODE AlphaMode
: back buffer的透明方式DXGI_ALPHA_MODE_UNSPECIFIED
: 未指定DXGI_ALPHA_MODE_PREMULTIPLIED
:预乘,每个颜色都先乘上alpha值,要保证颜色的值不大于alpha值,否则就是additive blend线性叠加。DXGI_ALPHA_MODE_STRAIGHT
: 直接DXGI_ALPHA_MODE_IGNORE
: 忽视
UINT Flags
: A combination ofDXGI_SWAP_CHAIN_FLAG
-typed values that are combined by using a bitwise OR operation. TheDXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING
flag should always be specified if tearing support is available. See Check for Tearing Support for more information on detecting tearing support.
当指定了desc后,就可以创建交换链了
ComPtr<IDXGISwapChain1> swapChain1;
ThrowIfFailed(dxgiFactory4->CreateSwapChainForHwnd(
commandQueue.Get(),
hWnd,
&swapChainDesc,
nullptr,
nullptr,
&swapChain1));
// Disable the Alt+Enter fullscreen toggle feature. Switching to fullscreen
// will be handled manually.
ThrowIfFailed(dxgiFactory4->MakeWindowAssociation(hWnd, DXGI_MWA_NO_ALT_ENTER));
ThrowIfFailed(swapChain1.As(&dxgiSwapChain4));
return dxgiSwapChain4;
}
HRESULT CreateSwapChainForHwnd(
[in] IUnknown *pDevice,
[in] HWND hWnd,
[in] const DXGI_SWAP_CHAIN_DESC1 *pDesc,
[in, optional] const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *pFullscreenDesc,
[in, optional] IDXGIOutput *pRestrictToOutput,
[out] IDXGISwapChain1 **ppSwapChain
);
IUnknown *pDevice
: 在DX12中,这是指向direct指令队列的指针,不能为空。.HWND hWnd
:HWND
handle ,即CreateSwapChainForHwnd
的返回值,不能为空。DXGI_SWAP_CHAIN_DESC1 *pDesc
:DXGI_SWAP_CHAIN_DESC1
结构体的指针,不能为空。DXGI_SWAP_CHAIN_FULLSCREEN_DESC *pFullscreenDesc
:DXGI_SWAP_CHAIN_FULLSCREEN_DESC
结构体的指针,如果是NULL则是窗口模式。IDXGIOutput *pRestrictToOutput
: A pointer to theIDXGIOutput
interface for the output to restrict content to. You must also pass theDXGI_PRESENT_RESTRICT_TO_OUTPUT
flag in aIDXGISwapChain1::Present1
call to force the content to appear blacked out on any other output. If you want to restrict the content to a different output, you must create a new swap chain. However, you can conditionally restrict content based on theDXGI_PRESENT_RESTRICT_TO_OUTPUT
flag.
Set this parameter toNULL
if you don't want to restrict content to an output target.IDXGISwapChain1 **ppSwapChain
: 一个变量的指针,这个变量用于接收由CreateSwapChainForHwnd创建的IDXGISwapChain1
的指针。
为了防止按下ALT+ENTER键后变成全屏模式, IDXGIFactory::MakeWindowAssociation
方法需要使用 DXGI_MWA_NO_ALT_ENTER
标签。
然后交换链使用 ComPtr::As
变成IDXGISwapChain4
类型接口,并且返回调用函数。
为了渲染交换链的back buffer,每个back buffer必须有个Render Target View,或者叫descriptor heap。