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
相关产品推荐
相关产品推荐

