トップページ > 記事閲覧
SwapChain を作り直さずに解像度を変更する方法について
名前:IRI 日時: 2022/11/20 12:33

ImGui と DxLib を使用する環境で作業をしています。 ImGui の初期化タイミングで DxLib から DirectX のデバイスを取り出し、ImGui に渡し初期化しています。 そして、ImGui 初期化完了後に DxLib::SetGraphMode を使用して解像度を変更すると、ImGui 側の描画が何も映らない状態になっていしまいます。 DirectX に詳しくないのですが、DxLib::SetGraphMode のタイミングで SwapChain の作り直しが走り、DxLib と ImGui でデバイスの状況に不整合がおき、描画が正しく行われなくなったのではと考えています。 ImGui が提供しているサンプルでは、WindowSize が変わったタイミングで、 > CleanupRenderTarget(); ※内容は関数名の通り、レンダーターゲットを開放しています。 > g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, 0); > CreateRenderTarget(); という処理を行っており、ImGui 側では SwapChain の作り直しをやっていないようです。 そこで、ImGui がやっているように DxLib でも SwapChain を作り直さずに、解像度の変更を可能にすることはできないか?という相談になります。 以下 ImGui サンプル // Dear ImGui: standalone example application for DirectX 11 // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: ttps://github.com/ocornut/imgui/tree/master/docs #include "imgui.h" #include "imgui_impl_win32.h" #include "imgui_impl_dx11.h" #include <d3d11.h> #include <tchar.h> #pragma comment(lib, "d3d11.lib") // Data static ID3D11Device* g_pd3dDevice = NULL; static ID3D11DeviceContext* g_pd3dDeviceContext = NULL; static IDXGISwapChain* g_pSwapChain = NULL; static ID3D11RenderTargetView* g_mainRenderTargetView = NULL; // Forward declarations of helper functions bool CreateDeviceD3D(HWND hWnd); void CleanupDeviceD3D(); void CreateRenderTarget(); void CleanupRenderTarget(); LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // Main code int main(int, char**) { // Create application window //ImGui_ImplWin32_EnableDpiAwareness(); WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, L"ImGui Example", NULL }; ::RegisterClassExW(&wc); HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX11 Example", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL); // Initialize Direct3D if (!CreateDeviceD3D(hwnd)) { CleanupDeviceD3D(); ::UnregisterClassW(wc.lpszClassName, wc.hInstance); return 1; } // Show the window ::ShowWindow(hwnd, SW_SHOWDEFAULT); ::UpdateWindow(hwnd); // Setup Dear ImGui context IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows //io.ConfigViewportsNoAutoMerge = true; //io.ConfigViewportsNoTaskBarIcon = true; //io.ConfigViewportsNoDefaultParent = true; //io.ConfigDockingAlwaysTabBar = true; //io.ConfigDockingTransparentPayload = true; //io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; // FIXME-DPI: Experimental. THIS CURRENTLY DOESN'T WORK AS EXPECTED. DON'T USE IN USER APP! //io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; // FIXME-DPI: Experimental. // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. ImGuiStyle& style = ImGui::GetStyle(); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { style.WindowRounding = 0.0f; style.Colors[ImGuiCol_WindowBg].w = 1.0f; } // Setup Platform/Renderer backends ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. // - Read 'docs/FONTS.md' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); //IM_ASSERT(font != NULL); // Our state bool show_demo_window = true; bool show_another_window = false; ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); // Main loop bool done = false; while (!done) { // Poll and handle messages (inputs, window resize, etc.) // See the WndProc() function below for our to dispatch events to the Win32 backend. MSG msg; while (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); if (msg.message == WM_QUIT) done = true; } if (done) break; // Start the Dear ImGui frame ImGui_ImplDX11_NewFrame(); ImGui_ImplWin32_NewFrame(); ImGui::NewFrame(); // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). if (show_demo_window) ImGui::ShowDemoWindow(&show_demo_window); // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. { static float f = 0.0f; static int counter = 0; ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state ImGui::Checkbox("Another Window", &show_another_window); ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) counter++; ImGui::SameLine(); ImGui::Text("counter = %d", counter); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); ImGui::End(); } // 3. Show another simple window. if (show_another_window) { ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) ImGui::Text("Hello from another window!"); if (ImGui::Button("Close Me")) show_another_window = false; ImGui::End(); } // Rendering ImGui::Render(); const float clear_color_with_alpha[4] = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w }; g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, NULL); g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color_with_alpha); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); // Update and Render additional Platform Windows if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); } g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync } // Cleanup ImGui_ImplDX11_Shutdown(); ImGui_ImplWin32_Shutdown(); ImGui::DestroyContext(); CleanupDeviceD3D(); ::DestroyWindow(hwnd); ::UnregisterClassW(wc.lpszClassName, wc.hInstance); return 0; } // Helper functions bool CreateDeviceD3D(HWND hWnd) { // Setup swap chain DXGI_SWAP_CHAIN_DESC sd; ZeroMemory(&sd, sizeof(sd)); sd.BufferCount = 2; sd.BufferDesc.Width = 0; sd.BufferDesc.Height = 0; sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; sd.BufferDesc.RefreshRate.Numerator = 60; sd.BufferDesc.RefreshRate.Denominator = 1; sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; sd.OutputWindow = hWnd; sd.SampleDesc.Count = 1; sd.SampleDesc.Quality = 0; sd.Windowed = TRUE; sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; UINT createDeviceFlags = 0; //createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; D3D_FEATURE_LEVEL featureLevel; const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, }; if (D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext) != S_OK) return false; CreateRenderTarget(); return true; } void CleanupDeviceD3D() { CleanupRenderTarget(); if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = NULL; } if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = NULL; } if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; } } void CreateRenderTarget() { ID3D11Texture2D* pBackBuffer; g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &g_mainRenderTargetView); pBackBuffer->Release(); } void CleanupRenderTarget() { if (g_mainRenderTargetView) { g_mainRenderTargetView->Release(); g_mainRenderTargetView = NULL; } } #ifndef WM_DPICHANGED #define WM_DPICHANGED 0x02E0 // From Windows SDK 8.1+ headers #endif // Forward declare message handler from imgui_impl_win32.cpp extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // Win32 message handler // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) return true; switch (msg) { case WM_SIZE: if (g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED) { CleanupRenderTarget(); g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, 0); CreateRenderTarget(); } return 0; case WM_SYSCOMMAND: if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu return 0; break; case WM_DESTROY: ::PostQuitMessage(0); return 0; case WM_DPICHANGED: if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) { //const int dpi = HIWORD(wParam); //printf("WM_DPICHANGED to %d (%.0f%%)\n", dpi, (float)dpi / 96.0f * 100.0f); const RECT* suggested_rect = (RECT*)lParam; ::SetWindowPos(hWnd, NULL, suggested_rect->left, suggested_rect->top, suggested_rect->right - suggested_rect->left, suggested_rect->bottom - suggested_rect->top, SWP_NOZORDER | SWP_NOACTIVATE); } break; } return ::DefWindowProc(hWnd, msg, wParam, lParam); }
メンテ

