1

I've been learning DirectX by following some tutorials but every time I type something myself then DirectX won't work. Here is an example of the latest error that I can't fix after hours of research:

     //Header.h
     static HWND hWnd;
    static IDXGISwapChain* swapChain;
    static ID3D11Device* dev;
    static ID3D11DeviceContext* devCon;
    static ID3D11RenderTargetView* renderTarget;

    //DirectX.cpp
    bool InitD3D11(HINSTANCE hInst)
{
    HRESULT hr;
    DXGI_MODE_DESC bufferDesc;
    ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC));
    bufferDesc.Width = 800;
    bufferDesc.Height = 600;
    bufferDesc.RefreshRate.Numerator = 60;
    bufferDesc.RefreshRate.Denominator = 1;
    bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

    DXGI_SWAP_CHAIN_DESC swapChainDesc;
    ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
    swapChainDesc.BufferDesc = bufferDesc;
    swapChainDesc.SampleDesc.Count = 1;
    swapChainDesc.SampleDesc.Quality = 0;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.BufferCount = 1;
    swapChainDesc.OutputWindow = hWnd;
    swapChainDesc.Windowed = true;
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

    hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, NULL, NULL, D3D11_SDK_VERSION, &swapChainDesc, &swapChain, &dev, NULL, &devCon);

    ID3D11Texture2D* backBuffer;
    hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);
    hr = dev->CreateRenderTargetView(backBuffer, NULL, &renderTarget);
    backBuffer->Release();
    devCon->OMSetRenderTargets(1, &renderTarget, NULL);
    return true;
}
//This is not the full code - I linked directx and lib files and stuff like that.
//If I copy and paste code from tutorials, everything runs fine

Whenever I call InitD3d11, I get an error saying that the swapChain is a NULL pointer. I assume that bufferDesc and/or swapChainDesc has some invalid data but the compiler can't give me any clue what is responsible for the error. Can someone please show me how to trace and fix errors like this? Thanks.

Werner
  • 81
  • 1
  • 1
  • 7

1 Answers1

6

You are not checking the HRESULT values so you are missing all error-handling. For COM programming, even with Direct3D, you must check the HRESULT of every method that can return one for failure. If the return value is safe to ignore, it returns void instead.

Checking HRESULT values in old-school C/C++ programs is done with either the FAILED or SUCCEEDED macros.

hr = D3D11CreateDeviceAndSwapChain( /* ... */ *);
if (FAILED(hr))
    return false;

In most cases, a failed HRESULT is treated as a 'fast-fail' or a fatal error. In other words, the program cannot proceed if the call fails.

In other cases there can be special case handling to recover from the error perhaps by using different options. For a detailed example of that, see Anatomy of Direct3D 11 Create Device.

In older Microsoft samples based on the legacy DXUT framework, the error handling was done with macros like V or V_RETURN which did some tracing or tracing & fatal exit.

In modern C++ samples, we actually use a helper DX::ThrowIfFailed that generates a C++ exception on a failed HRESULT for the fast-fail scenario. This makes the code more streamlined and readable:

DX::ThrowIfFailed(
    D3D11CreateDeviceAndSwapChain( /* ... */ *)
);

The function itself is defined as:

#include <exception>

namespace DX
{
    inline void ThrowIfFailed(HRESULT hr)
    {
        if (FAILED(hr))
        {
            // Set a breakpoint on this line to catch DirectX API errors
            throw std::exception();
        }
    }
}

Your program should be compiled with /EHsc which is already in the default Visual Studio templates. See this topic page for more details.

From your code snippet above, you are following a pretty old-school tutorial. A lot has changed even for DirectX 11 development and most of those older tutorials are going to lead to confusion. As you are new to DirectX, I recommend you take a look at the DirectX Tool Kit and the tutorials there first. You can then come back to the older tutorials with a better understanding of 'modern' Direct3D and be able to extract more relevant information from the old stuff.

After checking all HRESULTS, the next thing you should do is enable the Direct3D debug layer which provides extra debugging information in the output window.

DWORD createDeviceFlags = 0;
#ifdef _DEBUG
    createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

hr = D3D11CreateDeviceAndSwapChain(
    nullptr, D3D_DRIVER_TYPE_HARDWARE,
    nullptr, createDeviceFlags, nullptr,
    0, D3D11_SDK_VERSION,
    &swapChainDesc, &swapChain, &dev, nullptr, &devCon);

You'll note I'm using the C++11 nullptr which is supported on Visual C++ 2010 or later instead of the old-school NULL. That's because it is typed. You were using NULL in two places in your version where the parameter wasn't actually a pointer, it's a number.

With this, you'll get a lot of feedback on basic errors that would help diagnose why in your particular case the swapchain is failing to create. I suspect it's because you are providing some values that are only meaningful for "exclusive fullscreen mode" and not for windowed mode (bufferDesc.RefreshRate). Instead, try:

DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
swapChainDesc.BufferDesc.Width = 800; 
swapChainDesc.BufferDesc.Height = 600; 
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 
swapChainDesc.SampleDesc.Count = 1; 
swapChainDesc.SampleDesc.Quality = 0; 
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 
swapChainDesc.BufferCount = 1; 
swapChainDesc.OutputWindow = hWnd; 
swapChainDesc.Windowed = TRUE; 
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; 

This works on VS 2013 or VS 2015 which will initialize the structure to 0 with the ={}; syntax. On older compilers, you'll need to use ZeroMemory like you did in your old-school code above.

Note that using D3D11_CREATE_DEVICE_DEBUG will fail on a system that lacks the debugging device which is intended only for developers to use. Details on where you obtain the debug device layer differs based on which version of Windows you are using. See Direct3D SDK Debug Layer Tricks which has a little summary table at the bottom.

Also, debugging in the presence of uninitialized variables is a huge pain. Uninitialized pointers in particular can be a huge time waster. It would help if you did the following:

static HWND hWnd = nullptr;
static IDXGISwapChain* swapChain = nullptr;
static ID3D11Device* dev = nullptr;
static ID3D11DeviceContext* devCon = nullptr;
static ID3D11RenderTargetView* renderTarget = nullptr;

Even better, instead of using raw pointers for your COM objects, you should be using a C++ smart-pointer like Microsoft::WRL::ComPtr. See this topic page for details. Our modern samples use it, and it works for classic Win32 desktop apps as well as for Windows Store, UWP, and Xbox One apps since it's just a C++ template.

cube45
  • 3,429
  • 2
  • 24
  • 35
Chuck Walbourn
  • 38,259
  • 2
  • 58
  • 81
  • I am new to directx too but I read https://stackoverflow.com/questions/20392059/use-of-if-defined-debug the negative effects of using '#ifdef _DEBUG' i am confused is it still recommended to use this debugging style or not – ma1169 May 09 '19 at 12:15