Path: blob/master/platform/linuxbsd/x11/detect_prime_x11.cpp
10278 views
/**************************************************************************/1/* detect_prime_x11.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#if defined(X11_ENABLED) && defined(GLES3_ENABLED)3132#include "detect_prime_x11.h"3334#include "core/string/print_string.h"35#include "core/string/ustring.h"3637#include "thirdparty/glad/glad/gl.h"38#include "thirdparty/glad/glad/glx.h"3940#ifdef SOWRAP_ENABLED41#include "x11/dynwrappers/xlib-so_wrap.h"42#else43#include <X11/XKBlib.h>44#include <X11/Xlib.h>45#include <X11/Xutil.h>46#endif4748#include <sys/types.h>49#include <sys/wait.h>50#include <unistd.h>51#include <cstdlib>5253#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x209154#define GLX_CONTEXT_MINOR_VERSION_ARB 0x20925556typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLXContext, Bool, const int *);5758// To prevent shadowing warnings59#undef glGetString6061int silent_error_handler(Display *display, XErrorEvent *error) {62static char message[1024];63XGetErrorText(display, error->error_code, message, sizeof(message));64print_verbose(vformat("XServer error: %s"65"\n Major opcode of failed request: %d"66"\n Serial number of failed request: %d"67"\n Current serial number in output stream: %d",68String::utf8(message), (uint64_t)error->request_code, (uint64_t)error->minor_code, (uint64_t)error->serial));6970quick_exit(1);71return 0;72}7374// Runs inside a child. Exiting will not quit the engine.75void DetectPrimeX11::create_context() {76XSetErrorHandler(&silent_error_handler);7778Display *x11_display = XOpenDisplay(nullptr);79Window x11_window;80GLXContext glx_context;8182static int visual_attribs[] = {83GLX_RENDER_TYPE, GLX_RGBA_BIT,84GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,85GLX_DOUBLEBUFFER, true,86GLX_RED_SIZE, 1,87GLX_GREEN_SIZE, 1,88GLX_BLUE_SIZE, 1,89GLX_DEPTH_SIZE, 24,90None91};9293if (gladLoaderLoadGLX(x11_display, XScreenNumberOfScreen(XDefaultScreenOfDisplay(x11_display))) == 0) {94print_verbose("Unable to load GLX, GPU detection skipped.");95quick_exit(1);96}97int fbcount;98GLXFBConfig fbconfig = nullptr;99XVisualInfo *vi = nullptr;100101XSetWindowAttributes swa;102swa.event_mask = StructureNotifyMask;103swa.border_pixel = 0;104unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask;105106GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs, &fbcount);107if (!fbc) {108quick_exit(1);109}110111vi = glXGetVisualFromFBConfig(x11_display, fbc[0]);112113fbconfig = fbc[0];114115static int context_attribs[] = {116GLX_CONTEXT_MAJOR_VERSION_ARB, 3,117GLX_CONTEXT_MINOR_VERSION_ARB, 3,118GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,119GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,120None121};122123glx_context = glXCreateContextAttribsARB(x11_display, fbconfig, nullptr, true, context_attribs);124125swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone);126x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, 10, 10, 0, vi->depth, InputOutput, vi->visual, valuemask, &swa);127128if (!x11_window) {129quick_exit(1);130}131132glXMakeCurrent(x11_display, x11_window, glx_context);133XFree(vi);134}135136int DetectPrimeX11::detect_prime() {137pid_t p;138int priorities[2] = {};139String vendors[2];140String renderers[2];141142vendors[0] = "Unknown";143vendors[1] = "Unknown";144renderers[0] = "Unknown";145renderers[1] = "Unknown";146147for (int i = 0; i < 2; ++i) {148int fdset[2];149150if (pipe(fdset) == -1) {151print_verbose("Failed to pipe(), using default GPU");152return 0;153}154155// Fork so the driver initialization can crash without taking down the engine.156p = fork();157158if (p > 0) {159// Main thread160161int stat_loc = 0;162char string[201];163string[200] = '\0';164165close(fdset[1]);166167waitpid(p, &stat_loc, 0);168169if (!stat_loc) {170// No need to do anything complicated here. Anything less than171// PIPE_BUF will be delivered in one read() call.172// Leave it 'Unknown' otherwise.173if (read(fdset[0], string, sizeof(string) - 1) > 0) {174vendors[i] = string;175renderers[i] = string + strlen(string) + 1;176}177}178179close(fdset[0]);180181} else {182// In child, exit() here will not quit the engine.183184// Prevent false leak reports as we will not be properly185// cleaning up these processes, and fork() makes a copy186// of all globals.187CoreGlobals::leak_reporting_enabled = false;188189char string[201];190191close(fdset[0]);192193if (i) {194setenv("DRI_PRIME", "1", 1);195}196197create_context();198199PFNGLGETSTRINGPROC glGetString = (PFNGLGETSTRINGPROC)glXGetProcAddressARB((GLubyte *)"glGetString");200if (!glGetString) {201print_verbose("Unable to get glGetString, GPU detection skipped.");202quick_exit(1);203}204205const char *vendor = (const char *)glGetString(GL_VENDOR);206const char *renderer = (const char *)glGetString(GL_RENDERER);207208unsigned int vendor_len = strlen(vendor) + 1;209unsigned int renderer_len = strlen(renderer) + 1;210211if (vendor_len + renderer_len >= sizeof(string)) {212renderer_len = 200 - vendor_len;213}214215memcpy(&string, vendor, vendor_len);216memcpy(&string[vendor_len], renderer, renderer_len);217218if (write(fdset[1], string, vendor_len + renderer_len) == -1) {219print_verbose("Couldn't write vendor/renderer string.");220}221close(fdset[1]);222223// The function quick_exit() is used because exit() will call destructors on static objects copied by fork().224// These objects will be freed anyway when the process finishes execution.225quick_exit(0);226}227}228229int preferred = 0;230int priority = 0;231232if (vendors[0] == vendors[1]) {233print_verbose("Only one GPU found, using default.");234return 0;235}236237for (int i = 1; i >= 0; --i) {238const Vendor *v = vendor_map;239while (v->glxvendor) {240if (v->glxvendor == vendors[i]) {241priorities[i] = v->priority;242243if (v->priority >= priority) {244priority = v->priority;245preferred = i;246}247}248++v;249}250}251252print_verbose("Found renderers:");253for (int i = 0; i < 2; ++i) {254print_verbose("Renderer " + itos(i) + ": " + renderers[i] + " with priority: " + itos(priorities[i]));255}256257print_verbose("Using renderer: " + renderers[preferred]);258return preferred;259}260261#endif // X11_ENABLED && GLES3_ENABLED262263264