Search code examples
c++winapidirectxsdldxgi

Create DXGI Swapchain for SDL2 window


I'm trying to create a DXGI swapchain for a window created by SDL2 (v2.0.18), but every time I call the IDXGIFactory5::CreateSwapChainForHwnd method, I get the E_INVALIDARG error.

Steps I'm Following

First, I create the SDL window.

Then I go about creating the swapchain, following these steps:

  1. Determine the index of the adapter containing the window
  2. Create a D3D11Device
  3. Create a DXGI swapchain using said device via the CreateSwapChainForHwnd method, passing the underlying HWND handle in the SDL_Window structure

I'll now share the code I'm using to accomplish these steps

Creating the SDL window

int posX = 0, int posY = 0, int width = 1920, int height = 1080;

// Init video subsystem
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
                "SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
                SDL_GetError());
        return false;
}

window = SDL_CreateWindow("H264 HW Decoding with D3D11 Rendering to SDL window",
        posX,
        posY,
        width,
        height,
        SDL_WINDOW_ALLOW_HIGHDPI
);

if (!window) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
                "SDL_CreateWindow() failed: %s",
                SDL_GetError());

        SDL_QuitSubSystem(SDL_INIT_VIDEO);
}

Determine the index of the adapter containing the window

if (!SDL_DXGIGetOutputInfo(SDL_GetWindowDisplayIndex(params->window),
    &adapterIndex, &outputIndex))
{
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
        "SDL_DXGIGetOutputInfo() failed: %s",
        SDL_GetError());
    return false;
}

Create a D3D11Device

IDXGIFactory5* Factory = nullptr;
bool success = false;
IDXGIAdapter1* adapter = nullptr;
HRESULT hr;

hr = CreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG ,__uuidof(IDXGIFactory5), (void**)&Factory);
assert(!FAILED(hr));

hr = Factory->EnumAdapters1(adapterIndex, &adapter);
if (FAILED(hr))
{
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
        "IDXGIFactory::EnumAdapters1() failed: %x",
        hr);
    goto Exit;
}

hr = adapter->GetDesc1(&adapterDesc);
if (FAILED(hr))
{
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
        "IDXGIAdapter::GetDesc() failed: %x",
        hr);
    goto Exit;
}

