You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Direct3D 11/DXGI全屏模式默认分辨率异常问题咨询

Direct3D 11/DXGI全屏启动时未使用原生分辨率问题

刚接触Direct3D 11和DXGI,启动应用全屏模式时,未使用显示器原生分辨率(2560x1440),而是异常的1280x1440。该问题不仅出现在我的项目中,还影响了部分其他游戏/程序。

最小复现代码

// the only way not to use uuidof() intrinsic
#define INITGUID

#include <windows.h>

#include <dxgi1_6.h>
#include <d3d11_4.h>

#include <cstdint>
#include <iostream>
#include <vector>
#include <functional>


HINSTANCE g_instance = nullptr;
HWND g_window_handle = nullptr;

IDXGIFactory7* g_dxgi_factory = nullptr;
IDXGIAdapter4* g_dxgi_graphics_card = nullptr;
IDXGIOutput6* g_dxgi_monitor = nullptr;
IDXGIDevice4* g_dxgi_device = nullptr;
IDXGISwapChain1* g_swapchain = nullptr;

ID3D11Device* g_d3d11_device = nullptr;
ID3D11DeviceContext* g_d3d11_device_context = nullptr;

const D3D_FEATURE_LEVEL g_feature_level = D3D_FEATURE_LEVEL_11_1;

bool g_game_is_done = false; // never changes, for demonstration

void loop(std::function<bool()> engine_loop_callback);
bool queue_is_ok(MSG* in_out_message, bool& out_done);

LRESULT CALLBACK window_procedure(HWND window_handle, UINT message_id, WPARAM w_param, LPARAM l_param);

