[Windows] Offload RenderingDevice creation test to subprocess.

This commit is contained in:
Pāvels Nadtočajevs 2025-02-24 11:25:54 +02:00
parent 39c201ca58
commit ab717497ef
11 changed files with 276 additions and 3 deletions

View file

@ -109,6 +109,7 @@ protected:
HasServerFeatureCallback has_server_feature_callback = nullptr; HasServerFeatureCallback has_server_feature_callback = nullptr;
bool _separate_thread_render = false; bool _separate_thread_render = false;
bool _silent_crash_handler = false;
// Functions used by Main to initialize/deinitialize the OS. // Functions used by Main to initialize/deinitialize the OS.
void add_logger(Logger *p_logger); void add_logger(Logger *p_logger);
@ -262,6 +263,9 @@ public:
void set_stdout_enabled(bool p_enabled); void set_stdout_enabled(bool p_enabled);
void set_stderr_enabled(bool p_enabled); void set_stderr_enabled(bool p_enabled);
virtual void set_crash_handler_silent() { _silent_crash_handler = true; }
virtual bool is_crash_handler_silent() { return _silent_crash_handler; }
virtual void disable_crash_handler() {} virtual void disable_crash_handler() {}
virtual bool is_disable_crash_handler() const { return false; } virtual bool is_disable_crash_handler() const { return false; }
virtual void initialize_debugging() {} virtual void initialize_debugging() {}
@ -358,6 +362,10 @@ public:
// This is invoked by the GDExtensionManager after loading GDExtensions specified by the project. // This is invoked by the GDExtensionManager after loading GDExtensions specified by the project.
virtual void load_platform_gdextensions() const {} virtual void load_platform_gdextensions() const {}
// Windows only. Tests OpenGL context and Rendering Device simultaneous creation. This function is expected to crash on some NVIDIA drivers.
virtual bool _test_create_rendering_device_and_gl() const { return true; }
virtual bool _test_create_rendering_device() const { return true; }
OS(); OS();
virtual ~OS(); virtual ~OS();
}; };

View file

