From 96fb82e7697074c8fcdd3a4ae49e3785e5e0182c Mon Sep 17 00:00:00 2001 From: Alex Shvartzkop Date: Fri, 21 Jun 2024 21:30:51 +0300 Subject: [PATCH] Allow using custom getProcAddress for glew; Render OpenGL in paint event. Fixes OpenGL rendering on Linux. --- 3d-viewer/3d_canvas/eda_3d_canvas.cpp | 2 +- .../3d_model_viewer/eda_3d_model_viewer.cpp | 2 +- CMakeLists.txt | 97 ++++++++++--------- common/draw_panel_gal.cpp | 8 +- common/gal/opengl/opengl_compositor.cpp | 11 ++- common/gal/opengl/opengl_gal.cpp | 9 +- include/gal/opengl/gl_utils.h | 4 +- include/gal/opengl/kiglew.h | 29 +++++- libs/kiplatform/CMakeLists.txt | 1 + .../kiplatform/include/kiplatform/opengl_qt.h | 38 ++++++++ libs/kiplatform/port/wxqt/opengl_qt.cpp | 48 +++++++++ thirdparty/glew/CMakeLists.txt | 11 ++- thirdparty/glew/include/GL/glew.h | 5 + thirdparty/glew/src/glew.c | 24 +++-- 14 files changed, 218 insertions(+), 71 deletions(-) create mode 100644 libs/kiplatform/include/kiplatform/opengl_qt.h create mode 100644 libs/kiplatform/port/wxqt/opengl_qt.cpp diff --git a/3d-viewer/3d_canvas/eda_3d_canvas.cpp b/3d-viewer/3d_canvas/eda_3d_canvas.cpp index 2a2f53fa5a..578c46badb 100644 --- a/3d-viewer/3d_canvas/eda_3d_canvas.cpp +++ b/3d-viewer/3d_canvas/eda_3d_canvas.cpp @@ -223,7 +223,7 @@ bool EDA_3D_CANVAS::initializeOpenGL() { wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::initializeOpenGL" ) ); - const GLenum err = glewInit(); + const GLenum err = kiglewInit(); if( GLEW_OK != err ) { diff --git a/3d-viewer/3d_model_viewer/eda_3d_model_viewer.cpp b/3d-viewer/3d_model_viewer/eda_3d_model_viewer.cpp index fc1b7eaf72..a55a3c4a07 100644 --- a/3d-viewer/3d_model_viewer/eda_3d_model_viewer.cpp +++ b/3d-viewer/3d_model_viewer/eda_3d_model_viewer.cpp @@ -182,7 +182,7 @@ void EDA_3D_MODEL_VIEWER::Clear3DModel() void EDA_3D_MODEL_VIEWER::ogl_initialize() { - const GLenum err = glewInit(); + const GLenum err = kiglewInit(); if( GLEW_OK != err ) { diff --git a/CMakeLists.txt b/CMakeLists.txt index 6300f52d65..21d44cfec7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,10 +121,9 @@ cmake_dependent_option( KICAD_USE_EGL OFF "UNIX_NOT_APPLE" OFF) -cmake_dependent_option( KICAD_USE_BUNDLED_GLEW - "Use the bundled version of GLEW - only available when KICAD_USE_EGL is set" - ON "KICAD_USE_EGL" - OFF ) +option( KICAD_USE_BUNDLED_GLEW + "Use the bundled version of GLEW" + ${KICAD_USE_EGL} ) cmake_dependent_option( KICAD_WAYLAND "Support Wayland features" @@ -760,50 +759,6 @@ if( KICAD_BUILD_I18N ) find_package( Gettext REQUIRED ) endif() -# -# Find OpenGL library, required -# -if( KICAD_USE_EGL ) - find_package( OpenGL REQUIRED COMPONENTS OpenGL EGL ) -else() - set( OpenGL_GL_PREFERENCE "LEGACY" ) # CMake 3.11+ setting; see 'cmake --help-policy CMP0072' - find_package( OpenGL REQUIRED ) -endif() - -# -# Find GLEW library, required -# -# The EGL canvas on GTK requires the use of a GLEW version compiled with an EGL flag. -# The one built in the thirdparty directory has the flag for EGL set, so we use it unless told -# otherwise. Then we search for the system GLEW version and use that instead. -# -if( KICAD_USE_EGL AND KICAD_USE_BUNDLED_GLEW AND UNIX AND NOT APPLE ) - if( OpenGL_EGL_FOUND ) - message( STATUS "Found OpenGL EGL library: ${OPENGL_egl_LIBRARY}" ) - else() - message( FATAL_ERROR "OpenGL EGL library not found" ) - endif() - - # Add the custom GLEW target - add_subdirectory( thirdparty/glew ) - - # Set the standard package variables to point to our custom target to mimic the system version. - set( GLEW_LIBRARIES glew ) - set( GLEW_FOUND TRUE ) - include_directories( SYSTEM $ ) -else() - find_package( GLEW REQUIRED ) - check_find_package_result( GLEW_FOUND "GLEW" ) - include_directories( SYSTEM ${GLEW_INCLUDE_DIR} ) -endif() - -# -# Find GLM library, required -# -find_package( GLM 0.9.8 REQUIRED ) -add_compile_definitions( GLM_FORCE_CTOR_INIT ) -include_directories( SYSTEM ${GLM_INCLUDE_DIR} ) - # # Find zlib library, required # @@ -1098,6 +1053,52 @@ if( MINGW ) endif() endif() +# +# Find OpenGL library, required +# +if( KICAD_USE_EGL ) + find_package( OpenGL REQUIRED COMPONENTS OpenGL EGL ) +else() + set( OpenGL_GL_PREFERENCE "LEGACY" ) # CMake 3.11+ setting; see 'cmake --help-policy CMP0072' + find_package( OpenGL REQUIRED ) +endif() + +# +# Find GLEW library, required +# +# The EGL canvas on GTK requires the use of a GLEW version compiled with an EGL flag. +# The one built in the thirdparty directory has the flag for EGL set, so we use it unless told +# otherwise. Then we search for the system GLEW version and use that instead. +# +if( KICAD_USE_EGL ) + if( OpenGL_EGL_FOUND ) + message( STATUS "Found OpenGL EGL library: ${OPENGL_egl_LIBRARY}" ) + else() + message( FATAL_ERROR "OpenGL EGL library not found" ) + endif() +endif() + +if( KICAD_USE_BUNDLED_GLEW AND UNIX AND NOT APPLE ) + # Add the custom GLEW target + add_subdirectory( thirdparty/glew ) + + # Set the standard package variables to point to our custom target to mimic the system version. + set( GLEW_LIBRARIES glew ) + set( GLEW_FOUND TRUE ) + include_directories( SYSTEM $ ) +else() + find_package( GLEW REQUIRED ) + check_find_package_result( GLEW_FOUND "GLEW" ) + include_directories( SYSTEM ${GLEW_INCLUDE_DIR} ) +endif() + +# +# Find GLM library, required +# +find_package( GLM 0.9.8 REQUIRED ) +add_compile_definitions( GLM_FORCE_CTOR_INIT ) +include_directories( SYSTEM ${GLM_INCLUDE_DIR} ) + if( APPLE ) # Remove app bundles in ${KICAD_BIN} before installing anything new. # Must be defined before all includes so that it is executed first. diff --git a/common/draw_panel_gal.cpp b/common/draw_panel_gal.cpp index 538eb21475..f638fdba8f 100644 --- a/common/draw_panel_gal.cpp +++ b/common/draw_panel_gal.cpp @@ -200,6 +200,9 @@ void EDA_DRAW_PANEL_GAL::onPaint( wxPaintEvent& WXUNUSED( aEvent ) ) bool EDA_DRAW_PANEL_GAL::DoRePaint() { + if( m_gal->IsContextLocked() ) + return false; + if( !m_refreshMutex.try_lock() ) return false; @@ -389,8 +392,7 @@ void EDA_DRAW_PANEL_GAL::RequestRefresh() void EDA_DRAW_PANEL_GAL::Refresh( bool aEraseBackground, const wxRect* aRect ) { - if( !DoRePaint() ) - RequestRefresh(); + wxScrolledCanvas::Refresh( aEraseBackground, aRect ); } @@ -415,7 +417,7 @@ void EDA_DRAW_PANEL_GAL::ForceRefresh() } } - DoRePaint(); + Refresh(); } diff --git a/common/gal/opengl/opengl_compositor.cpp b/common/gal/opengl/opengl_compositor.cpp index 3db5168ccc..fa31bf97b9 100644 --- a/common/gal/opengl/opengl_compositor.cpp +++ b/common/gal/opengl/opengl_compositor.cpp @@ -384,7 +384,16 @@ void OPENGL_COMPOSITOR::bindFb( unsigned int aFb ) if( m_curFbo != aFb ) { - glBindFramebufferEXT( GL_FRAMEBUFFER, aFb ); + unsigned int actualBuffer = aFb; + +#ifdef __WXQT__ + // Qt needs to draw on a separate framebuffer first + if( aFb == DIRECT_RENDERING ) + actualBuffer = QtOpenGLGetOutputFBO(); +#endif + + glBindFramebufferEXT( GL_FRAMEBUFFER, actualBuffer ); + checkGlError( "switching framebuffer", __FILE__, __LINE__ ); m_curFbo = aFb; } diff --git a/common/gal/opengl/opengl_gal.cpp b/common/gal/opengl/opengl_gal.cpp index c982958056..4aa5b0b2d4 100644 --- a/common/gal/opengl/opengl_gal.cpp +++ b/common/gal/opengl/opengl_gal.cpp @@ -499,9 +499,9 @@ wxString OPENGL_GAL::CheckFeatures( GAL_DISPLAY_OPTIONS& aOptions ) void OPENGL_GAL::PostPaint( wxPaintEvent& aEvent ) { - // posts an event to m_paint_listener to ask for redraw the canvas. + // Repaint it immediately via m_paintListener. if( m_paintListener ) - wxPostEvent( m_paintListener, aEvent ); + m_paintListener->ProcessEvent( aEvent ); } @@ -2687,7 +2687,8 @@ void OPENGL_GAL::init() // Check correct initialization from the constructor if( m_tesselator == nullptr ) throw std::runtime_error( "Could not create the tesselator" ); - GLenum err = glewInit(); + + GLenum err = kiglewInit(); #ifdef KICAD_USE_EGL // TODO: better way to check when EGL is ready (init fails at "getString(GL_VERSION)") @@ -2697,7 +2698,7 @@ void OPENGL_GAL::init() break; std::this_thread::sleep_for( std::chrono::milliseconds( 250 ) ); - err = glewInit(); + err = kiglewInit(); } #endif // KICAD_USE_EGL diff --git a/include/gal/opengl/gl_utils.h b/include/gal/opengl/gl_utils.h index 4d2153e566..dc097c7455 100644 --- a/include/gal/opengl/gl_utils.h +++ b/include/gal/opengl/gl_utils.h @@ -48,7 +48,7 @@ public: */ static int SetSwapInterval( int aVal ) { -#if defined( __linux__ ) && !defined( KICAD_USE_EGL ) +#if defined( __linux__ ) && !defined( KICAD_USE_EGL ) && !defined( __WXQT__ ) if( Display* dpy = glXGetCurrentDisplay() ) { @@ -109,7 +109,7 @@ public: } } -#elif defined( _WIN32 ) and not defined( __WXQT__ ) +#elif defined( _WIN32 ) and !defined( __WXQT__ ) const GLubyte* vendor = glGetString( GL_VENDOR ); //const GLubyte* renderer = glGetString( GL_RENDERER ); diff --git a/include/gal/opengl/kiglew.h b/include/gal/opengl/kiglew.h index 7eaf8fccc3..e343368e4d 100644 --- a/include/gal/opengl/kiglew.h +++ b/include/gal/opengl/kiglew.h @@ -31,12 +31,16 @@ // Pull in the configuration options for wxWidgets #include +#ifdef __WXQT__ +#include +#endif + // Apple, in their infinite wisdom, has decided to mark OpenGL as deprecated. // Luckily we can silence warnings about its deprecation. This is needed on the GLEW // includes since they transitively include the OpenGL headers. #define GL_SILENCE_DEPRECATION 1 -#if defined( __unix__ ) and not defined( __APPLE__ ) +#if defined( __unix__ ) and not defined( __APPLE__ ) and not defined( __WXQT__ ) #ifdef KICAD_USE_EGL @@ -54,6 +58,11 @@ #else // wxWidgets wasn't compiled with the EGL canvas, so use the X11 GLEW #include + + // Resolve X.h conflict with wx 3.3 + #ifdef Success + #undef Success + #endif #endif #endif // KICAD_USE_EGL @@ -63,12 +72,24 @@ // Non-GTK platforms only need the normal GLEW include #include -#endif // defined( __unix__ ) and not defined( __APPLE__ ) +#endif // defined( __unix__ ) and not defined( __APPLE__ ) and not defined( __WXQT__ ) -#ifdef _WIN32 +#if defined( _WIN32 ) and not defined( __WXQT__ ) #include -#endif // _WIN32 +#endif // defined( _WIN32 ) and not defined( __WXQT__ ) + + +inline GLenum kiglewInit() +{ +#ifdef __WXQT__ + GLenum err = glewInit( &QtOpenGLGetProcAddress ); +#else + GLenum err = glewInit(); +#endif + return err; +} + #endif // KIGLEW_H_ diff --git a/libs/kiplatform/CMakeLists.txt b/libs/kiplatform/CMakeLists.txt index 140769d280..dfc72ee6c6 100644 --- a/libs/kiplatform/CMakeLists.txt +++ b/libs/kiplatform/CMakeLists.txt @@ -42,6 +42,7 @@ elseif( KICAD_WX_PORT STREQUAL gtk ) elseif( KICAD_WX_PORT STREQUAL qt ) set( PLATFORM_SRCS port/wxqt/ui.cpp + port/wxqt/opengl_qt.cpp ) set( QT_COMPONENTS Core Widgets Gui ) diff --git a/libs/kiplatform/include/kiplatform/opengl_qt.h b/libs/kiplatform/include/kiplatform/opengl_qt.h new file mode 100644 index 0000000000..54a977dce8 --- /dev/null +++ b/libs/kiplatform/include/kiplatform/opengl_qt.h @@ -0,0 +1,38 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 Alex Shvartzkop + * Copyright (C) 2024 Kicad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __OPENGL_QT_KICAD +#define __OPENGL_QT_KICAD + +#include + + +// A proxy function for Qt getProcAddress of current context +void* QtOpenGLGetProcAddress( const char* aName ); + +// A proxy function for Qt defaultFramebufferObject of current context +unsigned int QtOpenGLGetOutputFBO(); + + +#endif // __OPENGL_QT_KICAD \ No newline at end of file diff --git a/libs/kiplatform/port/wxqt/opengl_qt.cpp b/libs/kiplatform/port/wxqt/opengl_qt.cpp new file mode 100644 index 0000000000..9cbfafe849 --- /dev/null +++ b/libs/kiplatform/port/wxqt/opengl_qt.cpp @@ -0,0 +1,48 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 Alex Shvartzkop + * Copyright (C) 2024 Kicad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include + + +void* QtOpenGLGetProcAddress( const char* aName ) +{ + QOpenGLContext* ctx = QOpenGLContext::currentContext(); + + if( !ctx ) + return nullptr; + + return (void*) ctx->getProcAddress( aName ); +} + + +unsigned int QtOpenGLGetOutputFBO() +{ + QOpenGLContext* ctx = QOpenGLContext::currentContext(); + + if( !ctx ) + return 0; + + return ctx->defaultFramebufferObject(); +} \ No newline at end of file diff --git a/thirdparty/glew/CMakeLists.txt b/thirdparty/glew/CMakeLists.txt index 32dd3be9ca..48200d7fcc 100644 --- a/thirdparty/glew/CMakeLists.txt +++ b/thirdparty/glew/CMakeLists.txt @@ -8,9 +8,18 @@ target_include_directories( glew PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include" ) # Definitions for compiling GLEW staticly for EGL (extracted from the main GLEW CMakeLists.txt file) add_compile_definitions( GLEW_STATIC ) -add_compile_definitions( GLEW_EGL ) add_compile_definitions( GLEW_NO_GLU ) +if( KICAD_USE_EGL ) + add_compile_definitions( GLEW_EGL ) + target_compile_definitions( glew INTERFACE GLEW_EGL ) +endif() + +if( KICAD_WX_PORT STREQUAL "qt" ) + add_compile_definitions( GLEW_RAW_PTR ) + target_compile_definitions( glew INTERFACE GLEW_RAW_PTR ) +endif() + target_sources( glew PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/glew.c ) diff --git a/thirdparty/glew/include/GL/glew.h b/thirdparty/glew/include/GL/glew.h index 64e92d1a07..ca001eb55e 100644 --- a/thirdparty/glew/include/GL/glew.h +++ b/thirdparty/glew/include/GL/glew.h @@ -26512,7 +26512,12 @@ VERSION_MICRO 0 */ /* API */ +#ifdef GLEW_RAW_PTR +GLEWAPI GLenum GLEWAPIENTRY glewInit (void *(* getProcAddressFn)(const char *)); +#else GLEWAPI GLenum GLEWAPIENTRY glewInit (void); +#endif + GLEWAPI GLboolean GLEWAPIENTRY glewIsSupported (const char *name); #define glewIsExtensionSupported(x) glewIsSupported(x) diff --git a/thirdparty/glew/src/glew.c b/thirdparty/glew/src/glew.c index cdf17c162e..55d6bc6fe5 100644 --- a/thirdparty/glew/src/glew.c +++ b/thirdparty/glew/src/glew.c @@ -36,6 +36,7 @@ # include GLEW_INCLUDE #endif +#ifndef GLEW_RAW_PTR #if defined(GLEW_OSMESA) # define GLAPI extern # include @@ -54,10 +55,13 @@ #elif !defined(__ANDROID__) && !defined(__native_client__) && !defined(__HAIKU__) && (!defined(__APPLE__) || defined(GLEW_APPLE_GLX)) # include #endif +#endif #include /* For size_t */ -#if defined(GLEW_EGL) +#if defined(GLEW_RAW_PTR) +static void *(*glew_GetProcAddressPtr)(const unsigned char *procName); +#elif defined(GLEW_EGL) #elif defined(GLEW_REGAL) /* In GLEW_REGAL mode we call direcly into the linked @@ -158,7 +162,9 @@ void* NSGLGetProcAddress (const GLubyte *name) /* * Define glewGetProcAddress. */ -#if defined(GLEW_REGAL) +#if defined(GLEW_RAW_PTR) +# define glewGetProcAddress(name) (*glew_GetProcAddressPtr)(name) +#elif defined(GLEW_REGAL) # define glewGetProcAddress(name) regalGetProcAddress((const GLchar *)name) #elif defined(GLEW_OSMESA) # define glewGetProcAddress(name) OSMesaGetProcAddress((const char *)name) @@ -22137,7 +22143,7 @@ GLenum GLEWAPIENTRY wglewInit () return GLEW_OK; } -#elif !defined(__ANDROID__) && !defined(__native_client__) && !defined(__HAIKU__) && (!defined(__APPLE__) || defined(GLEW_APPLE_GLX)) +#elif !defined(GLEW_RAW_PTR) && !defined(__ANDROID__) && !defined(__native_client__) && !defined(__HAIKU__) && (!defined(__APPLE__) || defined(GLEW_APPLE_GLX)) PFNGLXGETCURRENTDISPLAYPROC __glewXGetCurrentDisplay = NULL; @@ -23226,7 +23232,7 @@ GLenum glxewInit () return GLEW_OK; } -#endif /* !defined(__ANDROID__) && !defined(__native_client__) && !defined(__HAIKU__) && (!defined(__APPLE__) || defined(GLEW_APPLE_GLX)) */ +#endif /* !defined(GLEW_RAW_PTR) && !defined(__ANDROID__) && !defined(__native_client__) && !defined(__HAIKU__) && (!defined(__APPLE__) || defined(GLEW_APPLE_GLX)) */ /* ------------------------------------------------------------------------ */ @@ -23262,8 +23268,14 @@ const GLubyte * GLEWAPIENTRY glewGetString (GLenum name) GLboolean glewExperimental = GL_FALSE; +#ifdef GLEW_RAW_PTR +GLenum GLEWAPIENTRY glewInit (void *(* getProcAddressFn)(const char *)) +{ + glew_GetProcAddressPtr = (void* (*) (const unsigned char*) ) getProcAddressFn; +#else GLenum GLEWAPIENTRY glewInit (void) { +#endif GLenum r; #if defined(GLEW_EGL) PFNEGLGETCURRENTDISPLAYPROC getCurrentDisplay = NULL; @@ -23273,7 +23285,7 @@ GLenum GLEWAPIENTRY glewInit (void) #if defined(GLEW_EGL) getCurrentDisplay = (PFNEGLGETCURRENTDISPLAYPROC) glewGetProcAddress("eglGetCurrentDisplay"); return eglewInit(getCurrentDisplay()); -#elif defined(GLEW_OSMESA) || defined(__ANDROID__) || defined(__native_client__) || defined(__HAIKU__) +#elif defined(GLEW_RAW_PTR) || defined(GLEW_OSMESA) || defined(__ANDROID__) || defined(__native_client__) || defined(__HAIKU__) return r; #elif defined(_WIN32) return wglewInit(); @@ -30432,7 +30444,7 @@ GLboolean GLEWAPIENTRY wglewIsSupported (const char* name) return ret; } -#elif !defined(GLEW_OSMESA) && !defined(GLEW_EGL) && !defined(__ANDROID__) && !defined(__native_client__) && !defined(__HAIKU__) && !defined(__APPLE__) || defined(GLEW_APPLE_GLX) +#elif !defined(GLEW_RAW_PTR) && !defined(GLEW_OSMESA) && !defined(GLEW_EGL) && !defined(__ANDROID__) && !defined(__native_client__) && !defined(__HAIKU__) && !defined(__APPLE__) || defined(GLEW_APPLE_GLX) GLboolean glxewIsSupported (const char* name) {