mirror of
https://github.com/godotengine/godot.git
synced 2025-10-19 16:03:29 +00:00
[Windows] Offload RenderingDevice
creation test to subprocess.
This commit is contained in:
parent
39c201ca58
commit
ab717497ef
11 changed files with 276 additions and 3 deletions
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue