c++direct3ddirect3d9

How to stop Direct3D from flicker on release on multihead setup?


Have a kiosk system with a small touchscreen and a large display, and I have an app that works well playing video on the large display. For some reason, when Direct3D stops (app quits), both screens flicker (turn black, then recover).

I simplified the problem down to a program that simply opens a focus window, initializes a Direct3D device, then releases it. On release (or stopping the program without releasing), both screens flicker.

Can someone who's familiar with Direct3D look at this and tell me if I'm doing something wrong?

#include <iostream>
#include <windows.h>
#include <d3d9.h>

const char g_szClassName[] = "myWindowClass";

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

    WNDCLASSEX wc;

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = g_szClassName;
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    if (!RegisterClassEx(&wc)) {
        MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    HWND hwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        g_szClassName,
        "Focus Window",
        WS_POPUP | WS_VISIBLE,
        CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
        NULL, NULL, hInstance, NULL);

    if (hwnd == NULL) {
        MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    IDirect3D9* m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (m_pD3D == NULL) {
        MessageBox(NULL, "Failed to initialize direct3D!", "Error!", MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    // second display is 1920 x 1080

    D3DPRESENT_PARAMETERS m_param;
    ZeroMemory(&m_param, sizeof(m_param));
    m_param.BackBufferWidth = 1920;
    m_param.BackBufferHeight = 1080;
    m_param.BackBufferFormat = D3DFMT_X8R8G8B8;
    m_param.BackBufferCount = 1;
    m_param.MultiSampleType = D3DMULTISAMPLE_NONE;
    m_param.SwapEffect = D3DSWAPEFFECT_COPY;
    m_param.hDeviceWindow = hwnd;
    m_param.Windowed = FALSE;
    m_param.Flags = D3DPRESENTFLAG_VIDEO;
    m_param.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
    m_param.PresentationInterval = D3DPRESENT_INTERVAL_ONE;

    HRESULT hr;
    IDirect3DDevice9* m_pD3DD;
    hr = m_pD3D->CreateDevice(
        1,
        D3DDEVTYPE_HAL,
        hwnd,
        D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED,
        &m_param,
        &m_pD3DD
    );

    if (hr != S_OK) {
        MessageBox(NULL, "Failed to create direct3D device!", "Error!", MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    Sleep(3000);

    // flicker occurs here
    m_pD3DD->Release();
    m_pD3D->Release();

    return 0;

}

NOTE: This program should run on any setup with 2 screens on the same video card, but the second screen's width/height might need to be adjusted (it's hardcoded in BackBufferWidth and BackBufferHeight)


Solution

  • Not sure if that is workable for you, but in trying various examples around the interwebs, it seems the only way to avoid the flicker is to use windowed mode with a borderless window:

    ...
    
    HWND hwnd = CreateWindowEx(
        0,
        g_szClassName,
        "Focus Window",
        WS_POPUP | WS_VISIBLE, // <-- borderless window
        800, 0,                // <-- position on 2nd screen
        1920, 1080,            // <-- fill entire 2nd screen
        NULL, NULL, hInstance, NULL);
    
    ...
    
    D3DPRESENT_PARAMETERS m_param;
    ZeroMemory(&m_param, sizeof(m_param));
    m_param.BackBufferWidth = 1920;
    m_param.BackBufferHeight = 1080;
    m_param.BackBufferFormat = D3DFMT_X8R8G8B8;
    m_param.BackBufferCount = 1;
    m_param.MultiSampleType = D3DMULTISAMPLE_NONE;
    m_param.SwapEffect = D3DSWAPEFFECT_COPY;
    m_param.hDeviceWindow = hwnd;
    m_param.Windowed = TRUE;   // <-- use windowed mode
    m_param.Flags = D3DPRESENTFLAG_VIDEO;
    m_param.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
    m_param.PresentationInterval = D3DPRESENT_INTERVAL_ONE;