Page: 1 |

Re: SwapChain を作り直さずに解像度を変更する方法について ( No.1 )
名前:管理人 日時:2022/11/20 13:48

SetGraphMode を実行する前に SetChangeScreenModeGraphicsSystemResetFlag( FALSE ); を実行しておくと SwapChain の作り直しが発生しないので、よろしければお試しください m(_ _)m // 画面モード変更時( とウインドウモード変更時 )にグラフィックスシステムの設定やグラフィックハンドルを // リセットするかどうかを設定する( Flag TRUE:リセットする( デフォルト ) FALSE:リセットしない ) int SetChangeScreenModeGraphicsSystemResetFlag( int Flag ) ; ただ、注釈の通り通常の SetGraphMode ではそれまでに作成していたグラフィックハンドルが削除されますが、 SetChangeScreenModeGraphicsSystemResetFlag( FALSE ); を実行した状態では SetGraphMode を実行しても グラフィックハンドルが削除されませんのでご注意ください m(_ _)m
メンテ
Re: SwapChain を作り直さずに解像度を変更する方法について ( No.2 )
名前:IRI 日時:2022/11/20 15:20

管理人さん > SetGraphMode を実行する前に SetChangeScreenModeGraphicsSystemResetFlag( FALSE ); を実行しておくと > SwapChain の作り直しが発生しないので、よろしければお試しください m(_ _)m DxLib::SetChangeScreenModeGraphicsSystemResetFlag( FALSE ) を使用した場合、DxLib::GetScreenState から得られるサイズは最初の値から変わらないようです。 DxLib::SetGraphMode で大きくなるように設定した場合、実際の描画画面も引き伸ばされてしまっています。 こちらの問題はさらに別の設定がいるのでしょか?
メンテ
Re: SwapChain を作り直さずに解像度を変更する方法について ( No.3 )
名前:IRI 日時:2022/11/20 15:31

>DxLib::SetChangeScreenModeGraphicsSystemResetFlag( FALSE ) を使用した場合、DxLib::GetScreenState から得られるサイズは最初の値から変わらないようです。 >DxLib::SetGraphMode で大きくなるように設定した場合、実際の描画画面も引き伸ばされてしまっています。 こちらは、自分がやっている SetWindowSize が原因でした。。。 管理人さん 疑似的なフルスクリーンを行うため、SetGraphMode でモニター解像度と同じ数値を設定し、 SetWindowPosition() SetWindowSize() でモニターの4隅にウインドウの角を合わせて、画面一杯にウインドウを表示したいのですが、これは可能なのでしょうか?
メンテ
Re: SwapChain を作り直さずに解像度を変更する方法について ( No.4 )
名前:IRI 日時:2022/11/20 21:18

管理人さん 色々といじっていたのですが自己解決しました。 > DxLib::SetChangeScreenModeGraphicsSystemResetFlag(false); > DxLib::SetGraphMode(x, y, 32); を呼び出した後に > SetWindowPosition(pos_); > SetWindowSize(size_); を実行していたのですが、こちらの処理で実際のディスプレイよりも少しだけウインドウサイズを大きくしていて、別のモニターにウインドウの隅が少しだけ映る状態になっていました。 この状態になっていると、画面が引き伸ばされるて表示されるようです。 今は、ほかのディスプレイにはみ出ないウインドウサイズにすることで問題なく表示されています。 管理人さん ありがとうございました。
メンテ

Page: 1 |

題名
名前
コメント
パスワード (記事メンテ時に使用)

   クッキー保存