1
\$\begingroup\$

I'm learning DirectX12 and I'm trying to create a simple application that clears the screen with a solid color, but I'm stuck in Direct3D initialization. I can't create the swap chain and the DXGI factory returns a nullptr. This is my initialization code:

bool Game::InitializeD3D()
{
ID3D12Debug *debug;
D3D12GetDebugInterface(IID_PPV_ARGS(&debug));
debug->EnableDebugLayer();

// hardware result returned by functions
HRESULT hr;

// create device
hr = D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&mDevice));
if (FAILED(hr))
    MessageBox(0, L"device creation failed", L"error", MB_OK);

// create fence
mDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence));

// query descriptor size
mRTVDescriptorSize = mDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
mDSVDescriptorSize = mDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
mCBVSRVDescriptorSize = mDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

// check for 4X MSAA quality level support
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS MSAAQuality;
MSAAQuality.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
MSAAQuality.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
MSAAQuality.NumQualityLevels = 0;
MSAAQuality.SampleCount = 4;
mDevice->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &MSAAQuality, sizeof MSAAQuality);
mMSAAQualityLevels = MSAAQuality.NumQualityLevels;

// create command queue
D3D12_COMMAND_QUEUE_DESC commandQueueDesc = {};
commandQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
commandQueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
hr = mDevice->CreateCommandQueue(&commandQueueDesc, IID_PPV_ARGS(&mCommandQueue));
if (FAILED(hr))
    MessageBox(0, L"command queue creation failed", L"Error", MB_OK);

// create command allocator
mDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAllocator));

// create main command list
mDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, mCommandAllocator, nullptr, IID_PPV_ARGS(&mCommandList));
static_cast<ID3D12GraphicsCommandList*>(mCommandList)->Close();

// create the swap chain
DXGI_MODE_DESC mode = {};
mode.Width = mWindowWidth;
mode.Height = mWindowHeight;
mode.Format = mBackBufferFormat;  
mode.RefreshRate.Numerator = 60;  // 60 Hz refresh rate
mode.RefreshRate.Denominator = 1;
mode.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
mode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
swapChainDesc.BufferCount = bufferCount; // double buffering
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferDesc = mode;
swapChainDesc.OutputWindow = mWindow;
swapChainDesc.SampleDesc.Count = 4; // 4X MSAA
swapChainDesc.SampleDesc.Quality = mMSAAQualityLevels - 1;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swapChainDesc.Windowed = true;
swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

hr = CreateDXGIFactory1(IID_PPV_ARGS(&mFactory));
if (FAILED(hr))
    MessageBox(0, L"factory creation failed", L"Error", MB_OK);

hr = mFactory->CreateSwapChain(mCommandQueue, &swapChainDesc, &mSwapChain);
if (FAILED(hr))
    MessageBox(0, L"swap chain creation failed", L"Error", MB_OK);

// more init code
......

My main window is created and the message box appears saying that the swap chain creation failed and Visual Studio raises an access violation exception. What am I doing wrong?

\$\endgroup\$
0
\$\begingroup\$

You haven't listed what type the mFactory variable is, but based on the fact that you are calling CreateSwapChain I'm assuming you are using the older DXGI 1.1 version (the original that shipped with DirectX 11).

For DirectX 12, you are expected to use the more modern DXGI 1.2 functions: CreateSwapChainForHwnd, CreateSwapChainForCoreWindow or CreateSwapChainForComposition. If your program requires DirectX 12, then it can safely require IDXGIFactory4. It does work to use the older DXGI 1.1 function CreateSwapChain but there's no reason to use it since you are already requiring a newer DirectX Runtime to use DirectX 12 in the first place.

// mFactory is type IDXGIFactory4
// mSwapChain is type IDXGISwapChain1

DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
swapChainDesc.Width = mWindowWidth;
swapChainDesc.Height = mWindowHeight;
swapChainDesc.Format = mBackBufferFormat;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 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_IGNORE;
swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

DXGI_SWAP_CHAIN_FULLSCREEN_DESC swapChainFSDesc = {};
swapChainFSDesc.Windowed = TRUE;

DWORD dxgiFactoryFlags = 0;
#ifdef _DEBUG
dxgiFactoryFlags = DXGI_CREATE_FACTORY_DEBUG;
#endif
hr = CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&mFactory));
if (FAILED(hr))
    MessageBox(0, L"factory creation failed", L"Error", MB_OK);

