GL_CONTEXT_MANAGER class to avoid unexpected GL context switches.

This commit is contained in:
Maciej Suminski 2016-06-03 15:46:12 +02:00
parent 4f7bb4c0f7
commit 01f32dd52c
10 changed files with 235 additions and 36 deletions

View File

@ -48,6 +48,7 @@
#include <info3d_visu.h>
#include <trackball.h>
#include <3d_viewer_id.h>
#include <gl_context_mgr.h>
#include <textures/text_silk.h>
#include <textures/text_pcb.h>
@ -108,7 +109,7 @@ EDA_3D_CANVAS::EDA_3D_CANVAS( EDA_3D_FRAME* parent, int* attribList ) :
m_glLists[ii] = 0;
// Explicitly create a new rendering context instance for this canvas.
m_glRC = new wxGLContext( this );
m_glRC = GL_CONTEXT_MANAGER::Get().CreateCtx( this );
DisplayStatus();
}
@ -116,21 +117,16 @@ EDA_3D_CANVAS::EDA_3D_CANVAS( EDA_3D_FRAME* parent, int* attribList ) :
EDA_3D_CANVAS::~EDA_3D_CANVAS()
{
#ifdef __LINUX__
if( IsShownOnScreen() )
SetCurrent( *m_glRC );
#else
SetCurrent( *m_glRC );
#endif
GL_CONTEXT_MANAGER::Get().LockCtx( m_glRC );
ClearLists();
m_init = false;
delete m_glRC;
// Free the list of parsers list
for( unsigned int i = 0; i < m_model_parsers_list.size(); i++ )
delete m_model_parsers_list[i];
GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC );
}

View File

@ -57,6 +57,7 @@
#include <trackball.h>
#include <3d_draw_basic_functions.h>
#include "3d_rendering/3d_render_ogl_legacy/ogl_legacy_utils.h"
#include <gl_context_mgr.h>
#include <cimage.h>
#include <reporter.h>
@ -301,7 +302,7 @@ void EDA_3D_CANVAS::Redraw()
// Display build time at the end of build
unsigned strtime = GetRunningMicroSecs();
SetCurrent( *m_glRC );
GL_CONTEXT_MANAGER::Get().LockCtx( m_glRC );
// Set the OpenGL viewport according to the client size of this canvas.
// This is done here rather than in a wxSizeEvent handler because our
@ -318,7 +319,7 @@ void EDA_3D_CANVAS::Redraw()
generateFakeShadowsTextures( &errorReporter, &activityReporter );
}
// *MUST* be called *after* SetCurrent( ):
// *MUST* be called *after* GL_CONTEXT_MANAGER::LockCtx():
glViewport( 0, 0, size.x, size.y );
// clear color and depth buffers
@ -609,6 +610,7 @@ void EDA_3D_CANVAS::Redraw()
*/
SwapBuffers();
GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC );
// Show calculation time if some activity was reported
if( activityReporter.HasMessage() )

View File

@ -35,6 +35,7 @@
#include "common_ogl/ogl_utils.h"
#include <wx/dcclient.h>
#include "convert_to_biu.h"
#include <gl_context_mgr.h>
#define UNITS3D_TO_UNITSPCB (IU_PER_MM)
@ -202,7 +203,7 @@ void C3D_MODEL_VIEWER::OnPaint( wxPaintEvent &event )
// "Makes the OpenGL state that is represented by the OpenGL rendering
// context context current, i.e. it will be used by all subsequent OpenGL calls.
// This function may only be called when the window is shown on screen"
SetCurrent( *m_glRC );
GL_CONTEXT_MANAGER::Get().LockCtx( m_glRC );
// Set the OpenGL viewport according to the client size of this canvas.
// This is done here rather than in a wxSizeEvent handler because our
@ -311,6 +312,7 @@ void C3D_MODEL_VIEWER::OnPaint( wxPaintEvent &event )
// front-buffer and vice versa, so that the output of the previous OpenGL
// commands is displayed on the window."
SwapBuffers();
GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC );
event.Skip();
}

View File

