Browse Source

Merge pull request #25391 from hpvb/fix-dri-prime-detect

Properly detect when to use DRI_PRIME
Rémi Verschelde 6 years ago
parent
commit
35bb52011a
4 changed files with 281 additions and 21 deletions
  1. 1 0
      platform/x11/SCsub
  2. 233 0
      platform/x11/detect_prime.cpp
  3. 37 0
      platform/x11/detect_prime.h
  4. 10 21
      platform/x11/os_x11.cpp

+ 1 - 0
platform/x11/SCsub

@@ -13,6 +13,7 @@ common_x11 = [
     "key_mapping_x11.cpp",
     "key_mapping_x11.cpp",
     "joypad_linux.cpp",
     "joypad_linux.cpp",
     "power_x11.cpp",
     "power_x11.cpp",
+    "detect_prime.cpp"
 ]
 ]
 
 
 prog = env.add_program('#bin/godot', ['godot_x11.cpp'] + common_x11)
 prog = env.add_program('#bin/godot', ['godot_x11.cpp'] + common_x11)

+ 233 - 0
platform/x11/detect_prime.cpp

@@ -0,0 +1,233 @@
+/*************************************************************************/
+/*  detect_prime.cpp                                                     */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifdef X11_ENABLED
+#if defined(OPENGL_ENABLED)
+
+#include "core/print_string.h"
+#include "core/ustring.h"
+
+#include <stdlib.h>
+
+#include <GL/gl.h>
+#include <GL/glx.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include <cstring>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
+
+typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLXContext, Bool, const int *);
+
+struct vendor {
+	const char *glxvendor;
+	int priority;
+};
+
+vendor vendormap[] = {
+	{ "Advanced Micro Devices, Inc.", 30 },
+	{ "NVIDIA Corporation", 30 },
+	{ "X.Org", 30 },
+	{ "Intel Open Source Technology Center", 20 },
+	{ "nouveau", 10 },
+	{ "Mesa Project", 0 },
+	{ NULL, 0 }
+};
+
+// Runs inside a child. Exiting will not quit the engine.
+void create_context() {
+	Display *x11_display = XOpenDisplay(NULL);
+	Window x11_window;
+	GLXContext glx_context;
+
+	GLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = (GLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((const GLubyte *)"glXCreateContextAttribsARB");
+
+	static int visual_attribs[] = {
+		GLX_RENDER_TYPE, GLX_RGBA_BIT,
+		GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
+		GLX_DOUBLEBUFFER, true,
+		GLX_RED_SIZE, 1,
+		GLX_GREEN_SIZE, 1,
+		GLX_BLUE_SIZE, 1,
+		GLX_DEPTH_SIZE, 24,
+		None
+	};
+
+	int fbcount;
+	GLXFBConfig fbconfig = 0;
+	XVisualInfo *vi = NULL;
+
+	XSetWindowAttributes swa;
+	swa.event_mask = StructureNotifyMask;
+	swa.border_pixel = 0;
+	unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask;
+
+	GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs, &fbcount);
+	if (!fbc)
+		exit(1);
+
+	vi = glXGetVisualFromFBConfig(x11_display, fbc[0]);
+
+	fbconfig = fbc[0];
+
+	static int context_attribs[] = {
+		GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
+		GLX_CONTEXT_MINOR_VERSION_ARB, 3,
+		GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
+		GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
+		None
+	};
+
+	glx_context = glXCreateContextAttribsARB(x11_display, fbconfig, NULL, true, context_attribs);
+
+	swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone);
+	x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, 10, 10, 0, vi->depth, InputOutput, vi->visual, valuemask, &swa);
+
+	if (!x11_window)
+		exit(1);
+
+	glXMakeCurrent(x11_display, x11_window, glx_context);
+	XFree(vi);
+}
+
+int detect_prime() {
+	pid_t p;
+	int priorities[2];
+	String vendors[2];
+	String renderers[2];
+
+	vendors[0] = "Unknown";
+	vendors[1] = "Unknown";
+	renderers[0] = "Unknown";
+	renderers[1] = "Unknown";
+
+	for (int i = 0; i < 2; ++i) {
+		int fdset[2];
+
+		if (pipe(fdset) == -1) {
+			print_verbose("Failed to pipe(), using default GPU");
+			return 0;
+		}
+
+		// Fork so the driver initialization can crash without taking down the engine.
+		p = fork();
+
+		if (p > 0) {
+			// Main thread
+
+			int stat_loc = 0;
+			char string[201];
+			string[200] = '\0';
+
+			close(fdset[1]);
+
+			waitpid(p, &stat_loc, 0);
+
+			if (!stat_loc) {
+				// No need to do anything complicated here. Anything less than
+				// PIPE_BUF will be delivered in one read() call.
+				read(fdset[0], string, sizeof(string) - 1);
+
+				vendors[i] = string;
+				renderers[i] = string + strlen(string) + 1;
+			}
+
+			close(fdset[0]);
+
+		} else {
+			// In child, exit() here will not quit the engine.
+
+			char string[201];
+
+			close(fdset[0]);
+
+			if (i) setenv("DRI_PRIME", "1", 1);
+			create_context();
+
+			const char *vendor = (const char *)glGetString(GL_VENDOR);
+			const char *renderer = (const char *)glGetString(GL_RENDERER);
+
+			int vendor_len = strlen(vendor) + 1;
+			int renderer_len = strlen(renderer) + 1;
+
+			if (vendor_len + renderer_len >= sizeof(string)) {
+				renderer_len = 200 - vendor_len;
+			}
+
+			memcpy(&string, vendor, vendor_len);
+			memcpy(&string[vendor_len], renderer, renderer_len);
+
+			write(fdset[1], string, vendor_len + renderer_len);
+
+			close(fdset[1]);
+			exit(0);
+		}
+	}
+
+	int preferred = 0;
+	int priority = 0;
+
+	if (vendors[0] == vendors[1]) {
+		print_verbose("Only one GPU found, using default.");
+		return 0;
+	}
+
+	for (int i = 1; i >= 0; --i) {
+		vendor *v = vendormap;
+		while (v->glxvendor) {
+			if (v->glxvendor == vendors[i]) {
+				priorities[i] = v->priority;
+
+				if (v->priority >= priority) {
+					priority = v->priority;
+					preferred = i;
+				}
+			}
+			++v;
+		}
+	}
+
+	print_verbose("Found renderers:");
+	for (int i = 0; i < 2; ++i) {
+		print_verbose("Renderer " + itos(i) + ": " + renderers[i] + " with priority: " + itos(priorities[i]));
+	}
+
+	print_verbose("Using renderer: " + renderers[preferred]);
+	return preferred;
+}
+
+#endif
+#endif

