如何在多屏设置中防止Direct3D释放时出现闪烁?

4

我有一个带有小触摸屏和大显示器的亭子系统,我有一个应用程序可以在大屏幕上播放视频。但是不知何故,当Direct3D停止(应用程序退出)时,两个屏幕都会闪烁(变黑,然后恢复)。

我将问题简化为一个程序,它只是打开聚焦窗口,初始化一个Direct3D设备,然后释放它。在释放(或未释放而停止程序)时,两个屏幕都会闪烁。

请熟悉Direct3D的人查看一下,告诉我是否有做错什么?

#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;

}

注意:该程序应在任何具有相同视频卡上的2个屏幕的设置上运行,但第二个屏幕的宽度/高度可能需要进行调整(已硬编码在BackBufferWidthBackBufferHeight中)。


1
您正在使用“全屏”模式,它具有一些专门的处理方式(例如确保当Direct3D全屏应用程序退出时用户实际上可以看到某些内容)。您应该尝试使用无边框窗口的“窗口化”模式。请参见此代码,其中包含一个DirectX 11示例的 WM_SYSKEYDOWN情况。 - Chuck Walbourn
@Chuck,感谢你的提示。然而,当我尝试使用m_param.Windowed = TRUE时,视频播放的质量会降低 - 页面撕裂或帧丢失。我猜这是因为一些优化(如D3DSWAPEFFECT_COPY与后备缓冲区)没有被执行?有没有办法避免退出全屏(独占)模式时的闪烁? - Andras Tompa
1个回答

2

不确定这对您是否可行,但在尝试互联网上的各种示例时,似乎避免闪烁的唯一方法是使用带无边框窗口的窗口模式:

...

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;

非常感谢您的帮助。使用 Windows = TRUE 屏幕闪烁确实消失了,但是我发现画面质量明显降低(我在 Direct3D 设备上播放视频)。出现了页面撕裂或帧率下降的情况。我认为全屏模式下进行的一些优化在窗口模式下没有被执行。有没有什么方法可以在窗口模式下获得相同的质量?可能是我错误地设置了一些 SwapEffectBackBufferCount 参数吗? - Andras Tompa
@AndrasTompa如果您没有使用XP,请尝试启用Aero主题。 - Super-intelligent Shade

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接