@ -973,7 +973,7 @@ ProjectDialog::ProjectDialog() {
default_renderer_type = EditorSettings::get_singleton()->get_setting("project_manager/default_renderer"); default_renderer_type = EditorSettings::get_singleton()->get_setting("project_manager/default_renderer");
} }
rendering_device_supported = DisplayServer::can_create_rendering_device(); rendering_device_supported = DisplayServer::is_rendering_device_supported();
if (!rendering_device_supported) { if (!rendering_device_supported) {
default_renderer_type = "gl_compatibility"; default_renderer_type = "gl_compatibility";

View file

@ -979,6 +979,10 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
String project_path = "."; String project_path = ".";
bool upwards = false; bool upwards = false;
String debug_uri = ""; String debug_uri = "";
#if defined(TOOLS_ENABLED) && defined(WINDOWS_ENABLED)
bool test_rd_creation = false;
bool test_rd_support = false;
#endif
bool skip_breakpoints = false; bool skip_breakpoints = false;
String main_pack; String main_pack;
bool quiet_stdout = false; bool quiet_stdout = false;
@ -1666,6 +1670,12 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
debug_canvas_item_redraw = true; debug_canvas_item_redraw = true;
} else if (arg == "--debug-stringnames") { } else if (arg == "--debug-stringnames") {
StringName::set_debug_stringnames(true); StringName::set_debug_stringnames(true);
#endif
#if defined(TOOLS_ENABLED) && defined(WINDOWS_ENABLED)
} else if (arg == "--test-rd-support") {
test_rd_support = true;
} else if (arg == "--test-rd-creation") {
test_rd_creation = true;
#endif #endif
} else if (arg == "--remote-debug") { } else if (arg == "--remote-debug") {
if (N) { if (N) {
@ -1870,6 +1880,30 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
#endif #endif
} }
#if defined(TOOLS_ENABLED) && defined(WINDOWS_ENABLED)
if (test_rd_support) {
// Test Rendering Device creation and exit.
OS::get_singleton()->set_crash_handler_silent();
if (OS::get_singleton()->_test_create_rendering_device()) {
exit_err = ERR_HELP;
} else {
exit_err = ERR_UNAVAILABLE;
}
goto error;
} else if (test_rd_creation) {
// Test OpenGL context and Rendering Device simultaneous creation and exit.
OS::get_singleton()->set_crash_handler_silent();
if (OS::get_singleton()->_test_create_rendering_device_and_gl()) {
exit_err = ERR_HELP;
} else {
exit_err = ERR_UNAVAILABLE;
}
goto error;
}
#endif
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
if (editor) { if (editor) {
Engine::get_singleton()->set_editor_hint(true); Engine::get_singleton()->set_editor_hint(true);

View file

@ -57,6 +57,10 @@ static void handle_crash(int sig) {
abort(); abort();
} }
if (OS::get_singleton()->is_crash_handler_silent()) {
std::_Exit(0);
}
void *bt_buffer[256]; void *bt_buffer[256];
size_t size = backtrace(bt_buffer, 256); size_t size = backtrace(bt_buffer, 256);
String _execpath = OS::get_singleton()->get_executable_path(); String _execpath = OS::get_singleton()->get_executable_path();

View file

@ -81,6 +81,10 @@ static void handle_crash(int sig) {
abort(); abort();
} }
if (OS::get_singleton()->is_crash_handler_silent()) {
std::_Exit(0);
}
void *bt_buffer[256]; void *bt_buffer[256];
size_t size = backtrace(bt_buffer, 256); size_t size = backtrace(bt_buffer, 256);
String _execpath = OS::get_singleton()->get_executable_path(); String _execpath = OS::get_singleton()->get_executable_path();

View file

@ -41,6 +41,7 @@
// Backtrace code based on: https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app // Backtrace code based on: https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app
#include <algorithm> #include <algorithm>
#include <cstdlib>
#include <iterator> #include <iterator>
#include <string> #include <string>
#include <vector> #include <vector>
@ -127,6 +128,10 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
if (OS::get_singleton()->is_crash_handler_silent()) {
std::_Exit(0);
}
String msg; String msg;
const ProjectSettings *proj_settings = ProjectSettings::get_singleton(); const ProjectSettings *proj_settings = ProjectSettings::get_singleton();
if (proj_settings) { if (proj_settings) {

View file

@ -41,6 +41,7 @@
#include <cxxabi.h> #include <cxxabi.h>
#include <signal.h> #include <signal.h>
#include <algorithm> #include <algorithm>
#include <cstdlib>
#include <iterator> #include <iterator>
#include <string> #include <string>
#include <vector> #include <vector>
@ -133,6 +134,10 @@ extern void CrashHandlerException(int signal) {
return; return;
} }
if (OS::get_singleton()->is_crash_handler_silent()) {
std::_Exit(0);
}
String msg; String msg;
const ProjectSettings *proj_settings = ProjectSettings::get_singleton(); const ProjectSettings *proj_settings = ProjectSettings::get_singleton();
if (proj_settings) { if (proj_settings) {

View file

@ -62,6 +62,24 @@
#include <wbemcli.h> #include <wbemcli.h>
#include <wincrypt.h> #include <wincrypt.h>
#if defined(RD_ENABLED)
#include "servers/rendering/rendering_device.h"
#endif
#if defined(GLES3_ENABLED)
#include "gl_manager_windows_native.h"
#endif
#if defined(VULKAN_ENABLED)
#include "rendering_context_driver_vulkan_windows.h"
#endif
#if defined(D3D12_ENABLED)
#include "drivers/d3d12/rendering_context_driver_d3d12.h"
#endif
#if defined(GLES3_ENABLED)
#include "drivers/gles3/rasterizer_gles3.h"
#endif
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
#pragma pack(push, before_imagehlp, 8) #pragma pack(push, before_imagehlp, 8)
#include <imagehlp.h> #include <imagehlp.h>
@ -2346,6 +2364,99 @@ void OS_Windows::add_frame_delay(bool p_can_draw) {
} }
} }
bool OS_Windows::_test_create_rendering_device() const {
// Tests Rendering Device creation.
bool ok = false;
#if defined(RD_ENABLED)
Error err;
RenderingContextDriver *rcd = nullptr;
#if defined(VULKAN_ENABLED)
rcd = memnew(RenderingContextDriverVulkan);
#endif
#ifdef D3D12_ENABLED
if (rcd == nullptr) {
rcd = memnew(RenderingContextDriverD3D12);
}
#endif
if (rcd != nullptr) {
err = rcd->initialize();
if (err == OK) {
RenderingDevice *rd = memnew(RenderingDevice);
err = rd->initialize(rcd);
memdelete(rd);
rd = nullptr;
if (err == OK) {
ok = true;
}
}
memdelete(rcd);
rcd = nullptr;
}
#endif
return ok;
}
bool OS_Windows::_test_create_rendering_device_and_gl() const {
// Tests OpenGL context and Rendering Device simultaneous creation. This function is expected to crash on some NVIDIA drivers.
WNDCLASSEXW wc_probe;
memset(&wc_probe, 0, sizeof(WNDCLASSEXW));
wc_probe.cbSize = sizeof(WNDCLASSEXW);
wc_probe.style = CS_OWNDC | CS_DBLCLKS;
wc_probe.lpfnWndProc = (WNDPROC)::DefWindowProcW;
wc_probe.cbClsExtra = 0;
wc_probe.cbWndExtra = 0;
wc_probe.hInstance = GetModuleHandle(nullptr);
wc_probe.hIcon = LoadIcon(nullptr, IDI_WINLOGO);
wc_probe.hCursor = nullptr;
wc_probe.hbrBackground = nullptr;
wc_probe.lpszMenuName = nullptr;
wc_probe.lpszClassName = L"Engine probe window";
if (!RegisterClassExW(&wc_probe)) {
return false;
}
HWND hWnd = CreateWindowExW(WS_EX_WINDOWEDGE, L"Engine probe window", L"", WS_OVERLAPPEDWINDOW, 0, 0, 800, 600, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
if (!hWnd) {
UnregisterClassW(L"Engine probe window", GetModuleHandle(nullptr));
return false;
}
bool ok = true;
#ifdef GLES3_ENABLED
GLManagerNative_Windows *test_gl_manager_native = memnew(GLManagerNative_Windows);
if (test_gl_manager_native->window_create(DisplayServer::MAIN_WINDOW_ID, hWnd, GetModuleHandle(nullptr), 800, 600) == OK) {
RasterizerGLES3::make_current(true);
} else {
ok = false;
}
#endif
MSG msg = {};
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
if (ok) {
ok = _test_create_rendering_device();
}
#ifdef GLES3_ENABLED
if (test_gl_manager_native) {
memdelete(test_gl_manager_native);
}
#endif
DestroyWindow(hWnd);
UnregisterClassW(L"Engine probe window", GetModuleHandle(nullptr));
return ok;
}
OS_Windows::OS_Windows(HINSTANCE _hInstance) { OS_Windows::OS_Windows(HINSTANCE _hInstance) {
hInstance = _hInstance; hInstance = _hInstance;

View file

@ -252,6 +252,9 @@ public:
void set_main_window(HWND p_main_window) { main_window = p_main_window; } void set_main_window(HWND p_main_window) { main_window = p_main_window; }
virtual bool _test_create_rendering_device_and_gl() const override;
virtual bool _test_create_rendering_device() const override;
HINSTANCE get_hinstance() { return hInstance; } HINSTANCE get_hinstance() { return hInstance; }
OS_Windows(HINSTANCE _hInstance); OS_Windows(HINSTANCE _hInstance);
~OS_Windows(); ~OS_Windows();

View file

@ -1316,8 +1316,87 @@ void DisplayServer::_input_set_custom_mouse_cursor_func(const Ref<Resource> &p_i
singleton->cursor_set_custom_image(p_image, (CursorShape)p_shape, p_hotspot); singleton->cursor_set_custom_image(p_image, (CursorShape)p_shape, p_hotspot);
} }
bool DisplayServer::is_rendering_device_supported() {
#if defined(RD_ENABLED)
RenderingDevice *device = RenderingDevice::get_singleton();
if (device) {
return true;
}
if (supported_rendering_device == RenderingDeviceCreationStatus::SUCCESS) {
return true;
} else if (supported_rendering_device == RenderingDeviceCreationStatus::FAILURE) {
return false;
}
Error err;
#ifdef WINDOWS_ENABLED
// On some NVIDIA drivers combining OpenGL and RenderingDevice can result in crash, offload the check to the subprocess.
List<String> arguments;
arguments.push_back("--test-rd-support");
String pipe;
int exitcode = 0;
err = OS::get_singleton()->execute(OS::get_singleton()->get_executable_path(), arguments, &pipe, &exitcode);
if (err == OK && exitcode == 0) {
supported_rendering_device = RenderingDeviceCreationStatus::SUCCESS;
return true;
} else {
supported_rendering_device = RenderingDeviceCreationStatus::FAILURE;
return false;
}
#endif
RenderingContextDriver *rcd = nullptr;
#if defined(VULKAN_ENABLED)
rcd = memnew(RenderingContextDriverVulkan);
#endif
#ifdef D3D12_ENABLED
if (rcd == nullptr) {
rcd = memnew(RenderingContextDriverD3D12);
}
#endif
#ifdef METAL_ENABLED
if (rcd == nullptr) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
// Eliminate "RenderingContextDriverMetal is only available on iOS 14.0 or newer".
rcd = memnew(RenderingContextDriverMetal);
#pragma clang diagnostic pop
}
#endif
if (rcd != nullptr) {
err = rcd->initialize();
if (err == OK) {
RenderingDevice *rd = memnew(RenderingDevice);
err = rd->initialize(rcd);
memdelete(rd);
rd = nullptr;
if (err == OK) {
// Creating a RenderingDevice is quite slow.
// Cache the result for future usage, so that it's much faster on subsequent calls.
supported_rendering_device = RenderingDeviceCreationStatus::SUCCESS;
memdelete(rcd);
rcd = nullptr;
return true;
} else {
supported_rendering_device = RenderingDeviceCreationStatus::FAILURE;
}
}
memdelete(rcd);
rcd = nullptr;
}
#endif // RD_ENABLED
return false;
}
bool DisplayServer::can_create_rendering_device() { bool DisplayServer::can_create_rendering_device() {
if (get_singleton()->get_name() == "headless") { if (get_singleton() && get_singleton()->get_name() == "headless") {
return false; return false;
} }
@ -1334,6 +1413,24 @@ bool DisplayServer::can_create_rendering_device() {
} }
Error err; Error err;
#ifdef WINDOWS_ENABLED
// On some NVIDIA drivers combining OpenGL and RenderingDevice can result in crash, offload the check to the subprocess.
List<String> arguments;
arguments.push_back("--test-rd-creation");
String pipe;
int exitcode = 0;
err = OS::get_singleton()->execute(OS::get_singleton()->get_executable_path(), arguments, &pipe, &exitcode);
if (err == OK && exitcode == 0) {
created_rendering_device = RenderingDeviceCreationStatus::SUCCESS;
return true;
} else {
created_rendering_device = RenderingDeviceCreationStatus::FAILURE;
return false;
}
#endif
RenderingContextDriver *rcd = nullptr; RenderingContextDriver *rcd = nullptr;
#if defined(VULKAN_ENABLED) #if defined(VULKAN_ENABLED)

View file

@ -652,9 +652,11 @@ public:
// Used to cache the result of `can_create_rendering_device()` when RenderingDevice isn't currently being used. // Used to cache the result of `can_create_rendering_device()` when RenderingDevice isn't currently being used.
// This is done as creating a RenderingDevice is quite slow. // This is done as creating a RenderingDevice is quite slow.
static inline RenderingDeviceCreationStatus created_rendering_device = RenderingDeviceCreationStatus::UNKNOWN; static inline RenderingDeviceCreationStatus created_rendering_device = RenderingDeviceCreationStatus::UNKNOWN;
static bool can_create_rendering_device(); static bool can_create_rendering_device();
static inline RenderingDeviceCreationStatus supported_rendering_device = RenderingDeviceCreationStatus::UNKNOWN;
static bool is_rendering_device_supported();
DisplayServer(); DisplayServer();
~DisplayServer(); ~DisplayServer();
}; };