buildin opengl info detection

This commit is contained in:
SoftFever 2025-08-05 00:01:34 +08:00
parent 632403781f
commit bc5ef7e81a
3 changed files with 373 additions and 33 deletions

View file

@ -9,11 +9,21 @@ OrcaSlicer now includes an automatic graphics backend detection and configuratio
## Problem Solved
Previously, users had to manually set environment variables like `GBM_BACKEND=dri` to make the 3D view work properly on systems like Kubuntu 25.04. This was especially problematic for:
Previously, users had to manually set environment variables like `GBM_BACKEND=dri` to make the 3D view work properly on systems like Kubuntu 25.04. Additionally, the system relied on external command-line tools for graphics detection. This was especially problematic for:
- **Wayland sessions** with NVIDIA drivers
- **Newer graphics drivers** that require specific configurations
- **Different graphics hardware** (NVIDIA, AMD, Intel) requiring different settings
- **Containerized environments** (Docker, Flatpak, AppImage) where external tools might not be available
- **Systems without mesa-utils packages** installed
## Improvements in Current Version
### Direct OpenGL Detection
- **No External Dependencies**: Graphics detection now uses direct OpenGL API calls instead of relying on `glxinfo`/`eglinfo` commands
- **Container-Friendly**: Works reliably in Docker, Flatpak, and AppImage environments
- **Better Performance**: Eliminates subprocess overhead from command execution
- **Enhanced Reliability**: Direct API calls are more dependable than parsing command output
## How It Works
@ -25,6 +35,26 @@ The system automatically detects:
- **Graphics Driver**: NVIDIA, AMD, Intel, or Mesa
- **Driver Version**: For NVIDIA drivers, detects version to apply appropriate settings
#### Detection Methodology
The graphics detection uses a multi-tier approach for maximum reliability:
1. **Direct OpenGL API Calls (Primary)**
- Creates minimal OpenGL contexts using GLX (X11) or EGL (Wayland/headless)
- Uses `glGetString(GL_VENDOR)` and `glGetString(GL_RENDERER)` directly
- Works reliably in containers and restricted environments
- No dependency on external command-line tools
2. **Command-Line Tools (Fallback)**
- Uses `glxinfo` and `eglinfo` commands only if direct detection fails
- Provides compatibility with older detection methods
- Tools are no longer required to be installed
3. **Hardware-Based Detection (Final Fallback)**
- Reads PCI vendor IDs from `/sys/class/drm/`
- Uses `nvidia-smi` for NVIDIA-specific detection
- Filesystem-based detection methods
### 2. Smart Configuration
Based on the detected environment, the system applies optimal settings:
@ -112,13 +142,16 @@ Common log messages:
The system now includes improved error handling and validation:
#### Command Execution Errors
- `GraphicsBackendManager: Failed to execute command: glxinfo`
#### Graphics Detection Errors
- `GraphicsBackendManager: Direct OpenGL detection failed, falling back to command-line tools`
- `GraphicsBackendManager: Failed to create or make current OpenGL context`
- `GraphicsBackendManager: Command failed with status 127: nvidia-smi`
- `GraphicsBackendManager: Command returned no output: eglinfo`
- `GraphicsBackendManager: glGetString returned null pointers`
#### Driver Detection Fallbacks
- `GraphicsBackendManager: GLX/EGL detection failed, trying glxinfo fallback`
#### Driver Detection Methods
- `GraphicsBackendManager: Successfully retrieved OpenGL info directly`
- `GraphicsBackendManager: Direct OpenGL and GLX/EGL detection failed, trying glxinfo fallback`
- `GraphicsBackendManager: Detected NVIDIA driver via glxinfo final fallback`
- `GraphicsBackendManager: Detected NVIDIA driver via nvidia-smi fallback`
- `GraphicsBackendManager: Failed to detect graphics driver, using Mesa as fallback`
@ -134,16 +167,20 @@ The system now includes improved error handling and validation:
### Troubleshooting Common Issues
#### 1. Missing System Tools
If you see "Failed to execute command" messages, install missing tools:
```bash
# For Ubuntu/Debian
sudo apt install mesa-utils glxinfo
#### 1. Graphics Detection Issues
The system now uses direct OpenGL API calls as the primary detection method, eliminating the need for external tools in most cases.
# For Arch Linux
sudo pacman -S mesa-utils glxinfo
If direct detection fails and you see "glxinfo fallback" messages, you can optionally install tools for debugging:
```bash
# For Ubuntu/Debian (optional, for debugging only)
sudo apt install mesa-utils
# For Arch Linux (optional, for debugging only)
sudo pacman -S mesa-utils
```
**Note**: These tools are no longer required for normal operation as the system uses direct OpenGL API calls.
#### 2. Driver Detection Failures
If driver detection fails, check:
```bash
@ -153,7 +190,8 @@ nvidia-smi
# Check graphics hardware
lspci | grep -i vga
# Check OpenGL information
# Check OpenGL information (the system now does this automatically via API calls)
# For manual checking, you can still use:
glxinfo | grep "OpenGL vendor"
```

View file

@ -12,11 +12,19 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <pwd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <EGL/egl.h>
#endif
namespace Slic3r {
namespace GUI {
// Thread safety for OpenGL detection operations
std::mutex GraphicsBackendManager::opengl_detection_mutex_;
GraphicsBackendManager& GraphicsBackendManager::get_instance()
{
static GraphicsBackendManager instance;
@ -79,7 +87,8 @@ GraphicsBackendManager::GraphicsDriver GraphicsBackendManager::detect_graphics_d
return container_driver;
}
// Try to get OpenGL vendor and renderer info
// Try to get OpenGL vendor and renderer info using direct OpenGL API calls
// This creates minimal OpenGL contexts (GLX/EGL) and uses glGetString() directly
std::string glx_info = get_glx_info();
std::string egl_info = get_egl_info();
@ -124,29 +133,29 @@ GraphicsBackendManager::GraphicsDriver GraphicsBackendManager::detect_graphics_d
return GraphicsDriver::Mesa;
}
// Try to run glxinfo as fallback (may not work in containers)
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: GLX/EGL detection failed, trying glxinfo fallback";
// Try to run glxinfo as final fallback (may not work in containers)
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Direct OpenGL and GLX/EGL detection failed, trying glxinfo fallback";
std::string glx_output = execute_command("glxinfo 2>/dev/null | grep 'OpenGL vendor\\|OpenGL renderer'");
if (!glx_output.empty()) {
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: glxinfo output: " << glx_output.substr(0, 200);
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: glxinfo fallback output: " << glx_output.substr(0, 200);
if (boost::icontains(glx_output, "nvidia")) {
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected NVIDIA driver via glxinfo fallback";
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected NVIDIA driver via glxinfo final fallback";
return GraphicsDriver::NVIDIA;
} else if (boost::icontains(glx_output, "amd") || boost::icontains(glx_output, "radeon")) {
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected AMD driver via glxinfo fallback";
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected AMD driver via glxinfo final fallback";
return GraphicsDriver::AMD;
} else if (boost::icontains(glx_output, "intel")) {
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected Intel driver via glxinfo fallback";
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected Intel driver via glxinfo final fallback";
return GraphicsDriver::Intel;
} else if (boost::icontains(glx_output, "mesa")) {
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected Mesa driver via glxinfo fallback";
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected Mesa driver via glxinfo final fallback";
return GraphicsDriver::Mesa;
}
}
// Try additional fallback methods
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: glxinfo fallback failed, trying additional methods";
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: All OpenGL detection methods failed, trying hardware-based fallbacks";
// Check for NVIDIA using nvidia-smi
std::string nvidia_check = execute_command("nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -n1");
@ -366,10 +375,259 @@ bool GraphicsBackendManager::is_nvidia_driver_newer_than(int major_version)
}
}
/**
* Helper function to query OpenGL strings and check for errors.
*
* @param context_type String describing the context type for logging (e.g., "GLX", "EGL")
* @return String containing OpenGL vendor, renderer, and version information,
* or empty string if detection fails or errors occur
*/
std::string GraphicsBackendManager::query_opengl_strings(const std::string& context_type)
{
#ifdef __linux__
// Query OpenGL information
const GLubyte* vendor = glGetString(GL_VENDOR);
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: OpenGL error after glGetString(GL_VENDOR): " << error;
return "";
}
const GLubyte* renderer = glGetString(GL_RENDERER);
error = glGetError();
if (error != GL_NO_ERROR) {
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: OpenGL error after glGetString(GL_RENDERER): " << error;
return "";
}
const GLubyte* version = glGetString(GL_VERSION);
error = glGetError();
if (error != GL_NO_ERROR) {
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: OpenGL error after glGetString(GL_VERSION): " << error;
return "";
}
if (vendor && renderer && version) {
std::string result = "OpenGL vendor string: " + std::string(reinterpret_cast<const char*>(vendor)) + "\n";
result += "OpenGL renderer string: " + std::string(reinterpret_cast<const char*>(renderer)) + "\n";
result += "OpenGL version string: " + std::string(reinterpret_cast<const char*>(version));
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Successfully retrieved OpenGL info via " << context_type;
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Vendor: " << reinterpret_cast<const char*>(vendor);
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Renderer: " << reinterpret_cast<const char*>(renderer);
return result;
}
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: glGetString returned null pointers via " << context_type;
return "";
#else
return "";
#endif
}
/**
* Direct OpenGL graphics detection using native API calls.
*
* This function creates minimal OpenGL contexts to query graphics information directly
* via OpenGL API calls, eliminating dependency on external tools like glxinfo/eglinfo.
*
* Detection process:
* 1. Try GLX context creation (for X11 environments)
* 2. Try EGL context creation (for Wayland/headless environments)
* 3. Query glGetString(GL_VENDOR/GL_RENDERER/GL_VERSION)
* 4. Clean up all created resources
*
* Benefits:
* - Works reliably in containers (Docker, Flatpak, AppImage)
* - No external command dependencies
* - Better error handling and logging
* - Supports both X11 and Wayland
*
* @return String containing OpenGL vendor, renderer, and version information,
* or empty string if detection fails
*/
std::string GraphicsBackendManager::get_opengl_info_direct()
{
#ifdef __linux__
// Thread safety: X11 and EGL operations are not thread-safe
std::lock_guard<std::mutex> lock(opengl_detection_mutex_);
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Attempting direct OpenGL detection";
std::string result;
Display* display = nullptr;
GLXContext context = nullptr;
Window window = 0;
Colormap colormap = 0;
XVisualInfo* visual = nullptr;
try {
// Try X11 first
display = XOpenDisplay(nullptr);
if (display) {
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: X11 display opened successfully";
// Get a visual
int screen = DefaultScreen(display);
int attribs[] = {
GLX_RGBA,
GLX_RED_SIZE, 1,
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
GLX_DEPTH_SIZE, 12,
GLX_DOUBLEBUFFER,
None
};
visual = glXChooseVisual(display, screen, attribs);
if (visual) {
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: GLX visual chosen successfully";
// Create a minimal window
Window root = RootWindow(display, screen);
colormap = XCreateColormap(display, root, visual->visual, AllocNone);
XSetWindowAttributes attrs;
attrs.colormap = colormap;
attrs.event_mask = 0;
window = XCreateWindow(display, root, 0, 0, 1, 1, 0,
visual->depth, InputOutput, visual->visual,
CWColormap | CWEventMask, &attrs);
if (window) {
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: X11 window created successfully";
// Create OpenGL context
context = glXCreateContext(display, visual, nullptr, GL_TRUE);
if (context && glXMakeCurrent(display, window, context)) {
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: OpenGL context created and made current";
// Query OpenGL information using helper function
result = query_opengl_strings("GLX");
glXMakeCurrent(display, None, nullptr);
} else {
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Failed to create or make current OpenGL context";
}
} else {
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Failed to create X11 window";
}
} else {
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Failed to choose GLX visual";
}
} else {
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: X11 display not available, trying EGL";
// Try EGL as fallback (for Wayland or headless)
EGLDisplay egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (egl_display != EGL_NO_DISPLAY) {
EGLint major, minor;
if (eglInitialize(egl_display, &major, &minor)) {
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: EGL initialized successfully";
EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_DEPTH_SIZE, 8,
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
EGL_NONE
};
EGLConfig config;
EGLint num_configs;
if (eglChooseConfig(egl_display, config_attribs, &config, 1, &num_configs) && num_configs > 0) {
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: EGL config chosen successfully";
// Create a pbuffer surface
EGLint pbuffer_attribs[] = {
EGL_WIDTH, 1,
EGL_HEIGHT, 1,
EGL_NONE
};
EGLSurface surface = eglCreatePbufferSurface(egl_display, config, pbuffer_attribs);
if (surface != EGL_NO_SURFACE) {
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: EGL pbuffer surface created successfully";
eglBindAPI(EGL_OPENGL_API);
EGLContext egl_context = eglCreateContext(egl_display, config, EGL_NO_CONTEXT, nullptr);
if (egl_context != EGL_NO_CONTEXT && eglMakeCurrent(egl_display, surface, surface, egl_context)) {
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: EGL context created and made current";
// Query OpenGL information using helper function
result = query_opengl_strings("EGL");
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(egl_display, egl_context);
} else {
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Failed to create or make current EGL context";
}
eglDestroySurface(egl_display, surface);
} else {
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Failed to create EGL pbuffer surface";
}
} else {
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Failed to choose EGL config";
}
eglTerminate(egl_display);
} else {
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Failed to initialize EGL";
}
} else {
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: EGL display not available";
}
}
} catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "GraphicsBackendManager: Exception during direct OpenGL detection: " << e.what();
} catch (...) {
BOOST_LOG_TRIVIAL(error) << "GraphicsBackendManager: Unknown exception during direct OpenGL detection";
}
// Cleanup X11 resources (fixing resource leaks)
if (context) {
glXDestroyContext(display, context);
}
if (window && display) {
XDestroyWindow(display, window);
}
if (colormap && display) {
XFreeColormap(display, colormap);
}
if (visual) {
XFree(visual);
}
if (display) {
XCloseDisplay(display);
}
if (result.empty()) {
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Direct OpenGL detection failed, falling back to command-line tools";
}
return result;
#else
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Direct OpenGL detection not available on this platform";
return "";
#endif
}
std::string GraphicsBackendManager::get_glx_info()
{
// In containers, this might not work or might interfere with initialization
// Use safer detection methods first
// Primary method: Use direct OpenGL API calls via GLX/EGL contexts
// This avoids dependency on external glxinfo command and works better in containers
std::string direct_info = get_opengl_info_direct();
if (!direct_info.empty()) {
return direct_info;
}
// Final fallback: Use glxinfo command if direct detection failed
// This may not work in containers or restricted environments
if (is_running_in_container()) {
return "";
}
@ -378,8 +636,15 @@ std::string GraphicsBackendManager::get_glx_info()
std::string GraphicsBackendManager::get_egl_info()
{
// In containers, this might not work or might interfere with initialization
// Use safer detection methods first
// Primary method: Use direct OpenGL API calls via EGL contexts
// This avoids dependency on external eglinfo command and works better in containers
std::string direct_info = get_opengl_info_direct();
if (!direct_info.empty()) {
return direct_info;
}
// Final fallback: Use eglinfo command if direct detection failed
// This may not work in containers or restricted environments
if (is_running_in_container()) {
return "";
}

View file

@ -2,10 +2,30 @@
#define slic3r_GraphicsBackendManager_hpp_
#include <string>
#include <mutex>
namespace Slic3r {
namespace GUI {
/**
* GraphicsBackendManager - Automatic Graphics Backend Detection and Configuration
*
* This class provides automatic detection of graphics hardware and session types,
* and applies optimal OpenGL/graphics configurations for Linux systems.
*
* Key Features:
* - Direct OpenGL API-based detection (no external tool dependencies)
* - Support for X11 and Wayland sessions
* - NVIDIA, AMD, Intel, and Mesa driver detection
* - Container-aware detection (Docker, Flatpak, AppImage)
* - Automatic environment variable configuration
*
* Detection Methods (in order of preference):
* 1. Direct OpenGL context creation and glGetString() API calls
* 2. Container-aware filesystem detection
* 3. Command-line tools (glxinfo/eglinfo) as fallback
* 4. Hardware-based detection (PCI vendor IDs, nvidia-smi)
*/
class GraphicsBackendManager
{
public:
@ -41,13 +61,23 @@ public:
static GraphicsBackendManager& get_instance();
// Detect the current graphics environment
/**
* Detect the current graphics environment using direct OpenGL API calls.
* Primary detection method uses glGetString() via minimal GLX/EGL contexts.
* Falls back to filesystem and command-line detection if needed.
*/
GraphicsConfig detect_graphics_environment();
// Apply graphics configuration
/**
* Apply graphics configuration by setting appropriate environment variables.
* Validates configuration before applying and logs the applied settings.
*/
void apply_graphics_config(const GraphicsConfig& config);
// Get recommended configuration for current system
/**
* Get recommended configuration for current system based on detected hardware
* and session type. Uses direct OpenGL detection for optimal compatibility.
*/
GraphicsConfig get_recommended_config();
// Check if current configuration is optimal
@ -65,7 +95,7 @@ private:
GraphicsBackendManager(const GraphicsBackendManager&) = delete;
GraphicsBackendManager& operator=(const GraphicsBackendManager&) = delete;
// Helper methods
// Detection helper methods
SessionType detect_session_type();
GraphicsDriver detect_graphics_driver();
GraphicsDriver detect_graphics_driver_container_aware();
@ -73,12 +103,19 @@ private:
std::string read_file_content(const std::string& filepath);
std::string get_nvidia_driver_version();
bool is_nvidia_driver_newer_than(int major_version);
std::string get_glx_info();
std::string get_egl_info();
// Graphics information retrieval (now uses direct OpenGL API calls as primary method)
std::string get_glx_info(); // GLX/X11 graphics info (direct OpenGL + fallback)
std::string get_egl_info(); // EGL graphics info (direct OpenGL + fallback)
std::string get_opengl_info_direct(); // Direct OpenGL detection via GLX/EGL contexts
std::string query_opengl_strings(const std::string& context_type); // Helper to query OpenGL info strings
void set_environment_variable(const std::string& name, const std::string& value);
void unset_environment_variable(const std::string& name);
std::string execute_command(const std::string& command);
// Thread safety for OpenGL operations
static std::mutex opengl_detection_mutex_;
// Configuration templates
GraphicsConfig get_nvidia_wayland_config();
GraphicsConfig get_nvidia_x11_config();