Alternative way of handling OpenGL initialization & errors
Some faults could result in a crash, as they were not properly handled. Now the rendering loop is wrapped with try..catch block which will revert to Cairo in case of an error and display an error message. Fixes: lp:1655766 * https://bugs.launchpad.net/kicad/+bug/1655766
This commit is contained in:
parent
ca085de6aa
commit
88eb648cbb
|
@ -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 <tomasz.wlostowski@cern.ch>
|
||||
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
||||
*
|
||||
|
@ -175,6 +175,8 @@ void EDA_DRAW_PANEL_GAL::onPaint( wxPaintEvent& WXUNUSED( aEvent ) )
|
|||
|
||||
m_view->UpdateItems();
|
||||
|
||||
try
|
||||
{
|
||||
m_gal->BeginDrawing();
|
||||
m_gal->ClearScreen( settings->GetBackgroundColor() );
|
||||
|
||||
|
@ -194,6 +196,19 @@ void EDA_DRAW_PANEL_GAL::onPaint( wxPaintEvent& WXUNUSED( aEvent ) )
|
|||
|
||||
m_gal->DrawCursor( m_viewControls->GetCursorPosition() );
|
||||
m_gal->EndDrawing();
|
||||
}
|
||||
catch( std::runtime_error& err )
|
||||
{
|
||||
assert( GetBackend() != GAL_TYPE_CAIRO );
|
||||
|
||||
// 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();
|
||||
|
|
|
@ -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 <maciej.suminski@cern.ch>
|
||||
*
|
||||
* 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
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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 <maciej.suminski@cern.ch>
|
||||
*
|
||||
* 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
|
||||
|
@ -220,11 +218,19 @@ void OPENGL_GAL::BeginDrawing()
|
|||
glOrtho( 0, (GLint) screenSize.x, (GLsizei) screenSize.y, 0, -depthRange.x, -depthRange.y );
|
||||
|
||||
if( !isFramebufferInitialized )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
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 );
|
||||
GL_CONTEXT_MANAGER::Get().LockCtx( glPrivContext, this );
|
||||
|
||||
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();
|
||||
|
||||
try
|
||||
{
|
||||
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" );
|
||||
}
|
||||
|
||||
GL_CONTEXT_MANAGER::Get().UnlockCtx( m_context );
|
||||
m_tested = true;
|
||||
throw std::runtime_error( "Requested texture size is not supported" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OPENGL_GAL::OPENGL_TEST::OnTimeout( wxTimerEvent& aEvent )
|
||||
catch( std::runtime_error& e )
|
||||
{
|
||||
error( "Could not create OpenGL canvas" );
|
||||
m_parent->EndModal( wxID_NONE );
|
||||
GL_CONTEXT_MANAGER::Get().UnlockCtx( glPrivContext );
|
||||
throw;
|
||||
}
|
||||
|
||||
// Make VBOs use shaders
|
||||
cachedManager->SetShader( *shader );
|
||||
nonCachedManager->SetShader( *shader );
|
||||
overlayManager->SetShader( *shader );
|
||||
|
||||
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 );
|
||||
GL_CONTEXT_MANAGER::Get().UnlockCtx( glPrivContext );
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
|
||||
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 )
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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 <maciej.suminski@cern.ch>
|
||||
*
|
||||
* 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
|
||||
|
||||
|
|
Loading…
Reference in New Issue