Skip to content

Commit cf2760a

Browse files
committed
Add user-defined exclusion list to RenderingDevice based on device names.
1 parent 321b8c9 commit cf2760a

8 files changed

Lines changed: 130 additions & 35 deletions

doc/classes/ProjectSettings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3348,6 +3348,10 @@
33483348
- [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.
33493349
[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].
33503350
</member>
3351+
<member name="rendering/rendering_device/excluded_device_list" type="String" setter="" getter="" default="&quot;&quot;">
3352+
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.
3353+
[b]Note:[/b] The name must be an exact match. Use [method RenderingDevice.get_device_name] to retrieve the device's name.
3354+
</member>
33513355
<member name="rendering/rendering_device/fallback_to_d3d12" type="bool" setter="" getter="" default="true">
33523356
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.
33533357
[b]Note:[/b] This setting is implemented only on Windows.

drivers/d3d12/rendering_context_driver_d3d12.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ Error RenderingContextDriverD3D12::initialize() {
227227
err = _initialize_devices();
228228
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
229229

230-
return OK;
230+
return _check_excluded_devices();
231231
}
232232

233233
const RenderingContextDriver::Device &RenderingContextDriverD3D12::device_get(uint32_t p_device_index) const {

drivers/metal/rendering_context_driver_metal.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ Error RenderingContextDriverMetal::initialize() {
7676
int version = (int)props.features.highestFamily - (int)MTL::GPUFamilyApple1 + 1;
7777
device.name = vformat("%s (Apple%d)", metal_device->name()->utf8String(), version);
7878

79-
return OK;
79+
return _check_excluded_devices();
8080
}
8181

8282
const RenderingContextDriver::Device &RenderingContextDriverMetal::device_get(uint32_t p_device_index) const {

drivers/vulkan/rendering_context_driver_vulkan.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -940,7 +940,7 @@ Error RenderingContextDriverVulkan::initialize() {
940940
err = _initialize_devices();
941941
ERR_FAIL_COND_V(err != OK, err);
942942

943-
return OK;
943+
return _check_excluded_devices();
944944
}
945945

946946
const RenderingContextDriver::Device &RenderingContextDriverVulkan::device_get(uint32_t p_device_index) const {

main/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2370,6 +2370,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
23702370
GLOBAL_DEF_RST("rendering/rendering_device/fallback_to_vulkan", true);
23712371
GLOBAL_DEF_RST("rendering/rendering_device/fallback_to_d3d12", true);
23722372
GLOBAL_DEF_RST("rendering/rendering_device/fallback_to_opengl3", true);
2373+
GLOBAL_DEF_RST("rendering/rendering_device/excluded_device_list", "");
23732374
}
23742375

23752376
{

servers/rendering/rendering_context_driver.cpp

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030

3131
#include "rendering_context_driver.h"
3232

33+
#include "core/config/project_settings.h"
34+
3335
RenderingContextDriver::~RenderingContextDriver() {
3436
}
3537

@@ -158,6 +160,58 @@ void RenderingContextDriver::window_destroy(DisplayServerEnums::WindowID p_windo
158160
window_surface_map.erase(p_window);
159161
}
160162

163+
int32_t RenderingContextDriver::pick_device(SurfaceID p_surface, bool p_print_verbose) {
164+
if (p_print_verbose) {
165+
print_verbose("Devices:");
166+
}
167+
168+
int32_t device_index = Engine::get_singleton()->get_gpu_index();
169+
const uint32_t device_count = device_get_count();
170+
const bool device_index_out_of_range = (device_index >= int32_t(device_count));
171+
if (device_index_out_of_range) {
172+
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));
173+
}
174+
175+
const bool detect_device = (device_index < 0) || device_index_out_of_range;
176+
uint32_t device_type_score = 0;
177+
for (uint32_t i = 0; i < device_count; i++) {
178+
RenderingContextDriver::Device device_option = device_get(i);
179+
String name = device_option.name;
180+
String vendor = get_device_vendor_name(device_option);
181+
String type = get_device_type_name(device_option);
182+
bool present_supported = p_surface != 0 ? device_supports_present(i, p_surface) : false;
183+
if (p_print_verbose) {
184+
print_verbose(" #" + itos(i) + ": " + vendor + " " + name + " - " + (present_supported ? "Supported" : "Unsupported") + ", " + type);
185+
}
186+
187+
if (detect_device && (present_supported || p_surface == 0)) {
188+
// If a window was specified, present must be supported by the device to be available as an option.
189+
// Assign a score for each type of device and prefer the device with the higher score.
190+
uint32_t option_score = get_device_type_score(device_option);
191+
if (option_score > device_type_score) {
192+
device_index = i;
193+
device_type_score = option_score;
194+
}
195+
}
196+
}
197+
198+
return device_index;
199+
}
200+
201+
Error RenderingContextDriver::_check_excluded_devices() {
202+
// Check if the picked device from the context is excluded from using RenderingDevice. The names must be an exact string match.
203+
String device_list_string = GLOBAL_GET("rendering/rendering_device/excluded_device_list");
204+
PackedStringArray device_list = device_list_string.split(",");
205+
int32_t device_index = pick_device(0, false);
206+
if (device_index >= 0 && device_index < (int32_t)(device_get_count())) {
207+
const RenderingContextDriver::Device &device = device_get(device_index);
208+
return device_list.has(device.name) ? ERR_CANT_CREATE : OK;
209+
} else {
210+
// No valid device was found, so just fail on this step instead.
211+
return ERR_CANT_CREATE;
212+
}
213+
}
214+
161215
String RenderingContextDriver::get_driver_and_device_memory_report() const {
162216
String report;
163217

@@ -242,3 +296,59 @@ uint64_t RenderingContextDriver::get_device_memory_by_object_type(uint32_t) cons
242296
uint64_t RenderingContextDriver::get_device_allocs_by_object_type(uint32_t) const {
243297
return 0;
244298
}
299+
300+
String RenderingContextDriver::get_device_vendor_name(const Device &p_device) {
301+
switch (p_device.vendor) {
302+
case RenderingContextDriver::Vendor::VENDOR_AMD:
303+
return "AMD";
304+
case RenderingContextDriver::Vendor::VENDOR_IMGTEC:
305+
return "ImgTec";
306+
case RenderingContextDriver::Vendor::VENDOR_APPLE:
307+
return "Apple";
308+
case RenderingContextDriver::Vendor::VENDOR_NVIDIA:
309+
return "NVIDIA";
310+
case RenderingContextDriver::Vendor::VENDOR_ARM:
311+
return "ARM";
312+
case RenderingContextDriver::Vendor::VENDOR_MICROSOFT:
313+
return "Microsoft";
314+
case RenderingContextDriver::Vendor::VENDOR_QUALCOMM:
315+
return "Qualcomm";
316+
case RenderingContextDriver::Vendor::VENDOR_INTEL:
317+
return "Intel";
318+
default:
319+
return "Unknown";
320+
}
321+
}
322+
323+
String RenderingContextDriver::get_device_type_name(const Device &p_device) {
324+
switch (p_device.type) {
325+
case RenderingContextDriver::DEVICE_TYPE_INTEGRATED_GPU:
326+
return "Integrated";
327+
case RenderingContextDriver::DEVICE_TYPE_DISCRETE_GPU:
328+
return "Discrete";
329+
case RenderingContextDriver::DEVICE_TYPE_VIRTUAL_GPU:
330+
return "Virtual";
331+
case RenderingContextDriver::DEVICE_TYPE_CPU:
332+
return "CPU";
333+
case RenderingContextDriver::DEVICE_TYPE_OTHER:
334+
default:
335+
return "Other";
336+
}
337+
}
338+
339+
uint32_t RenderingContextDriver::get_device_type_score(const Device &p_device) {
340+
static const bool prefer_integrated = OS::get_singleton()->get_user_prefers_integrated_gpu();
341+
switch (p_device.type) {
342+
case RenderingContextDriver::DEVICE_TYPE_INTEGRATED_GPU:
343+
return prefer_integrated ? 5 : 4;
344+
case RenderingContextDriver::DEVICE_TYPE_DISCRETE_GPU:
345+
return prefer_integrated ? 4 : 5;
346+
case RenderingContextDriver::DEVICE_TYPE_VIRTUAL_GPU:
347+
return 3;
348+
case RenderingContextDriver::DEVICE_TYPE_CPU:
349+
return 2;
350+
case RenderingContextDriver::DEVICE_TYPE_OTHER:
351+
default:
352+
return 1;
353+
}
354+
}

servers/rendering/rendering_context_driver.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ class RenderingContextDriver {
4545
private:
4646
HashMap<DisplayServerEnums::WindowID, SurfaceID> window_surface_map;
4747

48+
protected:
49+
Error _check_excluded_devices();
50+
4851
public:
4952
SurfaceID surface_get_from_window(DisplayServerEnums::WindowID p_window) const;
5053
Error window_create(DisplayServerEnums::WindowID p_window, const void *p_platform_data);
@@ -126,6 +129,7 @@ class RenderingContextDriver {
126129
virtual void surface_destroy(SurfaceID p_surface) = 0;
127130
virtual bool is_debug_utils_enabled() const = 0;
128131

132+
int32_t pick_device(SurfaceID p_surface, bool p_print_verbose);
129133
String get_driver_and_device_memory_report() const;
130134

131135
virtual const char *get_tracked_object_name(uint32_t p_type_index) const;
@@ -140,4 +144,8 @@ class RenderingContextDriver {
140144
virtual uint64_t get_device_allocation_count() const;
141145
virtual uint64_t get_device_memory_by_object_type(uint32_t p_type) const;
142146
virtual uint64_t get_device_allocs_by_object_type(uint32_t p_type) const;
147+
148+
static String get_device_vendor_name(const Device &p_device);
149+
static String get_device_type_name(const Device &p_device);
150+
static uint32_t get_device_type_score(const Device &p_device);
143151
};

servers/rendering/rendering_device.cpp

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7814,7 +7814,7 @@ void RenderingDevice::draw_command_end_label() {
78147814
}
78157815

78167816
String RenderingDevice::get_device_vendor_name() const {
7817-
return _get_device_vendor_name(device);
7817+
return RenderingContextDriver::get_device_vendor_name(device);
78187818
}
78197819

78207820
String RenderingDevice::get_device_name() const {
@@ -8308,36 +8308,8 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ
83088308
context = p_context;
83098309
driver = context->driver_create();
83108310

8311-
print_verbose("Devices:");
8312-
int32_t device_index = Engine::get_singleton()->get_gpu_index();
8313-
const uint32_t device_count = context->device_get_count();
8314-
const bool device_index_out_of_range = (device_index >= int32_t(device_count));
8315-
const bool detect_device = (device_index < 0) || device_index_out_of_range;
8316-
8317-
if (device_index_out_of_range) {
8318-
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));
8319-
}
8320-
8321-
uint32_t device_type_score = 0;
8322-
for (uint32_t i = 0; i < device_count; i++) {
8323-
RenderingContextDriver::Device device_option = context->device_get(i);
8324-
String name = device_option.name;
8325-
String vendor = _get_device_vendor_name(device_option);
8326-
String type = _get_device_type_name(device_option);
8327-
bool present_supported = main_surface != 0 ? context->device_supports_present(i, main_surface) : false;
8328-
print_verbose(" #" + itos(i) + ": " + vendor + " " + name + " - " + (present_supported ? "Supported" : "Unsupported") + ", " + type);
8329-
if (detect_device && (present_supported || main_surface == 0)) {
8330-
// If a window was specified, present must be supported by the device to be available as an option.
8331-
// Assign a score for each type of device and prefer the device with the higher score.
8332-
uint32_t option_score = _get_device_type_score(device_option);
8333-
if (option_score > device_type_score) {
8334-
device_index = i;
8335-
device_type_score = option_score;
8336-
}
8337-
}
8338-
}
8339-
8340-
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.");
8311+
int32_t device_index = context->pick_device(main_surface, true);
8312+
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.");
83418313

83428314
uint32_t frame_count = 1;
83438315
if (main_surface != 0) {
@@ -8361,7 +8333,7 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ
83618333
}
83628334

83638335
// Output our device version.
8364-
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));
8336+
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));
83658337
}
83668338

83678339
// Pick the main queue family. It is worth noting we explicitly do not request the transfer bit, as apparently the specification defines

0 commit comments

Comments
 (0)