if (adapterDesc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
{
    // Skip the WARP device. We know it will fail.
    goto Exit;
}

hr = D3D11CreateDevice(adapter,
    D3D_DRIVER_TYPE_UNKNOWN,
    nullptr,
    D3D11_CREATE_DEVICE_DEBUG | D3D11_CREATE_DEVICE_BGRA_SUPPORT,
    nullptr,
    0,
    D3D11_SDK_VERSION,
    &Device,
    nullptr,
    &DeviceContext);

Create a DXGI swapchain using said device via the CreateSwapChainForHwnd method, passing the underlying HWND handle in the SDL_Window structure

// Fill the swapchain description structure
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
swapChainDesc.Stereo = FALSE;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
swapChainDesc.Flags = 0;
swapChainDesc.BufferCount = 5;
// Use the current window size as the swapchain size
SDL_GetWindowSize(window, (int*)&swapChainDesc.Width, (int*)&swapChainDesc.Height);
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;

// Get the HWND from SDL
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
SDL_GetWindowWMInfo(params->window, &info);
SDL_assert(info.subsystem == SDL_SYSWWINDOWS);
assert(IsWindow(info.info.win.window));

// Create the swapchain and associate it to the SDL window
IDXGISwapChain1* swapChain;
hr = Factory->CreateSwapChainForHwnd(Device,
    info.info.win.window,
    &swapChainDesc,
    nullptr,
    nullptr,
    &swapChain);

Debug Layer Output

I've turned the DXGI debug layer on, and this is the output from it

D3D11 INFO: Create ID3D11Context: Name="unnamed", Addr=0x000001B3F7978990, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #2097225: CREATE_CONTEXT]
D3D11 INFO: Create ID3DDeviceContextState: Name="unnamed", Addr=0x000001B3F7996B50, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #3145735: CREATE_DEVICECONTEXTSTATE]
D3D11 INFO: Create ID3D11BlendState: Name="unnamed", Addr=0x000001B3F79963A0, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #2097270: CREATE_BLENDSTATE]
D3D11 INFO: Create ID3D11DepthStencilState: Name="unnamed", Addr=0x000001B3F79965E0, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #2097273: CREATE_DEPTHSTENCILSTATE]
D3D11 INFO: Create ID3D11RasterizerState: Name="unnamed", Addr=0x000001B3F79A1F00, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #2097276: CREATE_RASTERIZERSTATE]
D3D11 INFO: Create ID3D11Sampler: Name="unnamed", Addr=0x000001B3F79A2140, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #2097267: CREATE_SAMPLER]
D3D11 INFO: Create ID3D11Query: Name="unnamed", Addr=0x000001B3F79A2380, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #2097279: CREATE_QUERY]
D3D11 INFO: Create ID3D11Fence: Name="unnamed", Addr=0x000001B3F79A2600, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #3146250: CREATE_FENCE]
D3D11 INFO: Destroy ID3D11Fence: Name="unnamed", Addr=0x000001B3F79A2600 [ STATE_CREATION INFO #3146252: DESTROY_FENCE]
D3D11 INFO: Create ID3D11Texture2D: Name="unnamed", Addr=0x000001B3FA034FF0, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #2097234: CREATE_TEXTURE2D]
D3D11 INFO: Create ID3D11Texture2D: Name="unnamed", Addr=0x000001B3FA035430, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #2097234: CREATE_TEXTURE2D]
D3D11 INFO: Create ID3D11Texture2D: Name="unnamed", Addr=0x000001B3FA035870, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #2097234: CREATE_TEXTURE2D]
D3D11 INFO: Create ID3D11Texture2D: Name="unnamed", Addr=0x000001B3FA035CB0, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #2097234: CREATE_TEXTURE2D]
D3D11 INFO: Create ID3D11Texture2D: Name="unnamed", Addr=0x000001B3FA0360F0, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #2097234: CREATE_TEXTURE2D]
D3D11 INFO: Create ID3D11Texture2D: Name="unnamed", Addr=0x000001B3FA0870F0, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #2097234: CREATE_TEXTURE2D]
D3D11 INFO: Create ID3D11Texture2D: Name="unnamed", Addr=0x000001B3FA08B540, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #2097234: CREATE_TEXTURE2D]
D3D11 INFO: Create ID3D11Texture2D: Name="unnamed", Addr=0x000001B3FA08B980, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #2097234: CREATE_TEXTURE2D]
D3D11 INFO: Create ID3D11Texture2D: Name="unnamed", Addr=0x000001B3FA08C1D0, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #2097234: CREATE_TEXTURE2D]
D3D11 INFO: Create ID3D11Texture2D: Name="unnamed", Addr=0x000001B3FA08C610, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #2097234: CREATE_TEXTURE2D]
D3D11 INFO: Create ID3D11Fence: Name="unnamed", Addr=0x000001B3FA04E870, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #3146250: CREATE_FENCE]
D3D11 INFO: Create ID3D11Fence: Name="unnamed", Addr=0x000001B3FA090370, ExtRef=1, IntRef=0 [ STATE_CREATION INFO #3146250: CREATE_FENCE]
D3D11 INFO: Destroy ID3D11Fence: Name="unnamed", Addr=0x000001B3FA04E870 [ STATE_CREATION INFO #3146252: DESTROY_FENCE]
D3D11 INFO: Destroy ID3D11Fence: Name="unnamed", Addr=0x000001B3FA090370 [ STATE_CREATION INFO #3146252: DESTROY_FENCE]
Exception thrown at 0x00007FFD803AFE7C in decode_video_ffmpeg_example.exe: Microsoft C++ exception: _com_error at memory location 0x00000079E9AFEF88.
Exception thrown at 0x00007FFD803AFE7C in decode_video_ffmpeg_example.exe: Microsoft C++ exception: _com_error at memory location 0x00000079E9AFF000.
D3D11 INFO: Destroy ID3D11Texture2D: Name="unnamed", Addr=0x000001B3FA034FF0 [ STATE_CREATION INFO #2097236: DESTROY_TEXTURE2D]
D3D11 INFO: Destroy ID3D11Texture2D: Name="unnamed", Addr=0x000001B3FA035430 [ STATE_CREATION INFO #2097236: DESTROY_TEXTURE2D]
D3D11 INFO: Destroy ID3D11Texture2D: Name="unnamed", Addr=0x000001B3FA035870 [ STATE_CREATION INFO #2097236: DESTROY_TEXTURE2D]
D3D11 INFO: Destroy ID3D11Texture2D: Name="unnamed", Addr=0x000001B3FA035CB0 [ STATE_CREATION INFO #2097236: DESTROY_TEXTURE2D]
D3D11 INFO: Destroy ID3D11Texture2D: Name="unnamed", Addr=0x000001B3FA0360F0 [ STATE_CREATION INFO #2097236: DESTROY_TEXTURE2D]
D3D11 INFO: Destroy ID3D11Texture2D: Name="DXGI cross-adapter flip proxy (dGPU opened)", Addr=0x000001B3FA0870F0 [ STATE_CREATION INFO #2097236: DESTROY_TEXTURE2D]
D3D11 INFO: Destroy ID3D11Texture2D: Name="DXGI cross-adapter flip proxy (dGPU opened)", Addr=0x000001B3FA08B540 [ STATE_CREATION INFO #2097236: DESTROY_TEXTURE2D]
D3D11 INFO: Destroy ID3D11Texture2D: Name="DXGI cross-adapter flip proxy (dGPU opened)", Addr=0x000001B3FA08B980 [ STATE_CREATION INFO #2097236: DESTROY_TEXTURE2D]
D3D11 INFO: Destroy ID3D11Texture2D: Name="DXGI cross-adapter flip proxy (dGPU opened)", Addr=0x000001B3FA08C1D0 [ STATE_CREATION INFO #2097236: DESTROY_TEXTURE2D]
D3D11 INFO: Destroy ID3D11Texture2D: Name="DXGI cross-adapter flip proxy (dGPU opened)", Addr=0x000001B3FA08C610 [ STATE_CREATION INFO #2097236: DESTROY_TEXTURE2D]

The DirectX11 debug layer output is juts the following message

E_INVALIDARG One or more arguments are invalid.

Things I've already tried

  1. Using different versions of the DXGI factory (IDXGIFactory2 and IDXGIFactory3)
  2. Creating different types of D3D11Device (like D3D_DRIVER_TYPE_HARDWARE and D3D_DRIVER_TYPE_REFERENCE)
  3. Instead of calling SDL_CreateWindow, creating a native window with these style flags WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, and then creating the SDL window using the SDL_CreateWindowFrom function
  4. Following this tutorial from Microsoft, where the CreateSwapchainForHwnd is used. This program gives me no problems; the method executes successfully and the window is displayed correctly
  5. Changing the values set in the DXGI_SWAP_CHAIN_DESC1 structure (output format, swap effect, scaling, buffer count, window size)

My Environment

OS: Windows 11
Processor: AMD Ryzen 9 5900HX with Radeon Graphics
Graphic Adapters: NVIDIA RTX3060 Laptop GPU; AMD Radeon Graphics (integrated)

Solution

  • I've solved my problem. It had nothing to do with SDL or the parameters of the swapchain.

    There was an old version of dxgi.dll file in the same directory as the compiled project. This DLL was not compatible with my sample project. After removing this file the code executed correctly. Embarrassing...