13.9 KB、316行 生 Blame 履歴
1// Dear ImGui: standalone example application for Windows API + DirectX 11
2
3// Learn about Dear ImGui:
4// - FAQ https://dearimgui.com/faq
5// - Getting Started https://dearimgui.com/getting-started
6// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
7// - Introduction, links and more at the top of imgui.cpp
8
9#include "imgui.h"
10#include "imgui_impl_win32.h"
11#include "imgui_impl_dx11.h"
12#include <d3d11.h>
13#include <tchar.h>
14
15// Data
16static ID3D11Device* g_pd3dDevice = nullptr;
17static ID3D11DeviceContext* g_pd3dDeviceContext = nullptr;
18static IDXGISwapChain* g_pSwapChain = nullptr;
19static bool g_SwapChainOccluded = false;
20static UINT g_ResizeWidth = 0, g_ResizeHeight = 0;
21static ID3D11RenderTargetView* g_mainRenderTargetView = nullptr;
22
23// Forward declarations of helper functions
24bool CreateDeviceD3D(HWND hWnd);
25void CleanupDeviceD3D();
26void CreateRenderTarget();
27void CleanupRenderTarget();
28LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
29
30// Main code
31int main(int, char**)
32{
33 // Make process DPI aware and obtain main monitor scale
34 ImGui_ImplWin32_EnableDpiAwareness();
35 float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(::MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY));
36
37 // Create application window
38 WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr };
39 ::RegisterClassExW(&wc);
40 HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX11 Example", WS_OVERLAPPEDWINDOW, 100, 100, (int)(1280 * main_scale), (int)(800 * main_scale), nullptr, nullptr, wc.hInstance, nullptr);
41
42 // Initialize Direct3D
43 if (!CreateDeviceD3D(hwnd))
44 {
45 CleanupDeviceD3D();
46 ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
47 return 1;
48 }
49
50 // Show the window
51 ::ShowWindow(hwnd, SW_SHOWDEFAULT);
52 ::UpdateWindow(hwnd);
53
54 // Setup Dear ImGui context
55 IMGUI_CHECKVERSION();
56 ImGui::CreateContext();
57 ImGuiIO& io = ImGui::GetIO(); (void)io;
58 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
59 io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
60 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
61 io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
62 //io.ConfigViewportsNoAutoMerge = true;
63 //io.ConfigViewportsNoTaskBarIcon = true;
64 //io.ConfigDockingAlwaysTabBar = true;
65 //io.ConfigDockingTransparentPayload = true;
66
67 // Setup Dear ImGui style
68 ImGui::StyleColorsDark();
69 //ImGui::StyleColorsLight();
70
71 // Setup scaling
72 ImGuiStyle& style = ImGui::GetStyle();
73 style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again)
74 style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor)
75 io.ConfigDpiScaleFonts = true; // [Experimental] Automatically overwrite style.FontScaleDpi in Begin() when Monitor DPI changes. This will scale fonts but _NOT_ scale sizes/padding for now.
76 io.ConfigDpiScaleViewports = true; // [Experimental] Scale Dear ImGui and Platform Windows when Monitor DPI changes.
77
78 // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
79 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
80 {
81 style.WindowRounding = 0.0f;
82 style.Colors[ImGuiCol_WindowBg].w = 1.0f;
83 }
84
85 // Setup Platform/Renderer backends
86 ImGui_ImplWin32_Init(hwnd);
87 ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
88
89 // Load Fonts
90 // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap().
91 // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold.
92 // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
93 // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit).
94 // - Read 'docs/FONTS.md' for more instructions and details.
95 // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering.
96 // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
97 //style.FontSizeBase = 20.0f;
98 //io.Fonts->AddFontDefaultVector();
99 //io.Fonts->AddFontDefaultBitmap();
100 //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf");
101 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf");
102 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf");
103 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf");
104 //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf");
105 //IM_ASSERT(font != nullptr);
106
107 // Our state
108 bool show_demo_window = true;
109 bool show_another_window = false;
110 ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
111
112 // Main loop
113 bool done = false;
114 while (!done)
115 {
116 // Poll and handle messages (inputs, window resize, etc.)
117 // See the WndProc() function below for our to dispatch events to the Win32 backend.
118 MSG msg;
119 while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE))
120 {
121 ::TranslateMessage(&msg);
122 ::DispatchMessage(&msg);
123 if (msg.message == WM_QUIT)
124 done = true;
125 }
126 if (done)
127 break;
128
129 // Handle window being minimized or screen locked
130 if (g_SwapChainOccluded && g_pSwapChain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED)
131 {
132 ::Sleep(10);
133 continue;
134 }
135 g_SwapChainOccluded = false;
136
137 // Handle window resize (we don't resize directly in the WM_SIZE handler)
138 if (g_ResizeWidth != 0 && g_ResizeHeight != 0)
139 {
140 CleanupRenderTarget();
141 g_pSwapChain->ResizeBuffers(0, g_ResizeWidth, g_ResizeHeight, DXGI_FORMAT_UNKNOWN, 0);
142 g_ResizeWidth = g_ResizeHeight = 0;
143 CreateRenderTarget();
144 }
145
146 // Start the Dear ImGui frame
147 ImGui_ImplDX11_NewFrame();
148 ImGui_ImplWin32_NewFrame();
149 ImGui::NewFrame();
150
151 // 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!).
152 if (show_demo_window)
153 ImGui::ShowDemoWindow(&show_demo_window);
154
155 // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
156 {
157 static float f = 0.0f;
158 static int counter = 0;
159
160 ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
161
162 ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
163 ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state
164 ImGui::Checkbox("Another Window", &show_another_window);
165
166 ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
167 ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
168
169 if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
170 counter++;
171 ImGui::SameLine();
172 ImGui::Text("counter = %d", counter);
173
174 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
175 ImGui::End();
176 }
177
178 // 3. Show another simple window.
179 if (show_another_window)
180 {
181 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)
182 ImGui::Text("Hello from another window!");
183 if (ImGui::Button("Close Me"))
184 show_another_window = false;
185 ImGui::End();
186 }
187
188 // Rendering
189 ImGui::Render();
190 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 };
191 g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, nullptr);
192 g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color_with_alpha);
193 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
194
195 // Update and Render additional Platform Windows
196 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
197 {
198 ImGui::UpdatePlatformWindows();
199 ImGui::RenderPlatformWindowsDefault();
200 }
201
202 // Present
203 HRESULT hr = g_pSwapChain->Present(1, 0); // Present with vsync
204 //HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync
205 g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED);
206 }
207
208 // Cleanup
209 ImGui_ImplDX11_Shutdown();
210 ImGui_ImplWin32_Shutdown();
211 ImGui::DestroyContext();
212
213 CleanupDeviceD3D();
214 ::DestroyWindow(hwnd);
215 ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
216
217 return 0;
218}
219
220// Helper functions
221bool CreateDeviceD3D(HWND hWnd)
222{
223 // Setup swap chain
224 // This is a basic setup. Optimally could use e.g. DXGI_SWAP_EFFECT_FLIP_DISCARD and handle fullscreen mode differently. See #8979 for suggestions.
225 DXGI_SWAP_CHAIN_DESC sd;
226 ZeroMemory(&sd, sizeof(sd));
227 sd.BufferCount = 2;
228 sd.BufferDesc.Width = 0;
229 sd.BufferDesc.Height = 0;
230 sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
231 sd.BufferDesc.RefreshRate.Numerator = 60;
232 sd.BufferDesc.RefreshRate.Denominator = 1;
233 sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
234 sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
235 sd.OutputWindow = hWnd;
236 sd.SampleDesc.Count = 1;
237 sd.SampleDesc.Quality = 0;
238 sd.Windowed = TRUE;
239 sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
240
241 UINT createDeviceFlags = 0;
242 //createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
243 D3D_FEATURE_LEVEL featureLevel;
244 const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, };
245 HRESULT res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
246 if (res == DXGI_ERROR_UNSUPPORTED) // Try high-performance WARP software driver if hardware is not available.
247 res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
248 if (res != S_OK)
249 return false;
250
251 // Disable DXGI's default Alt+Enter fullscreen behavior.
252 // - You are free to leave this enabled, but it will not work properly with multiple viewports.
253 // - This must be done for all windows associated to the device. Our DX11 backend does this automatically for secondary viewports that it creates.
254 IDXGIFactory* pSwapChainFactory;
255 if (SUCCEEDED(g_pSwapChain->GetParent(IID_PPV_ARGS(&pSwapChainFactory))))
256 {
257 pSwapChainFactory->MakeWindowAssociation(hWnd, DXGI_MWA_NO_ALT_ENTER);
258 pSwapChainFactory->Release();
259 }
260
261 CreateRenderTarget();
262 return true;
263}
264
265void CleanupDeviceD3D()
266{
267 CleanupRenderTarget();
268 if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = nullptr; }
269 if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = nullptr; }
270 if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = nullptr; }
271}
272
273void CreateRenderTarget()
274{
275 ID3D11Texture2D* pBackBuffer;
276 g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
277 g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView);
278 pBackBuffer->Release();
279}
280
281void CleanupRenderTarget()
282{
283 if (g_mainRenderTargetView) { g_mainRenderTargetView->Release(); g_mainRenderTargetView = nullptr; }
284}
285
286// Forward declare message handler from imgui_impl_win32.cpp
287extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
288
289// Win32 message handler
290// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
291// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
292// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
293// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
294LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
295{
296 if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
297 return true;
298
299 switch (msg)
300 {
301 case WM_SIZE:
302 if (wParam == SIZE_MINIMIZED)
303 return 0;
304 g_ResizeWidth = (UINT)LOWORD(lParam); // Queue resize
305 g_ResizeHeight = (UINT)HIWORD(lParam);
306 return 0;
307 case WM_SYSCOMMAND:
308 if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu
309 return 0;
310 break;
311 case WM_DESTROY:
312 ::PostQuitMessage(0);
313 return 0;
314 }
315 return ::DefWindowProcW(hWnd, msg, wParam, lParam);
316}