summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2021-04-10 21:13:34 -0700
committerrtk0c <[email protected]>2021-04-10 21:13:34 -0700
commit568fcc1dfe40c37b57b7baa2dea93b291d3fa956 (patch)
tree826b410c502a950c1d4804d351959da914003b36
parent4303d0be47526b35e5bb3e3be001da227dae5d96 (diff)
Add dx11, dx12, and vulkan backends
-rw-r--r--3rdparty/imgui/backend/imgui_impl_win32.cpp542
-rw-r--r--3rdparty/imgui/backend/imgui_impl_win32.h41
-rw-r--r--core/CMakeLists.txt53
-rw-r--r--core/src/Entrypoint/Backend.hpp22
-rw-r--r--core/src/Entrypoint/Backend_DirectX11.cpp238
-rw-r--r--core/src/Entrypoint/Backend_DirectX12.cpp454
-rw-r--r--core/src/Entrypoint/Backend_Metal.mm34
-rw-r--r--core/src/Entrypoint/Backend_OpenGL2.cpp98
-rw-r--r--core/src/Entrypoint/Backend_OpenGL3.cpp113
-rw-r--r--core/src/Entrypoint/Backend_Vulkan.cpp424
-rw-r--r--core/src/Entrypoint/Common.cpp14
-rw-r--r--core/src/Entrypoint/Common.hpp19
-rw-r--r--core/src/Entrypoint/DirectX11.cpp13
-rw-r--r--core/src/Entrypoint/DirectX11.hpp11
-rw-r--r--core/src/Entrypoint/DirectX12.cpp13
-rw-r--r--core/src/Entrypoint/DirectX12.hpp11
-rw-r--r--core/src/Entrypoint/Metal.hpp11
-rw-r--r--core/src/Entrypoint/Metal.mm13
-rw-r--r--core/src/Entrypoint/OpenGL2.cpp87
-rw-r--r--core/src/Entrypoint/OpenGL2.hpp15
-rw-r--r--core/src/Entrypoint/OpenGL3.cpp102
-rw-r--r--core/src/Entrypoint/OpenGL3.hpp14
-rw-r--r--core/src/Entrypoint/Vulkan.cpp13
-rw-r--r--core/src/Entrypoint/Vulkan.hpp11
-rw-r--r--core/src/Entrypoint/main.cpp87
25 files changed, 2034 insertions, 419 deletions
diff --git a/3rdparty/imgui/backend/imgui_impl_win32.cpp b/3rdparty/imgui/backend/imgui_impl_win32.cpp
new file mode 100644
index 0000000..39e381f
--- /dev/null
+++ b/3rdparty/imgui/backend/imgui_impl_win32.cpp
@@ -0,0 +1,542 @@
+// dear imgui: Platform Backend for Windows (standard windows API for 32 and 64 bits applications)
+// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
+
+// Implemented features:
+// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
+// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+// [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
+// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
+
+// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
+// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
+// Read online: https://github.com/ocornut/imgui/tree/master/docs
+
+#include "imgui.h"
+#include "imgui_impl_win32.h"
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <tchar.h>
+#include <dwmapi.h>
+
+// Configuration flags to add in your imconfig.h file:
+//#define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD // Disable gamepad support (this used to be meaningful before <1.81) but we know load XInput dynamically so the option is less relevant now.
+
+// Using XInput for gamepad (will load DLL dynamically)
+#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
+#include <XInput.h>
+typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*);
+typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*);
+#endif
+
+// CHANGELOG
+// (minor and older changes stripped away, please see git history for details)
+// 2021-03-23: Inputs: Clearing keyboard down array when losing focus (WM_KILLFOCUS).
+// 2021-02-18: Added ImGui_ImplWin32_EnableAlphaCompositing(). Non Visual Studio users will need to link with dwmapi.lib (MinGW/gcc: use -ldwmapi).
+// 2021-02-17: Fixed ImGui_ImplWin32_EnableDpiAwareness() attempting to get SetProcessDpiAwareness from shcore.dll on Windows 8 whereas it is only supported on Windows 8.1.
+// 2021-01-25: Inputs: Dynamically loading XInput DLL.
+// 2020-12-04: Misc: Fixed setting of io.DisplaySize to invalid/uninitialized data when after hwnd has been closed.
+// 2020-03-03: Inputs: Calling AddInputCharacterUTF16() to support surrogate pairs leading to codepoint >= 0x10000 (for more complete CJK inputs)
+// 2020-02-17: Added ImGui_ImplWin32_EnableDpiAwareness(), ImGui_ImplWin32_GetDpiScaleForHwnd(), ImGui_ImplWin32_GetDpiScaleForMonitor() helper functions.
+// 2020-01-14: Inputs: Added support for #define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD/IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT.
+// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
+// 2019-05-11: Inputs: Don't filter value from WM_CHAR before calling AddInputCharacter().
+// 2019-01-17: Misc: Using GetForegroundWindow()+IsChild() instead of GetActiveWindow() to be compatible with windows created in a different thread or parent.
+// 2019-01-17: Inputs: Added support for mouse buttons 4 and 5 via WM_XBUTTON* messages.
+// 2019-01-15: Inputs: Added support for XInput gamepads (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
+// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
+// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
+// 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads).
+// 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples.
+// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag.
+// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling).
+// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
+// 2018-02-06: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).
+// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
+// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
+// 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert.
+// 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag.
+// 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read.
+// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging.
+// 2016-11-12: Inputs: Only call Win32 ::SetCursor(NULL) when io.MouseDrawCursor is set.
+
+// Win32 Data
+static HWND g_hWnd = NULL;
+static INT64 g_Time = 0;
+static INT64 g_TicksPerSecond = 0;
+static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT;
+static bool g_HasGamepad = false;
+static bool g_WantUpdateHasGamepad = true;
+
+// XInput DLL and functions
+#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
+static HMODULE g_XInputDLL = NULL;
+static PFN_XInputGetCapabilities g_XInputGetCapabilities = NULL;
+static PFN_XInputGetState g_XInputGetState = NULL;
+#endif
+
+// Functions
+bool ImGui_ImplWin32_Init(void* hwnd)
+{
+ if (!::QueryPerformanceFrequency((LARGE_INTEGER*)&g_TicksPerSecond))
+ return false;
+ if (!::QueryPerformanceCounter((LARGE_INTEGER*)&g_Time))
+ return false;
+
+ // Setup backend capabilities flags
+ g_hWnd = (HWND)hwnd;
+ ImGuiIO& io = ImGui::GetIO();
+ io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
+ io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
+ io.BackendPlatformName = "imgui_impl_win32";
+ io.ImeWindowHandle = hwnd;
+
+ // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime.
+ io.KeyMap[ImGuiKey_Tab] = VK_TAB;
+ io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT;
+ io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT;
+ io.KeyMap[ImGuiKey_UpArrow] = VK_UP;
+ io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN;
+ io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR;
+ io.KeyMap[ImGuiKey_PageDown] = VK_NEXT;
+ io.KeyMap[ImGuiKey_Home] = VK_HOME;
+ io.KeyMap[ImGuiKey_End] = VK_END;
+ io.KeyMap[ImGuiKey_Insert] = VK_INSERT;
+ io.KeyMap[ImGuiKey_Delete] = VK_DELETE;
+ io.KeyMap[ImGuiKey_Backspace] = VK_BACK;
+ io.KeyMap[ImGuiKey_Space] = VK_SPACE;
+ io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
+ io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
+ io.KeyMap[ImGuiKey_KeyPadEnter] = VK_RETURN;
+ io.KeyMap[ImGuiKey_A] = 'A';
+ io.KeyMap[ImGuiKey_C] = 'C';
+ io.KeyMap[ImGuiKey_V] = 'V';
+ io.KeyMap[ImGuiKey_X] = 'X';
+ io.KeyMap[ImGuiKey_Y] = 'Y';
+ io.KeyMap[ImGuiKey_Z] = 'Z';
+
+ // Dynamically load XInput library
+#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
+ const char* xinput_dll_names[] =
+ {
+ "xinput1_4.dll", // Windows 8+
+ "xinput1_3.dll", // DirectX SDK
+ "xinput9_1_0.dll", // Windows Vista, Windows 7
+ "xinput1_2.dll", // DirectX SDK
+ "xinput1_1.dll" // DirectX SDK
+ };
+ for (int n = 0; n < IM_ARRAYSIZE(xinput_dll_names); n++)
+ if (HMODULE dll = ::LoadLibraryA(xinput_dll_names[n]))
+ {
+ g_XInputDLL = dll;
+ g_XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities");
+ g_XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState");
+ break;
+ }
+#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
+
+ return true;
+}
+
+void ImGui_ImplWin32_Shutdown()
+{
+ // Unload XInput library
+#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
+ if (g_XInputDLL)
+ ::FreeLibrary(g_XInputDLL);
+ g_XInputDLL = NULL;
+ g_XInputGetCapabilities = NULL;
+ g_XInputGetState = NULL;
+#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
+
+ g_hWnd = NULL;
+ g_Time = 0;
+ g_TicksPerSecond = 0;
+ g_LastMouseCursor = ImGuiMouseCursor_COUNT;
+ g_HasGamepad = false;
+ g_WantUpdateHasGamepad = true;
+}
+
+static bool ImGui_ImplWin32_UpdateMouseCursor()
+{
+ ImGuiIO& io = ImGui::GetIO();
+ if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
+ return false;
+
+ ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
+ if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
+ {
+ // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
+ ::SetCursor(NULL);
+ }
+ else
+ {
+ // Show OS mouse cursor
+ LPTSTR win32_cursor = IDC_ARROW;
+ switch (imgui_cursor)
+ {
+ case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break;
+ case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break;
+ case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break;
+ case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break;
+ case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break;
+ case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break;
+ case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break;
+ case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break;
+ case ImGuiMouseCursor_NotAllowed: win32_cursor = IDC_NO; break;
+ }
+ ::SetCursor(::LoadCursor(NULL, win32_cursor));
+ }
+ return true;
+}
+
+static void ImGui_ImplWin32_UpdateMousePos()
+{
+ ImGuiIO& io = ImGui::GetIO();
+ IM_ASSERT(g_hWnd != 0);
+
+ // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
+ if (io.WantSetMousePos)
+ {
+ POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
+ if (::ClientToScreen(g_hWnd, &pos))
+ ::SetCursorPos(pos.x, pos.y);
+ }
+
+ // Set mouse position
+ io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
+ POINT pos;
+ if (HWND active_window = ::GetForegroundWindow())
+ if (active_window == g_hWnd || ::IsChild(active_window, g_hWnd))
+ if (::GetCursorPos(&pos) && ::ScreenToClient(g_hWnd, &pos))
+ io.MousePos = ImVec2((float)pos.x, (float)pos.y);
+}
+
+// Gamepad navigation mapping
+static void ImGui_ImplWin32_UpdateGamepads()
+{
+#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
+ ImGuiIO& io = ImGui::GetIO();
+ memset(io.NavInputs, 0, sizeof(io.NavInputs));
+ if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
+ return;
+
+ // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow.
+ // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE.
+ if (g_WantUpdateHasGamepad)
+ {
+ XINPUT_CAPABILITIES caps;
+ g_HasGamepad = g_XInputGetCapabilities ? (g_XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false;
+ g_WantUpdateHasGamepad = false;
+ }
+
+ io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
+ XINPUT_STATE xinput_state;
+ if (g_HasGamepad && g_XInputGetState && g_XInputGetState(0, &xinput_state) == ERROR_SUCCESS)
+ {
+ const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad;
+ io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
+
+#define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; }
+#define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; }
+ MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A
+ MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B
+ MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X
+ MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y); // Triangle / Y
+ MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); // D-Pad Left
+ MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); // D-Pad Right
+ MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP); // D-Pad Up
+ MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN); // D-Pad Down
+ MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
+ MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
+ MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
+ MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
+ MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
+ MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
+ MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
+ MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767);
+#undef MAP_BUTTON
+#undef MAP_ANALOG
+ }
+#endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
+}
+
+void ImGui_ImplWin32_NewFrame()
+{
+ ImGuiIO& io = ImGui::GetIO();
+ IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer backend. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
+
+ // Setup display size (every frame to accommodate for window resizing)
+ RECT rect = { 0, 0, 0, 0 };
+ ::GetClientRect(g_hWnd, &rect);
+ io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));
+
+ // Setup time step
+ INT64 current_time = 0;
+ ::QueryPerformanceCounter((LARGE_INTEGER*)&current_time);
+ io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond;
+ g_Time = current_time;
+
+ // Read keyboard modifiers inputs
+ io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0;
+ io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0;
+ io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0;
+ io.KeySuper = false;
+ // io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below.
+
+ // Update OS mouse position
+ ImGui_ImplWin32_UpdateMousePos();
+
+ // Update OS mouse cursor with the cursor requested by imgui
+ ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
+ if (g_LastMouseCursor != mouse_cursor)
+ {
+ g_LastMouseCursor = mouse_cursor;
+ ImGui_ImplWin32_UpdateMouseCursor();
+ }
+
+ // Update game controllers (if enabled and available)
+ ImGui_ImplWin32_UpdateGamepads();
+}
+
+// Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions.
+#ifndef WM_MOUSEHWHEEL
+#define WM_MOUSEHWHEEL 0x020E
+#endif
+#ifndef DBT_DEVNODES_CHANGED
+#define DBT_DEVNODES_CHANGED 0x0007
+#endif
+
+// Win32 message handler (process Win32 mouse/keyboard inputs, etc.)
+// Call from your application's message handler.
+// When implementing your own backend, 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.
+// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
+// Generally you may always pass all inputs to Dear ImGui, and hide them from your application based on those two flags.
+// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinates when dragging mouse outside of our window bounds.
+// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag.
+#if 0
+// Copy this line into your .cpp file to forward declare the function.
+extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+#endif
+IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ if (ImGui::GetCurrentContext() == NULL)
+ return 0;
+
+ ImGuiIO& io = ImGui::GetIO();
+ switch (msg)
+ {
+ case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
+ case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
+ case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
+ case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
+ {
+ int button = 0;
+ if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; }
+ if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; }
+ if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; }
+ if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
+ if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL)
+ ::SetCapture(hwnd);
+ io.MouseDown[button] = true;
+ return 0;
+ }
+ case WM_LBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_XBUTTONUP:
+ {
+ int button = 0;
+ if (msg == WM_LBUTTONUP) { button = 0; }
+ if (msg == WM_RBUTTONUP) { button = 1; }
+ if (msg == WM_MBUTTONUP) { button = 2; }
+ if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
+ io.MouseDown[button] = false;
+ if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd)
+ ::ReleaseCapture();
+ return 0;
+ }
+ case WM_MOUSEWHEEL:
+ io.MouseWheel += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
+ return 0;
+ case WM_MOUSEHWHEEL:
+ io.MouseWheelH += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
+ return 0;
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ if (wParam < 256)
+ io.KeysDown[wParam] = 1;
+ return 0;
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ if (wParam < 256)
+ io.KeysDown[wParam] = 0;
+ return 0;
+ case WM_KILLFOCUS:
+ memset(io.KeysDown, 0, sizeof(io.KeysDown));
+ return 0;
+ case WM_CHAR:
+ // You can also use ToAscii()+GetKeyboardState() to retrieve characters.
+ if (wParam > 0 && wParam < 0x10000)
+ io.AddInputCharacterUTF16((unsigned short)wParam);
+ return 0;
+ case WM_SETCURSOR:
+ if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor())
+ return 1;
+ return 0;
+ case WM_DEVICECHANGE:
+ if ((UINT)wParam == DBT_DEVNODES_CHANGED)
+ g_WantUpdateHasGamepad = true;
+ return 0;
+ }
+ return 0;
+}
+
+
+//--------------------------------------------------------------------------------------------------------
+// DPI-related helpers (optional)
+//--------------------------------------------------------------------------------------------------------
+// - Use to enable DPI awareness without having to create an application manifest.
+// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps.
+// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc.
+// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime,
+// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies.
+//---------------------------------------------------------------------------------------------------------
+// This is the scheme successfully used by GLFW (from which we borrowed some of the code) and other apps aiming to be highly portable.
+// ImGui_ImplWin32_EnableDpiAwareness() is just a helper called by main.cpp, we don't call it automatically.
+// If you are trying to implement your own backend for your own engine, you may ignore that noise.
+//---------------------------------------------------------------------------------------------------------
+
+// Implement some of the functions and types normally declared in recent Windows SDK.
+#if !defined(_versionhelpers_H_INCLUDED_) && !defined(_INC_VERSIONHELPERS)
+static BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp)
+{
+ OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, { 0 }, sp, 0, 0, 0, 0 };
+ DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR;
+ ULONGLONG cond = ::VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL);
+ cond = ::VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL);
+ cond = ::VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
+ return ::VerifyVersionInfoW(&osvi, mask, cond);
+}
+#define IsWindowsVistaOrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0600), LOBYTE(0x0600), 0) // _WIN32_WINNT_VISTA
+#define IsWindows8OrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0602), LOBYTE(0x0602), 0) // _WIN32_WINNT_WIN8
+#define IsWindows8Point1OrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0603), LOBYTE(0x0603), 0) // _WIN32_WINNT_WINBLUE
+#endif
+
+#ifndef DPI_ENUMS_DECLARED
+typedef enum { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS;
+typedef enum { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE;
+#endif
+#ifndef _DPI_AWARENESS_CONTEXTS_
+DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
+#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE (DPI_AWARENESS_CONTEXT)-3
+#endif
+#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
+#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 (DPI_AWARENESS_CONTEXT)-4
+#endif
+typedef HRESULT(WINAPI* PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); // Shcore.lib + dll, Windows 8.1+
+typedef HRESULT(WINAPI* PFN_GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); // Shcore.lib + dll, Windows 8.1+
+typedef DPI_AWARENESS_CONTEXT(WINAPI* PFN_SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); // User32.lib + dll, Windows 10 v1607+ (Creators Update)
+
+// Helper function to enable DPI awareness without setting up a manifest
+void ImGui_ImplWin32_EnableDpiAwareness()
+{
+ // if (IsWindows10OrGreater()) // This needs a manifest to succeed. Instead we try to grab the function pointer!
+ {
+ static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process
+ if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext"))
+ {
+ SetThreadDpiAwarenessContextFn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
+ return;
+ }
+ }
+ if (IsWindows8Point1OrGreater())
+ {
+ static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process
+ if (PFN_SetProcessDpiAwareness SetProcessDpiAwarenessFn = (PFN_SetProcessDpiAwareness)::GetProcAddress(shcore_dll, "SetProcessDpiAwareness"))
+ {
+ SetProcessDpiAwarenessFn(PROCESS_PER_MONITOR_DPI_AWARE);
+ return;
+ }
+ }
+#if _WIN32_WINNT >= 0x0600
+ ::SetProcessDPIAware();
+#endif
+}
+
+#if defined(_MSC_VER) && !defined(NOGDI)
+#pragma comment(lib, "gdi32") // Link with gdi32.lib for GetDeviceCaps(). MinGW will require linking with '-lgdi32'
+#endif
+
+float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor)
+{
+ UINT xdpi = 96, ydpi = 96;
+ static BOOL bIsWindows8Point1OrGreater = IsWindows8Point1OrGreater();
+ if (bIsWindows8Point1OrGreater)
+ {
+ static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process
+ if (PFN_GetDpiForMonitor GetDpiForMonitorFn = (PFN_GetDpiForMonitor)::GetProcAddress(shcore_dll, "GetDpiForMonitor"))
+ GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi);
+ }
+#ifndef NOGDI
+ else
+ {
+ const HDC dc = ::GetDC(NULL);
+ xdpi = ::GetDeviceCaps(dc, LOGPIXELSX);
+ ydpi = ::GetDeviceCaps(dc, LOGPIXELSY);
+ ::ReleaseDC(NULL, dc);
+ }
+#endif
+ IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert!
+ return xdpi / 96.0f;
+}
+
+float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd)
+{
+ HMONITOR monitor = ::MonitorFromWindow((HWND)hwnd, MONITOR_DEFAULTTONEAREST);
+ return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor);
+}
+
+//---------------------------------------------------------------------------------------------------------
+// Transparency related helpers (optional)
+//--------------------------------------------------------------------------------------------------------
+
+#if defined(_MSC_VER)
+#pragma comment(lib, "dwmapi") // Link with dwmapi.lib. MinGW will require linking with '-ldwmapi'
+#endif
+
+// [experimental]
+// Borrowed from GLFW's function updateFramebufferTransparency() in src/win32_window.c
+// (the Dwm* functions are Vista era functions but we are borrowing logic from GLFW)
+void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd)
+{
+ if (!IsWindowsVistaOrGreater())
+ return;
+
+ BOOL composition;
+ if (FAILED(::DwmIsCompositionEnabled(&composition)) || !composition)
+ return;
+
+ BOOL opaque;
+ DWORD color;
+ if (IsWindows8OrGreater() || (SUCCEEDED(::DwmGetColorizationColor(&color, &opaque)) && !opaque))
+ {
+ HRGN region = ::CreateRectRgn(0, 0, -1, -1);
+ DWM_BLURBEHIND bb = {};
+ bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
+ bb.hRgnBlur = region;
+ bb.fEnable = TRUE;
+ ::DwmEnableBlurBehindWindow((HWND)hwnd, &bb);
+ ::DeleteObject(region);
+ }
+ else
+ {
+ DWM_BLURBEHIND bb = {};
+ bb.dwFlags = DWM_BB_ENABLE;
+ ::DwmEnableBlurBehindWindow((HWND)hwnd, &bb);
+ }
+}
+
+//---------------------------------------------------------------------------------------------------------
diff --git a/3rdparty/imgui/backend/imgui_impl_win32.h b/3rdparty/imgui/backend/imgui_impl_win32.h
new file mode 100644
index 0000000..5197b7f
--- /dev/null
+++ b/3rdparty/imgui/backend/imgui_impl_win32.h
@@ -0,0 +1,41 @@
+// dear imgui: Platform Backend for Windows (standard windows API for 32 and 64 bits applications)
+// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
+
+// Implemented features:
+// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
+// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+// [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
+// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
+
+// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
+// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
+// Read online: https://github.com/ocornut/imgui/tree/master/docs
+
+#pragma once
+#include "imgui.h" // IMGUI_IMPL_API
+
+IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd);
+IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();
+IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame();
+
+// Win32 message handler your application need to call.
+// - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on <windows.h> from this helper.
+// - You should COPY the line below into your .cpp code to forward declare the function and then you can call it.
+#if 0
+extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+#endif
+
+// DPI-related helpers (optional)
+// - Use to enable DPI awareness without having to create an application manifest.
+// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps.
+// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc.
+// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime,
+// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies.
+IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness();
+IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd
+IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor
+
+// Transparency related helpers (optional) [experimental]
+// - Use to enable alpha compositing transparency with the desktop.
+// - Use together with e.g. clearing your framebuffer with zero-alpha.
+IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index 320a5de..e8abe5b 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -24,13 +24,12 @@ endfunction()
set(ENTRYPOINT_MODULE_SOURCES
src/Entrypoint/main.cpp
- src/Entrypoint/Common.cpp
- src/Entrypoint/OpenGL2.cpp
- src/Entrypoint/OpenGL3.cpp
- src/Entrypoint/Vulkan.cpp
- src/Entrypoint/DirectX11.cpp
- src/Entrypoint/DirectX12.cpp
- src/Entrypoint/Metal.mm
+ src/Entrypoint/Backend_OpenGL2.cpp
+ src/Entrypoint/Backend_OpenGL3.cpp
+ src/Entrypoint/Backend_Vulkan.cpp
+ src/Entrypoint/Backend_DirectX11.cpp
+ src/Entrypoint/Backend_DirectX12.cpp
+ src/Entrypoint/Backend_Metal.mm
)
add_source_group(MODEL_MODULE_SOURCES
@@ -61,12 +60,27 @@ add_source_group(UTILS_MODULE_SOURCES
function(add_executable_variant TARGET_NAME)
message("CpltCore: generating executable ${TARGET_NAME}")
+ if(BUILD_CORE_WITH_OPENGL2_BACKEND OR
+ BUILD_CORE_WITH_OPENGL3_BACKEND OR
+ BUILD_CORE_WITH_VULKAN_BACKEND OR
+ BUILD_CORE_WITH_METAL_BACKEND)
+ list(APPEND IMGUI_BACKEND_SOURCES
+ ${CMAKE_SOURCE_DIR}/3rdparty/imgui/backend/imgui_impl_glfw.cpp
+ )
+ endif()
+ if(BUILD_CORE_WITH_DX11_BACKEND OR BUILD_CORE_WITH_DX12_BACKEND)
+ list(APPEND IMGUI_BACKEND_SOURCES
+ ${CMAKE_SOURCE_DIR}/3rdparty/imgui/backend/imgui_impl_win32.cpp
+ )
+ endif()
+
add_executable(${TARGET_NAME}
${ENTRYPOINT_MODULE_SOURCES}
${MODEL_MODULE_SOURCES}
${UI_MODULE_SOURCES}
${UTILS_MODULE_SOURCES}
${UTILS_DIALOG_MODULE_SOURCES}
+ ${IMGUI_BACKEND_SOURCES}
)
target_include_directories(${TARGET_NAME} PRIVATE
${CMAKE_CURRENT_LIST_DIR}/src
@@ -95,12 +109,13 @@ function(add_executable_variant TARGET_NAME)
BUILD_CORE_WITH_DX11_BACKEND=$<BOOL:${BUILD_CORE_WITH_DX11_BACKEND}>
BUILD_CORE_WITH_DX12_BACKEND=$<BOOL:${BUILD_CORE_WITH_DX12_BACKEND}>
)
- else()
- target_compile_definitions(${TARGET_NAME}
- PRIVATE
- BUILD_CORE_WITH_DX11_BACKEND=0
- BUILD_CORE_WITH_DX12_BACKEND=0
- )
+
+ if(BUILD_CORE_WITH_DX11_BACKEND)
+ target_link_libraries(${TARGET_NAME} PRIVATE dxgi.lib d3d11.lib)
+ endif()
+ if(BUILD_CORE_WITH_DX12_BACKEND)
+ target_link_libraries(${TARGET_NAME} PRIVATE dxgi.lib d3d12.lib)
+ endif()
endif()
if(APPLE)
@@ -109,11 +124,6 @@ function(add_executable_variant TARGET_NAME)
PRIVATE
BUILD_CORE_WITH_METAL_BACKEND=$<BOOL:${BUILD_CORE_WITH_METAL_BACKEND}>
)
- else()
- target_compile_definitions(${TARGET_NAME}
- PRIVATE
- BUILD_CORE_WITH_METAL_BACKEND=0
- )
endif()
if(NOT APPLE)
@@ -126,6 +136,13 @@ function(add_executable_variant TARGET_NAME)
BUILD_CORE_WITH_OPENGL3_BACKEND=$<BOOL:${BUILD_CORE_WITH_OPENGL3_BACKEND}>
BUILD_CORE_WITH_VULKAN_BACKEND=$<BOOL:${BUILD_CORE_WITH_VULKAN_BACKEND}>
)
+
+ # TODO conditionally add opengl libraries
+ if(BUILD_CORE_WITH_VULKAN_BACKEND)
+ find_package(Vulkan REQUIRED FATAL_ERROR)
+ target_include_directories(${TARGET_NAME} PRIVATE ${Vulkan_INCLUDE_DIRS})
+ target_link_libraries(${TARGET_NAME} PRIVATE ${Vulkan_LIBRARIES})
+ endif()
endif()
# Platform specific dependencies for Utils/Dialog, not covered by conan
diff --git a/core/src/Entrypoint/Backend.hpp b/core/src/Entrypoint/Backend.hpp
new file mode 100644
index 0000000..9ceccb1
--- /dev/null
+++ b/core/src/Entrypoint/Backend.hpp
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <memory>
+
+class RenderingBackend {
+public:
+ // Implemented in Backend_OpenGL2.cpp
+ static std::unique_ptr<RenderingBackend> CreateOpenGL2Backend();
+ // Implemented in Backend_OpenGL3.cpp
+ static std::unique_ptr<RenderingBackend> CreateOpenGL3Backend();
+ // Implemented in Backend_Vulkan.cpp
+ static std::unique_ptr<RenderingBackend> CreateVulkanBackend();
+ // Implemented in Backend_DirectX11.cpp
+ static std::unique_ptr<RenderingBackend> CreateDx11Backend();
+ // Implemented in Backend_DirectX12.cpp
+ static std::unique_ptr<RenderingBackend> CreateDx12Backend();
+ // Implemented in Backend_Metal.cpp
+ static std::unique_ptr<RenderingBackend> CreateMetalBackend();
+
+ virtual ~RenderingBackend() = default;
+ virtual void RunUntilWindowClose(void (*windowContent)()) = 0;
+};
diff --git a/core/src/Entrypoint/Backend_DirectX11.cpp b/core/src/Entrypoint/Backend_DirectX11.cpp
new file mode 100644
index 0000000..8d46bf4
--- /dev/null
+++ b/core/src/Entrypoint/Backend_DirectX11.cpp
@@ -0,0 +1,238 @@
+#include "Backend.hpp"
+
+#if BUILD_CORE_WITH_DX11_BACKEND
+# include <backend/imgui_impl_dx11.h>
+# include <backend/imgui_impl_dx11.cpp>
+# include <stdexcept>
+# include <d3d11.h>
+# include <tchar.h>
+# include <backend/imgui_impl_win32.h>
+
+// 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);
+
+class DirectX11Backend : public RenderingBackend {
+private:
+ HWND hWnd;
+ WNDCLASSEX wc;
+
+ ID3D11Device* mD3dDevice = nullptr;
+ ID3D11DeviceContext* mD3dDeviceContext = nullptr;
+ IDXGISwapChain* mSwapChain = nullptr;
+ ID3D11RenderTargetView* mMainRenderTargetView = nullptr;
+
+public:
+ DirectX11Backend() {
+ ImGui_ImplWin32_EnableDpiAwareness();
+
+ wc.cbSize = sizeof(WNDCLASSEX);
+ wc.style = CS_CLASSDC;
+ wc.lpfnWndProc = &StaticWndProc;
+ wc.cbClsExtra = 0L;
+ wc.cbWndExtra = 0L;
+ wc.hInstance = GetModuleHandle(nullptr);
+ wc.hIcon = nullptr;
+ wc.hCursor = nullptr;
+ wc.hbrBackground = nullptr;
+ wc.lpszMenuName = nullptr;
+ wc.lpszClassName = _T("Cplt");
+ wc.hIconSm = nullptr;
+ ::RegisterClassEx(&wc);
+
+ hWnd = ::CreateWindow(
+ wc.lpszClassName,
+ _T("Cplt main window"),
+ WS_OVERLAPPEDWINDOW,
+ /* x */ 100,
+ /* y */ 100,
+ /* window width */ 1280,
+ /* window height */ 800,
+ nullptr,
+ nullptr,
+ wc.hInstance,
+ this);
+
+ if (!CreateDeviceD3D()) {
+ CleanupDeviceD3D();
+ ::UnregisterClass(wc.lpszClassName, wc.hInstance);
+ throw std::runtime_error("Failed to create d3d device.");
+ }
+
+ ::ShowWindow(hWnd, SW_SHOWDEFAULT);
+ ::UpdateWindow(hWnd);
+
+ IMGUI_CHECKVERSION();
+ ImGui::CreateContext();
+
+ ImGui_ImplWin32_Init(hWnd);
+ ImGui_ImplDX11_Init(mD3dDevice, mD3dDeviceContext);
+ }
+
+ virtual ~DirectX11Backend() {
+ ImGui_ImplDX11_Shutdown();
+ ImGui_ImplWin32_Shutdown();
+ ImGui::DestroyContext();
+
+ CleanupDeviceD3D();
+ ::DestroyWindow(hWnd);
+ ::UnregisterClass(wc.lpszClassName, wc.hInstance);
+ }
+
+ virtual void RunUntilWindowClose(void (*windowContent)()) {
+ while (true) {
+ MSG msg;
+ bool done = false;
+ while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE)) {
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+ if (msg.message == WM_QUIT) {
+ done = true;
+ }
+ }
+ if (done) break;
+
+ ImGui_ImplDX11_NewFrame();
+ ImGui_ImplWin32_NewFrame();
+ ImGui::NewFrame();
+
+ windowContent();
+
+ ImGui::Render();
+ const ImVec4 kClearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
+ const float kClearColorWithAlpha[4] = { kClearColor.x * kClearColor.w, kClearColor.y * kClearColor.w, kClearColor.z * kClearColor.w, kClearColor.w };
+ mD3dDeviceContext->OMSetRenderTargets(1, &mMainRenderTargetView, nullptr);
+ mD3dDeviceContext->ClearRenderTargetView(mMainRenderTargetView, kClearColorWithAlpha);
+ ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
+
+ mSwapChain->Present(1, 0); // Present with vsync
+ }
+ }
+
+private:
+ bool CreateDeviceD3D() {
+ // 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(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &mSwapChain, &mD3dDevice, &featureLevel, &mD3dDeviceContext) != S_OK) {
+ return false;
+ }
+
+ CreateRenderTarget();
+ return true;
+ }
+
+ void CleanupDeviceD3D() {
+ CleanupRenderTarget();
+ if (mSwapChain) {
+ mSwapChain->Release();
+ mSwapChain = nullptr;
+ }
+ if (mD3dDeviceContext) {
+ mD3dDeviceContext->Release();
+ mD3dDeviceContext = nullptr;
+ }
+ if (mD3dDevice) {
+ mD3dDevice->Release();
+ mD3dDevice = nullptr;
+ }
+ }
+
+ void CreateRenderTarget() {
+ ID3D11Texture2D* pBackBuffer;
+ mSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
+ mD3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &mMainRenderTargetView);
+ pBackBuffer->Release();
+ }
+
+ void CleanupRenderTarget() {
+ if (mMainRenderTargetView) {
+ mMainRenderTargetView->Release();
+ mMainRenderTargetView = nullptr;
+ }
+ }
+
+ static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ DirectX11Backend* self;
+ if (uMsg == WM_NCCREATE) {
+ auto lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
+ self = static_cast<DirectX11Backend*>(lpcs->lpCreateParams);
+ self->hWnd = hWnd;
+ SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(self));
+ } else {
+ self = reinterpret_cast<DirectX11Backend*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
+ }
+
+ if (self) {
+ return self->WndProc(uMsg, wParam, lParam);
+ } else {
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+ }
+ }
+
+ LRESULT WndProc(UINT msg, WPARAM wParam, LPARAM lParam) {
+ if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) {
+ return true;
+ }
+
+ switch (msg) {
+ case WM_SIZE: {
+ if (mD3dDevice != nullptr && wParam != SIZE_MINIMIZED) {
+ CleanupRenderTarget();
+ mSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, 0);
+ CreateRenderTarget();
+ }
+ return 0;
+ }
+
+ case WM_SYSCOMMAND: {
+ // Disable ALT application menu
+ if ((wParam & 0xfff0) == SC_KEYMENU) {
+ return 0;
+ }
+ } break;
+
+ case WM_DESTROY: {
+ ::PostQuitMessage(0);
+ return 0;
+ }
+ }
+ return ::DefWindowProc(hWnd, msg, wParam, lParam);
+ }
+};
+
+std::unique_ptr<RenderingBackend> RenderingBackend::CreateDx11Backend() {
+ try {
+ return std::make_unique<DirectX11Backend>();
+ } catch (std::exception& e) {
+ return nullptr;
+ }
+}
+
+#else // ^^ BUILD_CORE_WITH_DX11_BACKEND | BUILD_CORE_WITH_DX11_BACKEND vv
+
+std::unique_ptr<RenderingBackend> RenderingBackend::CreateDx11Backend() {
+ return nullptr;
+}
+
+#endif
diff --git a/core/src/Entrypoint/Backend_DirectX12.cpp b/core/src/Entrypoint/Backend_DirectX12.cpp
new file mode 100644
index 0000000..c0492c2
--- /dev/null
+++ b/core/src/Entrypoint/Backend_DirectX12.cpp
@@ -0,0 +1,454 @@
+#include "Backend.hpp"
+
+#if BUILD_CORE_WITH_DX12_BACKEND
+# include <backend/imgui_impl_dx12.h>
+# include <backend/imgui_impl_win32.h>
+# include <d3d12.h>
+# include <dxgi1_4.h>
+# include <tchar.h>
+# include <backend/imgui_impl_dx12.cpp>
+# include <stdexcept>
+
+constexpr int kNumFramesInFlight = 3;
+constexpr int kNumBackBuffers = 3;
+
+// 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);
+
+class DirectX12Backend : public RenderingBackend {
+private:
+ struct FrameContext {
+ ID3D12CommandAllocator* CommandAllocator;
+ UINT64 FenceValue;
+ };
+
+ HWND hWnd;
+ WNDCLASSEX wc;
+
+ FrameContext mFrameContext[kNumFramesInFlight] = {};
+ UINT mFrameIndex = 0;
+
+ ID3D12Device* mD3dDevice = nullptr;
+ ID3D12DescriptorHeap* mD3dRtvDescHeap = nullptr;
+ ID3D12DescriptorHeap* mD3dSrvDescHeap = nullptr;
+ ID3D12CommandQueue* mD3dCommandQueue = nullptr;
+ ID3D12GraphicsCommandList* mD3dCommandList = nullptr;
+ ID3D12Fence* mFence = nullptr;
+ HANDLE mFenceEvent = nullptr;
+ UINT64 mFenceLastSignaledValue = 0;
+ IDXGISwapChain3* mSwapChain = nullptr;
+ HANDLE mSwapChainWaitableObject = nullptr;
+ ID3D12Resource* mMainRenderTargetResource[kNumBackBuffers] = {};
+ D3D12_CPU_DESCRIPTOR_HANDLE mMainRenderTargetDescriptor[kNumBackBuffers] = {};
+
+public:
+ DirectX12Backend() {
+ ImGui_ImplWin32_EnableDpiAwareness();
+
+ wc.cbSize = sizeof(WNDCLASSEX);
+ wc.style = CS_CLASSDC;
+ wc.lpfnWndProc = &StaticWndProc;
+ wc.cbClsExtra = 0L;
+ wc.cbWndExtra = 0L;
+ wc.hInstance = GetModuleHandle(nullptr);
+ wc.hIcon = nullptr;
+ wc.hCursor = nullptr;
+ wc.hbrBackground = nullptr;
+ wc.lpszMenuName = nullptr;
+ wc.lpszClassName = _T("Cplt");
+ wc.hIconSm = nullptr;
+ ::RegisterClassEx(&wc);
+
+ hWnd = ::CreateWindow(
+ wc.lpszClassName,
+ _T("Cplt main window"),
+ WS_OVERLAPPEDWINDOW,
+ /* x */ 100,
+ /* y */ 100,
+ /* window width */ 1280,
+ /* window height */ 800,
+ nullptr,
+ nullptr,
+ wc.hInstance,
+ this);
+
+ if (!CreateDeviceD3D()) {
+ CleanupDeviceD3D();
+ ::UnregisterClass(wc.lpszClassName, wc.hInstance);
+ throw std::runtime_error("Failed to create d3d device.");
+ }
+
+ ::ShowWindow(hWnd, SW_SHOWDEFAULT);
+ ::UpdateWindow(hWnd);
+
+ IMGUI_CHECKVERSION();
+ ImGui::CreateContext();
+
+ ImGui_ImplWin32_Init(hWnd);
+ ImGui_ImplDX12_Init(mD3dDevice, kNumFramesInFlight, DXGI_FORMAT_R8G8B8A8_UNORM, mD3dSrvDescHeap, mD3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(), mD3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart());
+ }
+
+ virtual ~DirectX12Backend() {
+ WaitForLastSubmittedFrame();
+
+ // Cleanup
+ ImGui_ImplDX12_Shutdown();
+ ImGui_ImplWin32_Shutdown();
+ ImGui::DestroyContext();
+
+ CleanupDeviceD3D();
+ ::DestroyWindow(hWnd);
+ ::UnregisterClass(wc.lpszClassName, wc.hInstance);
+ }
+
+ virtual void RunUntilWindowClose(void (*windowContent)()) {
+ while (true) {
+ MSG msg;
+ bool done = false;
+ while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE))
+ {
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+ if (msg.message == WM_QUIT) {
+ done = true;
+ }
+ }
+ if (done) break;
+
+ // Start the Dear ImGui frame
+ ImGui_ImplDX12_NewFrame();
+ ImGui_ImplWin32_NewFrame();
+ ImGui::NewFrame();
+
+ windowContent();
+
+ ImGui::Render();
+
+ FrameContext* frameCtx = WaitForNextFrameResources();
+ UINT backBufferIdx = mSwapChain->GetCurrentBackBufferIndex();
+ frameCtx->CommandAllocator->Reset();
+
+ D3D12_RESOURCE_BARRIER barrier = {};
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+ barrier.Transition.pResource = mMainRenderTargetResource[backBufferIdx];
+ barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+ barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
+ barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
+ mD3dCommandList->Reset(frameCtx->CommandAllocator, nullptr);
+ mD3dCommandList->ResourceBarrier(1, &barrier);
+
+ const ImVec4 kClearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
+ const float kClearColorWithAlpha[4] = { kClearColor.x * kClearColor.w, kClearColor.y * kClearColor.w, kClearColor.z * kClearColor.w, kClearColor.w };
+ mD3dCommandList->ClearRenderTargetView(mMainRenderTargetDescriptor[backBufferIdx], kClearColorWithAlpha, 0, nullptr);
+ mD3dCommandList->OMSetRenderTargets(1, &mMainRenderTargetDescriptor[backBufferIdx], FALSE, nullptr);
+ mD3dCommandList->SetDescriptorHeaps(1, &mD3dSrvDescHeap);
+ ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), mD3dCommandList);
+ barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
+ barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
+ mD3dCommandList->ResourceBarrier(1, &barrier);
+ mD3dCommandList->Close();
+
+ mD3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&mD3dCommandList);
+
+ mSwapChain->Present(1, 0); // Present with vsync
+
+ UINT64 fenceValue = mFenceLastSignaledValue + 1;
+ mD3dCommandQueue->Signal(mFence, fenceValue);
+ mFenceLastSignaledValue = fenceValue;
+ frameCtx->FenceValue = fenceValue;
+ }
+ }
+
+private:
+ bool CreateDeviceD3D() {
+ // Setup swap chain
+ DXGI_SWAP_CHAIN_DESC1 sd;
+ {
+ ZeroMemory(&sd, sizeof(sd));
+ sd.BufferCount = kNumBackBuffers;
+ sd.Width = 0;
+ sd.Height = 0;
+ sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ sd.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
+ sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ sd.SampleDesc.Count = 1;
+ sd.SampleDesc.Quality = 0;
+ sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+ sd.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
+ sd.Scaling = DXGI_SCALING_STRETCH;
+ sd.Stereo = FALSE;
+ }
+
+ // Create device
+ D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0;
+ if (D3D12CreateDevice(nullptr, featureLevel, IID_PPV_ARGS(&mD3dDevice)) != S_OK) {
+ return false;
+ }
+
+ {
+ D3D12_DESCRIPTOR_HEAP_DESC desc = {};
+ desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
+ desc.NumDescriptors = kNumBackBuffers;
+ desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+ desc.NodeMask = 1;
+ if (mD3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&mD3dRtvDescHeap)) != S_OK) {
+ return false;
+ }
+
+ SIZE_T rtvDescriptorSize = mD3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
+ D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = mD3dRtvDescHeap->GetCPUDescriptorHandleForHeapStart();
+ for (UINT i = 0; i < kNumBackBuffers; i++) {
+ mMainRenderTargetDescriptor[i] = rtvHandle;
+ rtvHandle.ptr += rtvDescriptorSize;
+ }
+ }
+
+ {
+ D3D12_DESCRIPTOR_HEAP_DESC desc = {};
+ desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
+ desc.NumDescriptors = 1;
+ desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
+ if (mD3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&mD3dSrvDescHeap)) != S_OK) {
+ return false;
+ }
+ }
+
+ {
+ D3D12_COMMAND_QUEUE_DESC desc = {};
+ desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
+ desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
+ desc.NodeMask = 1;
+ if (mD3dDevice->CreateCommandQueue(&desc, IID_PPV_ARGS(&mD3dCommandQueue)) != S_OK) {
+ return false;
+ }
+ }
+
+ for (UINT i = 0; i < kNumFramesInFlight; i++) {
+ if (mD3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mFrameContext[i].CommandAllocator)) != S_OK) {
+ return false;
+ }
+ }
+
+ if (mD3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, mFrameContext[0].CommandAllocator, nullptr, IID_PPV_ARGS(&mD3dCommandList)) != S_OK ||
+ mD3dCommandList->Close() != S_OK)
+ {
+ return false;
+ }
+
+ if (mD3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)) != S_OK) return false;
+
+ mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+ if (mFenceEvent == nullptr) return false;
+
+ {
+ IDXGIFactory4* dxgiFactory = nullptr;
+ IDXGISwapChain1* swapChain1 = nullptr;
+ if (CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)) != S_OK)
+ return false;
+ if (dxgiFactory->CreateSwapChainForHwnd(mD3dCommandQueue, hWnd, &sd, nullptr, nullptr, &swapChain1) != S_OK)
+ return false;
+ if (swapChain1->QueryInterface(IID_PPV_ARGS(&mSwapChain)) != S_OK)
+ return false;
+ swapChain1->Release();
+ dxgiFactory->Release();
+ mSwapChain->SetMaximumFrameLatency(kNumBackBuffers);
+ mSwapChainWaitableObject = mSwapChain->GetFrameLatencyWaitableObject();
+ }
+
+ CreateRenderTarget();
+ return true;
+ }
+
+ void CleanupDeviceD3D() {
+ CleanupRenderTarget();
+ if (mSwapChain) {
+ mSwapChain->Release();
+ mSwapChain = nullptr;
+ }
+ if (mSwapChainWaitableObject != nullptr) {
+ CloseHandle(mSwapChainWaitableObject);
+ }
+ for (UINT i = 0; i < kNumFramesInFlight; i++)
+ if (mFrameContext[i].CommandAllocator) {
+ mFrameContext[i].CommandAllocator->Release();
+ mFrameContext[i].CommandAllocator = nullptr;
+ }
+ if (mD3dCommandQueue) {
+ mD3dCommandQueue->Release();
+ mD3dCommandQueue = nullptr;
+ }
+ if (mD3dCommandList) {
+ mD3dCommandList->Release();
+ mD3dCommandList = nullptr;
+ }
+ if (mD3dRtvDescHeap) {
+ mD3dRtvDescHeap->Release();
+ mD3dRtvDescHeap = nullptr;
+ }
+ if (mD3dSrvDescHeap) {
+ mD3dSrvDescHeap->Release();
+ mD3dSrvDescHeap = nullptr;
+ }
+ if (mFence) {
+ mFence->Release();
+ mFence = nullptr;
+ }
+ if (mFenceEvent) {
+ CloseHandle(mFenceEvent);
+ mFenceEvent = nullptr;
+ }
+ if (mD3dDevice) {
+ mD3dDevice->Release();
+ mD3dDevice = nullptr;
+ }
+ }
+
+ void CreateRenderTarget() {
+ for (UINT i = 0; i < kNumBackBuffers; i++)
+ {
+ ID3D12Resource* pBackBuffer = nullptr;
+ mSwapChain->GetBuffer(i, IID_PPV_ARGS(&pBackBuffer));
+ mD3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, mMainRenderTargetDescriptor[i]);
+ mMainRenderTargetResource[i] = pBackBuffer;
+ }
+ }
+
+ void CleanupRenderTarget() {
+ WaitForLastSubmittedFrame();
+
+ for (UINT i = 0; i < kNumBackBuffers; i++)
+ if (mMainRenderTargetResource[i]) {
+ mMainRenderTargetResource[i]->Release();
+ mMainRenderTargetResource[i] = nullptr;
+ }
+ }
+
+ void WaitForLastSubmittedFrame() {
+ FrameContext* frameCtx = &mFrameContext[mFrameIndex % kNumFramesInFlight];
+
+ UINT64 fenceValue = frameCtx->FenceValue;
+ if (fenceValue == 0)
+ return; // No fence was signaled
+
+ frameCtx->FenceValue = 0;
+ if (mFence->GetCompletedValue() >= fenceValue)
+ return;
+
+ mFence->SetEventOnCompletion(fenceValue, mFenceEvent);
+ WaitForSingleObject(mFenceEvent, INFINITE);
+ }
+
+ FrameContext* WaitForNextFrameResources() {
+ UINT nextFrameIndex = mFrameIndex + 1;
+ mFrameIndex = nextFrameIndex;
+
+ HANDLE waitableObjects[] = { mSwapChainWaitableObject, nullptr };
+ DWORD numWaitableObjects = 1;
+
+ FrameContext* frameCtx = &mFrameContext[nextFrameIndex % kNumFramesInFlight];
+ UINT64 fenceValue = frameCtx->FenceValue;
+ if (fenceValue != 0) // means no fence was signaled
+ {
+ frameCtx->FenceValue = 0;
+ mFence->SetEventOnCompletion(fenceValue, mFenceEvent);
+ waitableObjects[1] = mFenceEvent;
+ numWaitableObjects = 2;
+ }
+
+ WaitForMultipleObjects(numWaitableObjects, waitableObjects, TRUE, INFINITE);
+
+ return frameCtx;
+ }
+
+ void ResizeSwapChain(int width, int height) {
+ DXGI_SWAP_CHAIN_DESC1 sd;
+ mSwapChain->GetDesc1(&sd);
+ sd.Width = width;
+ sd.Height = height;
+
+ IDXGIFactory4* dxgiFactory = nullptr;
+ mSwapChain->GetParent(IID_PPV_ARGS(&dxgiFactory));
+
+ mSwapChain->Release();
+ CloseHandle(mSwapChainWaitableObject);
+
+ IDXGISwapChain1* swapChain1 = nullptr;
+ dxgiFactory->CreateSwapChainForHwnd(mD3dCommandQueue, hWnd, &sd, nullptr, nullptr, &swapChain1);
+ swapChain1->QueryInterface(IID_PPV_ARGS(&mSwapChain));
+ swapChain1->Release();
+ dxgiFactory->Release();
+
+ mSwapChain->SetMaximumFrameLatency(kNumBackBuffers);
+
+ mSwapChainWaitableObject = mSwapChain->GetFrameLatencyWaitableObject();
+ assert(mSwapChainWaitableObject != nullptr);
+ }
+
+ static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ DirectX12Backend* self;
+ if (uMsg == WM_NCCREATE) {
+ auto lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
+ self = static_cast<DirectX12Backend*>(lpcs->lpCreateParams);
+ self->hWnd = hWnd;
+ SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(self));
+ } else {
+ self = reinterpret_cast<DirectX12Backend*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
+ }
+
+ if (self) {
+ return self->WndProc(uMsg, wParam, lParam);
+ } else {
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+ }
+ }
+
+ LRESULT WndProc(UINT msg, WPARAM wParam, LPARAM lParam) {
+ if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) {
+ return true;
+ }
+
+ switch (msg) {
+ case WM_SIZE: {
+ if (mD3dDevice != nullptr && wParam != SIZE_MINIMIZED) {
+ WaitForLastSubmittedFrame();
+ ImGui_ImplDX12_InvalidateDeviceObjects();
+ CleanupRenderTarget();
+ ResizeSwapChain((UINT)LOWORD(lParam), (UINT)HIWORD(lParam));
+ CreateRenderTarget();
+ ImGui_ImplDX12_CreateDeviceObjects();
+ }
+ return 0;
+ }
+
+ case WM_SYSCOMMAND: {
+ // Disable ALT application menu
+ if ((wParam & 0xfff0) == SC_KEYMENU) {
+ return 0;
+ }
+ } break;
+
+ case WM_DESTROY: {
+ ::PostQuitMessage(0);
+ return 0;
+ }
+ }
+ return ::DefWindowProc(hWnd, msg, wParam, lParam);
+ }
+};
+
+std::unique_ptr<RenderingBackend> RenderingBackend::CreateDx12Backend() {
+ try {
+ return std::make_unique<DirectX12Backend>();
+ } catch (std::exception& e) {
+ return nullptr;
+ }
+}
+
+#else // ^^ BUILD_CORE_WITH_DX12_BACKEND | BUILD_CORE_WITH_DX12_BACKEND vv
+
+std::unique_ptr<RenderingBackend> RenderingBackend::CreateDx12Backend() {
+ return nullptr;
+}
+
+#endif
diff --git a/core/src/Entrypoint/Backend_Metal.mm b/core/src/Entrypoint/Backend_Metal.mm
new file mode 100644
index 0000000..a1f4993
--- /dev/null
+++ b/core/src/Entrypoint/Backend_Metal.mm
@@ -0,0 +1,34 @@
+#include "Backend.hpp"
+
+#if BUILD_CORE_WITH_METAL_BACKEND
+
+class MetalBackend : public RenderingBackend {
+public:
+ MetalBackend() {
+ // TODO
+ }
+
+ virtual ~MetalBackend() {
+ // TODO
+ }
+
+ virtual void RunUntilWindowClose(void (*windowContent)()) {
+ // TODO
+ }
+};
+
+std::unique_ptr<RenderingBackend> RenderingBackend::CreateMetalBackend() {
+ try {
+ return std::make_unique<MetalBackend>();
+ } catch (std::exception& e) {
+ return nullptr;
+ }
+}
+
+#else // ^^ BUILD_CORE_WITH_METAL_BACKEND | BUILD_CORE_WITH_METAL_BACKEND vv
+
+std::unique_ptr<RenderingBackend> RenderingBackend::CreateMetalBackend() {
+ return nullptr;
+}
+
+#endif
diff --git a/core/src/Entrypoint/Backend_OpenGL2.cpp b/core/src/Entrypoint/Backend_OpenGL2.cpp
new file mode 100644
index 0000000..422ad91
--- /dev/null
+++ b/core/src/Entrypoint/Backend_OpenGL2.cpp
@@ -0,0 +1,98 @@
+#include "Entrypoint/Backend.hpp"
+
+#if BUILD_CORE_WITH_OPENGL2_BACKEND
+# include <glad/glad.h>
+# include <GLFW/glfw3.h>
+# include <backend/imgui_impl_glfw.h>
+# include <backend/imgui_impl_opengl2.h>
+# include <imgui.h>
+# include <stdexcept>
+# include <iostream>
+
+# define IMGUI_IMPL_OPENGL_LOADER_GLAD
+# include <backend/imgui_impl_opengl2.cpp>
+
+class OpenGL2Backend : public RenderingBackend {
+private:
+ GLFWwindow* mWindow;
+
+public:
+ OpenGL2Backend() {
+ glfwSetErrorCallback(&GlfwErrorCallback);
+ if (!glfwInit()) {
+ throw std::runtime_error("Failed to initialize GLFW.");
+ }
+
+ mWindow = glfwCreateWindow(1280, 720, "Cplt", nullptr, nullptr);
+ if (mWindow == nullptr) {
+ throw std::runtime_error("Failed to create GLFW window.");
+ }
+ glfwMakeContextCurrent(mWindow);
+ glfwSwapInterval(1); // Enable vsync
+
+ if (gladLoadGLLoader((GLADloadproc)glfwGetProcAddress) == 0) {
+ throw std::runtime_error("Failed to initialize OpenGL.");
+ }
+
+ IMGUI_CHECKVERSION();
+ ImGui::CreateContext();
+
+ ImGui_ImplGlfw_InitForOpenGL(mWindow, true);
+ ImGui_ImplOpenGL2_Init();
+ }
+
+ virtual ~OpenGL2Backend() {
+ ImGui_ImplOpenGL2_Shutdown();
+ ImGui_ImplGlfw_Shutdown();
+ ImGui::DestroyContext();
+
+ glfwDestroyWindow(mWindow);
+ glfwTerminate();
+ }
+
+ virtual void RunUntilWindowClose(void (*windowContent)()) {
+ while (!glfwWindowShouldClose(mWindow)) {
+ glfwPollEvents();
+
+ ImGui_ImplOpenGL2_NewFrame();
+ ImGui_ImplGlfw_NewFrame();
+ ImGui::NewFrame();
+
+ windowContent();
+
+ int displayWidth, displayHeight;
+ glfwGetFramebufferSize(mWindow, &displayWidth, &displayHeight);
+ glViewport(0, 0, displayWidth, displayHeight);
+
+ const ImVec4 kClearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
+ glClearColor(kClearColor.x * kClearColor.w, kClearColor.y * kClearColor.w, kClearColor.z * kClearColor.w, kClearColor.w);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ ImGui::Render();
+ ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData());
+
+ glfwMakeContextCurrent(mWindow);
+ glfwSwapBuffers(mWindow);
+ }
+ }
+
+ static void GlfwErrorCallback(int errorCode, const char* message) {
+ std::cerr << "GLFW Error " << errorCode << ": " << message << "\n";
+ }
+};
+
+std::unique_ptr<RenderingBackend> RenderingBackend::CreateOpenGL2Backend() {
+ try {
+ return std::make_unique<OpenGL2Backend>();
+ } catch (std::exception& e) {
+ return nullptr;
+ }
+}
+
+#else // ^^ BUILD_CORE_WITH_OPENGL2_BACKEND | !BUILD_CORE_WITH_OPENGL2_BACKEND vv
+
+std::unique_ptr<RenderingBackend> RenderingBackend::CreateOpenGL2Backend(){}() {
+ return nullptr;
+}
+
+#endif
diff --git a/core/src/Entrypoint/Backend_OpenGL3.cpp b/core/src/Entrypoint/Backend_OpenGL3.cpp
new file mode 100644
index 0000000..36a0cb3
--- /dev/null
+++ b/core/src/Entrypoint/Backend_OpenGL3.cpp
@@ -0,0 +1,113 @@
+#include "Entrypoint/Backend.hpp"
+
+#if BUILD_CORE_WITH_OPENGL3_BACKEND
+# include <glad/glad.h>
+# include <GLFW/glfw3.h>
+# include <iostream>
+# include <backend/imgui_impl_glfw.h>
+# include <backend/imgui_impl_opengl3.h>
+# include <imgui.h>
+# include <stdexcept>
+
+# define IMGUI_IMPL_OPENGL_LOADER_GLAD
+# include <backend/imgui_impl_opengl3.cpp>
+
+class OpenGL3Backend : public RenderingBackend {
+private:
+ GLFWwindow* mWindow;
+
+public:
+ OpenGL3Backend() {
+ glfwSetErrorCallback(&GlfwErrorCallback);
+ if (!glfwInit()) {
+ throw std::runtime_error("Failed to initialize GLFW.");
+ }
+
+# if PLATFORM_APPLE
+ // GL 3.2 + GLSL 150
+ const char* glslVersion = "#version 150";
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
+# else
+ // GL 3.0 + GLSL 130
+ const char* glslVersion = "#version 130";
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+ //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
+ //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
+# endif
+
+ mWindow = glfwCreateWindow(1280, 720, "Cplt", nullptr, nullptr);
+ if (mWindow == nullptr) {
+ throw std::runtime_error("Failed to create GLFW window.");
+ }
+ glfwMakeContextCurrent(mWindow);
+ glfwSwapInterval(1); // Enable vsync
+
+ if (gladLoadGLLoader((GLADloadproc)glfwGetProcAddress) == 0) {
+ throw std::runtime_error("Failed to initialize OpenGL.");
+ }
+
+ IMGUI_CHECKVERSION();
+ ImGui::CreateContext();
+
+ ImGui_ImplGlfw_InitForOpenGL(mWindow, true);
+ ImGui_ImplOpenGL3_Init(glslVersion);
+ }
+
+ virtual ~OpenGL3Backend() {
+ ImGui_ImplOpenGL3_Shutdown();
+ ImGui_ImplGlfw_Shutdown();
+ ImGui::DestroyContext();
+
+ glfwDestroyWindow(mWindow);
+ glfwTerminate();
+ }
+
+ virtual void RunUntilWindowClose(void (*windowContent)()) {
+ while (!glfwWindowShouldClose(mWindow)) {
+ glfwPollEvents();
+
+ ImGui_ImplOpenGL3_NewFrame();
+ ImGui_ImplGlfw_NewFrame();
+ ImGui::NewFrame();
+
+ windowContent();
+
+ int displayWidth, displayHeight;
+ glfwGetFramebufferSize(mWindow, &displayWidth, &displayHeight);
+ glViewport(0, 0, displayWidth, displayHeight);
+
+ const ImVec4 kClearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
+ glClearColor(kClearColor.x * kClearColor.w, kClearColor.y * kClearColor.w, kClearColor.z * kClearColor.w, kClearColor.w);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ ImGui::Render();
+ ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
+
+ glfwSwapBuffers(mWindow);
+ }
+ }
+
+ static void GlfwErrorCallback(int errorCode, const char* message) {
+ std::cerr << "GLFW Error " << errorCode << ": " << message << "\n";
+ }
+};
+
+std::unique_ptr<RenderingBackend> RenderingBackend::CreateOpenGL3Backend() {
+ try {
+ return std::make_unique<OpenGL3Backend>();
+ } catch (std::exception& e) {
+ return nullptr;
+ }
+}
+
+#else // ^^ BUILD_CORE_WITH_OPENGL3_BACKEND | !BUILD_CORE_WITH_OPENGL3_BACKEND vv
+
+std::unique_ptr<RenderingBackend> RenderingBackend::CreateOpenGL3Backend() {
+ return nullptr;
+}
+
+#endif
diff --git a/core/src/Entrypoint/Backend_Vulkan.cpp b/core/src/Entrypoint/Backend_Vulkan.cpp
new file mode 100644
index 0000000..9d79acf
--- /dev/null
+++ b/core/src/Entrypoint/Backend_Vulkan.cpp
@@ -0,0 +1,424 @@
+#include "Entrypoint/Backend.hpp"
+
+#if BUILD_CORE_WITH_VULKAN_BACKEND
+# include <iostream>
+# include <stdexcept>
+
+# define GLFW_INCLUDE_NONE
+# define GLFW_INCLUDE_VULKAN
+# include <GLFW/glfw3.h>
+
+# include <backend/imgui_impl_glfw.h>
+# include <backend/imgui_impl_vulkan.h>
+# include <backend/imgui_impl_vulkan.cpp>
+
+class VulkanBackend : public RenderingBackend {
+private:
+ GLFWwindow* mWindow;
+
+ VkAllocationCallbacks* mAllocator = NULL;
+ VkInstance mInstance = VK_NULL_HANDLE;
+ VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
+ VkDevice mDevice = VK_NULL_HANDLE;
+ uint32_t mQueueFamily = (uint32_t)-1;
+ VkQueue mQueue = VK_NULL_HANDLE;
+ VkDebugReportCallbackEXT mDebugReport = VK_NULL_HANDLE;
+ VkPipelineCache mPipelineCache = VK_NULL_HANDLE;
+ VkDescriptorPool mDescriptorPool = VK_NULL_HANDLE;
+
+ ImGui_ImplVulkanH_Window mMainWindowData;
+ int mMinImageCount = 2;
+ bool mSwapChainRebuild = false;
+
+public:
+ VulkanBackend() {
+ glfwSetErrorCallback(&GlfwErrorCallback);
+ if (!glfwInit()) {
+ throw std::runtime_error("Failed to initialize GLFW.");
+ }
+
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
+ mWindow = glfwCreateWindow(1280, 720, "Cplt", nullptr, nullptr);
+ if (mWindow == nullptr) {
+ throw std::runtime_error("Failed to create GLFW window.");
+ }
+
+ if (!glfwVulkanSupported()) {
+ throw std::runtime_error("GLFW reports vulkan not supported.");
+ }
+
+ uint32_t extensionsCount = 0;
+ const char** extensions = glfwGetRequiredInstanceExtensions(&extensionsCount);
+ SetupVulkan(extensions, extensionsCount);
+
+ // Create window surface
+ VkSurfaceKHR surface;
+ VkResult err = glfwCreateWindowSurface(mInstance, mWindow, mAllocator, &surface);
+ CheckVkResults(err);
+
+ // Create framebuffers
+ int w, h;
+ glfwGetFramebufferSize(mWindow, &w, &h);
+ SetupVulkanWindow(&mMainWindowData, surface, w, h);
+
+ IMGUI_CHECKVERSION();
+ ImGui::CreateContext();
+
+ ImGui_ImplGlfw_InitForVulkan(mWindow, true);
+ ImGui_ImplVulkan_InitInfo init_info = {};
+ init_info.Instance = mInstance;
+ init_info.PhysicalDevice = mPhysicalDevice;
+ init_info.Device = mDevice;
+ init_info.QueueFamily = mQueueFamily;
+ init_info.Queue = mQueue;
+ init_info.PipelineCache = mPipelineCache;
+ init_info.DescriptorPool = mDescriptorPool;
+ init_info.Allocator = mAllocator;
+ init_info.MinImageCount = mMinImageCount;
+ init_info.ImageCount = mMainWindowData.ImageCount;
+ init_info.CheckVkResultFn = CheckVkResults;
+ ImGui_ImplVulkan_Init(&init_info, mMainWindowData.RenderPass);
+ }
+
+ virtual ~VulkanBackend() {
+ auto err = vkDeviceWaitIdle(mDevice);
+ CheckVkResults(err);
+ ImGui_ImplVulkan_Shutdown();
+ ImGui_ImplGlfw_Shutdown();
+ ImGui::DestroyContext();
+
+ CleanupVulkanWindow();
+ CleanupVulkan();
+
+ glfwDestroyWindow(mWindow);
+ glfwTerminate();
+ }
+
+ virtual void RunUntilWindowClose(void (*windowContent)()) override {
+ // Upload Fonts
+ {
+ // Use any command queue
+ VkCommandPool commandPool = mMainWindowData.Frames[mMainWindowData.FrameIndex].CommandPool;
+ VkCommandBuffer commandBuffer = mMainWindowData.Frames[mMainWindowData.FrameIndex].CommandBuffer;
+
+ CheckVkResults(vkResetCommandPool(mDevice, commandPool, 0));
+ VkCommandBufferBeginInfo beginInfo = {};
+ beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ beginInfo.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+ CheckVkResults(vkBeginCommandBuffer(commandBuffer, &beginInfo));
+
+ ImGui_ImplVulkan_CreateFontsTexture(commandBuffer);
+
+ VkSubmitInfo endInfo = {};
+ endInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ endInfo.commandBufferCount = 1;
+ endInfo.pCommandBuffers = &commandBuffer;
+ CheckVkResults(vkEndCommandBuffer(commandBuffer));
+ CheckVkResults(vkQueueSubmit(mQueue, 1, &endInfo, VK_NULL_HANDLE));
+
+ CheckVkResults(vkDeviceWaitIdle(mDevice));
+ ImGui_ImplVulkan_DestroyFontUploadObjects();
+ }
+
+ while (!glfwWindowShouldClose(mWindow)) {
+ glfwPollEvents();
+
+ // Resize swap chain?
+ if (mSwapChainRebuild) {
+ int width, height;
+ glfwGetFramebufferSize(mWindow, &width, &height);
+ if (width > 0 && height > 0) {
+ ImGui_ImplVulkan_SetMinImageCount(mMinImageCount);
+ ImGui_ImplVulkanH_CreateOrResizeWindow(mInstance, mPhysicalDevice, mDevice, &mMainWindowData, mQueueFamily, mAllocator, width, height, mMinImageCount);
+ mMainWindowData.FrameIndex = 0;
+ mSwapChainRebuild = false;
+ }
+ }
+
+ // Start the Dear ImGui frame
+ ImGui_ImplVulkan_NewFrame();
+ ImGui_ImplGlfw_NewFrame();
+ ImGui::NewFrame();
+
+ windowContent();
+
+ ImGui::Render();
+ ImDrawData* drawData = ImGui::GetDrawData();
+ const bool isMinimized = (drawData->DisplaySize.x <= 0.0f || drawData->DisplaySize.y <= 0.0f);
+ if (!isMinimized) {
+ const ImVec4 kClearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
+ mMainWindowData.ClearValue.color.float32[0] = kClearColor.x * kClearColor.w;
+ mMainWindowData.ClearValue.color.float32[1] = kClearColor.y * kClearColor.w;
+ mMainWindowData.ClearValue.color.float32[2] = kClearColor.z * kClearColor.w;
+ mMainWindowData.ClearValue.color.float32[3] = kClearColor.w;
+ FrameRender(&mMainWindowData, drawData);
+ FramePresent(&mMainWindowData);
+ }
+ }
+ }
+
+private:
+ void SetupVulkan(const char** extensions, uint32_t extensions_count) {
+ VkResult err;
+
+ // Create Vulkan Instance
+ {
+ VkInstanceCreateInfo createInfo = {};
+ createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+ createInfo.enabledExtensionCount = extensions_count;
+ createInfo.ppEnabledExtensionNames = extensions;
+ // Create Vulkan Instance without any debug feature
+ err = vkCreateInstance(&createInfo, mAllocator, &mInstance);
+ CheckVkResults(err);
+ }
+
+ // Select GPU
+ {
+ uint32_t gpuCount;
+ err = vkEnumeratePhysicalDevices(mInstance, &gpuCount, NULL);
+ CheckVkResults(err);
+ IM_ASSERT(gpuCount > 0);
+
+ VkPhysicalDevice* gpus = (VkPhysicalDevice*)malloc(sizeof(VkPhysicalDevice) * gpuCount);
+ err = vkEnumeratePhysicalDevices(mInstance, &gpuCount, gpus);
+ CheckVkResults(err);
+
+ // If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers
+ // most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple
+ // dedicated GPUs) is out of scope of this sample.
+ int useGpu = 0;
+ for (int i = 0; i < (int)gpuCount; i++)
+ {
+ VkPhysicalDeviceProperties properties;
+ vkGetPhysicalDeviceProperties(gpus[i], &properties);
+ if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
+ {
+ useGpu = i;
+ break;
+ }
+ }
+
+ mPhysicalDevice = gpus[useGpu];
+ free(gpus);
+ }
+
+ // Select graphics queue family
+ {
+ uint32_t count;
+ vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &count, NULL);
+
+ auto queues = std::make_unique<VkQueueFamilyProperties[]>(count);
+ vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &count, queues.get());
+ for (uint32_t i = 0; i < count; i++) {
+ if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
+ {
+ mQueueFamily = i;
+ break;
+ }
+ }
+
+ IM_ASSERT(mQueueFamily != (uint32_t)-1);
+ }
+
+ // Create Logical Device (with 1 queue)
+ {
+ int deviceExtensionCount = 1;
+ const char* deviceExtensions[] = { "VK_KHR_swapchain" };
+ const float queuePriority[] = { 1.0f };
+ VkDeviceQueueCreateInfo queue_info[1] = {};
+ queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ queue_info[0].queueFamilyIndex = mQueueFamily;
+ queue_info[0].queueCount = 1;
+ queue_info[0].pQueuePriorities = queuePriority;
+ VkDeviceCreateInfo createInfo = {};
+ createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+ createInfo.queueCreateInfoCount = sizeof(queue_info) / sizeof(queue_info[0]);
+ createInfo.pQueueCreateInfos = queue_info;
+ createInfo.enabledExtensionCount = deviceExtensionCount;
+ createInfo.ppEnabledExtensionNames = deviceExtensions;
+ err = vkCreateDevice(mPhysicalDevice, &createInfo, mAllocator, &mDevice);
+ CheckVkResults(err);
+ vkGetDeviceQueue(mDevice, mQueueFamily, 0, &mQueue);
+ }
+
+ // Create Descriptor Pool
+ {
+ VkDescriptorPoolSize poolSizes[] = {
+ { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
+ { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
+ { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
+ { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
+ { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
+ { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
+ { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
+ { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
+ { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
+ { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
+ { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
+ };
+ VkDescriptorPoolCreateInfo poolInfo = {};
+ poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+ poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
+ poolInfo.maxSets = 1000 * IM_ARRAYSIZE(poolSizes);
+ poolInfo.poolSizeCount = (uint32_t)IM_ARRAYSIZE(poolSizes);
+ poolInfo.pPoolSizes = poolSizes;
+ err = vkCreateDescriptorPool(mDevice, &poolInfo, mAllocator, &mDescriptorPool);
+ CheckVkResults(err);
+ }
+ }
+
+ void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height) {
+ wd->Surface = surface;
+
+ // Check for WSI support
+ VkBool32 res;
+ vkGetPhysicalDeviceSurfaceSupportKHR(mPhysicalDevice, mQueueFamily, wd->Surface, &res);
+ if (res != VK_TRUE) {
+ throw "Error no WSI support on physical device 0.";
+ }
+
+ // Select Surface Format
+ const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM };
+ const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
+ wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(mPhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace);
+
+ // Select Present Mode
+ VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR };
+ wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(mPhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes));
+
+ // Create SwapChain, RenderPass, Framebuffer, etc.
+ IM_ASSERT(mMinImageCount >= 2);
+ ImGui_ImplVulkanH_CreateOrResizeWindow(mInstance, mPhysicalDevice, mDevice, wd, mQueueFamily, mAllocator, width, height, mMinImageCount);
+ }
+
+ void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* drawData) {
+ VkResult err;
+
+ VkSemaphore imageAcquiredSemaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore;
+ VkSemaphore renderCompleteSemaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore;
+ err = vkAcquireNextImageKHR(mDevice, wd->Swapchain, UINT64_MAX, imageAcquiredSemaphore, VK_NULL_HANDLE, &wd->FrameIndex);
+ if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) {
+ mSwapChainRebuild = true;
+ return;
+ }
+ CheckVkResults(err);
+
+ ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex];
+ {
+ err = vkWaitForFences(mDevice, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking
+ CheckVkResults(err);
+
+ err = vkResetFences(mDevice, 1, &fd->Fence);
+ CheckVkResults(err);
+ }
+ {
+ err = vkResetCommandPool(mDevice, fd->CommandPool, 0);
+ CheckVkResults(err);
+ VkCommandBufferBeginInfo info = {};
+ info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+ err = vkBeginCommandBuffer(fd->CommandBuffer, &info);
+ CheckVkResults(err);
+ }
+ {
+ VkRenderPassBeginInfo info = {};
+ info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+ info.renderPass = wd->RenderPass;
+ info.framebuffer = fd->Framebuffer;
+ info.renderArea.extent.width = wd->Width;
+ info.renderArea.extent.height = wd->Height;
+ info.clearValueCount = 1;
+ info.pClearValues = &wd->ClearValue;
+ vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE);
+ }
+
+ // Record dear imgui primitives into command buffer
+ ImGui_ImplVulkan_RenderDrawData(drawData, fd->CommandBuffer);
+
+ // Submit command buffer
+ vkCmdEndRenderPass(fd->CommandBuffer);
+ {
+ VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ VkSubmitInfo info = {};
+ info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ info.waitSemaphoreCount = 1;
+ info.pWaitSemaphores = &imageAcquiredSemaphore;
+ info.pWaitDstStageMask = &wait_stage;
+ info.commandBufferCount = 1;
+ info.pCommandBuffers = &fd->CommandBuffer;
+ info.signalSemaphoreCount = 1;
+ info.pSignalSemaphores = &renderCompleteSemaphore;
+
+ err = vkEndCommandBuffer(fd->CommandBuffer);
+ CheckVkResults(err);
+ err = vkQueueSubmit(mQueue, 1, &info, fd->Fence);
+ CheckVkResults(err);
+ }
+ }
+
+ void FramePresent(ImGui_ImplVulkanH_Window* wd) {
+ if (mSwapChainRebuild) {
+ return;
+ }
+
+ VkSemaphore renderCompleteSemaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore;
+ VkPresentInfoKHR info = {};
+ info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+ info.waitSemaphoreCount = 1;
+ info.pWaitSemaphores = &renderCompleteSemaphore;
+ info.swapchainCount = 1;
+ info.pSwapchains = &wd->Swapchain;
+ info.pImageIndices = &wd->FrameIndex;
+ VkResult err = vkQueuePresentKHR(mQueue, &info);
+ if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) {
+ mSwapChainRebuild = true;
+ return;
+ }
+ CheckVkResults(err);
+ wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->ImageCount; // Now we can use the next set of semaphores
+ }
+
+ void CleanupVulkan() {
+ vkDestroyDescriptorPool(mDevice, mDescriptorPool, mAllocator);
+
+ vkDestroyDevice(mDevice, mAllocator);
+ vkDestroyInstance(mInstance, mAllocator);
+ }
+
+ void CleanupVulkanWindow() {
+ ImGui_ImplVulkanH_DestroyWindow(mInstance, mDevice, &mMainWindowData, mAllocator);
+ }
+
+ static void CheckVkResults(VkResult err) {
+ if (err == 0) return;
+
+ std::string message;
+ message += "Vulkan error: VkResult = ";
+ message += err;
+
+ if (err < 0) {
+ throw std::runtime_error(message);
+ } else {
+ std::cerr << message << '\n';
+ }
+ }
+ static void GlfwErrorCallback(int errorCode, const char* message) {
+ std::cerr << "GLFW Error " << errorCode << ": " << message << "\n";
+ }
+};
+
+std::unique_ptr<RenderingBackend> RenderingBackend::CreateVulkanBackend() {
+ try {
+ return std::make_unique<VulkanBackend>();
+ } catch (std::exception& e) {
+ return nullptr;
+ }
+}
+
+#else // ^^ BUILD_CORE_WITH_VULKAN_BACKEND | ~BUILD_CORE_WITH_VULKAN_BACKEND vv
+
+std::unique_ptr<RenderingBackend> RenderingBackend::CreateVulkanBackend() {
+ return nullptr;
+}
+
+#endif
diff --git a/core/src/Entrypoint/Common.cpp b/core/src/Entrypoint/Common.cpp
deleted file mode 100644
index c949830..0000000
--- a/core/src/Entrypoint/Common.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#include "Common.hpp"
-
-#include <backend/imgui_impl_glfw.h>
-#include <iostream>
-
-#include <backend/imgui_impl_glfw.cpp>
-
-GLFWwindow* RenderingBackend::GetWindow() const {
- return mWindow;
-}
-
-void RenderingBackend::GlfwErrorCallback(int error, const char* message) {
- std::cerr << "GLFW Error " << error << ": " << message << "\n";
-}
diff --git a/core/src/Entrypoint/Common.hpp b/core/src/Entrypoint/Common.hpp
deleted file mode 100644
index 216c885..0000000
--- a/core/src/Entrypoint/Common.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#include <glad/glad.h>
-#include <GLFW/glfw3.h>
-
-class RenderingBackend {
-protected:
- GLFWwindow* mWindow;
-
-public:
- virtual ~RenderingBackend() = default;
- virtual void BeginFrame() = 0;
- virtual void EndFrame() = 0;
-
- GLFWwindow* GetWindow() const;
-
- /// Common GLFW error handle callback for each rendering backend to use.
- static void GlfwErrorCallback(int error, const char* message);
-};
diff --git a/core/src/Entrypoint/DirectX11.cpp b/core/src/Entrypoint/DirectX11.cpp
deleted file mode 100644
index 617447c..0000000
--- a/core/src/Entrypoint/DirectX11.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "DirectX11.hpp"
-
-DirectX11Backend::DirectX11Backend() {
- // TODO
-}
-
-void DirectX11Backend::BeginFrame() {
- // TODO
-}
-
-void DirectX11Backend::EndFrame() {
- // TODO
-}
diff --git a/core/src/Entrypoint/DirectX11.hpp b/core/src/Entrypoint/DirectX11.hpp
deleted file mode 100644
index 134a20f..0000000
--- a/core/src/Entrypoint/DirectX11.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include "Entrypoint/Common.hpp"
-
-class DirectX11Backend : public RenderingBackend {
-public:
- DirectX11Backend();
- virtual ~DirectX11Backend() = default;
- virtual void BeginFrame() override;
- virtual void EndFrame() override;
-};
diff --git a/core/src/Entrypoint/DirectX12.cpp b/core/src/Entrypoint/DirectX12.cpp
deleted file mode 100644
index 1769d24..0000000
--- a/core/src/Entrypoint/DirectX12.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "DirectX12.hpp"
-
-DirectX12Backend::DirectX12Backend() {
- // TODO
-}
-
-void DirectX12Backend::BeginFrame() {
- // TODO
-}
-
-void DirectX12Backend::EndFrame() {
- // TODO
-}
diff --git a/core/src/Entrypoint/DirectX12.hpp b/core/src/Entrypoint/DirectX12.hpp
deleted file mode 100644
index 8b9a4f0..0000000
--- a/core/src/Entrypoint/DirectX12.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include "Entrypoint/Common.hpp"
-
-class DirectX12Backend : public RenderingBackend {
-public:
- DirectX12Backend();
- virtual ~DirectX12Backend() = default;
- virtual void BeginFrame() override;
- virtual void EndFrame() override;
-};
diff --git a/core/src/Entrypoint/Metal.hpp b/core/src/Entrypoint/Metal.hpp
deleted file mode 100644
index cd96d0f..0000000
--- a/core/src/Entrypoint/Metal.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include "Entrypoint/Common.hpp"
-
-class MetalBackend : public RenderingBackend {
-public:
- MetalBackend();
- virtual ~MetalBackend() = default;
- virtual void BeginFrame() override;
- virtual void EndFrame() override;
-};
diff --git a/core/src/Entrypoint/Metal.mm b/core/src/Entrypoint/Metal.mm
deleted file mode 100644
index 3f69634..0000000
--- a/core/src/Entrypoint/Metal.mm
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "Metal.hpp"
-
-MetalBackend::MetalBackend() {
- // TODO
-}
-
-void MetalBackend::BeginFrame() {
- // TODO
-}
-
-void MetalBackend::EndFrame() {
- // TODO
-}
diff --git a/core/src/Entrypoint/OpenGL2.cpp b/core/src/Entrypoint/OpenGL2.cpp
deleted file mode 100644
index 216399e..0000000
--- a/core/src/Entrypoint/OpenGL2.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-#include "OpenGL2.hpp"
-
-#if BUILD_CORE_WITH_OPENGL2_BACKEND
-# include <glad/glad.h>
-# include <GLFW/glfw3.h>
-# include <backend/imgui_impl_glfw.h>
-# include <backend/imgui_impl_opengl2.h>
-# include <imgui.h>
-# include <stdexcept>
-
-# define IMGUI_IMPL_OPENGL_LOADER_GLAD
-# include <backend/imgui_impl_opengl2.cpp>
-
-OpenGL2Backend::OpenGL2Backend() {
- glfwSetErrorCallback(GlfwErrorCallback);
- if (!glfwInit()) {
- throw std::runtime_error("Failed to initialize GLFW.");
- }
-
- mWindow = glfwCreateWindow(1280, 720, "Cplt", nullptr, nullptr);
- if (mWindow == nullptr) {
- throw std::runtime_error("Failed to create GLFW window.");
- }
- glfwMakeContextCurrent(mWindow);
- glfwSwapInterval(1); // Enable vsync
-
- if (gladLoadGLLoader((GLADloadproc)glfwGetProcAddress) == 0) {
- throw std::runtime_error("Failed to initialize OpenGL.");
- }
-
- IMGUI_CHECKVERSION();
- ImGui::CreateContext();
-
- ImGui_ImplGlfw_InitForOpenGL(mWindow, true);
- ImGui_ImplOpenGL2_Init();
-}
-
-OpenGL2Backend::~OpenGL2Backend() {
- ImGui_ImplOpenGL2_Shutdown();
- ImGui_ImplGlfw_Shutdown();
- ImGui::DestroyContext();
-
- glfwDestroyWindow(mWindow);
- glfwTerminate();
-}
-
-void OpenGL2Backend::BeginFrame() {
- glfwPollEvents();
-
- ImGui_ImplOpenGL2_NewFrame();
- ImGui_ImplGlfw_NewFrame();
- ImGui::NewFrame();
-}
-
-void OpenGL2Backend::EndFrame() {
- int displayWidth, displayHeight;
- glfwGetFramebufferSize(mWindow, &displayWidth, &displayHeight);
- glViewport(0, 0, displayWidth, displayHeight);
-
- const ImVec4 kClearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
- glClearColor(kClearColor.x * kClearColor.w, kClearColor.y * kClearColor.w, kClearColor.z * kClearColor.w, kClearColor.w);
- glClear(GL_COLOR_BUFFER_BIT);
-
- ImGui::Render();
- ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData());
-
- glfwMakeContextCurrent(mWindow);
- glfwSwapBuffers(mWindow);
-}
-
-#else // ^^ BUILD_CORE_WITH_OPENGL2_BACKEND | !BUILD_CORE_WITH_OPENGL2_BACKEND vv
-# include <stdexcept>
-
-OpenGL2Backend::OpenGL2Backend() {
- throw std::runtime_error("Backend opengl2 is not available in this build.\n");
-}
-
-OpenGL2Backend::~OpenGL2Backend() {
-}
-
-void OpenGL2Backend::BeginFrame() {
-}
-
-void OpenGL2Backend::EndFrame() {
-}
-
-#endif
diff --git a/core/src/Entrypoint/OpenGL2.hpp b/core/src/Entrypoint/OpenGL2.hpp
deleted file mode 100644
index 86fbad7..0000000
--- a/core/src/Entrypoint/OpenGL2.hpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#pragma once
-
-#include "Entrypoint/Common.hpp"
-
-#include <glad/glad.h>
-
-#include <GLFW/glfw3.h>
-
-class OpenGL2Backend : public RenderingBackend {
-public:
- OpenGL2Backend();
- virtual ~OpenGL2Backend();
- virtual void BeginFrame() override;
- virtual void EndFrame() override;
-};
diff --git a/core/src/Entrypoint/OpenGL3.cpp b/core/src/Entrypoint/OpenGL3.cpp
deleted file mode 100644
index 7d5cae1..0000000
--- a/core/src/Entrypoint/OpenGL3.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-#include "OpenGL3.hpp"
-
-#if BUILD_CORE_WITH_OPENGL3_BACKEND
-# include <glad/glad.h>
-# include <GLFW/glfw3.h>
-# include <backend/imgui_impl_glfw.h>
-# include <backend/imgui_impl_opengl3.h>
-# include <imgui.h>
-# include <stdexcept>
-
-# define IMGUI_IMPL_OPENGL_LOADER_GLAD
-# include <backend/imgui_impl_opengl3.cpp>
-
-OpenGL3Backend::OpenGL3Backend() {
- glfwSetErrorCallback(GlfwErrorCallback);
- if (!glfwInit()) {
- throw std::runtime_error("Failed to initialize GLFW.");
- }
-
-# if PLATFORM_APPLE
- // GL 3.2 + GLSL 150
- const char* glslVersion = "#version 150";
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
- glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
- glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
-# else
- // GL 3.0 + GLSL 130
- const char* glslVersion = "#version 130";
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
- //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
- //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
-# endif
-
- mWindow = glfwCreateWindow(1280, 720, "Cplt", nullptr, nullptr);
- if (mWindow == nullptr) {
- throw std::runtime_error("Failed to create GLFW window.");
- }
- glfwMakeContextCurrent(mWindow);
- glfwSwapInterval(1); // Enable vsync
-
- if (gladLoadGLLoader((GLADloadproc)glfwGetProcAddress) == 0) {
- throw std::runtime_error("Failed to initialize OpenGL.");
- }
-
- IMGUI_CHECKVERSION();
- ImGui::CreateContext();
-
- ImGui_ImplGlfw_InitForOpenGL(mWindow, true);
- ImGui_ImplOpenGL3_Init(glslVersion);
-}
-
-OpenGL3Backend::~OpenGL3Backend() {
- ImGui_ImplOpenGL3_Shutdown();
- ImGui_ImplGlfw_Shutdown();
- ImGui::DestroyContext();
-
- glfwDestroyWindow(mWindow);
- glfwTerminate();
-}
-
-void OpenGL3Backend::BeginFrame() {
- glfwPollEvents();
-
- ImGui_ImplOpenGL3_NewFrame();
- ImGui_ImplGlfw_NewFrame();
- ImGui::NewFrame();
-}
-
-void OpenGL3Backend::EndFrame() {
- int displayWidth, displayHeight;
- glfwGetFramebufferSize(mWindow, &displayWidth, &displayHeight);
- glViewport(0, 0, displayWidth, displayHeight);
-
- const ImVec4 kClearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
- glClearColor(kClearColor.x * kClearColor.w, kClearColor.y * kClearColor.w, kClearColor.z * kClearColor.w, kClearColor.w);
- glClear(GL_COLOR_BUFFER_BIT);
-
- ImGui::Render();
- ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
-
- glfwSwapBuffers(mWindow);
-}
-
-#else // ^^ BUILD_CORE_WITH_OPENGL3_BACKEND | !BUILD_CORE_WITH_OPENGL3_BACKEND vv
-# include <stdexcept>
-
-OpenGL3Backend::OpenGL3Backend() {
- throw std::runtime_error("Backend opengl3 is not available in this build.\n");
-}
-
-OpenGL3Backend::~OpenGL3Backend() {
-}
-
-void OpenGL3Backend::BeginFrame() {
-}
-
-void OpenGL3Backend::EndFrame() {
-}
-
-#endif
diff --git a/core/src/Entrypoint/OpenGL3.hpp b/core/src/Entrypoint/OpenGL3.hpp
deleted file mode 100644
index 52978fa..0000000
--- a/core/src/Entrypoint/OpenGL3.hpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#pragma once
-
-#include "Entrypoint/Common.hpp"
-
-#include <glad/glad.h>
-#include <GLFW/glfw3.h>
-
-class OpenGL3Backend : public RenderingBackend {
-public:
- OpenGL3Backend();
- virtual ~OpenGL3Backend() ;
- virtual void BeginFrame() override;
- virtual void EndFrame() override;
-};
diff --git a/core/src/Entrypoint/Vulkan.cpp b/core/src/Entrypoint/Vulkan.cpp
deleted file mode 100644
index 5af1772..0000000
--- a/core/src/Entrypoint/Vulkan.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "Vulkan.hpp"
-
-VulkanBackend::VulkanBackend() {
- // TODO
-}
-
-void VulkanBackend::BeginFrame() {
- // TODO
-}
-
-void VulkanBackend::EndFrame() {
- // TODO
-}
diff --git a/core/src/Entrypoint/Vulkan.hpp b/core/src/Entrypoint/Vulkan.hpp
deleted file mode 100644
index 6404806..0000000
--- a/core/src/Entrypoint/Vulkan.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include "Entrypoint/Common.hpp"
-
-class VulkanBackend : public RenderingBackend {
-public:
- VulkanBackend();
- virtual ~VulkanBackend() = default;
- virtual void BeginFrame() override;
- virtual void EndFrame() override;
-};
diff --git a/core/src/Entrypoint/main.cpp b/core/src/Entrypoint/main.cpp
index 4f5dc4b..3016467 100644
--- a/core/src/Entrypoint/main.cpp
+++ b/core/src/Entrypoint/main.cpp
@@ -1,15 +1,10 @@
-#include "Entrypoint/Common.hpp"
-#include "Entrypoint/DirectX11.hpp"
-#include "Entrypoint/DirectX12.hpp"
-#include "Entrypoint/Metal.hpp"
-#include "Entrypoint/OpenGL2.hpp"
-#include "Entrypoint/OpenGL3.hpp"
-#include "Entrypoint/Vulkan.hpp"
+#include "Entrypoint/Backend.hpp"
#include "Model/GlobalStates.hpp"
#include "UI/Localization.hpp"
#include "UI/States.hpp"
#include "UI/UI.hpp"
#include "Utils/I18n.hpp"
+#include "Utils/ScopeGuard.hpp"
#include "Utils/Sigslot.hpp"
#include <IconsFontAwesome.h>
@@ -29,57 +24,47 @@ using namespace std::literals::string_view_literals;
static std::unique_ptr<RenderingBackend> CreateDefaultBackend() {
#if PLATFORM_WIN32
# if BUILD_CORE_WITH_DX12_BACKEND
- try {
- auto backend = std::make_unique<DirectX12Backend>();
+ if (auto backend = RenderingBackend::CreateDx12Backend()) {
return backend;
- } catch (const std::exception&) {
}
-# elif BUILD_CORE_WITH_DX11_BACKEND
- try {
- auto backend = std::make_unique<DirectX11Backend>();
+# endif
+# if BUILD_CORE_WITH_DX11_BACKEND
+ if (auto backend = RenderingBackend::CreateDx11Backend()) {
return backend;
- } catch (const std::exception&) {
}
-# elif BUILD_CORE_WITH_VULKAN_BACKEND
- try {
- auto backend = std::make_unique<VulkanBackend>();
+# endif
+# if BUILD_CORE_WITH_VULKAN_BACKEND
+ if (auto backend = RenderingBackend::CreateVulkanBackend()) {
return backend;
- } catch (const std::exception&) {
}
-# elif BUILD_CORE_WITH_OPENGL3_BACKEND
- try {
- auto backend = std::make_unique<OpenGL3Backend>();
+# endif
+# if BUILD_CORE_WITH_OPENGL3_BACKEND
+ if (auto backend = RenderingBackend::CreateOpenGL3Backend()) {
return backend;
- } catch (const std::exception&) {
}
-# elif BUILD_CORE_WITH_OPENGL2_BACKEND
- try {
- auto backend = std::make_unique<OpenGL2Backend>();
+# endif
+# if BUILD_CORE_WITH_OPENGL2_BACKEND
+ if (auto backend = RenderingBackend::CreateOpenGL2Backend()) {
return backend;
- } catch (const std::exception&) {
}
# endif
#elif PLATFORM_MACOS
// We currently only support using metal on macos
- backend = std::make_unique<MetalBackend>();
+ return RenderingBackend::CreateMetalBackend();
#elif PLATFORM_LINUX
# if BUILD_CORE_WITH_VULKAN_BACKEND
- try {
- auto backend = std::make_unique<VulkanBackend>();
+ if (auto backend = RenderingBackend::CreateVulkanBackend()) {
return backend;
- } catch (const std::exception&) {
}
-# elif BUILD_CORE_WITH_OPENGL3_BACKEND
- try {
- auto backend = std::make_unique<OpenGL3Backend>();
+# endif
+# if BUILD_CORE_WITH_OPENGL3_BACKEND
+ if (auto backend = RenderingBackend::CreateOpenGL3Backend()) {
return backend;
- } catch (const std::exception&) {
}
-# elif BUILD_CORE_WITH_OPENGL2_BACKEND
- try {
- auto backend = std::make_unique<OpenGL2Backend>();
+# endif
+# if BUILD_CORE_WITH_OPENGL2_BACKEND
+ if (auto backend = RenderingBackend::CreateOpenGL2Backend()) {
return backend;
- } catch (const std::exception&) {
}
# endif
#endif
@@ -91,24 +76,23 @@ static std::unique_ptr<RenderingBackend> CreateBackend(std::string_view option)
if (option == "default") {
return CreateDefaultBackend();
} else if (option == "opengl2") {
- return std::make_unique<OpenGL2Backend>();
+ return RenderingBackend::CreateOpenGL2Backend();
} else if (option == "opengl3") {
- return std::make_unique<OpenGL3Backend>();
+ return RenderingBackend::CreateOpenGL3Backend();
} else if (option == "vulkan") {
- return std::make_unique<VulkanBackend>();
+ return RenderingBackend::CreateVulkanBackend();
} else if (option == "dx11") {
- return std::make_unique<DirectX11Backend>();
+ return RenderingBackend::CreateDx11Backend();
} else if (option == "dx12") {
- return std::make_unique<DirectX12Backend>();
+ return RenderingBackend::CreateDx12Backend();
} else if (option == "metal") {
- return std::make_unique<MetalBackend>();
+ return RenderingBackend::CreateMetalBackend();
} else {
std::string message;
message += "Unknown backend '";
message += option;
message += "'.\n";
throw std::runtime_error(message);
- return nullptr;
}
}
@@ -175,18 +159,13 @@ int main(int argc, char* argv[]) {
GlobalStates::Init();
}
}
+ DEFER { GlobalStates::Shutdown(); };
UIState::Init();
+ DEFER { UIState::Shutdown(); };
- auto window = backend->GetWindow();
- while (!glfwWindowShouldClose(window)) {
- backend->BeginFrame();
- UI::MainWindow();
- backend->EndFrame();
- }
-
- UIState::Shutdown();
- GlobalStates::Shutdown();
+ // Main loop
+ backend->RunUntilWindowClose(&UI::MainWindow);
return 0;
}