4

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)

  • 1
    You are using 'fullscreen' mode, which has some specialized handling (like making sure the user can actually see something when a Direct3D fullscreen app exits). You should try using 'windowed' mode using a borderless window. See [this code](https://github.com/walbourn/directx-vs-templates/blob/master/d3d11game_win32/Main.cpp) in the case for ``WM_SYSKEYDOWN`` for a DirectX 11 example. – Chuck Walbourn Jul 05 '17 at 05:52
  • @Chuck, Thanks for the clue. However, when I try m_param.Windowed = TRUE, I get lowered quality on video playback - page tearing or frame drops. I assume it's because some of the optimizations (like D3DSWAPEFFECT_COPY with the backbuffer) are not being done? Is there a way to avoid the flash/flicker on exiting full-screen (exclusive) move? – Andras Tompa Jul 06 '17 at 00:56

1 Answers1

2

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;
  • Thanks very much for this. With `Windows = TRUE` the screen flicker does go away, however I get a noticeably lower quality (I play video on the Direct3D device). There are page tearing or frame drops. I think some of the optimizations being done on full-screen are not being done with windowed mode. Is there some way to do windowed mode and still get the same quality? Possibly some of the `SwapEffect` or `BackBufferCount` params I have wrong? – Andras Tompa Jul 06 '17 at 00:59
  • @AndrasTompa if you are not using XP, try enabling the Aero theme – Super-intelligent Shade Jul 06 '17 at 14:02