From 709697ea4007f98e610fe92ae9361aa4fa768243 Mon Sep 17 00:00:00 2001 From: Maciej Suminski Date: Mon, 20 Apr 2015 14:16:41 +0200 Subject: [PATCH] Better way to determine supported OpenGL version and extensions. --- common/gal/opengl/opengl_gal.cpp | 172 ++++++++++++++++++++----------- include/gal/opengl/opengl_gal.h | 36 +++++-- 2 files changed, 143 insertions(+), 65 deletions(-) diff --git a/common/gal/opengl/opengl_gal.cpp b/common/gal/opengl/opengl_gal.cpp index 09a9cc1046..462e10cde4 100644 --- a/common/gal/opengl/opengl_gal.cpp +++ b/common/gal/opengl/opengl_gal.cpp @@ -36,46 +36,29 @@ #endif /* __WXDEBUG__ */ #include +#include using namespace KIGFX; -// Prototypes -void InitTesselatorCallbacks( GLUtesselator* aTesselator ); - +static void InitTesselatorCallbacks( GLUtesselator* aTesselator ); const int glAttributes[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, 0 }; - wxGLContext* OPENGL_GAL::glContext = NULL; OPENGL_GAL::OPENGL_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener, wxEvtHandler* aPaintListener, const wxString& aName ) : wxGLCanvas( aParent, wxID_ANY, (int*) glAttributes, wxDefaultPosition, wxDefaultSize, wxEXPAND, aName ), - parentWindow( aParent ), mouseListener( aMouseListener ), paintListener( aPaintListener ), cachedManager( true ), nonCachedManager( false ), overlayManager( false ) { - // Create the OpenGL-Context if( glContext == NULL ) glContext = new wxGLContext( this ); - aParent->Show(); // wxWidgets require the window to be visible to set its GL context - - // Initialize GLEW, FBOs & VBOs - SetCurrent( *glContext ); - initGlew(); - - // Prepare shaders - if( !shader.LoadBuiltinShader( 0, SHADER_TYPE_VERTEX ) ) - throw std::runtime_error( "Cannot compile vertex shader!" ); - - if( !shader.LoadBuiltinShader( 1, SHADER_TYPE_FRAGMENT ) ) - throw std::runtime_error( "Cannot compile fragment shader!" ); - - if( !shader.Link() ) - throw std::runtime_error( "Cannot link the shaders!" ); + // Check if OpenGL requirements are met + runTest(); // Make VBOs use shaders cachedManager.SetShader( shader ); @@ -88,7 +71,7 @@ OPENGL_GAL::OPENGL_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener, groupCounter = 0; // Connecting the event handlers - Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::onPaint ) ); + Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::onPaint ) ); // Mouse events are skipped to the parent Connect( wxEVT_MOTION, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); @@ -145,7 +128,8 @@ void OPENGL_GAL::BeginDrawing() glViewport( 0, 0, (GLsizei) screenSize.x, (GLsizei) screenSize.y ); // Create the screen transformation - glOrtho( 0, (GLint) screenSize.x, 0, (GLsizei) screenSize.y, -depthRange.x, -depthRange.y ); + glOrtho( 0, (GLint) screenSize.x, 0, (GLsizei) screenSize.y, + -depthRange.x, -depthRange.y ); if( !isFramebufferInitialized ) { @@ -914,37 +898,6 @@ void OPENGL_GAL::skipMouseEvent( wxMouseEvent& aEvent ) } -void OPENGL_GAL::initGlew() -{ - // Initialize GLEW library - GLenum err = glewInit(); - - if( GLEW_OK != err ) - { - throw std::runtime_error( (const char*) glewGetErrorString( err ) ); - } - else - { - wxLogDebug( wxString( wxT( "Status: Using GLEW " ) ) + - FROM_UTF8( (char*) glewGetString( GLEW_VERSION ) ) ); - } - - // Check the OpenGL version (minimum 2.1 is required) - if( GLEW_VERSION_2_1 ) - wxLogInfo( wxT( "OpenGL 2.1 supported." ) ); - else - throw std::runtime_error( "OpenGL 2.1 or higher is required!" ); - - // Framebuffers have to be supported - if( !GLEW_EXT_framebuffer_object ) - throw std::runtime_error( "Framebuffer objects are not supported!" ); - - // Vertex buffer has to be supported - if( !GLEW_ARB_vertex_buffer_object ) - throw std::runtime_error( "Vertex buffer objects are not supported!" ); -} - - void OPENGL_GAL::blitCursor() { if( !isCursorEnabled ) @@ -984,11 +937,112 @@ unsigned int OPENGL_GAL::getNewGroupNumber() } -// ------------------------------------- -// Callback functions for the tesselator -// ------------------------------------- +bool OPENGL_GAL::runTest() +{ + wxDialog* dialog = new wxDialog( GetParent(), -1, wxT( "opengl test" ), + wxPoint( 50, 50 ), wxSize( 50, 50 ) ); + OPENGL_TEST* test = new OPENGL_TEST( dialog, this ); -// Compare Redbook Chapter 11 + dialog->ShowModal(); + bool result = test->IsOk(); + + if( !result ) + throw std::runtime_error( test->GetError() ); + + return result; +} + + +OPENGL_GAL::OPENGL_TEST::OPENGL_TEST( wxDialog* aParent, OPENGL_GAL* aGal ) : + wxGLCanvas( aParent, wxID_ANY, glAttributes, wxDefaultPosition, + wxDefaultSize, 0, wxT( "GLCanvas" ) ), + m_parent( aParent ), m_gal( aGal ), m_tested( false ), m_result( false ) +{ + Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::OPENGL_TEST::Render ) ); +} + + +void OPENGL_GAL::OPENGL_TEST::Render( wxPaintEvent& WXUNUSED( aEvent ) ) +{ + if( !m_tested ) + { + m_result = true; // Assume everything is fine, until proven otherwise + + // One test is enough - close the testing dialog when the test is finished + Disconnect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::OPENGL_TEST::Render ) ); + CallAfter( boost::bind( &wxDialog::EndModal, m_parent, wxID_NONE ) ); + + SetCurrent( *OPENGL_GAL::glContext ); + GLenum err = glewInit(); + + if( GLEW_OK != err ) + { + error( (const char*) glewGetErrorString( err ) ); + return; + } + else + { + wxLogDebug( wxString( wxT( "Status: Using GLEW " ) ) + + FROM_UTF8( (char*) glewGetString( GLEW_VERSION ) ) ); + } + + // Check the OpenGL version (minimum 2.1 is required) + if( GLEW_VERSION_2_1 ) + { + wxLogInfo( wxT( "OpenGL 2.1 supported." ) ); + } + else + { + error( "OpenGL 2.1 or higher is required!" ); + return; + } + + // Framebuffers have to be supported + if( !GLEW_EXT_framebuffer_object ) + { + error( "Framebuffer objects are not supported!" ); + return; + } + + // Vertex buffer has to be supported + if( !GLEW_ARB_vertex_buffer_object ) + { + error( "Vertex buffer objects are not supported!" ); + return; + } + + // Prepare shaders + if( !m_gal->shader.LoadBuiltinShader( 0, SHADER_TYPE_VERTEX ) ) + { + error( "Cannot compile vertex shader!" ); + return; + } + + if( !m_gal->shader.LoadBuiltinShader( 1, SHADER_TYPE_FRAGMENT ) ) + { + error( "Cannot compile fragment shader!" ); + return; + } + + if( !m_gal->shader.Link() ) + { + error( "Cannot link the shaders!" ); + return; + } + + m_tested = true; + } +} + + +void OPENGL_GAL::OPENGL_TEST::error(const std::string& aError ) +{ + m_result = false; + m_tested = true; + m_error = aError; +} + +// ------------------------------------- // Callback functions for the tesselator // ------------------------------------- // Compare Redbook Chapter 11 void CALLBACK VertexCallback( GLvoid* aVertexPtr, void* aData ) { GLdouble* vertex = static_cast( aVertexPtr ); @@ -1029,7 +1083,7 @@ void CALLBACK ErrorCallback( GLenum aErrorCode ) } -void InitTesselatorCallbacks( GLUtesselator* aTesselator ) +static void InitTesselatorCallbacks( GLUtesselator* aTesselator ) { gluTessCallback( aTesselator, GLU_TESS_VERTEX_DATA, ( void (CALLBACK*)() )VertexCallback ); gluTessCallback( aTesselator, GLU_TESS_COMBINE_DATA, ( void (CALLBACK*)() )CombineCallback ); diff --git a/include/gal/opengl/opengl_gal.h b/include/gal/opengl/opengl_gal.h index ff618b9554..bfaafedc74 100644 --- a/include/gal/opengl/opengl_gal.h +++ b/include/gal/opengl/opengl_gal.h @@ -3,7 +3,7 @@ * * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. - * Copyright (C) 2013 CERN + * Copyright (C) 2013-2015 CERN * @author Maciej Suminski * * Graphics Abstraction Layer (GAL) for OpenGL @@ -55,7 +55,7 @@ class SHADER; * @brief Class OpenGL_GAL is the OpenGL implementation of the Graphics Abstraction Layer. * * This is a direct OpenGL-implementation and uses low-level graphics primitives like triangles - * and quads. The purpuse is to provide a fast graphics interface, that takes advantage of modern + * and quads. The purpose is to provide a fast graphics interface, that takes advantage of modern * graphics card GPUs. All methods here benefit thus from the hardware acceleration. */ class OPENGL_GAL : public GAL, public wxGLCanvas @@ -254,7 +254,6 @@ private: wxClientDC* clientDC; ///< Drawing context static wxGLContext* glContext; ///< OpenGL context of wxWidgets - wxWindow* parentWindow; ///< Parent window wxEvtHandler* mouseListener; wxEvtHandler* paintListener; @@ -340,9 +339,6 @@ private: */ void skipMouseEvent( wxMouseEvent& aEvent ); - /// Initialize GLEW - void initGlew(); - /** * @brief Blits cursor into the current screen. */ @@ -354,6 +350,34 @@ private: * @return An unique group number that is not used by any other group. */ unsigned int getNewGroupNumber(); + + /** + * @brief Checks if the required OpenGL version and extensions are supported. + * @return true in case of success. + */ + bool runTest(); + + // Helper class to determine OpenGL capabilities + class OPENGL_TEST: public wxGLCanvas + { + public: + OPENGL_TEST( wxDialog* aParent, OPENGL_GAL* aGal ); + void Render( wxPaintEvent& aEvent ); + inline bool IsTested() const { return m_tested; } + inline bool IsOk() const { return m_result && m_tested; } + inline std::string GetError() const { return m_error; } + + private: + void error( const std::string& aError ); + + wxDialog* m_parent; + OPENGL_GAL* m_gal; + bool m_tested; + bool m_result; + std::string m_error; + }; + + friend class OPENGL_TEST; }; } // namespace KIGFX