Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/imview/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ add_library(imview
src/popup_manager.cpp
src/logging/log_processor.cpp
src/logging/app_log_handler.cpp
src/opengl_capability_checker.cpp
${AUTO_LAYOUT_SRC}
# terminal
${TUI_COMP_SRC})
Expand Down
68 changes: 68 additions & 0 deletions src/imview/include/imview/opengl_capability_checker.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* opengl_capability_checker.hpp
*
* Created on: 2025-01-09
* Description: OpenGL capability detection and validation utility
*
* Copyright (c) 2025 Ruixiang Du (rdu)
*/

#pragma once

#include <string>
#include <vector>

namespace quickviz {

struct OpenGLCapabilities {
int major_version = 0;
int minor_version = 0;
std::string version_string;
std::string vendor;
std::string renderer;
std::string glsl_version;
bool core_profile = false;
bool supports_required_version = false;
std::vector<std::string> available_extensions;
std::string error_message;
};

class OpenGLCapabilityChecker {
public:
/**
* @brief Check OpenGL capabilities and validate minimum requirements
* @param required_major Minimum required OpenGL major version (default: 3)
* @param required_minor Minimum required OpenGL minor version (default: 3)
* @return OpenGLCapabilities structure with detected capabilities
*/
static OpenGLCapabilities CheckCapabilities(int required_major = 3,
int required_minor = 3);

/**
* @brief Print detailed OpenGL capability information to console
* @param capabilities OpenGL capabilities structure
*/
static void PrintCapabilities(const OpenGLCapabilities& capabilities);

/**
* @brief Check if a specific OpenGL extension is supported
* @param extension_name Name of the extension to check
* @return True if extension is supported, false otherwise
*/
static bool IsExtensionSupported(const std::string& extension_name);

/**
* @brief Validate that the OpenGL context supports required features
* @param capabilities OpenGL capabilities structure
* @return True if all required features are supported, false otherwise
*/
static bool ValidateRequiredFeatures(const OpenGLCapabilities& capabilities);

private:
static std::vector<std::string> GetAvailableExtensions();
static bool IsIntelGPU(const std::string& vendor, const std::string& renderer);
static bool IsAMDGPU(const std::string& vendor, const std::string& renderer);
static bool IsNVIDIAGPU(const std::string& vendor, const std::string& renderer);
};

} // namespace quickviz
1 change: 1 addition & 0 deletions src/imview/include/imview/viewer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class Viewer : public Window {
private:
void LoadDefaultStyle();
void OnResize(GLFWwindow* window, int width, int height);
void CheckOpenGLCapabilities();

void EnumerateJoysticks();
void OnJoystickEvent(int id, int event);
Expand Down
213 changes: 213 additions & 0 deletions src/imview/src/opengl_capability_checker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
/*
* opengl_capability_checker.cpp
*
* Created on: 2025-01-09
* Description: OpenGL capability detection and validation utility
*
* Copyright (c) 2025 Ruixiang Du (rdu)
*/

#include "imview/opengl_capability_checker.hpp"

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstring>

#ifdef IMVIEW_WITH_GLAD
#include "glad/glad.h"
#else
#include <GL/gl.h>
#endif

namespace quickviz {

OpenGLCapabilities OpenGLCapabilityChecker::CheckCapabilities(int required_major,
int required_minor) {
OpenGLCapabilities caps;

try {
// Get OpenGL version string
const char* version_str = reinterpret_cast<const char*>(glGetString(GL_VERSION));
if (version_str) {
caps.version_string = version_str;
} else {
caps.error_message = "Failed to get OpenGL version string";
return caps;
}

// Get vendor information
const char* vendor_str = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
if (vendor_str) {
caps.vendor = vendor_str;
}

// Get renderer information
const char* renderer_str = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
if (renderer_str) {
caps.renderer = renderer_str;
}

// Get GLSL version
const char* glsl_version_str = reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
if (glsl_version_str) {
caps.glsl_version = glsl_version_str;
}

// Parse OpenGL version numbers
if (sscanf(version_str, "%d.%d", &caps.major_version, &caps.minor_version) != 2) {
// Try alternative parsing for some drivers
if (sscanf(version_str, "OpenGL ES %d.%d", &caps.major_version, &caps.minor_version) != 2) {
caps.error_message = "Failed to parse OpenGL version numbers from: " + caps.version_string;
return caps;
}
}

// Check if we have the required OpenGL version
if (caps.major_version > required_major ||
(caps.major_version == required_major && caps.minor_version >= required_minor)) {
caps.supports_required_version = true;
} else {
std::stringstream ss;
ss << "OpenGL " << required_major << "." << required_minor
<< " required, but only " << caps.major_version << "." << caps.minor_version
<< " is available";
caps.error_message = ss.str();
}

// Check for core profile (OpenGL 3.2+)
if (caps.major_version >= 3 && caps.minor_version >= 2) {
GLint profile = 0;
glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profile);
caps.core_profile = (profile & GL_CONTEXT_CORE_PROFILE_BIT) != 0;
}

// Get available extensions
caps.available_extensions = GetAvailableExtensions();

// Check for common problematic configurations
if (IsIntelGPU(caps.vendor, caps.renderer)) {
// Intel GPUs sometimes have issues with OpenGL 3.3+ on older drivers
if (caps.major_version == 3 && caps.minor_version >= 3) {
std::cout << "WARNING: Intel GPU detected. If you experience shader issues, "
<< "consider updating your graphics drivers." << std::endl;
}
}

// Check for Mesa software rendering
if (caps.renderer.find("llvmpipe") != std::string::npos ||
caps.renderer.find("softpipe") != std::string::npos) {
std::cout << "WARNING: Software rendering detected (" << caps.renderer
<< "). Performance may be poor." << std::endl;
}

} catch (const std::exception& e) {
caps.error_message = "Exception during OpenGL capability check: " + std::string(e.what());
}

return caps;
}

void OpenGLCapabilityChecker::PrintCapabilities(const OpenGLCapabilities& capabilities) {
std::cout << "=== OpenGL Capabilities ===" << std::endl;
std::cout << "Version: " << capabilities.version_string << std::endl;
std::cout << "Vendor: " << capabilities.vendor << std::endl;
std::cout << "Renderer: " << capabilities.renderer << std::endl;
std::cout << "GLSL Version: " << capabilities.glsl_version << std::endl;
std::cout << "Parsed Version: " << capabilities.major_version << "." << capabilities.minor_version << std::endl;
std::cout << "Core Profile: " << (capabilities.core_profile ? "Yes" : "No") << std::endl;
std::cout << "Required Version Support: " << (capabilities.supports_required_version ? "Yes" : "No") << std::endl;

if (!capabilities.error_message.empty()) {
std::cout << "Error: " << capabilities.error_message << std::endl;
}

std::cout << "Available Extensions: " << capabilities.available_extensions.size() << std::endl;
std::cout << "=========================" << std::endl;
}

bool OpenGLCapabilityChecker::IsExtensionSupported(const std::string& extension_name) {
auto extensions = GetAvailableExtensions();
return std::find(extensions.begin(), extensions.end(), extension_name) != extensions.end();
}

bool OpenGLCapabilityChecker::ValidateRequiredFeatures(const OpenGLCapabilities& capabilities) {
if (!capabilities.supports_required_version) {
return false;
}

// Check for essential OpenGL 3.3 features
if (capabilities.major_version >= 3 && capabilities.minor_version >= 3) {
// Vertex Array Objects should be available
if (!IsExtensionSupported("GL_ARB_vertex_array_object") &&
!(capabilities.major_version > 3 || (capabilities.major_version == 3 && capabilities.minor_version >= 3))) {
std::cout << "WARNING: Vertex Array Objects may not be supported" << std::endl;
}
}

return true;
}

std::vector<std::string> OpenGLCapabilityChecker::GetAvailableExtensions() {
std::vector<std::string> extensions;

// Try modern way first (OpenGL 3.0+)
GLint num_extensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);

if (num_extensions > 0) {
for (GLint i = 0; i < num_extensions; i++) {
const char* ext = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i));
if (ext) {
extensions.push_back(ext);
}
}
} else {
// Fallback to legacy method (OpenGL < 3.0)
const char* extensions_str = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
if (extensions_str) {
std::string ext_string(extensions_str);
std::istringstream iss(ext_string);
std::string ext;
while (iss >> ext) {
extensions.push_back(ext);
}
}
}

