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