From 99ed1801b6590c97d7cf7e6644e3b4ba7deffab1 Mon Sep 17 00:00:00 2001 From: Dario Date: Tue, 13 Jan 2026 12:21:58 -0300 Subject: [PATCH] Add user-defined exclusion list to RenderingDevice based on device names. --- doc/classes/ProjectSettings.xml | 4 + .../d3d12/rendering_context_driver_d3d12.cpp | 2 +- .../metal/rendering_context_driver_metal.cpp | 2 +- .../rendering_context_driver_vulkan.cpp | 2 +- main/main.cpp | 1 + .../rendering/rendering_context_driver.cpp | 112 ++++++++++++++++++ servers/rendering/rendering_context_driver.h | 8 ++ servers/rendering/rendering_device.cpp | 92 +------------- 8 files changed, 132 insertions(+), 91 deletions(-) diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index d90c13c9379d..2d5905239edb 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -3348,6 +3348,10 @@ - [code]d3d12[/code], Direct3D 12 from native drivers. If [member rendering/rendering_device/fallback_to_d3d12] is enabled, this is used as a fallback if Vulkan is not supported. [b]Note:[/b] Starting with Godot 4.6, new projects are configured by default to use [code]d3d12[/code] on Windows. Projects created before Godot 4.6 keep [code]vulkan[/code] for compatibility reasons, but it is recommended to switch them manually to [code]d3d12[/code]. + + Comma-separated list of device names that should be excluded from initializing a RenderingDevice. When used in combination with [member rendering/rendering_device/fallback_to_opengl3], the Compatibility renderer will be used instead if the platform supports it. This setting can be used to redirect users with underperforming or broken drivers to the Compatibility renderer. + [b]Note:[/b] The name must be an exact match of the full GPU model name (case-sensitive). Use [method RenderingDevice.get_device_name] to retrieve the device's name. + If [code]true[/code], the Forward+ renderer will fall back to Direct3D 12 if Vulkan is not supported. The fallback is always attempted regardless of this setting if Vulkan driver support was disabled at compile time. [b]Note:[/b] This setting is implemented only on Windows. diff --git a/drivers/d3d12/rendering_context_driver_d3d12.cpp b/drivers/d3d12/rendering_context_driver_d3d12.cpp index 0e5741e78423..ca8d9cb4209c 100644 --- a/drivers/d3d12/rendering_context_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_context_driver_d3d12.cpp @@ -227,7 +227,7 @@ Error RenderingContextDriverD3D12::initialize() { err = _initialize_devices(); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); - return OK; + return _check_excluded_devices(); } const RenderingContextDriver::Device &RenderingContextDriverD3D12::device_get(uint32_t p_device_index) const { diff --git a/drivers/metal/rendering_context_driver_metal.cpp b/drivers/metal/rendering_context_driver_metal.cpp index 951ff862d57d..f711f4993867 100644 --- a/drivers/metal/rendering_context_driver_metal.cpp +++ b/drivers/metal/rendering_context_driver_metal.cpp @@ -76,7 +76,7 @@ Error RenderingContextDriverMetal::initialize() { int version = (int)props.features.highestFamily - (int)MTL::GPUFamilyApple1 + 1; device.name = vformat("%s (Apple%d)", metal_device->name()->utf8String(), version); - return OK; + return _check_excluded_devices(); } const RenderingContextDriver::Device &RenderingContextDriverMetal::device_get(uint32_t p_device_index) const { diff --git a/drivers/vulkan/rendering_context_driver_vulkan.cpp b/drivers/vulkan/rendering_context_driver_vulkan.cpp index 01510a55ea3f..29943aa47eda 100644 --- a/drivers/vulkan/rendering_context_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_context_driver_vulkan.cpp @@ -940,7 +940,7 @@ Error RenderingContextDriverVulkan::initialize() { err = _initialize_devices(); ERR_FAIL_COND_V(err != OK, err); - return OK; + return _check_excluded_devices(); } const RenderingContextDriver::Device &RenderingContextDriverVulkan::device_get(uint32_t p_device_index) const { diff --git a/main/main.cpp b/main/main.cpp index b8a7c6c35656..48e58ebb7814 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2391,6 +2391,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph GLOBAL_DEF_RST("rendering/rendering_device/fallback_to_vulkan", true); GLOBAL_DEF_RST("rendering/rendering_device/fallback_to_d3d12", true); GLOBAL_DEF_RST("rendering/rendering_device/fallback_to_opengl3", true); + GLOBAL_DEF_RST("rendering/rendering_device/excluded_device_list", ""); } { diff --git a/servers/rendering/rendering_context_driver.cpp b/servers/rendering/rendering_context_driver.cpp index b88b9f5e67be..d3aff43f5bd0 100644 --- a/servers/rendering/rendering_context_driver.cpp +++ b/servers/rendering/rendering_context_driver.cpp @@ -30,6 +30,10 @@ #include "rendering_context_driver.h" +#include "core/config/engine.h" +#include "core/config/project_settings.h" +#include "core/os/os.h" + RenderingContextDriver::~RenderingContextDriver() { } @@ -158,6 +162,58 @@ void RenderingContextDriver::window_destroy(DisplayServerEnums::WindowID p_windo window_surface_map.erase(p_window); } +int32_t RenderingContextDriver::pick_device(SurfaceID p_surface, bool p_print_verbose) { + if (p_print_verbose) { + print_verbose("Devices:"); + } + + int32_t device_index = Engine::get_singleton()->get_gpu_index(); + const uint32_t device_count = device_get_count(); + const bool device_index_out_of_range = (device_index >= int32_t(device_count)); + if (device_index_out_of_range) { + WARN_PRINT(vformat("The specified GPU index %d is out of range on this system (0-%d). Falling back to automatic device selection.", device_index, device_count - 1)); + } + + const bool detect_device = (device_index < 0) || device_index_out_of_range; + uint32_t device_type_score = 0; + for (uint32_t i = 0; i < device_count; i++) { + RenderingContextDriver::Device device_option = device_get(i); + String name = device_option.name; + String vendor = get_device_vendor_name(device_option); + String type = get_device_type_name(device_option); + bool present_supported = p_surface != 0 ? device_supports_present(i, p_surface) : false; + if (p_print_verbose) { + print_verbose(" #" + itos(i) + ": " + vendor + " " + name + " - " + (present_supported ? "Supported" : "Unsupported") + ", " + type); + } + + if (detect_device && (present_supported || p_surface == 0)) { + // If a window was specified, present must be supported by the device to be available as an option. + // Assign a score for each type of device and prefer the device with the higher score. + uint32_t option_score = get_device_type_score(device_option); + if (option_score > device_type_score) { + device_index = i; + device_type_score = option_score; + } + } + } + + return device_index; +} + +Error RenderingContextDriver::_check_excluded_devices() { + // Check if the picked device from the context is excluded from using RenderingDevice. The names must be an exact string match. + String device_list_string = GLOBAL_GET("rendering/rendering_device/excluded_device_list"); + PackedStringArray device_list = device_list_string.split(","); + int32_t device_index = pick_device(0, false); + if (device_index >= 0 && device_index < (int32_t)(device_get_count())) { + const RenderingContextDriver::Device &device = device_get(device_index); + return device_list.has(device.name) ? ERR_CANT_CREATE : OK; + } else { + // No valid device was found, so just fail on this step instead. + return ERR_CANT_CREATE; + } +} + String RenderingContextDriver::get_driver_and_device_memory_report() const { String report; @@ -242,3 +298,59 @@ uint64_t RenderingContextDriver::get_device_memory_by_object_type(uint32_t) cons uint64_t RenderingContextDriver::get_device_allocs_by_object_type(uint32_t) const { return 0; } + +String RenderingContextDriver::get_device_vendor_name(const Device &p_device) { + switch (p_device.vendor) { + case RenderingContextDriver::Vendor::VENDOR_AMD: + return "AMD"; + case RenderingContextDriver::Vendor::VENDOR_IMGTEC: + return "ImgTec"; + case RenderingContextDriver::Vendor::VENDOR_APPLE: + return "Apple"; + case RenderingContextDriver::Vendor::VENDOR_NVIDIA: + return "NVIDIA"; + case RenderingContextDriver::Vendor::VENDOR_ARM: + return "ARM"; + case RenderingContextDriver::Vendor::VENDOR_MICROSOFT: + return "Microsoft"; + case RenderingContextDriver::Vendor::VENDOR_QUALCOMM: + return "Qualcomm"; + case RenderingContextDriver::Vendor::VENDOR_INTEL: + return "Intel"; + default: + return "Unknown"; + } +} + +String RenderingContextDriver::get_device_type_name(const Device &p_device) { + switch (p_device.type) { + case RenderingContextDriver::DEVICE_TYPE_INTEGRATED_GPU: + return "Integrated"; + case RenderingContextDriver::DEVICE_TYPE_DISCRETE_GPU: + return "Discrete"; + case RenderingContextDriver::DEVICE_TYPE_VIRTUAL_GPU: + return "Virtual"; + case RenderingContextDriver::DEVICE_TYPE_CPU: + return "CPU"; + case RenderingContextDriver::DEVICE_TYPE_OTHER: + default: + return "Other"; + } +} + +uint32_t RenderingContextDriver::get_device_type_score(const Device &p_device) { + static const bool prefer_integrated = OS::get_singleton()->get_user_prefers_integrated_gpu(); + switch (p_device.type) { + case RenderingContextDriver::DEVICE_TYPE_INTEGRATED_GPU: + return prefer_integrated ? 5 : 4; + case RenderingContextDriver::DEVICE_TYPE_DISCRETE_GPU: + return prefer_integrated ? 4 : 5; + case RenderingContextDriver::DEVICE_TYPE_VIRTUAL_GPU: + return 3; + case RenderingContextDriver::DEVICE_TYPE_CPU: + return 2; + case RenderingContextDriver::DEVICE_TYPE_OTHER: + default: + return 1; + } +} diff --git a/servers/rendering/rendering_context_driver.h b/servers/rendering/rendering_context_driver.h index dfe4bfbd756a..cc3b77e46066 100644 --- a/servers/rendering/rendering_context_driver.h +++ b/servers/rendering/rendering_context_driver.h @@ -45,6 +45,9 @@ class RenderingContextDriver { private: HashMap window_surface_map; +protected: + Error _check_excluded_devices(); + public: SurfaceID surface_get_from_window(DisplayServerEnums::WindowID p_window) const; Error window_create(DisplayServerEnums::WindowID p_window, const void *p_platform_data); @@ -126,6 +129,7 @@ class RenderingContextDriver { virtual void surface_destroy(SurfaceID p_surface) = 0; virtual bool is_debug_utils_enabled() const = 0; + int32_t pick_device(SurfaceID p_surface, bool p_print_verbose); String get_driver_and_device_memory_report() const; virtual const char *get_tracked_object_name(uint32_t p_type_index) const; @@ -140,4 +144,8 @@ class RenderingContextDriver { virtual uint64_t get_device_allocation_count() const; virtual uint64_t get_device_memory_by_object_type(uint32_t p_type) const; virtual uint64_t get_device_allocs_by_object_type(uint32_t p_type) const; + + static String get_device_vendor_name(const Device &p_device); + static String get_device_type_name(const Device &p_device); + static uint32_t get_device_type_score(const Device &p_device); }; diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index b2f943b66884..0dc825dd04f3 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -60,62 +60,6 @@ /**** HELPER FUNCTIONS ****/ /**************************/ -static String _get_device_vendor_name(const RenderingContextDriver::Device &p_device) { - switch (p_device.vendor) { - case RenderingContextDriver::Vendor::VENDOR_AMD: - return "AMD"; - case RenderingContextDriver::Vendor::VENDOR_IMGTEC: - return "ImgTec"; - case RenderingContextDriver::Vendor::VENDOR_APPLE: - return "Apple"; - case RenderingContextDriver::Vendor::VENDOR_NVIDIA: - return "NVIDIA"; - case RenderingContextDriver::Vendor::VENDOR_ARM: - return "ARM"; - case RenderingContextDriver::Vendor::VENDOR_MICROSOFT: - return "Microsoft"; - case RenderingContextDriver::Vendor::VENDOR_QUALCOMM: - return "Qualcomm"; - case RenderingContextDriver::Vendor::VENDOR_INTEL: - return "Intel"; - default: - return "Unknown"; - } -} - -static String _get_device_type_name(const RenderingContextDriver::Device &p_device) { - switch (p_device.type) { - case RenderingContextDriver::DEVICE_TYPE_INTEGRATED_GPU: - return "Integrated"; - case RenderingContextDriver::DEVICE_TYPE_DISCRETE_GPU: - return "Discrete"; - case RenderingContextDriver::DEVICE_TYPE_VIRTUAL_GPU: - return "Virtual"; - case RenderingContextDriver::DEVICE_TYPE_CPU: - return "CPU"; - case RenderingContextDriver::DEVICE_TYPE_OTHER: - default: - return "Other"; - } -} - -static uint32_t _get_device_type_score(const RenderingContextDriver::Device &p_device) { - static const bool prefer_integrated = OS::get_singleton()->get_user_prefers_integrated_gpu(); - switch (p_device.type) { - case RenderingContextDriver::DEVICE_TYPE_INTEGRATED_GPU: - return prefer_integrated ? 5 : 4; - case RenderingContextDriver::DEVICE_TYPE_DISCRETE_GPU: - return prefer_integrated ? 4 : 5; - case RenderingContextDriver::DEVICE_TYPE_VIRTUAL_GPU: - return 3; - case RenderingContextDriver::DEVICE_TYPE_CPU: - return 2; - case RenderingContextDriver::DEVICE_TYPE_OTHER: - default: - return 1; - } -} - static uint32_t _decode_hit_sbt_range_offset(RD::HitShaderBindingTableRange p_range) { return uint32_t(uint64_t(p_range) & 0xFFFFFFFF); } @@ -7814,7 +7758,7 @@ void RenderingDevice::draw_command_end_label() { } String RenderingDevice::get_device_vendor_name() const { - return _get_device_vendor_name(device); + return RenderingContextDriver::get_device_vendor_name(device); } String RenderingDevice::get_device_name() const { @@ -8308,36 +8252,8 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ context = p_context; driver = context->driver_create(); - print_verbose("Devices:"); - int32_t device_index = Engine::get_singleton()->get_gpu_index(); - const uint32_t device_count = context->device_get_count(); - const bool device_index_out_of_range = (device_index >= int32_t(device_count)); - const bool detect_device = (device_index < 0) || device_index_out_of_range; - - if (device_index_out_of_range) { - WARN_PRINT(vformat("The specified GPU index %d is out of range on this system (0-%d). Falling back to automatic device selection.", device_index, device_count - 1)); - } - - uint32_t device_type_score = 0; - for (uint32_t i = 0; i < device_count; i++) { - RenderingContextDriver::Device device_option = context->device_get(i); - String name = device_option.name; - String vendor = _get_device_vendor_name(device_option); - String type = _get_device_type_name(device_option); - bool present_supported = main_surface != 0 ? context->device_supports_present(i, main_surface) : false; - print_verbose(" #" + itos(i) + ": " + vendor + " " + name + " - " + (present_supported ? "Supported" : "Unsupported") + ", " + type); - if (detect_device && (present_supported || main_surface == 0)) { - // If a window was specified, present must be supported by the device to be available as an option. - // Assign a score for each type of device and prefer the device with the higher score. - uint32_t option_score = _get_device_type_score(device_option); - if (option_score > device_type_score) { - device_index = i; - device_type_score = option_score; - } - } - } - - ERR_FAIL_COND_V_MSG((device_index < 0) || (device_index >= int32_t(device_count)), ERR_CANT_CREATE, "None of the devices supports both graphics and present queues."); + int32_t device_index = context->pick_device(main_surface, true); + ERR_FAIL_COND_V_MSG((device_index < 0) || (device_index >= int32_t(context->device_get_count())), ERR_CANT_CREATE, "None of the devices supports both graphics and present queues."); uint32_t frame_count = 1; if (main_surface != 0) { @@ -8361,7 +8277,7 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ } // Output our device version. - Engine::get_singleton()->print_header(vformat("%s %s - %s - Using Device #%d: %s - %s", get_device_api_name(), get_device_api_version(), rendering_method, device_index, _get_device_vendor_name(device), device.name)); + Engine::get_singleton()->print_header(vformat("%s %s - %s - Using Device #%d: %s - %s", get_device_api_name(), get_device_api_version(), rendering_method, device_index, RenderingContextDriver::get_device_vendor_name(device), device.name)); } // Pick the main queue family. It is worth noting we explicitly do not request the transfer bit, as apparently the specification defines