@ -44,6 +44,7 @@ set( GAL_SRCS
newstroke_font.cpp
worksheet_viewitem.cpp
origin_viewitem.cpp
gl_context_mgr.cpp
gal/graphics_abstraction_layer.cpp
gal/stroke_font.cpp
gal/color4d.cpp

View File

@ -126,8 +126,11 @@ EDA_DRAW_PANEL_GAL::EDA_DRAW_PANEL_GAL( wxWindow* aParentWindow, wxWindowID aWin
EDA_DRAW_PANEL_GAL::~EDA_DRAW_PANEL_GAL()
{
StopDrawing();
SaveGalSettings();
assert( !m_drawing );
delete m_painter;
delete m_viewControls;
delete m_view;
@ -288,7 +291,6 @@ void EDA_DRAW_PANEL_GAL::StopDrawing()
m_drawingEnabled = false;
Disconnect( wxEVT_PAINT, wxPaintEventHandler( EDA_DRAW_PANEL_GAL::onPaint ), NULL, this );
m_pendingRefresh = false;
m_drawing = true;
m_refreshTimer.Stop();
}

View File

@ -29,6 +29,7 @@
#include <gal/opengl/opengl_gal.h>
#include <gal/opengl/utils.h>
#include <gal/definitions.h>
#include <gl_context_mgr.h>
#include <macros.h>
@ -49,8 +50,9 @@ using namespace KIGFX;
static void InitTesselatorCallbacks( GLUtesselator* aTesselator );
static const int glAttributes[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 8, 0 };
wxGLContext* OPENGL_GAL::glContext = NULL;
wxGLContext* OPENGL_GAL::glMainContext = NULL;
int OPENGL_GAL::instanceCounter = 0;
GLuint OPENGL_GAL::fontTexture = 0;
bool OPENGL_GAL::isBitmapFontLoaded = false;
@ -64,8 +66,15 @@ OPENGL_GAL::OPENGL_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener,
nonCachedManager( false ),
overlayManager( false )
{
if( glContext == NULL )
glContext = new wxGLContext( this );
if( glMainContext == NULL )
{
glMainContext = GL_CONTEXT_MANAGER::Get().CreateCtx( this );
glPrivContext = glMainContext;
}
else
{
glPrivContext = GL_CONTEXT_MANAGER::Get().CreateCtx( this, glMainContext );
}
// Check if OpenGL requirements are met
runTest();
@ -129,16 +138,10 @@ OPENGL_GAL::OPENGL_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener,
OPENGL_GAL::~OPENGL_GAL()
{
GL_CONTEXT_MANAGER::Get().LockCtx( glPrivContext );
gluDeleteTess( tesselator );
ClearCache();
#ifdef __LINUX__
if( IsShownOnScreen() )
SetCurrent( *OPENGL_GAL::glContext );
#else
SetCurrent( *OPENGL_GAL::glContext );
#endif
glFlush();
if( --instanceCounter == 0 )
@ -148,10 +151,9 @@ OPENGL_GAL::~OPENGL_GAL()
glDeleteTextures( 1, &fontTexture );
isBitmapFontLoaded = false;
}
delete OPENGL_GAL::glContext;
glContext = NULL;
}
GL_CONTEXT_MANAGER::Get().UnlockCtx( glPrivContext );
}
@ -165,7 +167,7 @@ void OPENGL_GAL::BeginDrawing()
prof_start( &totalRealTime );
#endif /* __WXDEBUG__ */
SetCurrent( *glContext );
GL_CONTEXT_MANAGER::Get().LockCtx( glPrivContext );
#ifdef RETINA_OPENGL_PATCH
const float scaleFactor = GetBackingScaleFactor();
@ -237,6 +239,7 @@ void OPENGL_GAL::BeginDrawing()
// Keep bitmap font texture always bound to the second texturing unit
const GLint FONT_TEXTURE_UNIT = 2;
// Either load the font atlas to video memory, or simply bind it to a texture unit
if( !isBitmapFontLoaded )
{
glActiveTexture( GL_TEXTURE0 + FONT_TEXTURE_UNIT );
@ -253,6 +256,12 @@ void OPENGL_GAL::BeginDrawing()
isBitmapFontLoaded = true;
}
else
{
glActiveTexture( GL_TEXTURE0 + FONT_TEXTURE_UNIT );
glBindTexture( GL_TEXTURE_2D, fontTexture );
glActiveTexture( GL_TEXTURE0 );
}
// Set shader parameter
GLint ufm_fontTexture = shader.AddParameter( "fontTexture" );
@ -300,6 +309,7 @@ void OPENGL_GAL::EndDrawing()
blitCursor();
SwapBuffers();
GL_CONTEXT_MANAGER::Get().UnlockCtx( glPrivContext );
#ifdef __WXDEBUG__
prof_end( &totalRealTime );
@ -310,7 +320,7 @@ void OPENGL_GAL::EndDrawing()
void OPENGL_GAL::BeginUpdate()
{
SetCurrent( *OPENGL_GAL::glContext );
GL_CONTEXT_MANAGER::Get().LockCtx( glPrivContext );
cachedManager.Map();
}
@ -318,6 +328,7 @@ void OPENGL_GAL::BeginUpdate()
void OPENGL_GAL::EndUpdate()
{
cachedManager.Unmap();
GL_CONTEXT_MANAGER::Get().UnlockCtx( glPrivContext );
}
@ -1454,7 +1465,7 @@ bool OPENGL_GAL::runTest()
{
wxDialog dlgtest( GetParent(), -1, wxT( "opengl test" ), wxPoint( 50, 50 ),
wxDLG_UNIT( GetParent(), wxSize( 50, 50 ) ) );
OPENGL_TEST* test = new OPENGL_TEST( &dlgtest, this );
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();
@ -1471,10 +1482,10 @@ bool OPENGL_GAL::runTest()
}
OPENGL_GAL::OPENGL_TEST::OPENGL_TEST( wxDialog* aParent, OPENGL_GAL* aGal ) :
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_tested( false ), m_result( false )
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 ) );
@ -1497,7 +1508,7 @@ void OPENGL_GAL::OPENGL_TEST::Render( wxPaintEvent& WXUNUSED( aEvent ) )
Disconnect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::OPENGL_TEST::Render ) );
CallAfter( boost::bind( &wxDialog::EndModal, m_parent, wxID_NONE ) );
SetCurrent( *OPENGL_GAL::glContext );
GL_CONTEXT_MANAGER::Get().LockCtx( m_context, this );
GLenum err = glewInit();
if( GLEW_OK != err )
@ -1536,6 +1547,7 @@ void OPENGL_GAL::OPENGL_TEST::Render( wxPaintEvent& WXUNUSED( aEvent ) )
error( "Requested texture size is not supported" );
}
GL_CONTEXT_MANAGER::Get().UnlockCtx( m_context );
m_tested = true;
}
}