return extensions;
}

bool OpenGLCapabilityChecker::IsIntelGPU(const std::string& vendor, const std::string& renderer) {
std::string vendor_lower = vendor;
std::string renderer_lower = renderer;
std::transform(vendor_lower.begin(), vendor_lower.end(), vendor_lower.begin(), ::tolower);
std::transform(renderer_lower.begin(), renderer_lower.end(), renderer_lower.begin(), ::tolower);

return vendor_lower.find("intel") != std::string::npos ||
renderer_lower.find("intel") != std::string::npos;
}

bool OpenGLCapabilityChecker::IsAMDGPU(const std::string& vendor, const std::string& renderer) {
std::string vendor_lower = vendor;
std::string renderer_lower = renderer;
std::transform(vendor_lower.begin(), vendor_lower.end(), vendor_lower.begin(), ::tolower);
std::transform(renderer_lower.begin(), renderer_lower.end(), renderer_lower.begin(), ::tolower);

return vendor_lower.find("amd") != std::string::npos ||
vendor_lower.find("ati") != std::string::npos ||
renderer_lower.find("radeon") != std::string::npos;
}

bool OpenGLCapabilityChecker::IsNVIDIAGPU(const std::string& vendor, const std::string& renderer) {
std::string vendor_lower = vendor;
std::string renderer_lower = renderer;
std::transform(vendor_lower.begin(), vendor_lower.end(), vendor_lower.begin(), ::tolower);
std::transform(renderer_lower.begin(), renderer_lower.end(), renderer_lower.begin(), ::tolower);

return vendor_lower.find("nvidia") != std::string::npos ||
renderer_lower.find("geforce") != std::string::npos ||
renderer_lower.find("quadro") != std::string::npos;
}

} // namespace quickviz
39 changes: 39 additions & 0 deletions src/imview/src/viewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "implot/implot.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include "imview/opengl_capability_checker.hpp"

