diff --git a/common/draw_panel_gal.cpp b/common/draw_panel_gal.cpp index 87b19cb4db..614a0689a6 100644 --- a/common/draw_panel_gal.cpp +++ b/common/draw_panel_gal.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2013-2016 CERN + * Copyright (C) 2013-2017 CERN * @author Tomasz Wlostowski * @author Maciej Suminski * @@ -175,25 +175,40 @@ void EDA_DRAW_PANEL_GAL::onPaint( wxPaintEvent& WXUNUSED( aEvent ) ) m_view->UpdateItems(); - m_gal->BeginDrawing(); - m_gal->ClearScreen( settings->GetBackgroundColor() ); - - KIGFX::COLOR4D gridColor = settings->GetLayerColor( ITEM_GAL_LAYER( GRID_VISIBLE ) ); - m_gal->SetGridColor( gridColor ); - - if( m_view->IsDirty() ) + try { - m_view->ClearTargets(); + m_gal->BeginDrawing(); + m_gal->ClearScreen( settings->GetBackgroundColor() ); - // Grid has to be redrawn only when the NONCACHED target is redrawn - if( m_view->IsTargetDirty( KIGFX::TARGET_NONCACHED ) ) - m_gal->DrawGrid(); + KIGFX::COLOR4D gridColor = settings->GetLayerColor( ITEM_GAL_LAYER( GRID_VISIBLE ) ); + m_gal->SetGridColor( gridColor ); - m_view->Redraw(); + if( m_view->IsDirty() ) + { + m_view->ClearTargets(); + + // Grid has to be redrawn only when the NONCACHED target is redrawn + if( m_view->IsTargetDirty( KIGFX::TARGET_NONCACHED ) ) + m_gal->DrawGrid(); + + m_view->Redraw(); + } + + m_gal->DrawCursor( m_viewControls->GetCursorPosition() ); + m_gal->EndDrawing(); } + catch( std::runtime_error& err ) + { + assert( GetBackend() != GAL_TYPE_CAIRO ); - m_gal->DrawCursor( m_viewControls->GetCursorPosition() ); - m_gal->EndDrawing(); + // Cairo is supposed to be the safe backend, there is not a single "throw" in its code + SwitchBackend( GAL_TYPE_CAIRO ); + + if( m_edaFrame ) + m_edaFrame->UseGalCanvas( true ); + + DisplayError( m_parent, wxString( err.what() ) ); + } #ifdef PROFILE totalRealTime.Stop(); diff --git a/common/gal/opengl/opengl_compositor.cpp b/common/gal/opengl/opengl_compositor.cpp index 2cb99088db..2aa302575d 100644 --- a/common/gal/opengl/opengl_compositor.cpp +++ b/common/gal/opengl/opengl_compositor.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2013-2016 CERN + * Copyright (C) 2013-2017 CERN * @author Maciej Suminski * * This program is free software; you can redistribute it and/or @@ -91,6 +91,18 @@ void OPENGL_COMPOSITOR::Initialize() } VECTOR2U dims = m_antialiasing->GetInternalBufferSize(); + assert( dims.x != 0 && dims.y != 0 ); + + GLint maxBufSize; + glGetIntegerv( GL_MAX_RENDERBUFFER_SIZE_EXT, &maxBufSize ); + + // VECTOR2U is unsigned, so no need to check if < 0 + if( dims.x > (unsigned) maxBufSize || dims.y >= (unsigned) maxBufSize ) + { + throw std::runtime_error( + wxString::Format( "Requested render buffer size is %ux%u, but the max supported size is %dx%d", + dims.x, dims.y, maxBufSize, maxBufSize ) ); + } // We need framebuffer objects for drawing the screen contents // Generate framebuffer and a depth buffer diff --git a/common/gal/opengl/opengl_gal.cpp b/common/gal/opengl/opengl_gal.cpp index c9a5ab87f8..e8ec21d5f1 100644 --- a/common/gal/opengl/opengl_gal.cpp +++ b/common/gal/opengl/opengl_gal.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de * Copyright (C) 2012-2016 Kicad Developers, see AUTHORS.txt for contributors. - * Copyright (C) 2013-2016 CERN + * Copyright (C) 2013-2017 CERN * @author Maciej Suminski * * Graphics Abstraction Layer (GAL) for OpenGL @@ -82,24 +82,17 @@ OPENGL_GAL::OPENGL_GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions, wxWindow* aParent, ++instanceCounter; - // Check if OpenGL requirements are met. The test procedure also initializes a few parts - // of the OpenGL backend (e.g. GLEW), so it is required that it is invoked before any GL calls. - runTest(); - - // Make VBOs use shaders - cachedManager = new VERTEX_MANAGER( true ); - cachedManager->SetShader( *shader ); - nonCachedManager = new VERTEX_MANAGER( false ); - nonCachedManager->SetShader( *shader ); - overlayManager = new VERTEX_MANAGER( false ); - overlayManager->SetShader( *shader ); - compositor = new OPENGL_COMPOSITOR; compositor->SetAntialiasingMode( options.gl_antialiasing_mode ); + cachedManager = new VERTEX_MANAGER( true ); + nonCachedManager = new VERTEX_MANAGER( false ); + overlayManager = new VERTEX_MANAGER( false ); + // Initialize the flags isFramebufferInitialized = false; isBitmapFontInitialized = false; + isInitialized = false; isGrouping = false; groupCounter = 0; @@ -191,6 +184,7 @@ OPENGL_GAL::~OPENGL_GAL() } + void OPENGL_GAL::OnGalDisplayOptionsChanged( const GAL_DISPLAY_OPTIONS& aDisplayOptions ) { if( options.gl_antialiasing_mode != compositor->GetAntialiasingMode() ) @@ -201,6 +195,7 @@ void OPENGL_GAL::OnGalDisplayOptionsChanged( const GAL_DISPLAY_OPTIONS& aDisplay } } + void OPENGL_GAL::BeginDrawing() { if( !IsShownOnScreen() ) @@ -210,6 +205,9 @@ void OPENGL_GAL::BeginDrawing() PROF_COUNTER totalRealTime( "OPENGL_GAL::BeginDrawing()", true ); #endif /* __WXDEBUG__ */ + if( !isInitialized ) + init(); + GL_CONTEXT_MANAGER::Get().LockCtx( glPrivContext, this ); // Set up the view port @@ -221,10 +219,18 @@ void OPENGL_GAL::BeginDrawing() if( !isFramebufferInitialized ) { - // Prepare rendering target buffers - compositor->Initialize(); - mainBuffer = compositor->CreateBuffer(); - overlayBuffer = compositor->CreateBuffer(); + try + { + // Prepare rendering target buffers + compositor->Initialize(); + mainBuffer = compositor->CreateBuffer(); + overlayBuffer = compositor->CreateBuffer(); + } + catch( std::runtime_error& e ) + { + GL_CONTEXT_MANAGER::Get().UnlockCtx( glPrivContext ); + throw; // DRAW_PANEL_GAL will handle it + } isFramebufferInitialized = true; } @@ -363,6 +369,12 @@ void OPENGL_GAL::EndDrawing() void OPENGL_GAL::BeginUpdate() { + if( !IsShownOnScreen() ) + return; + + if( !isInitialized ) + init(); + GL_CONTEXT_MANAGER::Get().LockCtx( glPrivContext, this ); cachedManager->Map(); } @@ -370,6 +382,9 @@ void OPENGL_GAL::BeginUpdate() void OPENGL_GAL::EndUpdate() { + if( !isInitialized ) + return; + cachedManager->Unmap(); GL_CONTEXT_MANAGER::Get().UnlockCtx( glPrivContext ); } @@ -1500,88 +1515,42 @@ unsigned int OPENGL_GAL::getNewGroupNumber() } -bool OPENGL_GAL::runTest() +void OPENGL_GAL::init() { - static bool tested = false; - static bool testResult = false; - std::string errorMessage = "OpenGL test failed"; + wxASSERT( IsShownOnScreen() ); - if( !tested ) + GL_CONTEXT_MANAGER::Get().LockCtx( glPrivContext, this ); + + GLenum err = glewInit(); + + try { - wxDialog dlgtest( GetParent(), -1, wxT( "opengl test" ), wxPoint( 50, 50 ), - wxDLG_UNIT( GetParent(), wxSize( 50, 50 ) ) ); - OPENGL_TEST* test = new OPENGL_TEST( &dlgtest, this, glPrivContext ); - - dlgtest.Raise(); // on Linux, on some windows managers (Unity for instance) this is needed to actually show the dialog - dlgtest.ShowModal(); - testResult = test->IsOk(); - - if( !testResult ) - errorMessage = test->GetError(); - } - - if( !testResult ) - throw std::runtime_error( errorMessage ); - - return testResult; -} - - -OPENGL_GAL::OPENGL_TEST::OPENGL_TEST( wxDialog* aParent, OPENGL_GAL* aGal, wxGLContext* aContext ) : - wxGLCanvas( aParent, wxID_ANY, glAttributes, wxDefaultPosition, - wxDefaultSize, 0, wxT( "GLCanvas" ) ), - m_parent( aParent ), m_gal( aGal ), m_context( aContext ), m_tested( false ), m_result( false ) -{ - m_timeoutTimer.SetOwner( this ); - Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::OPENGL_TEST::Render ) ); - Connect( wxEVT_TIMER, wxTimerEventHandler( OPENGL_GAL::OPENGL_TEST::OnTimeout ) ); - m_parent->Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::OPENGL_TEST::OnDialogPaint ), NULL, this ); -} - - -void OPENGL_GAL::OPENGL_TEST::Render( wxPaintEvent& WXUNUSED( aEvent ) ) -{ - if( !m_tested ) - { - if( !IsShownOnScreen() ) - return; - - m_timeoutTimer.Stop(); - 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( std::bind( &wxDialog::EndModal, m_parent, wxID_NONE ) ); - - GL_CONTEXT_MANAGER::Get().LockCtx( m_context, this ); - GLenum err = glewInit(); - if( GLEW_OK != err ) - error( (const char*) glewGetErrorString( err ) ); + throw std::runtime_error( (const char*) glewGetErrorString( err ) ); // Check the OpenGL version (minimum 2.1 is required) - else if( !GLEW_VERSION_2_1 ) - error( "OpenGL 2.1 or higher is required!" ); + if( !GLEW_VERSION_2_1 ) + throw std::runtime_error( "OpenGL 2.1 or higher is required!" ); // Framebuffers have to be supported - else if( !GLEW_EXT_framebuffer_object ) - error( "Framebuffer objects are not supported!" ); + if( !GLEW_EXT_framebuffer_object ) + throw std::runtime_error( "Framebuffer objects are not supported!" ); // Vertex buffer has to be supported - else if( !GLEW_ARB_vertex_buffer_object ) - error( "Vertex buffer objects are not supported!" ); + if( !GLEW_ARB_vertex_buffer_object ) + throw std::runtime_error( "Vertex buffer objects are not supported!" ); // Prepare shaders - else if( !m_gal->shader->IsLinked() && !m_gal->shader->LoadShaderFromStrings( SHADER_TYPE_VERTEX, BUILTIN_SHADERS::kicad_vertex_shader ) ) - error( "Cannot compile vertex shader!" ); + if( !shader->IsLinked() && !shader->LoadShaderFromStrings( SHADER_TYPE_VERTEX, BUILTIN_SHADERS::kicad_vertex_shader ) ) + throw std::runtime_error( "Cannot compile vertex shader!" ); - else if( !m_gal->shader->IsLinked() && !m_gal->shader->LoadShaderFromStrings(SHADER_TYPE_FRAGMENT, BUILTIN_SHADERS::kicad_fragment_shader ) ) - error( "Cannot compile fragment shader!" ); + if( !shader->IsLinked() && !shader->LoadShaderFromStrings( SHADER_TYPE_FRAGMENT, BUILTIN_SHADERS::kicad_fragment_shader ) ) + throw std::runtime_error( "Cannot compile fragment shader!" ); - else if( !m_gal->shader->IsLinked() && !m_gal->shader->Link() ) - error( "Cannot link the shaders!" ); + if( !shader->IsLinked() && !shader->Link() ) + throw std::runtime_error( "Cannot link the shaders!" ); - // Check if video card supports textures big enough to fit font atlas + // Check if video card supports textures big enough to fit the font atlas int maxTextureSize; glGetIntegerv( GL_MAX_TEXTURE_SIZE, &maxTextureSize ); @@ -1589,41 +1558,25 @@ void OPENGL_GAL::OPENGL_TEST::Render( wxPaintEvent& WXUNUSED( aEvent ) ) { // TODO implement software texture scaling // for bitmap fonts and use a higher resolution texture? - error( "Requested texture size is not supported" ); + throw std::runtime_error( "Requested texture size is not supported" ); } - - GL_CONTEXT_MANAGER::Get().UnlockCtx( m_context ); - m_tested = true; } + catch( std::runtime_error& e ) + { + GL_CONTEXT_MANAGER::Get().UnlockCtx( glPrivContext ); + throw; + } + + // Make VBOs use shaders + cachedManager->SetShader( *shader ); + nonCachedManager->SetShader( *shader ); + overlayManager->SetShader( *shader ); + + GL_CONTEXT_MANAGER::Get().UnlockCtx( glPrivContext ); + isInitialized = true; } -void OPENGL_GAL::OPENGL_TEST::OnTimeout( wxTimerEvent& aEvent ) -{ - error( "Could not create OpenGL canvas" ); - m_parent->EndModal( wxID_NONE ); -} - - -void OPENGL_GAL::OPENGL_TEST::OnDialogPaint( wxPaintEvent& aEvent ) -{ - // GL canvas may never appear on the screen (e.g. due to missing GL extensions), and the test - // will not be run. Therefore give at most a second to perform the test, otherwise we conclude - // it has failed. - // Also, wxWidgets OnShow event is triggered before a window is shown, therefore here we use - // OnPaint event, which is executed when a window is actually visible. - m_timeoutTimer.StartOnce( 1000 ); -} - - -void OPENGL_GAL::OPENGL_TEST::error( const std::string& aError ) -{ - m_timeoutTimer.Stop(); - 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 ) { diff --git a/include/gal/opengl/opengl_gal.h b/include/gal/opengl/opengl_gal.h index dc703003f2..f685c6eaf5 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-2016 CERN + * Copyright (C) 2013-2017 CERN * @author Maciej Suminski * * Graphics Abstraction Layer (GAL) for OpenGL @@ -87,7 +87,11 @@ public: virtual ~OPENGL_GAL(); /// @copydoc GAL::IsInitialized() - virtual bool IsInitialized() const override { return IsShownOnScreen(); } + virtual bool IsInitialized() const override + { + // is*Initialized flags, but it is enough for OpenGL to show up + return IsShownOnScreen(); + } ///> @copydoc GAL::IsVisible() bool IsVisible() const override @@ -312,6 +316,8 @@ private: bool isFramebufferInitialized; ///< Are the framebuffers initialized? static bool isBitmapFontLoaded; ///< Is the bitmap font texture loaded? bool isBitmapFontInitialized; ///< Is the shader set to use bitmap fonts? + bool isInitialized; ///< Basic initialization flag, has to be done + ///< when the window is visible bool isGrouping; ///< Was a group started? // Polygon tesselation @@ -416,38 +422,9 @@ private: unsigned int getNewGroupNumber(); /** - * @brief Checks if the required OpenGL version and extensions are supported. - * @return true in case of success. + * @brief Basic OpenGL initialization. */ - bool runTest(); - - // Helper class to determine OpenGL capabilities - class OPENGL_TEST: public wxGLCanvas - { - public: - OPENGL_TEST( wxDialog* aParent, OPENGL_GAL* aGal, wxGLContext* aContext ); - - void Render( wxPaintEvent& aEvent ); - void OnTimeout( wxTimerEvent& aEvent ); - void OnDialogPaint( 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; - wxGLContext* m_context; - bool m_tested; - bool m_result; - std::string m_error; - wxTimer m_timeoutTimer; - }; - - friend class OPENGL_TEST; + void init(); }; } // namespace KIGFX