mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-12-24 00:28:38 -07:00
- Implement fallback detection for NVIDIA drivers in Flatpak environments. - Enhance detection for NVIDIA libraries and configurations specific to Flatpak. - Improve logging for graphics backend detection in Flatpak. - Create a test script to validate graphics backend functionality in various environments.
1159 lines
No EOL
47 KiB
C++
1159 lines
No EOL
47 KiB
C++
#include "GraphicsBackendManager.hpp"
|
|
#include <boost/log/trivial.hpp>
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
|
|
#ifdef __linux__
|
|
#include <unistd.h>
|
|
#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;
|
|
return instance;
|
|
}
|
|
|
|
GraphicsBackendManager::GraphicsConfig GraphicsBackendManager::detect_graphics_environment()
|
|
{
|
|
GraphicsConfig config;
|
|
|
|
config.session_type = detect_session_type();
|
|
config.driver = detect_graphics_driver();
|
|
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected session type: "
|
|
<< (config.session_type == SessionType::Wayland ? "Wayland" :
|
|
config.session_type == SessionType::X11 ? "X11" : "Unknown");
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected driver: "
|
|
<< (config.driver == GraphicsDriver::NVIDIA ? "NVIDIA" :
|
|
config.driver == GraphicsDriver::AMD ? "AMD" :
|
|
config.driver == GraphicsDriver::Intel ? "Intel" :
|
|
config.driver == GraphicsDriver::Mesa ? "Mesa" : "Unknown");
|
|
|
|
return config;
|
|
}
|
|
|
|
GraphicsBackendManager::SessionType GraphicsBackendManager::detect_session_type()
|
|
{
|
|
#ifdef __linux__
|
|
// In Flatpak, we should check both the host session and the sandbox session
|
|
bool in_flatpak = (std::getenv("FLATPAK_ID") != nullptr);
|
|
|
|
const char* xdg_session_type = std::getenv("XDG_SESSION_TYPE");
|
|
if (xdg_session_type) {
|
|
std::string session_type(xdg_session_type);
|
|
if (boost::iequals(session_type, "wayland")) {
|
|
if (in_flatpak) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Flatpak with Wayland session detected";
|
|
}
|
|
return SessionType::Wayland;
|
|
} else if (boost::iequals(session_type, "x11")) {
|
|
if (in_flatpak) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Flatpak with X11 session detected";
|
|
}
|
|
return SessionType::X11;
|
|
}
|
|
}
|
|
|
|
// Fallback detection methods
|
|
const char* wayland_display = std::getenv("WAYLAND_DISPLAY");
|
|
if (wayland_display) {
|
|
if (in_flatpak) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Flatpak with WAYLAND_DISPLAY=" << wayland_display;
|
|
}
|
|
return SessionType::Wayland;
|
|
}
|
|
|
|
const char* display = std::getenv("DISPLAY");
|
|
if (display) {
|
|
if (in_flatpak) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Flatpak with DISPLAY=" << display;
|
|
}
|
|
return SessionType::X11;
|
|
}
|
|
#endif
|
|
|
|
return SessionType::Unknown;
|
|
}
|
|
|
|
GraphicsBackendManager::GraphicsDriver GraphicsBackendManager::detect_graphics_driver()
|
|
{
|
|
#ifdef __linux__
|
|
// First try container-aware detection methods
|
|
GraphicsDriver container_driver = detect_graphics_driver_container_aware();
|
|
if (container_driver != GraphicsDriver::Unknown) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected driver via container-aware method";
|
|
return container_driver;
|
|
}
|
|
|
|
// 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();
|
|
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: GLX info: " << (glx_info.empty() ? "empty" : glx_info.substr(0, 100));
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: EGL info: " << (egl_info.empty() ? "empty" : egl_info.substr(0, 100));
|
|
|
|
// Check for NVIDIA
|
|
if (boost::icontains(glx_info, "nvidia") ||
|
|
boost::icontains(egl_info, "nvidia") ||
|
|
boost::icontains(glx_info, "NVIDIA") ||
|
|
boost::icontains(egl_info, "NVIDIA")) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected NVIDIA driver via GLX/EGL";
|
|
return GraphicsDriver::NVIDIA;
|
|
}
|
|
|
|
// Check for AMD
|
|
if (boost::icontains(glx_info, "amd") ||
|
|
boost::icontains(egl_info, "amd") ||
|
|
boost::icontains(glx_info, "AMD") ||
|
|
boost::icontains(egl_info, "AMD") ||
|
|
boost::icontains(glx_info, "radeon") ||
|
|
boost::icontains(egl_info, "radeon")) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected AMD driver via GLX/EGL";
|
|
return GraphicsDriver::AMD;
|
|
}
|
|
|
|
// Check for Intel
|
|
if (boost::icontains(glx_info, "intel") ||
|
|
boost::icontains(egl_info, "intel") ||
|
|
boost::icontains(glx_info, "Intel") ||
|
|
boost::icontains(egl_info, "Intel")) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected Intel driver via GLX/EGL";
|
|
return GraphicsDriver::Intel;
|
|
}
|
|
|
|
// Check for Mesa
|
|
if (boost::icontains(glx_info, "mesa") ||
|
|
boost::icontains(egl_info, "mesa") ||
|
|
boost::icontains(glx_info, "Mesa") ||
|
|
boost::icontains(egl_info, "Mesa")) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected Mesa driver via GLX/EGL";
|
|
return GraphicsDriver::Mesa;
|
|
}
|
|
|
|
// 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 fallback output: " << glx_output.substr(0, 200);
|
|
|
|
if (boost::icontains(glx_output, "nvidia")) {
|
|
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 final fallback";
|
|
return GraphicsDriver::AMD;
|
|
} else if (boost::icontains(glx_output, "intel")) {
|
|
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 final fallback";
|
|
return GraphicsDriver::Mesa;
|
|
}
|
|
}
|
|
|
|
// Try additional fallback 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");
|
|
if (!nvidia_check.empty() && nvidia_check != "N/A") {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected NVIDIA driver via nvidia-smi fallback";
|
|
return GraphicsDriver::NVIDIA;
|
|
}
|
|
|
|
// Check for AMD using lspci
|
|
std::string amd_check = execute_command("lspci | grep -i 'vga\\|3d' | grep -i amd 2>/dev/null");
|
|
if (!amd_check.empty()) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected AMD driver via lspci fallback";
|
|
return GraphicsDriver::AMD;
|
|
}
|
|
|
|
// Check for Intel using lspci
|
|
std::string intel_check = execute_command("lspci | grep -i 'vga\\|3d' | grep -i intel 2>/dev/null");
|
|
if (!intel_check.empty()) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected Intel driver via lspci fallback";
|
|
return GraphicsDriver::Intel;
|
|
}
|
|
|
|
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Failed to detect graphics driver, using Mesa as fallback";
|
|
return GraphicsDriver::Mesa; // Default to Mesa as safest fallback
|
|
#else
|
|
return GraphicsDriver::Unknown;
|
|
#endif
|
|
}
|
|
|
|
GraphicsBackendManager::GraphicsDriver GraphicsBackendManager::detect_graphics_driver_container_aware()
|
|
{
|
|
#ifdef __linux__
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Using container-aware graphics detection";
|
|
|
|
// Check if running in container
|
|
bool in_container = is_running_in_container();
|
|
if (in_container) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Running in container, using filesystem-based detection";
|
|
}
|
|
|
|
// Try environment variable based detection first
|
|
const char* gl_vendor = std::getenv("__GLX_VENDOR_LIBRARY_NAME");
|
|
if (gl_vendor) {
|
|
std::string vendor(gl_vendor);
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: __GLX_VENDOR_LIBRARY_NAME=" << vendor;
|
|
if (boost::icontains(vendor, "nvidia")) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected NVIDIA via environment variable";
|
|
return GraphicsDriver::NVIDIA;
|
|
} else if (boost::icontains(vendor, "mesa")) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected Mesa via environment variable";
|
|
return GraphicsDriver::Mesa;
|
|
}
|
|
}
|
|
|
|
// Try to detect NVIDIA via filesystem access
|
|
// Check for NVIDIA driver files (also check Flatpak-specific paths)
|
|
std::string nvidia_version = read_file_content("/proc/driver/nvidia/version");
|
|
if (!nvidia_version.empty()) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected NVIDIA driver via /proc/driver/nvidia/version";
|
|
return GraphicsDriver::NVIDIA;
|
|
}
|
|
|
|
// In Flatpak, NVIDIA libs might be exposed via runtime extensions
|
|
if (in_container) {
|
|
// Check for NVIDIA libraries in Flatpak runtime paths
|
|
std::string nvidia_lib_check = execute_command("find /usr/lib* /run/host/usr/lib* -name 'libGLX_nvidia.so*' 2>/dev/null | head -1");
|
|
if (!nvidia_lib_check.empty()) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected NVIDIA driver via Flatpak runtime libraries";
|
|
return GraphicsDriver::NVIDIA;
|
|
}
|
|
|
|
// Check if nvidia-smi is available in Flatpak
|
|
std::string nvidia_smi_check = execute_command("which nvidia-smi 2>/dev/null");
|
|
if (!nvidia_smi_check.empty()) {
|
|
std::string gpu_info = execute_command("nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -n1");
|
|
if (!gpu_info.empty() && gpu_info != "N/A") {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected NVIDIA driver via nvidia-smi in Flatpak";
|
|
return GraphicsDriver::NVIDIA;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for DRM devices to detect graphics hardware
|
|
std::string drm_devices = execute_command("ls -1 /sys/class/drm/card*/device/vendor 2>/dev/null | head -5");
|
|
if (!drm_devices.empty()) {
|
|
// Read vendor IDs from DRM
|
|
std::istringstream iss(drm_devices);
|
|
std::string vendor_file;
|
|
while (std::getline(iss, vendor_file)) {
|
|
std::string vendor_id = read_file_content(vendor_file);
|
|
if (!vendor_id.empty()) {
|
|
// Remove newline and whitespace
|
|
boost::trim(vendor_id);
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Found DRM vendor ID: " << vendor_id;
|
|
|
|
if (vendor_id == "0x10de") { // NVIDIA
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected NVIDIA via DRM vendor ID";
|
|
return GraphicsDriver::NVIDIA;
|
|
} else if (vendor_id == "0x1002") { // AMD
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected AMD via DRM vendor ID";
|
|
return GraphicsDriver::AMD;
|
|
} else if (vendor_id == "0x8086") { // Intel
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected Intel via DRM vendor ID";
|
|
return GraphicsDriver::Intel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for Mesa drivers in common locations
|
|
std::string mesa_check = execute_command("find /usr/lib* -name 'libGL.so*' -exec readlink -f {} \\; 2>/dev/null | grep -i mesa | head -1");
|
|
if (!mesa_check.empty()) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected Mesa via library search";
|
|
return GraphicsDriver::Mesa;
|
|
}
|
|
|
|
// If we're in a container and couldn't detect anything specific, check environment
|
|
if (in_container) {
|
|
// Check for common Flatpak environment variables that might hint at graphics setup
|
|
const char* flatpak_gl_drivers = std::getenv("FLATPAK_GL_DRIVERS");
|
|
if (flatpak_gl_drivers) {
|
|
std::string drivers(flatpak_gl_drivers);
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: FLATPAK_GL_DRIVERS=" << drivers;
|
|
if (boost::icontains(drivers, "nvidia")) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Detected NVIDIA via FLATPAK_GL_DRIVERS";
|
|
return GraphicsDriver::NVIDIA;
|
|
}
|
|
}
|
|
|
|
// In containers, we often rely on the host system's graphics
|
|
const char* display = std::getenv("DISPLAY");
|
|
const char* wayland_display = std::getenv("WAYLAND_DISPLAY");
|
|
|
|
if (display || wayland_display) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Container with display forwarding, will try additional detection";
|
|
// Don't immediately default to Mesa - let other detection methods run
|
|
}
|
|
}
|
|
|
|
return GraphicsDriver::Unknown;
|
|
#else
|
|
return GraphicsDriver::Unknown;
|
|
#endif
|
|
}
|
|
|
|
bool GraphicsBackendManager::is_running_in_container()
|
|
{
|
|
#ifdef __linux__
|
|
// Check for Flatpak
|
|
const char* flatpak_id = std::getenv("FLATPAK_ID");
|
|
if (flatpak_id) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Running in Flatpak: " << flatpak_id;
|
|
|
|
// Log additional Flatpak environment information for debugging
|
|
const char* flatpak_sandbox = std::getenv("FLATPAK_SANDBOX_DIR");
|
|
if (flatpak_sandbox) {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Flatpak sandbox dir: " << flatpak_sandbox;
|
|
}
|
|
|
|
const char* xdg_runtime_dir = std::getenv("XDG_RUNTIME_DIR");
|
|
if (xdg_runtime_dir) {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: XDG_RUNTIME_DIR: " << xdg_runtime_dir;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Check for AppImage
|
|
const char* appimage = std::getenv("APPIMAGE");
|
|
const char* orcaslicer_appimage = std::getenv("ORCASLICER_APPIMAGE");
|
|
if (appimage || orcaslicer_appimage) {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Running in AppImage: "
|
|
<< (appimage ? appimage : "detected via ORCASLICER_APPIMAGE");
|
|
return true;
|
|
}
|
|
|
|
// Check for Docker/other containers via cgroup
|
|
std::string cgroup = read_file_content("/proc/1/cgroup");
|
|
if (!cgroup.empty() && (boost::icontains(cgroup, "docker") ||
|
|
boost::icontains(cgroup, "lxc") ||
|
|
boost::icontains(cgroup, "containerd"))) {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Running in container (detected via cgroup)";
|
|
return true;
|
|
}
|
|
|
|
// Check for container via /.dockerenv or other indicators
|
|
std::ifstream dockerenv("/.dockerenv");
|
|
if (dockerenv.good()) {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Running in Docker container";
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
std::string GraphicsBackendManager::read_file_content(const std::string& filepath)
|
|
{
|
|
#ifdef __linux__
|
|
std::ifstream file(filepath);
|
|
if (!file.is_open()) {
|
|
return "";
|
|
}
|
|
|
|
std::stringstream buffer;
|
|
try {
|
|
buffer << file.rdbuf();
|
|
return buffer.str();
|
|
} catch (const std::exception& e) {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Error reading " << filepath << ": " << e.what();
|
|
return "";
|
|
}
|
|
#else
|
|
return "";
|
|
#endif
|
|
}
|
|
|
|
std::string GraphicsBackendManager::get_nvidia_driver_version()
|
|
{
|
|
#ifdef __linux__
|
|
return execute_command("nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -n1");
|
|
#else
|
|
return "";
|
|
#endif
|
|
}
|
|
|
|
bool GraphicsBackendManager::is_nvidia_driver_newer_than(int major_version)
|
|
{
|
|
std::string version = get_nvidia_driver_version();
|
|
if (version.empty()) {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: No NVIDIA driver version detected";
|
|
return false;
|
|
}
|
|
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: NVIDIA driver version: " << version;
|
|
|
|
try {
|
|
size_t dot_pos = version.find('.');
|
|
if (dot_pos != std::string::npos) {
|
|
std::string major_str = version.substr(0, dot_pos);
|
|
int driver_major = std::stoi(major_str);
|
|
bool is_newer = driver_major > major_version;
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Driver major version: " << driver_major
|
|
<< ", threshold: " << major_version << ", is newer: " << is_newer;
|
|
return is_newer;
|
|
} else {
|
|
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Invalid NVIDIA driver version format: " << version;
|
|
return false;
|
|
}
|
|
} catch (const std::exception& e) {
|
|
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Failed to parse NVIDIA driver version '"
|
|
<< version << "': " << e.what();
|
|
return false;
|
|
} catch (...) {
|
|
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Unknown error parsing NVIDIA driver version: " << version;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
// In Flatpak on Wayland, we should try to get the Wayland display
|
|
EGLDisplay egl_display = EGL_NO_DISPLAY;
|
|
|
|
// First try to get Wayland display if we're on Wayland
|
|
const char* wayland_display_env = std::getenv("WAYLAND_DISPLAY");
|
|
if (wayland_display_env) {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Attempting EGL with Wayland display: " << wayland_display_env;
|
|
// Try to get platform display for Wayland (requires EGL 1.5 or EGL_EXT_platform_wayland)
|
|
typedef EGLDisplay (*PFNEGLGETPLATFORMDISPLAYPROC)(EGLenum platform, void *native_display, const EGLint *attrib_list);
|
|
PFNEGLGETPLATFORMDISPLAYPROC eglGetPlatformDisplay =
|
|
(PFNEGLGETPLATFORMDISPLAYPROC) eglGetProcAddress("eglGetPlatformDisplay");
|
|
|
|
if (eglGetPlatformDisplay) {
|
|
// EGL_PLATFORM_WAYLAND_KHR = 0x31D8
|
|
egl_display = eglGetPlatformDisplay(0x31D8, EGL_DEFAULT_DISPLAY, nullptr);
|
|
if (egl_display != EGL_NO_DISPLAY) {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Got EGL display via Wayland platform";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fallback to default display if Wayland-specific didn't work
|
|
if (egl_display == EGL_NO_DISPLAY) {
|
|
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()
|
|
{
|
|
// 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
|
|
// In Flatpak, this might actually work if the runtime has the tools
|
|
std::string glxinfo_result = execute_command("glxinfo 2>/dev/null | grep -E 'OpenGL vendor|OpenGL renderer' | head -10");
|
|
if (!glxinfo_result.empty()) {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: glxinfo fallback succeeded in container/host environment";
|
|
}
|
|
return glxinfo_result;
|
|
}
|
|
|
|
std::string GraphicsBackendManager::get_egl_info()
|
|
{
|
|
// 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
|
|
// In Flatpak, this might actually work if the runtime has the tools
|
|
std::string eglinfo_result = execute_command("eglinfo 2>/dev/null | grep -E 'EGL vendor|EGL renderer' | head -10");
|
|
if (!eglinfo_result.empty()) {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: eglinfo fallback succeeded in container/host environment";
|
|
}
|
|
return eglinfo_result;
|
|
}
|
|
|
|
void GraphicsBackendManager::set_environment_variable(const std::string& name, const std::string& value)
|
|
{
|
|
#ifdef __linux__
|
|
if (!value.empty()) {
|
|
setenv(name.c_str(), value.c_str(), 1);
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Set " << name << "=" << value;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void GraphicsBackendManager::unset_environment_variable(const std::string& name)
|
|
{
|
|
#ifdef __linux__
|
|
unsetenv(name.c_str());
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Unset " << name;
|
|
#endif
|
|
}
|
|
|
|
GraphicsBackendManager::GraphicsConfig GraphicsBackendManager::get_nvidia_wayland_config()
|
|
{
|
|
GraphicsConfig config;
|
|
config.session_type = SessionType::Wayland;
|
|
config.driver = GraphicsDriver::NVIDIA;
|
|
|
|
// Check if we're running in Flatpak
|
|
bool in_flatpak = (std::getenv("FLATPAK_ID") != nullptr);
|
|
|
|
// For NVIDIA on Wayland, we need specific configuration
|
|
if (is_nvidia_driver_newer_than(555)) {
|
|
// Newer NVIDIA drivers (555+) work better with Zink
|
|
config.use_zink = true;
|
|
config.gbm_backend = "dri";
|
|
config.mesa_loader_driver = "zink";
|
|
config.gallium_driver = "zink";
|
|
config.glx_vendor_library = "mesa";
|
|
|
|
// In Flatpak, the EGL vendor library path might be different
|
|
if (in_flatpak) {
|
|
// Try multiple possible paths for Flatpak
|
|
std::string egl_paths[] = {
|
|
"/usr/share/glvnd/egl_vendor.d/50_mesa.json",
|
|
"/run/host/usr/share/glvnd/egl_vendor.d/50_mesa.json",
|
|
"/usr/lib/x86_64-linux-gnu/GL/egl_vendor.d/50_mesa.json"
|
|
};
|
|
|
|
for (const auto& path : egl_paths) {
|
|
std::ifstream test_file(path);
|
|
if (test_file.good()) {
|
|
config.egl_vendor_library = path;
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Found EGL vendor file at " << path;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (config.egl_vendor_library.empty()) {
|
|
// Use default if not found
|
|
config.egl_vendor_library = "/usr/share/glvnd/egl_vendor.d/50_mesa.json";
|
|
}
|
|
} else {
|
|
config.egl_vendor_library = "/usr/share/glvnd/egl_vendor.d/50_mesa.json";
|
|
}
|
|
|
|
config.disable_dmabuf = true;
|
|
} else {
|
|
// Older NVIDIA drivers need different approach
|
|
config.gbm_backend = "dri";
|
|
config.force_dri_backend = true;
|
|
|
|
// In Flatpak with older drivers, we might need additional settings
|
|
if (in_flatpak) {
|
|
// Force software rendering fallback if needed
|
|
config.glx_vendor_library = "mesa";
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Using mesa GLX vendor for Flatpak with older NVIDIA drivers";
|
|
}
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
GraphicsBackendManager::GraphicsConfig GraphicsBackendManager::get_nvidia_x11_config()
|
|
{
|
|
GraphicsConfig config;
|
|
config.session_type = SessionType::X11;
|
|
config.driver = GraphicsDriver::NVIDIA;
|
|
|
|
// For NVIDIA on X11, we can use native drivers
|
|
config.gbm_backend = "dri";
|
|
config.force_dri_backend = true;
|
|
|
|
return config;
|
|
}
|
|
|
|
GraphicsBackendManager::GraphicsConfig GraphicsBackendManager::get_amd_config()
|
|
{
|
|
GraphicsConfig config;
|
|
config.driver = GraphicsDriver::AMD;
|
|
|
|
// AMD drivers generally work well with Mesa
|
|
config.gbm_backend = "dri";
|
|
config.mesa_loader_driver = "radeonsi";
|
|
config.gallium_driver = "radeonsi";
|
|
|
|
return config;
|
|
}
|
|
|
|
GraphicsBackendManager::GraphicsConfig GraphicsBackendManager::get_intel_config()
|
|
{
|
|
GraphicsConfig config;
|
|
config.driver = GraphicsDriver::Intel;
|
|
|
|
// Intel drivers work well with Mesa
|
|
config.gbm_backend = "dri";
|
|
config.mesa_loader_driver = "i965";
|
|
config.gallium_driver = "i965";
|
|
|
|
return config;
|
|
}
|
|
|
|
GraphicsBackendManager::GraphicsConfig GraphicsBackendManager::get_mesa_config()
|
|
{
|
|
GraphicsConfig config;
|
|
config.driver = GraphicsDriver::Mesa;
|
|
|
|
// Mesa software rendering
|
|
config.gbm_backend = "dri";
|
|
config.mesa_loader_driver = "swrast";
|
|
config.gallium_driver = "swrast";
|
|
|
|
return config;
|
|
}
|
|
|
|
GraphicsBackendManager::GraphicsConfig GraphicsBackendManager::get_recommended_config()
|
|
{
|
|
GraphicsConfig detected = detect_graphics_environment();
|
|
GraphicsConfig config;
|
|
|
|
switch (detected.driver) {
|
|
case GraphicsDriver::NVIDIA:
|
|
if (detected.session_type == SessionType::Wayland) {
|
|
config = get_nvidia_wayland_config();
|
|
} else {
|
|
config = get_nvidia_x11_config();
|
|
}
|
|
break;
|
|
case GraphicsDriver::AMD:
|
|
config = get_amd_config();
|
|
break;
|
|
case GraphicsDriver::Intel:
|
|
config = get_intel_config();
|
|
break;
|
|
case GraphicsDriver::Mesa:
|
|
config = get_mesa_config();
|
|
break;
|
|
default:
|
|
// Fallback to basic configuration
|
|
config.gbm_backend = "dri";
|
|
config.force_dri_backend = true;
|
|
break;
|
|
}
|
|
|
|
// Special handling for Flatpak environments
|
|
if (std::getenv("FLATPAK_ID")) {
|
|
// Ensure GBM_BACKEND is always set to "dri" in Flatpak
|
|
if (config.gbm_backend.empty() || config.gbm_backend != "dri") {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Forcing GBM_BACKEND=dri for Flatpak environment";
|
|
config.gbm_backend = "dri";
|
|
}
|
|
|
|
// In Flatpak with Wayland, we might need to disable dmabuf
|
|
if (detected.session_type == SessionType::Wayland) {
|
|
config.disable_dmabuf = true;
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Disabling DMABUF for Flatpak Wayland session";
|
|
}
|
|
|
|
// For unknown drivers in Flatpak, use safe defaults
|
|
if (detected.driver == GraphicsDriver::Unknown) {
|
|
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Unknown driver in Flatpak, using safe Mesa defaults";
|
|
config.driver = GraphicsDriver::Mesa;
|
|
config.gbm_backend = "dri";
|
|
config.force_dri_backend = true;
|
|
config.disable_dmabuf = true;
|
|
}
|
|
}
|
|
|
|
// Copy detected session type to config
|
|
config.session_type = detected.session_type;
|
|
|
|
return config;
|
|
}
|
|
|
|
void GraphicsBackendManager::apply_graphics_config(const GraphicsConfig& config)
|
|
{
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Applying graphics configuration...";
|
|
|
|
// Log if we're in Flatpak for debugging
|
|
if (std::getenv("FLATPAK_ID")) {
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Applying configuration in Flatpak environment";
|
|
}
|
|
|
|
// Validate configuration before applying
|
|
if (!validate_configuration(config)) {
|
|
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Configuration validation failed, but continuing with application";
|
|
}
|
|
|
|
if (config.session_type == SessionType::Unknown) {
|
|
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Unknown session type, using fallback configuration";
|
|
}
|
|
|
|
if (config.driver == GraphicsDriver::Unknown) {
|
|
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Unknown graphics driver, using fallback configuration";
|
|
}
|
|
|
|
// Apply environment variables based on configuration
|
|
if (!config.gbm_backend.empty()) {
|
|
set_environment_variable("GBM_BACKEND", config.gbm_backend);
|
|
} else {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: No GBM_BACKEND specified in configuration";
|
|
}
|
|
|
|
if (!config.mesa_loader_driver.empty()) {
|
|
set_environment_variable("MESA_LOADER_DRIVER_OVERRIDE", config.mesa_loader_driver);
|
|
} else {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: No MESA_LOADER_DRIVER_OVERRIDE specified in configuration";
|
|
}
|
|
|
|
if (!config.gallium_driver.empty()) {
|
|
set_environment_variable("GALLIUM_DRIVER", config.gallium_driver);
|
|
} else {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: No GALLIUM_DRIVER specified in configuration";
|
|
}
|
|
|
|
if (!config.glx_vendor_library.empty()) {
|
|
set_environment_variable("__GLX_VENDOR_LIBRARY_NAME", config.glx_vendor_library);
|
|
} else {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: No __GLX_VENDOR_LIBRARY_NAME specified in configuration";
|
|
}
|
|
|
|
if (!config.egl_vendor_library.empty()) {
|
|
set_environment_variable("__EGL_VENDOR_LIBRARY_FILENAMES", config.egl_vendor_library);
|
|
} else {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: No __EGL_VENDOR_LIBRARY_FILENAMES specified in configuration";
|
|
}
|
|
|
|
if (config.disable_dmabuf) {
|
|
set_environment_variable("WEBKIT_DISABLE_DMABUF_RENDERER", "1");
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Disabled DMABUF renderer";
|
|
}
|
|
|
|
// Force DRI backend if needed
|
|
if (config.force_dri_backend) {
|
|
set_environment_variable("LIBGL_ALWAYS_SOFTWARE", "0");
|
|
set_environment_variable("MESA_GL_VERSION_OVERRIDE", "3.3");
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Forced DRI backend configuration";
|
|
}
|
|
|
|
// Log the final configuration summary
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Configuration applied successfully";
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Session: "
|
|
<< (config.session_type == SessionType::Wayland ? "Wayland" :
|
|
config.session_type == SessionType::X11 ? "X11" : "Unknown");
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Driver: "
|
|
<< (config.driver == GraphicsDriver::NVIDIA ? "NVIDIA" :
|
|
config.driver == GraphicsDriver::AMD ? "AMD" :
|
|
config.driver == GraphicsDriver::Intel ? "Intel" :
|
|
config.driver == GraphicsDriver::Mesa ? "Mesa" : "Unknown");
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Use Zink: " << (config.use_zink ? "Yes" : "No");
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Force DRI: " << (config.force_dri_backend ? "Yes" : "No");
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Disable DMABUF: " << (config.disable_dmabuf ? "Yes" : "No");
|
|
}
|
|
|
|
bool GraphicsBackendManager::is_configuration_optimal()
|
|
{
|
|
// Check if current environment variables are set optimally
|
|
const char* gbm_backend = std::getenv("GBM_BACKEND");
|
|
if (!gbm_backend || std::string(gbm_backend) != "dri") {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GraphicsBackendManager::validate_configuration(const GraphicsConfig& config)
|
|
{
|
|
bool valid = true;
|
|
|
|
// Check for required fields based on driver type
|
|
switch (config.driver) {
|
|
case GraphicsDriver::NVIDIA:
|
|
if (config.session_type == SessionType::Wayland) {
|
|
if (config.gbm_backend.empty()) {
|
|
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: NVIDIA Wayland config missing GBM_BACKEND";
|
|
valid = false;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GraphicsDriver::AMD:
|
|
if (config.mesa_loader_driver.empty()) {
|
|
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: AMD config missing MESA_LOADER_DRIVER_OVERRIDE";
|
|
valid = false;
|
|
}
|
|
break;
|
|
|
|
case GraphicsDriver::Intel:
|
|
if (config.mesa_loader_driver.empty()) {
|
|
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Intel config missing MESA_LOADER_DRIVER_OVERRIDE";
|
|
valid = false;
|
|
}
|
|
break;
|
|
|
|
case GraphicsDriver::Mesa:
|
|
// Mesa config is usually minimal, no specific requirements
|
|
break;
|
|
|
|
case GraphicsDriver::Unknown:
|
|
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Unknown driver type in configuration";
|
|
valid = false;
|
|
break;
|
|
}
|
|
|
|
// Check for conflicting settings
|
|
if (config.use_zink && config.force_dri_backend) {
|
|
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Conflicting settings: use_zink and force_dri_backend both true";
|
|
valid = false;
|
|
}
|
|
|
|
if (valid) {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Configuration validation passed";
|
|
} else {
|
|
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Configuration validation failed";
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
void GraphicsBackendManager::log_graphics_info()
|
|
{
|
|
BOOST_LOG_TRIVIAL(info) << "GraphicsBackendManager: Current graphics information:";
|
|
|
|
try {
|
|
GraphicsConfig detected = detect_graphics_environment();
|
|
BOOST_LOG_TRIVIAL(info) << " Session Type: "
|
|
<< (detected.session_type == SessionType::Wayland ? "Wayland" :
|
|
detected.session_type == SessionType::X11 ? "X11" : "Unknown");
|
|
BOOST_LOG_TRIVIAL(info) << " Graphics Driver: "
|
|
<< (detected.driver == GraphicsDriver::NVIDIA ? "NVIDIA" :
|
|
detected.driver == GraphicsDriver::AMD ? "AMD" :
|
|
detected.driver == GraphicsDriver::Intel ? "Intel" :
|
|
detected.driver == GraphicsDriver::Mesa ? "Mesa" : "Unknown");
|
|
|
|
if (detected.driver == GraphicsDriver::NVIDIA) {
|
|
std::string nvidia_version = get_nvidia_driver_version();
|
|
if (!nvidia_version.empty()) {
|
|
BOOST_LOG_TRIVIAL(info) << " NVIDIA Driver Version: " << nvidia_version;
|
|
}
|
|
}
|
|
} catch (const std::exception& e) {
|
|
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Error during graphics info logging: " << e.what();
|
|
}
|
|
|
|
// Log current environment variables (these are always safe)
|
|
const char* gbm_backend = std::getenv("GBM_BACKEND");
|
|
if (gbm_backend) {
|
|
BOOST_LOG_TRIVIAL(info) << " GBM_BACKEND: " << gbm_backend;
|
|
}
|
|
|
|
const char* mesa_loader = std::getenv("MESA_LOADER_DRIVER_OVERRIDE");
|
|
if (mesa_loader) {
|
|
BOOST_LOG_TRIVIAL(info) << " MESA_LOADER_DRIVER_OVERRIDE: " << mesa_loader;
|
|
}
|
|
|
|
const char* gallium_driver = std::getenv("GALLIUM_DRIVER");
|
|
if (gallium_driver) {
|
|
BOOST_LOG_TRIVIAL(info) << " GALLIUM_DRIVER: " << gallium_driver;
|
|
}
|
|
|
|
// Log container detection
|
|
if (is_running_in_container()) {
|
|
BOOST_LOG_TRIVIAL(info) << " Container Environment: Detected";
|
|
}
|
|
}
|
|
|
|
// Helper function to execute shell commands
|
|
std::string GraphicsBackendManager::execute_command(const std::string& command)
|
|
{
|
|
#ifdef __linux__
|
|
std::string result;
|
|
FILE* pipe = popen(command.c_str(), "r");
|
|
if (!pipe) {
|
|
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Failed to execute command: " << command;
|
|
return "";
|
|
}
|
|
|
|
char buffer[128];
|
|
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
|
|
result += buffer;
|
|
}
|
|
|
|
int status = pclose(pipe);
|
|
if (status != 0) {
|
|
BOOST_LOG_TRIVIAL(warning) << "GraphicsBackendManager: Command failed with status " << status << ": " << command;
|
|
// Don't return empty string for failed commands, as some commands may fail but still provide useful output
|
|
}
|
|
|
|
// Remove trailing newline
|
|
if (!result.empty() && result[result.length()-1] == '\n') {
|
|
result.erase(result.length()-1);
|
|
}
|
|
|
|
if (result.empty()) {
|
|
BOOST_LOG_TRIVIAL(debug) << "GraphicsBackendManager: Command returned no output: " << command;
|
|
}
|
|
|
|
return result;
|
|
#else
|
|
return "";
|
|
#endif
|
|
}
|
|
|
|
} // namespace GUI
|
|
} // namespace Slic3r
|