namespace quickviz {
namespace {
Expand Down Expand Up @@ -86,6 +87,9 @@ Viewer::Viewer(std::string title, uint32_t width, uint32_t height,
#endif
ImGui_ImplOpenGL3_Init(glsl_version);

// Check OpenGL capabilities after context creation
CheckOpenGLCapabilities();

// set up callbacks
// convert callback-function to c-pointer first
FramebufferSizeCallback<void(GLFWwindow *, int, int)>::func =
Expand Down Expand Up @@ -411,6 +415,41 @@ void Viewer::OnResize(GLFWwindow *window, int width, int height) {
}
}

void Viewer::CheckOpenGLCapabilities() {
// Check OpenGL capabilities and validate requirements
auto capabilities = OpenGLCapabilityChecker::CheckCapabilities(3, 3);

if (!capabilities.error_message.empty()) {
std::cerr << "[ERROR] OpenGL Capability Check Failed: " << capabilities.error_message << std::endl;
OpenGLCapabilityChecker::PrintCapabilities(capabilities);

// Try to provide helpful error messages
if (!capabilities.supports_required_version) {
std::cerr << std::endl << "=== TROUBLESHOOTING ===" << std::endl;
std::cerr << "This application requires OpenGL 3.3 or higher." << std::endl;
std::cerr << "Your system only supports OpenGL " << capabilities.major_version
<< "." << capabilities.minor_version << std::endl;
std::cerr << "Possible solutions:" << std::endl;
std::cerr << "1. Update your graphics drivers" << std::endl;
std::cerr << "2. Check if your GPU supports OpenGL 3.3+" << std::endl;
std::cerr << "3. For Intel GPUs, ensure you have the latest drivers" << std::endl;
std::cerr << "4. Try running with software rendering (slower performance)" << std::endl;
std::cerr << "===========================================" << std::endl;
}

throw std::runtime_error("OpenGL capability check failed: " + capabilities.error_message);
}

// Print capabilities for debugging
std::cout << "[INFO] OpenGL capabilities validated successfully" << std::endl;
OpenGLCapabilityChecker::PrintCapabilities(capabilities);

// Validate required features
if (!OpenGLCapabilityChecker::ValidateRequiredFeatures(capabilities)) {
std::cerr << "[WARNING] Some required OpenGL features may not be fully supported" << std::endl;
}
}

void Viewer::Show() {
// initialize layers
int display_w, display_h;
Expand Down
25 changes: 24 additions & 1 deletion src/imview/src/window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "imview/window.hpp"

#include <stdexcept>
#include <iostream>

namespace quickviz {
namespace {
Expand All @@ -34,7 +35,29 @@ Window::Window(std::string title, uint32_t width, uint32_t height,
// create GLFW window
win_ = glfwCreateWindow(width, height, title.c_str(), NULL, NULL);
if (win_ == NULL) {
throw std::runtime_error("Failed to create GLFW window");
std::cerr << "Failed to create GLFW window with requested OpenGL version" << std::endl;

// Try fallback to compatibility profile
std::cerr << "Attempting fallback to compatibility profile..." << std::endl;
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
win_ = glfwCreateWindow(width, height, title.c_str(), NULL, NULL);

if (win_ == NULL) {
// Try even lower OpenGL version
std::cerr << "Attempting fallback to OpenGL 3.0..." << std::endl;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
win_ = glfwCreateWindow(width, height, title.c_str(), NULL, NULL);

if (win_ == NULL) {
throw std::runtime_error("Failed to create GLFW window even with fallback options");
} else {
std::cerr << "Successfully created window with OpenGL 3.0 compatibility profile" << std::endl;
}
} else {
std::cerr << "Successfully created window with compatibility profile" << std::endl;
}
}
glfwMakeContextCurrent(win_);
glfwSwapInterval(1);
Expand Down
Loading