84
common/gl_context_mgr.cpp Normal file
View File

@ -0,0 +1,84 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2016 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* 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 <gl_context_mgr.h>
GL_CONTEXT_MANAGER& GL_CONTEXT_MANAGER::Get()
{
static GL_CONTEXT_MANAGER instance;
return instance;
}
wxGLContext* GL_CONTEXT_MANAGER::CreateCtx( wxGLCanvas* aCanvas, const wxGLContext* aOther )
{
wxGLContext* context = new wxGLContext( aCanvas, aOther );
assert( context /* && context->IsOK() */ ); // IsOK() is available in wx3.1+
assert( m_glContexts.count( context ) == 0 );
m_glContexts.insert( std::make_pair( context, aCanvas ) );
return context;
}
void GL_CONTEXT_MANAGER::DeleteAll()
{
for( auto& ctx : m_glContexts )
delete ctx.first;
m_glContexts.clear();
}
void GL_CONTEXT_MANAGER::LockCtx( wxGLContext* aContext, wxGLCanvas* aCanvas )
{
assert( aCanvas || m_glContexts.count( aContext ) > 0 );
m_glCtxMutex.lock();
wxGLCanvas* canvas = aCanvas ? aCanvas : m_glContexts.at( aContext );
canvas->SetCurrent( *aContext );
m_glCtx = aContext;
}
void GL_CONTEXT_MANAGER::UnlockCtx( wxGLContext* aContext )
{
assert( m_glContexts.count( aContext ) > 0 );
if( m_glCtx == aContext ){
m_glCtxMutex.unlock();
m_glCtx = NULL;
}
else
{
wxLogDebug( "Trying to unlock GL context mutex from a wrong context" );
}
}
GL_CONTEXT_MANAGER::GL_CONTEXT_MANAGER()
: m_glCtx( NULL )
{
}

View File