int APIENTRY WinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance, _In_ char* command_line, _In_ int32_t show_command)
{
    //  store the application instance
    g_instance = instance;

    // set up window class
    WNDCLASSEXA window_class {};
    window_class.cbSize        = sizeof(WNDCLASSEXA);     //  size of the structure
    window_class.style         = 0;                       //  no special styles required
    window_class.lpfnWndProc   = window_procedure;        //  use our static window procedure
    window_class.cbClsExtra    = 0;                       //  no extra memory for now
    window_class.cbWndExtra    = 0;                       //  no extra memory for now
    window_class.hInstance     = g_instance;              //  the hInstance is the .exe (because zenosys is static lib)
    window_class.hIcon         = nullptr;                 //  no icon loaders for now
    window_class.hCursor       = nullptr;                 //  no cursor loaders for now
    window_class.hbrBackground = nullptr;                 //  no background color as we don't use gdi for rendering
    window_class.lpszMenuName  = nullptr;                 //  don't use menu (games don't have toolbars lmao)
    window_class.lpszClassName = "MyApplication";       //  class name to identify it in the system
    window_class.hIconSm       = nullptr;                 //  no icon loaders for now

    // use atom to store the class name
    ATOM class_atom = RegisterClassExA(&window_class);
    
    // create the window using mostly default params
    g_window_handle = CreateWindowExA(
        0,
        MAKEINTATOM(class_atom), "Title",
        0,
        CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT,
        nullptr, nullptr, g_instance, nullptr
    );
    
    // check if window is successfully created
    int32_t error;
    if (not g_window_handle)
    {
        error = GetLastError();
        return 1;
    }

    // show it to the user
    ShowWindow(g_window_handle, SW_SHOWDEFAULT);

    // create the dxgi factory and check for error.
    HRESULT result = S_OK;
    result = CreateDXGIFactory2(0, IID_IDXGIFactory7, reinterpret_cast<void**>(&g_dxgi_factory));
    if (FAILED(result))
    {
        std::cerr << "Failed on CreateDXGIFactory2!	" << std::hex << result << std::endl;
        return 1;
    }

    // vector holding all found adapters
    std::vector<IDXGIAdapter4*> adapters;

    // taken directly and a little rewritten from https://learn.microsoft.com/en-us/windows/win32/api/DXGI/nf-dxgi-idxgifactory1-enumadapters1
    {
        IDXGIAdapter4* adapter;  //  used to temporary store found adapter

        //  getting all the adapters
        bool           could_find;  //  to check wether there are adapters left
        for (uint8_t index = 0;; index++)
        {
            // get the adapter in the order of "highest performance first"
            could_find = g_dxgi_factory->EnumAdapterByGpuPreference(
                index, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE,
                IID_IDXGIAdapter4, reinterpret_cast<void**>(&adapter)
            ) != DXGI_ERROR_NOT_FOUND;

            // finish if couldn't find the next adapter
            if (not could_find)
                break;

            adapters.push_back(adapter);  //  store the found adapter
        }
    }

    // store the first graphics adapter. There's always at least one (generic windows renderer)
    g_dxgi_graphics_card = adapters[0];

    // get the first monitor of the graphics card (temporal solution)
    result = g_dxgi_graphics_card->EnumOutputs(0, reinterpret_cast<IDXGIOutput**>(&g_dxgi_monitor));
    if(FAILED(result))
    {
        std::cerr << "Failed on EnumOutputs!	" << std::hex << result << std::endl;
        return 1;
    }

    // Create Direct3D device
    result = D3D11CreateDevice(
        g_dxgi_graphics_card,
        D3D_DRIVER_TYPE_UNKNOWN,
        nullptr,
        0,
        &g_feature_level,
        1,
        D3D11_SDK_VERSION,
        reinterpret_cast<ID3D11Device**>(&g_d3d11_device),
        nullptr,
        reinterpret_cast<ID3D11DeviceContext**>(&g_d3d11_device_context)
    );
    if(FAILED(result))
    {
        std::cerr << "Failed to create Direct3D device!	" << std::hex << result << std::endl;
        return 1;
    }

    // From Direct3D device obtain its DXGI counterpart
    result = g_d3d11_device->QueryInterface<IDXGIDevice4>(&g_dxgi_device);
    if(FAILED(result))
    {
        std::cerr << "Failed to obtain DXGI device!	" << std::hex << result << std::endl;
        return 1;
    }

    // settings for regular, windowed swapchain
    DXGI_SWAP_CHAIN_DESC1 swapchain_descriptor {};
    swapchain_descriptor.Width       = 0;                               //  determine by the set window size
    swapchain_descriptor.Height      = 0;                               //  determine by the set window size
    swapchain_descriptor.Format      = DXGI_FORMAT_R16G16B16A16_FLOAT;  //  linear color format that is supported by flip swap chain
    swapchain_descriptor.Stereo      = false;                           //  true is for VR-like things (two screens rendering at the same time)
    swapchain_descriptor.SampleDesc  = {1, 0};                          //  disable MSAA
    swapchain_descriptor.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;  //  how can the swapchain be used
    swapchain_descriptor.BufferCount = 2;                                                          //  we want the front and the back buffer (sums up to 2)
    swapchain_descriptor.Scaling     = DXGI_SCALING_NONE;                                          //  stretch preserving the original rendered aspect ratio
    swapchain_descriptor.SwapEffect  = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;                           //  use flip model instead of bitblt model (more in MSDN)
    swapchain_descriptor.AlphaMode   = DXGI_ALPHA_MODE_UNSPECIFIED;                                //  IDK what is this, but setting other values doesn't work
    swapchain_descriptor.Flags       = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;                     // allows alt+tab functionality and windowed to full screen transition

    //  get display modes and take the best one (highest resolution and refresh rate)
    uint32_t number_of_display_modes = 0;

    //  take only the number of display modes
    g_dxgi_monitor->GetDisplayModeList1(
        DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_ENUM_MODES_SCALING,
        &number_of_display_modes, nullptr
    );

    // use this number to dynamically init the array of modes
    DXGI_MODE_DESC1* display_modes = new DXGI_MODE_DESC1 [number_of_display_modes];

    // fill the array
    g_dxgi_monitor->GetDisplayModeList1(
        DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_ENUM_MODES_SCALING,
        &number_of_display_modes, display_modes
    );

    // pick the best display mode from it
    DXGI_MODE_DESC1 best_display_mode = display_modes [number_of_display_modes - 1];

    // settings for fullscreen swapchain
    DXGI_SWAP_CHAIN_FULLSCREEN_DESC swapchain_fullscreen_descriptor {};
    swapchain_fullscreen_descriptor.RefreshRate      = best_display_mode.RefreshRate;       //  get the highest refresh rate from the monitor
    swapchain_fullscreen_descriptor.ScanlineOrdering = best_display_mode.ScanlineOrdering;  //  get scanline ordering from the monitor
    swapchain_fullscreen_descriptor.Scaling          = DXGI_MODE_SCALING_CENTERED;          //  never use scaling (msdn docs)
    swapchain_fullscreen_descriptor.Windowed         = false;                               //  start the application in fullscreen

    // create the swapchain
    result = g_dxgi_factory->CreateSwapChainForHwnd(
        g_d3d11_device, g_window_handle,
        &swapchain_descriptor, &swapchain_fullscreen_descriptor,
        nullptr, reinterpret_cast<IDXGISwapChain1**>(&g_swapchain)
    );
    if(FAILED(result))
    {
        std::cerr << "Couldn't create swapchain for the window	" << std::hex << result << std::endl;
        return 1;
    }

    // PAY ATTENTION HERE. WITHOUT THIS THE RESOLUTION IS INCORRECT

    // create the new mode to force the swapchain to the native res. Otherwise the resolution is incorrect
    DXGI_MODE_DESC new_resolution {};
    new_resolution.Width            = best_display_mode.Width;
    new_resolution.Height           = best_display_mode.Height;
    new_resolution.RefreshRate      = best_display_mode.RefreshRate;
    new_resolution.Format           = best_display_mode.Format;
    new_resolution.ScanlineOrdering = best_display_mode.ScanlineOrdering;
    new_resolution.Scaling          = best_display_mode.Scaling;

    //  apply the new resolution
    g_swapchain->ResizeTarget(&new_resolution);

    // END PAY ATTENTION

    // delete unused display modes
    delete[] display_modes;

    // create a call back function. It's not used here but in my project it is
    auto engine_loop = []() -> bool
    {
        // do nothing for example

        return g_game_is_done;
    };

    loop(engine_loop);

    return 0;
}

