Skip to content

Commit b10fdd6

Browse files
wh1t3lordmikke89
andcommitted
Add DirectX 12 backends
Co-authored-by: Michael Ragazzon <michael.ragazzon@gmail.com>
1 parent a5ceb26 commit b10fdd6

19 files changed

Lines changed: 25489 additions & 6 deletions

Backends/CMakeLists.txt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,17 @@ target_sources(rmlui_backend_Win32_VK INTERFACE
4949
)
5050
target_link_libraries(rmlui_backend_Win32_VK INTERFACE rmlui_backend_common_headers)
5151

52+
add_library(rmlui_backend_Win32_DX12 INTERFACE)
53+
target_sources(rmlui_backend_Win32_DX12 INTERFACE
54+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_Win32.cpp"
55+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_DX12.cpp"
56+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_Win32_DX12.cpp"
57+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_Win32.h"
58+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_DX12.h"
59+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Include_DirectX_12.h"
60+
)
61+
target_link_libraries(rmlui_backend_Win32_DX12 INTERFACE rmlui_backend_common_headers)
62+
5263
add_library(rmlui_backend_X11_GL2 INTERFACE)
5364
target_sources(rmlui_backend_X11_GL2 INTERFACE
5465
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_X11.cpp"
@@ -113,6 +124,17 @@ if(UNIX)
113124
target_link_libraries(rmlui_backend_SDL_VK INTERFACE ${CMAKE_DL_LIBS})
114125
endif()
115126

127+
add_library(rmlui_backend_SDL_DX12 INTERFACE)
128+
target_sources(rmlui_backend_SDL_DX12 INTERFACE
129+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_SDL.cpp"
130+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_DX12.cpp"
131+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_SDL_DX12.cpp"
132+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_SDL.h"
133+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_DX12.h"
134+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Include_DirectX_12.h"
135+
)
136+
target_link_libraries(rmlui_backend_SDL_DX12 INTERFACE rmlui_backend_common_headers SDL::SDL SDL_image::SDL_image)
137+
116138
add_library(rmlui_backend_SDL_SDLrenderer INTERFACE)
117139
target_sources(rmlui_backend_SDL_SDLrenderer INTERFACE
118140
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_SDL.cpp"
@@ -188,6 +210,17 @@ if(UNIX)
188210
target_link_libraries(rmlui_backend_GLFW_VK INTERFACE ${CMAKE_DL_LIBS})
189211
endif()
190212

213+
add_library(rmlui_backend_GLFW_DX12 INTERFACE)
214+
target_sources(rmlui_backend_GLFW_DX12 INTERFACE
215+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_GLFW.cpp"
216+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_DX12.cpp"
217+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_GLFW_DX12.cpp"
218+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_GLFW.h"
219+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_DX12.h"
220+
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Include_DirectX_12.h"
221+
)
222+
target_link_libraries(rmlui_backend_GLFW_DX12 INTERFACE rmlui_backend_common_headers glfw)
223+
191224
add_library(rmlui_backend_BackwardCompatible_GLFW_GL2 INTERFACE)
192225
target_sources(rmlui_backend_BackwardCompatible_GLFW_GL2 INTERFACE
193226
"${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_GLFW.cpp"
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
#include "RmlUi_Backend.h"
2+
#include "RmlUi_Platform_GLFW.h"
3+
#include "RmlUi_Renderer_DX12.h"
4+
#include <RmlUi/Core/Context.h>
5+
#include <RmlUi/Core/Core.h>
6+
#include <RmlUi/Core/Log.h>
7+
#include <RmlUi/Core/Profiling.h>
8+
#include <GLFW/glfw3.h>
9+
#include <thread>
10+
11+
#define GLFW_EXPOSE_NATIVE_WIN32
12+
#include <GLFW/glfw3native.h>
13+
14+
static void SetupCallbacks(GLFWwindow* window);
15+
16+
static void LogErrorFromGLFW(int error, const char* description)
17+
{
18+
Rml::Log::Message(Rml::Log::LT_ERROR, "GLFW error (0x%x): %s", error, description);
19+
}
20+
21+
/**
22+
Global data used by this backend.
23+
24+
Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown().
25+
*/
26+
struct BackendData {
27+
SystemInterface_GLFW system_interface;
28+
Rml::UniquePtr<RenderInterface_DX12> render_interface;
29+
30+
GLFWwindow* window = nullptr;
31+
Rml::Context* context = nullptr;
32+
KeyDownCallback key_down_callback = nullptr;
33+
34+
int glfw_active_modifiers = 0;
35+
bool running = true;
36+
bool context_dimensions_dirty = true;
37+
};
38+
static Rml::UniquePtr<BackendData> data;
39+
40+
bool Backend::Initialize(const char* window_name, int width, int height, bool allow_resize)
41+
{
42+
RMLUI_ASSERT(!data);
43+
44+
glfwSetErrorCallback(LogErrorFromGLFW);
45+
46+
if (!glfwInit())
47+
{
48+
glfwTerminate();
49+
return false;
50+
}
51+
52+
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
53+
glfwWindowHint(GLFW_RESIZABLE, allow_resize ? GLFW_TRUE : GLFW_FALSE);
54+
glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
55+
56+
GLFWwindow* window = glfwCreateWindow(width, height, window_name, nullptr, nullptr);
57+
58+
if (!window)
59+
{
60+
Rml::Log::Message(Rml::Log::LT_ERROR, "GLFW failed to create window");
61+
return false;
62+
}
63+
64+
HWND window_handle = glfwGetWin32Window(window);
65+
if (!window_handle)
66+
{
67+
Rml::Log::Message(Rml::Log::LT_ERROR, "Could not retrieve native window handle from GLFW");
68+
return false;
69+
}
70+
71+
data = Rml::MakeUnique<BackendData>();
72+
data->window = window;
73+
74+
RmlRendererSettings settings = {};
75+
settings.vsync = true;
76+
settings.msaa_sample_count = RMLUI_RENDER_BACKEND_FIELD_MSAA_SAMPLE_COUNT;
77+
78+
data->render_interface = Rml::MakeUnique<RenderInterface_DX12>(window_handle, settings);
79+
80+
if (!data->render_interface || !(*data->render_interface))
81+
{
82+
Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to initialize DirectX 12 render interface");
83+
data.reset();
84+
return false;
85+
}
86+
87+
// The window size may have been scaled by DPI settings, get the actual pixel size.
88+
glfwGetFramebufferSize(window, &width, &height);
89+
90+
data->system_interface.SetWindow(window);
91+
data->render_interface->SetViewport(width, height);
92+
93+
// Receive num lock and caps lock modifiers for proper handling of numpad inputs in text fields.
94+
glfwSetInputMode(window, GLFW_LOCK_KEY_MODS, GLFW_TRUE);
95+
96+
SetupCallbacks(window);
97+
98+
return true;
99+
}
100+
101+
void Backend::Shutdown()
102+
{
103+
RMLUI_ASSERT(data);
104+
105+
data->render_interface.reset();
106+
107+
glfwDestroyWindow(data->window);
108+
109+
data.reset();
110+
111+
glfwTerminate();
112+
}
113+
114+
Rml::SystemInterface* Backend::GetSystemInterface()
115+
{
116+
RMLUI_ASSERT(data);
117+
return &data->system_interface;
118+
}
119+
120+
Rml::RenderInterface* Backend::GetRenderInterface()
121+
{
122+
RMLUI_ASSERT(data);
123+
return data->render_interface.get();
124+
}
125+
126+
static bool WaitForValidSwapchain()
127+
{
128+
bool result = true;
129+
130+
// In some situations the swapchain may become invalid, such as when the window is minimized. In this state the renderer cannot accept any render
131+
// calls. Since we don't have full control over the main loop here we may risk calls to Context::Render if we were to return. Instead, we keep the
132+
// application inside this loop until we are able to recreate the swapchain and render again.
133+
while (!data->render_interface->IsSwapchainValid())
134+
{
135+
std::this_thread::sleep_for(std::chrono::milliseconds(10));
136+
137+
if (glfwWindowShouldClose(data->window))
138+
{
139+
glfwRestoreWindow(data->window);
140+
result = false;
141+
}
142+
143+
glfwPollEvents();
144+
data->render_interface->RecreateSwapchain();
145+
}
146+
147+
return result;
148+
}
149+
150+
bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save)
151+
{
152+
RMLUI_ASSERT(data && context);
153+
154+
bool result = data->running;
155+
156+
// The initial window size may have been affected by system DPI settings, apply the actual pixel size and dp-ratio to the context.
157+
if (data->context_dimensions_dirty)
158+
{
159+
data->context_dimensions_dirty = false;
160+
161+
Rml::Vector2i window_size;
162+
float dp_ratio = 1.f;
163+
glfwGetFramebufferSize(data->window, &window_size.x, &window_size.y);
164+
glfwGetWindowContentScale(data->window, &dp_ratio, nullptr);
165+
166+
context->SetDimensions(window_size);
167+
context->SetDensityIndependentPixelRatio(dp_ratio);
168+
}
169+
170+
data->context = context;
171+
data->key_down_callback = key_down_callback;
172+
173+
if (power_save)
174+
glfwWaitEventsTimeout(Rml::Math::Min(context->GetNextUpdateDelay(), 10.0));
175+
else
176+
glfwPollEvents();
177+
178+
if (!WaitForValidSwapchain())
179+
result = false;
180+
181+
data->context = nullptr;
182+
data->key_down_callback = nullptr;
183+
184+
result = !glfwWindowShouldClose(data->window);
185+
glfwSetWindowShouldClose(data->window, GLFW_FALSE);
186+
187+
return result;
188+
}
189+
190+
void Backend::RequestExit()
191+
{
192+
RMLUI_ASSERT(data);
193+
data->running = false;
194+
glfwSetWindowShouldClose(data->window, GLFW_TRUE);
195+
}
196+
197+
void Backend::BeginFrame()
198+
{
199+
RMLUI_ASSERT(data);
200+
data->render_interface->BeginFrame();
201+
data->render_interface->Clear();
202+
}
203+
204+
void Backend::PresentFrame()
205+
{
206+
RMLUI_ASSERT(data);
207+
data->render_interface->EndFrame();
208+
209+
// Optional, used to mark frames during performance profiling.
210+
RMLUI_FrameMark;
211+
}
212+
213+
static void SetupCallbacks(GLFWwindow* window)
214+
{
215+
RMLUI_ASSERT(data);
216+
217+
// Key input
218+
glfwSetKeyCallback(window, [](GLFWwindow* /*window*/, int glfw_key, int /*scancode*/, int glfw_action, int glfw_mods) {
219+
if (!data->context)
220+
return;
221+
222+
// Store the active modifiers for later because GLFW doesn't provide them in the callbacks to the mouse input events.
223+
data->glfw_active_modifiers = glfw_mods;
224+
225+
// Override the default key event callback to add global shortcuts for the samples.
226+
Rml::Context* context = data->context;
227+
KeyDownCallback key_down_callback = data->key_down_callback;
228+
229+
switch (glfw_action)
230+
{
231+
case GLFW_PRESS:
232+
case GLFW_REPEAT:
233+
{
234+
const Rml::Input::KeyIdentifier key = RmlGLFW::ConvertKey(glfw_key);
235+
const int key_modifier = RmlGLFW::ConvertKeyModifiers(glfw_mods);
236+
float dp_ratio = 1.f;
237+
glfwGetWindowContentScale(data->window, &dp_ratio, nullptr);
238+
239+
// See if we have any global shortcuts that take priority over the context.
240+
if (key_down_callback && !key_down_callback(context, key, key_modifier, dp_ratio, true))
241+
break;
242+
// Otherwise, hand the event over to the context by calling the input handler as normal.
243+
if (!RmlGLFW::ProcessKeyCallback(context, glfw_key, glfw_action, glfw_mods))
244+
break;
245+
// The key was not consumed by the context either, try keyboard shortcuts of lower priority.
246+
if (key_down_callback && !key_down_callback(context, key, key_modifier, dp_ratio, false))
247+
break;
248+
}
249+
break;
250+
case GLFW_RELEASE: RmlGLFW::ProcessKeyCallback(context, glfw_key, glfw_action, glfw_mods); break;
251+
}
252+
});
253+
254+
glfwSetCharCallback(window, [](GLFWwindow* /*window*/, unsigned int codepoint) { RmlGLFW::ProcessCharCallback(data->context, codepoint); });
255+
256+
glfwSetCursorEnterCallback(window, [](GLFWwindow* /*window*/, int entered) { RmlGLFW::ProcessCursorEnterCallback(data->context, entered); });
257+
258+
// Mouse input
259+
glfwSetCursorPosCallback(window, [](GLFWwindow* window, double xpos, double ypos) {
260+
RmlGLFW::ProcessCursorPosCallback(data->context, window, xpos, ypos, data->glfw_active_modifiers);
261+
});
262+
263+
glfwSetMouseButtonCallback(window, [](GLFWwindow* /*window*/, int button, int action, int mods) {
264+
data->glfw_active_modifiers = mods;
265+
RmlGLFW::ProcessMouseButtonCallback(data->context, button, action, mods);
266+
});
267+
268+
glfwSetScrollCallback(window, [](GLFWwindow* /*window*/, double /*xoffset*/, double yoffset) {
269+
RmlGLFW::ProcessScrollCallback(data->context, yoffset, data->glfw_active_modifiers);
270+
});
271+
272+
// Window events
273+
glfwSetFramebufferSizeCallback(window, [](GLFWwindow* /*window*/, int width, int height) {
274+
data->render_interface->SetViewport(width, height);
275+
RmlGLFW::ProcessFramebufferSizeCallback(data->context, width, height);
276+
});
277+
278+
glfwSetWindowContentScaleCallback(window,
279+
[](GLFWwindow* /*window*/, float xscale, float /*yscale*/) { RmlGLFW::ProcessContentScaleCallback(data->context, xscale); });
280+
}

0 commit comments

Comments
 (0)