@ -273,12 +273,13 @@ private:
static const int CIRCLE_POINTS = 64; ///< The number of points for circle approximation
static const int CURVE_POINTS = 32; ///< The number of points for curve approximation
static wxGLContext* glContext; ///< OpenGL context of wxWidgets
static wxGLContext* glMainContext; ///< Parent OpenGL context
wxGLContext* glPrivContext; ///< Canvas-specific OpenGL context
wxEvtHandler* mouseListener;
wxEvtHandler* paintListener;
static int instanceCounter;
GLuint fontTexture; ///< Bitmap font texture handle
static GLuint fontTexture; ///< Bitmap font texture handle (shared)
// Vertex buffer objects related fields
typedef std::map< unsigned int, boost::shared_ptr<VERTEX_ITEM> > GROUPS_MAP;
@ -415,7 +416,7 @@ private:
class OPENGL_TEST: public wxGLCanvas
{
public:
OPENGL_TEST( wxDialog* aParent, OPENGL_GAL* aGal );
OPENGL_TEST( wxDialog* aParent, OPENGL_GAL* aGal, wxGLContext* aContext );
void Render( wxPaintEvent& aEvent );
void OnTimeout( wxTimerEvent& aEvent );
@ -430,6 +431,7 @@ private:
wxDialog* m_parent;
OPENGL_GAL* m_gal;
wxGLContext* m_context;
bool m_tested;
bool m_result;
std::string m_error;

93
include/gl_context_mgr.h Normal file
View File

@ -0,0 +1,93 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2016 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* 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 GL_CONTEXT_MANAGER_H
#define GL_CONTEXT_MANAGER_H
#include <wx/glcanvas.h>
#include <ki_mutex.h>
#include <map>
class GL_CONTEXT_MANAGER
{
public:
/**
* Function Get
* returns the GL_CONTEXT_MANAGER instance (singleton).
*/
static GL_CONTEXT_MANAGER& Get();
/**
* Function CreateCtx
* creates a managed OpenGL context. It is assured that the created context is freed upon
* exit.
* See wxGLContext documentation for the parameters description.
* @return Created OpenGL context.
*/
wxGLContext* CreateCtx( wxGLCanvas* aCanvas, const wxGLContext* aOther = NULL );
/**
* Function DeleteAll
* destroys all managed OpenGL contexts. This method should be called in the
* final deinitialization routine.
*/
void DeleteAll();
/**
* Function LockCtx
* sets a context as current and prevents other canvases from switching it.
* Requires calling UnlockCtx() when there are no more GL calls for the context.
* If another canvas has already locked a GL context, then the calling process is blocked.
* @param aContext is the GL context to be bound.
* @param aCanvas (optional) allows caller to bind the context to a non-parent canvas
* (e.g. when a few canvases share a single GL context).
*/
void LockCtx( wxGLContext* aContext, wxGLCanvas* aCanvas = NULL );
/**
* Function UnlockCtx
* allows other canvases to bind an OpenGL context.
* @param aContext is the currently bound context. It is only a check to assure the right
* canvas wants to unlock GL context.
*/
void UnlockCtx( wxGLContext* aContext );
private:
///> Map of GL contexts & their parent canvases.
std::map<wxGLContext*, wxGLCanvas*> m_glContexts;
///> Currently bound GL context.
wxGLContext* m_glCtx;
///> Lock to prevent unexpected GL context switching.
MUTEX m_glCtxMutex;
// Singleton
GL_CONTEXT_MANAGER();
GL_CONTEXT_MANAGER( const GL_CONTEXT_MANAGER& );
void operator=( const GL_CONTEXT_MANAGER& );
};
#endif /* GL_CONTEXT_MANAGER_H */

View File

@ -57,6 +57,7 @@
#include <module_editor_frame.h>
#include <modview_frame.h>
#include <footprint_wizard_frame.h>
#include <gl_context_mgr.h>
extern bool IsWxPythonLoaded();
@ -367,8 +368,12 @@ bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits )
void IFACE::OnKifaceEnd()
{
end_common();
// This function deletes OpenGL contexts used (if any) by wxGLCanvas objects.
// It can be called only when closing the application, because it deletes an OpenGL context
// which can still be in usage. Destroying OpenGL contexts earlier may crash the application.
GL_CONTEXT_MANAGER::Get().DeleteAll();
end_common();
#if defined( KICAD_SCRIPTING_WXPYTHON )
// Restore the thread state and tell Python to cleanup after itself.
// wxPython will do its own cleanup as part of that process.