void loop(std::function<bool()> engine_loop_callback)
{
    //  create a message
    MSG  message {};

    bool done = false;

    //  as long as we are not done
    while (not done)
    {
        //  check whether we are safe to call engine loop and if we can handle message
        if (not queue_is_ok(&message, done))
            continue;

        //  do the engine suff
        done = engine_loop_callback();
    }
}

bool queue_is_ok(MSG* in_out_message, bool& out_done)
{
    //  check the message queue if it's empty
    int32_t queue_is_empty = not PeekMessageA(in_out_message, g_window_handle, 0, 0, PM_NOREMOVE);  //  returns 0 if empty so we negate it

    if (queue_is_empty)
        return true;  //  go straight to engine loop if no messages were found

    //  check the queue for errors
    switch (GetMessageA(in_out_message, g_window_handle, 0, 0))
    {
        case 0:  //  WM_QUIT -> must quit the application
        {
            out_done = true;  //  mark as done

            return false;  //  don't call engine loop
        }
        case -1:  //  there was an error
        {
            int32_t error_code = GetLastError();  //  get error

            std::cerr << "Error code:	" << std::hex << error_code << std::endl;

            PostQuitMessage(0);  //  leave the application
            return false;        //  jump to next loop iter to handle WM_QUIT
        }
        default:  //  if everything's fine
        {
            DispatchMessageA(in_out_message);  //  dispatch the message

            return true; // proceed to engine loop
        }
    }
}

LRESULT CALLBACK window_procedure(HWND window_handle, UINT message_id, WPARAM w_param, LPARAM l_param)
{
    switch (message_id)
    {
    case WM_DESTROY:
        return 0;
    
    default:
        return DefWindowProcA(window_handle, message_id, w_param, l_param);
    }
}

预期行为

期望DXGI默认使用桌面原生分辨率。

已尝试方案

  • 调试发现枚举的最后一个显示模式为最优,但DXGI默认使用异常分辨率;
  • 创建swapchain后设置分辨率可行,但会导致其他全屏程序的桌面分辨率异常;
  • 在搭载Intel HD 630的电脑上运行正常,自身搭载NVIDIA RTX 2060的电脑出现问题。

问题猜测

怀疑NVIDIA驱动向DXGI提供了错误数据,想确认是代码问题还是硬件驱动问题。


内容的提问来源于stack exchange,提问作者bub1ick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 03:40:30