+ 37 - 0
platform/x11/detect_prime.h

@@ -0,0 +1,37 @@
+/*************************************************************************/
+/*  detect_prime.h                                                       */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifdef X11_ENABLED
+#if defined(OPENGL_ENABLED)
+
+int detect_prime();
+
+#endif
+#endif

+ 10 - 21
platform/x11/os_x11.cpp

@@ -29,6 +29,8 @@
 /*************************************************************************/
 /*************************************************************************/
 
 
 #include "os_x11.h"
 #include "os_x11.h"
+#include "detect_prime.h"
+
 #include "core/os/dir_access.h"
 #include "core/os/dir_access.h"
 #include "core/print_string.h"
 #include "core/print_string.h"
 #include "drivers/gles2/rasterizer_gles2.h"
 #include "drivers/gles2/rasterizer_gles2.h"
@@ -240,28 +242,15 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
 
 
 // maybe contextgl wants to be in charge of creating the window
 // maybe contextgl wants to be in charge of creating the window
 #if defined(OPENGL_ENABLED)
 #if defined(OPENGL_ENABLED)
-	// Set DRI_PRIME if not set. This means that Godot should default to a higher-power GPU if it exists.
-	// Note: Due to the final '0' parameter to setenv any existing DRI_PRIME environment variables will not
-	// be overwritten.
-	bool enable_dri_prime = true;
-	// Check if Nouveau is loaded, we don't want to force dGPU usage with that driver.
-	if (FileAccess *f = FileAccess::open("/proc/modules", FileAccess::READ)) {
-		// Match driver name + space
-		String nouveau_str = "nouveau ";
-
-		while (!f->eof_reached()) {
-			String line = f->get_line();
-
-			if (line.begins_with(nouveau_str)) {
-				enable_dri_prime = false;
-				break;
-			}
+	if (getenv("DRI_PRIME") == NULL) {
+		print_verbose("Detecting GPUs, set DRI_PRIME in the environment to override GPU detection logic.");
+		int use_prime = detect_prime();
+
+		if (use_prime) {
+			print_line("Found discrete GPU, setting DRI_PRIME=1 to use it.");
+			print_line("Note: Set DRI_PRIME=0 in the environment to disable Godot from using the discrete GPU.");
+			setenv("DRI_PRIME", "1", 1);
 		}
 		}
-		f->close();
-		memdelete(f);
-	}
-	if (enable_dri_prime) {
-		setenv("DRI_PRIME", "1", 0);
 	}
 	}
 
 
 	ContextGL_X11::ContextType opengl_api_type = ContextGL_X11::GLES_3_0_COMPATIBLE;
 	ContextGL_X11::ContextType opengl_api_type = ContextGL_X11::GLES_3_0_COMPATIBLE;