hr = mFactory->CreateSwapChainForHwnd(
    mCommandQueue.Get(),
    // You should be using a smart-pointer like Microsoft::WRL::ComPtr
    // and not raw pointers for COM interfaces!
    hWnd, &swapChainDesc, &swapChainFSDesc,
    nullptr, &mSwapChain);

Note that your original code also has a problem in that it assumes that a refresh rate of 1 / 60 is always valid, which is not actually the case. In practice, some vendor's drivers enumerate even 60 Hz with a different numerator/denominator than 1, 60 so it wouldn't match and you'd get a performance hit. You have to enumerate modes to do this correctly with DXGI if doing 'true fullscreen'. Of course, I'd advocate using 'fake full screen' in any case.

See Anatomy of Direct3D 12 Create Device and the Direct3D Game VS Templates for best practices for DirectX 12 device creation.

NOTE: If you are new to DirectX generally you should consider learning DirectX 11 first. DirectX 12 is an expert API that assumes you are already very familiar with DirectX. See the DirectX Tool Kit tutorials for DirectX 11 and DirectX 12.

UPDATE: The problem here is that MSAA is being used. Part of the 'modern swapchain' is that you cannot create an MSAA swapchain. If you enable DXGI device debugging

hr = CreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG, IID_PPV_ARGS(&mFactory));

Then you'd get:

DXGI ERROR: IDXGIFactory::CreateSwapChain: Flip model swapchains (DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL and DXGI_SWAP_EFFECT_FLIP_DISCARD) do not support multisampling. DXGI_SWAP_CHAIN_DESC{ SwapChainType = ..._HWND, BufferDesc = DXGI_MODE_DESC1{Width = 2032, Height = 954, RefreshRate = DXGI_RATIONAL{ Numerator = 60, Denominator = 1 }, Format = B8G8R8A8_UNORM, ScanlineOrdering = ..._UNSPECIFIED, Scaling = ..._UNSPECIFIED, Stereo = FALSE }, SampleDesc = DXGI_SAMPLE_DESC{ Count = 4, Quality = 0 }, BufferUsage = 0x20, BufferCount = 2, OutputWindow = 0x000D0620, Scaling = ..._STRETCH, Windowed = TRUE, SwapEffect = ..._FLIP_DISCARD, AlphaMode = ..._IGNORE, Flags = 0x2 } [ MISCELLANEOUS ERROR #102: ]

Instead you always create a single-sample swapchain, and then you create your own MSAA render target that you then explicitly resolve. This is true of DX11 UWP as well as DX12 on UWP/PC. See SimpleMSAA.

While I'm on the topic, you also cannot create an sRGB swapchain with 'modern swapchains'. You instead create a non-sRGB swapchain and then an sRGB render target view. MSAA + sRGB linearly correct rendering is also a little complicated due to the fact that the debug validation layer had some bugs with older versions of Windows 10 that are all fixed for Windows 10 Creators Update for DirectX 12. The SimpleMSAA sample covers this.

\$\endgroup\$
  • \$\begingroup\$ I'm following Frank Luna's book "Introduction to directX12 game programming" and the code in the book uses IDXGIFactory4 interface and calls CreateSwapChain. His code works but mine doesn't, so I must be doing something wrong somewhere. \$\endgroup\$ – Luca Oct 23 '17 at 10:24
  • \$\begingroup\$ when checking for MSAA qualty levels the system returns 1 so I'm setting a quality level of 0 in the swap chain descriptor struct. If I use 1 as number of samples it works, but if I use 4 (I checked for 4x support when it returned 1 quality level) it doesn't. Why? \$\endgroup\$ – Luca Oct 23 '17 at 12:49
  • \$\begingroup\$ DXGI_SWAP_EFFECT_FLIP_* swapchains do not support MSAA directly. You must always create a single-sample swapchain, then create your own MSAA render target which you explicitly resolve to the swapchain buffer. If you were using DXGI debugging you'd have gotten more information at the failure point. Frank's code probably worked on a Windows 10 preview, but it shouldn't work now. \$\endgroup\$ – Chuck Walbourn Oct 23 '17 at 17:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.