Reworked GAL switching.

Now OpenGL can report its problems instead of shutting down the application.
This commit is contained in:
Maciej Suminski 2015-02-15 02:18:35 +01:00
parent b19010ff8b
commit b1ace1607b
7 changed files with 136 additions and 171 deletions

View File

@ -1,8 +1,9 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2014 CERN
* Copyright (C) 2013-2015 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* @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
@ -28,6 +29,7 @@
#include <wx/event.h>
#include <wx/colour.h>
#include <wx/filename.h>
#include <confirm.h>
#include <class_draw_panel_gal.h>
#include <view/view.h>
@ -116,30 +118,30 @@ void EDA_DRAW_PANEL_GAL::onPaint( wxPaintEvent& WXUNUSED( aEvent ) )
m_pendingRefresh = false;
m_lastRefresh = wxGetLocalTimeMillis();
if( !m_drawing )
if( m_drawing )
return;
m_drawing = true;
m_view->UpdateItems();
m_gal->BeginDrawing();
m_gal->ClearScreen( m_painter->GetSettings()->GetBackgroundColor() );
if( m_view->IsDirty() )
{
m_drawing = true;
m_view->ClearTargets();
m_view->UpdateItems();
m_gal->BeginDrawing();
m_gal->ClearScreen( m_painter->GetSettings()->GetBackgroundColor() );
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 ) )
// 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();
m_drawing = false;
m_view->Redraw();
}
m_gal->DrawCursor( m_viewControls->GetCursorPosition() );
m_gal->EndDrawing();
m_drawing = false;
}
@ -233,7 +235,8 @@ void EDA_DRAW_PANEL_GAL::SetEventDispatcher( TOOL_DISPATCHER* aEventDispatcher )
void EDA_DRAW_PANEL_GAL::StartDrawing()
{
m_pendingRefresh = false;
m_drawing = false;
m_pendingRefresh = true;
Connect( wxEVT_PAINT, wxPaintEventHandler( EDA_DRAW_PANEL_GAL::onPaint ), NULL, this );
wxPaintEvent redrawEvent;
@ -243,7 +246,8 @@ void EDA_DRAW_PANEL_GAL::StartDrawing()
void EDA_DRAW_PANEL_GAL::StopDrawing()
{
m_pendingRefresh = true;
m_pendingRefresh = false;
m_drawing = true;
m_refreshTimer.Stop();
Disconnect( wxEVT_PAINT, wxPaintEventHandler( EDA_DRAW_PANEL_GAL::onPaint ), NULL, this );
}
@ -271,41 +275,54 @@ void EDA_DRAW_PANEL_GAL::SetTopLayer( LAYER_ID aLayer )
}
void EDA_DRAW_PANEL_GAL::SwitchBackend( GalType aGalType )
bool EDA_DRAW_PANEL_GAL::SwitchBackend( GalType aGalType )
{
// Do not do anything if the currently used GAL is correct
if( aGalType == m_backend && m_gal != NULL )
return;
return true;
// Prevent refreshing canvas during backend switch
StopDrawing();
delete m_gal;
KIGFX::GAL* new_gal = NULL;
switch( aGalType )
try
{
case GAL_TYPE_OPENGL:
m_gal = new KIGFX::OPENGL_GAL( this, this, this );
break;
switch( aGalType )
{
case GAL_TYPE_OPENGL:
new_gal = new KIGFX::OPENGL_GAL( this, this, this );
break;
case GAL_TYPE_CAIRO:
m_gal = new KIGFX::CAIRO_GAL( this, this, this );
break;
case GAL_TYPE_CAIRO:
new_gal = new KIGFX::CAIRO_GAL( this, this, this );
break;
case GAL_TYPE_NONE:
return;
case GAL_TYPE_NONE:
return false;
}
delete m_gal;
m_gal = new_gal;
wxSize size = GetClientSize();
m_gal->ResizeScreen( size.GetX(), size.GetY() );
if( m_painter )
m_painter->SetGAL( m_gal );
if( m_view )
m_view->SetGAL( m_gal );
m_backend = aGalType;
}
catch (std::runtime_error& err)
{
DisplayError( m_parent, wxString( err.what() ) );
return false;
}
wxSize size = GetClientSize();
m_gal->ResizeScreen( size.GetX(), size.GetY() );
if( m_painter )
m_painter->SetGAL( m_gal );
if( m_view )
m_view->SetGAL( m_gal );
m_backend = aGalType;
return true;
}

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* Copyright (C) 2013-2015 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
@ -24,13 +24,14 @@
/**
* @file opengl_compositor.cpp
* @brief Class that handles multitarget rendering (ie. to different textures/surfaces) and
* @brief Class that handles multitarget rendering (i.e. to different textures/surfaces) and
* later compositing into a single image (OpenGL flavour).
*/
#include <gal/opengl/opengl_compositor.h>
#include <wx/msgdlg.h>
#include <confirm.h>
#include <stdexcept>
#include <cassert>
using namespace KIGFX;
@ -89,7 +90,7 @@ void OPENGL_COMPOSITOR::Resize( unsigned int aWidth, unsigned int aHeight )
unsigned int OPENGL_COMPOSITOR::CreateBuffer()
{
wxASSERT( m_initialized );
assert( m_initialized );
unsigned int maxBuffers;
@ -98,10 +99,9 @@ unsigned int OPENGL_COMPOSITOR::CreateBuffer()
if( usedBuffers() >= maxBuffers )
{
DisplayError( NULL, wxT( "Cannot create more framebuffers. OpenGL rendering "
throw std::runtime_error("Cannot create more framebuffers. OpenGL rendering "
"backend requires at least 3 framebuffers. You may try to update/change "
"your graphic drivers." ) );
return 0; // Unfortunately we have no more free buffers left
"your graphic drivers.");
}
// GL_COLOR_ATTACHMENTn are consecutive integers
@ -133,38 +133,38 @@ unsigned int OPENGL_COMPOSITOR::CreateBuffer()
switch( status )
{
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
DisplayError( NULL,wxT( "Cannot create the framebuffer." ) );
throw std::runtime_error( "Cannot create the framebuffer." );
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
DisplayError( NULL, wxT( "The framebuffer attachment points are incomplete." ) );
throw std::runtime_error( "The framebuffer attachment points are incomplete." );
break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
DisplayError( NULL, wxT( "The framebuffer does not have at least "
"one image attached to it." ) );
throw std::runtime_error( "The framebuffer does not have at least one "
"image attached to it." );
break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
DisplayError( NULL, wxT( "The framebuffer read buffer is incomplete." ) );
throw std::runtime_error( "The framebuffer read buffer is incomplete." );
break;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
DisplayError( NULL, wxT( "The combination of internal formats of the attached images "
"violates an implementation-dependent set of restrictions." ) );
throw std::runtime_error( "The combination of internal formats of the attached "
"images violates an implementation-dependent set of restrictions." );
break;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
DisplayError( NULL, wxT( "GL_RENDERBUFFER_SAMPLES is not the same "
"for all attached renderbuffers" ) );
throw std::runtime_error( "GL_RENDERBUFFER_SAMPLES is not the same for "
"all attached renderbuffers" );
break;
case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT:
DisplayError( NULL, wxT( "Framebuffer incomplete layer targets errors." ) );
throw std::runtime_error( "Framebuffer incomplete layer targets errors." );
break;
default:
DisplayError( NULL, wxT( "Cannot create the framebuffer." ) );
throw std::runtime_error( "Cannot create the framebuffer." );
break;
}
@ -211,7 +211,7 @@ void OPENGL_COMPOSITOR::SetBuffer( unsigned int aBufferHandle )
void OPENGL_COMPOSITOR::ClearBuffer()
{
wxASSERT( m_initialized );
assert( m_initialized );
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
@ -220,8 +220,8 @@ void OPENGL_COMPOSITOR::ClearBuffer()
void OPENGL_COMPOSITOR::DrawBuffer( unsigned int aBufferHandle )
{
wxASSERT( m_initialized );
wxASSERT( aBufferHandle != 0 && aBufferHandle <= usedBuffers() );
assert( m_initialized );
assert( aBufferHandle != 0 && aBufferHandle <= usedBuffers() );
// Switch to the main framebuffer and blit the scene
glBindFramebufferEXT( GL_FRAMEBUFFER, DIRECT_RENDERING );
@ -267,7 +267,7 @@ void OPENGL_COMPOSITOR::DrawBuffer( unsigned int aBufferHandle )
void OPENGL_COMPOSITOR::clean()
{
wxASSERT( m_initialized );
assert( m_initialized );
glBindFramebufferEXT( GL_FRAMEBUFFER, DIRECT_RENDERING );
m_currentFbo = DIRECT_RENDERING;

View File

@ -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 CERN
* Copyright (C) 2013-2015 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* Graphics Abstraction Layer (GAL) for OpenGL
@ -31,7 +31,6 @@
#include <wx/log.h>
#include <macros.h>
#include <confirm.h>
#ifdef __WXDEBUG__
#include <profile.h>
#endif /* __WXDEBUG__ */
@ -51,6 +50,9 @@ 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 )
@ -59,14 +61,29 @@ OPENGL_GAL::OPENGL_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener,
if( glContext == NULL )
glContext = new wxGLContext( this );
parentWindow = aParent;
mouseListener = aMouseListener;
paintListener = aPaintListener;
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!" );
// Make VBOs use shaders
cachedManager.SetShader( shader );
nonCachedManager.SetShader( shader );
overlayManager.SetShader( shader );
// Initialize the flags
isGlewInitialized = false;
isFramebufferInitialized = false;
isShaderInitialized = false;
isGrouping = false;
groupCounter = 0;
@ -100,10 +117,7 @@ OPENGL_GAL::OPENGL_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener,
InitTesselatorCallbacks( tesselator );
if( tesselator == NULL )
{
DisplayError( parentWindow, wxT( "Could not create the tesselator" ) );
exit( 1 );
}
throw std::runtime_error( "Could not create the tesselator" );
gluTessProperty( tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
@ -123,13 +137,8 @@ OPENGL_GAL::~OPENGL_GAL()
void OPENGL_GAL::BeginDrawing()
{
SetCurrent( *glContext );
clientDC = new wxClientDC( this );
// Initialize GLEW, FBOs & VBOs
if( !isGlewInitialized )
initGlew();
// Set up the view port
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
@ -148,35 +157,6 @@ void OPENGL_GAL::BeginDrawing()
isFramebufferInitialized = true;
}
// Compile the shaders
if( !isShaderInitialized )
{
if( !shader.LoadBuiltinShader( 0, SHADER_TYPE_VERTEX ) )
{
DisplayError( parentWindow, wxT( "Cannot compile vertex shader!" ) );
exit( 1 );
}
if( !shader.LoadBuiltinShader( 1, SHADER_TYPE_FRAGMENT ) )
{
DisplayError( parentWindow, wxT( "Cannot compile fragment shader!" ) );
exit( 1 );
}
if( !shader.Link() )
{
DisplayError( parentWindow, wxT( "Cannot link the shaders!" ) );
exit( 1 );
}
// Make VBOs use shaders
cachedManager.SetShader( shader );
nonCachedManager.SetShader( shader );
overlayManager.SetShader( shader );
isShaderInitialized = true;
}
// Disable 2D Textures
glDisable( GL_TEXTURE_2D );
@ -815,7 +795,7 @@ void OPENGL_GAL::drawLineQuad( const VECTOR2D& aStartPoint, const VECTOR2D& aEnd
if( lineLength <= 0.0 )
return;
double scale = 0.5 * lineWidth / lineLength;
// The perpendicular vector also needs transformations
@ -941,8 +921,7 @@ void OPENGL_GAL::initGlew()
if( GLEW_OK != err )
{
DisplayError( parentWindow, wxString::FromUTF8( (char*) glewGetErrorString( err ) ) );
exit( 1 );
throw std::runtime_error( (const char*) glewGetErrorString( err ) );
}
else
{
@ -952,30 +931,17 @@ void OPENGL_GAL::initGlew()
// Check the OpenGL version (minimum 2.1 is required)
if( GLEW_VERSION_2_1 )
{
wxLogInfo( wxT( "OpenGL 2.1 supported." ) );
}
else
{
DisplayError( parentWindow, wxT( "OpenGL 2.1 or higher is required!" ) );
exit( 1 );
}
throw std::runtime_error( "OpenGL 2.1 or higher is required!" );
// Framebuffers have to be supported
if( !GLEW_EXT_framebuffer_object )
{
DisplayError( parentWindow, wxT( "Framebuffer objects are not supported!" ) );
exit( 1 );
}
throw std::runtime_error( "Framebuffer objects are not supported!" );
// Vertex buffer has to be supported
if( !GLEW_ARB_vertex_buffer_object )
{
DisplayError( parentWindow, wxT( "Vertex buffer objects are not supported!" ) );
exit( 1 );
}
isGlewInitialized = true;
throw std::runtime_error( "Vertex buffer objects are not supported!" );
}
@ -1058,12 +1024,8 @@ void CALLBACK EdgeCallback( GLboolean aEdgeFlag )
void CALLBACK ErrorCallback( GLenum aErrorCode )
{
const GLubyte* eString = gluErrorString( aErrorCode );
DisplayError( NULL, wxT( "Tessellation error: " ) +
wxString( (const char*)( eString ), wxConvUTF8 ) );
exit( 1 );
//throw std::runtime_error( std::string( "Tessellation error: " ) +
//std::string( (const char*) gluErrorString( aErrorCode ) );
}

View File

@ -28,10 +28,10 @@
#include <iostream>
#include <fstream>
#include <stdexcept>
#include <wx/log.h>
#include <wx/gdicmn.h>
#include <confirm.h>
#include <cstring>
#include <cassert>
#include <gal/opengl/shader.h>
#include "shader_src.h"
@ -102,8 +102,7 @@ bool SHADER::Link()
glGetObjectParameterivARB( programNumber, GL_OBJECT_LINK_STATUS_ARB,
(GLint*) &isShaderLinked );
#ifdef __WXDEBUG__
#ifdef DEBUG
if( !isShaderLinked )
{
int maxLength;
@ -115,8 +114,7 @@ bool SHADER::Link()
std::cerr << linkInfoLog;
delete[] linkInfoLog;
}
#endif /* __WXDEBUG__ */
#endif /* DEBUG */
return isShaderLinked;
}
@ -127,9 +125,7 @@ int SHADER::AddParameter( const std::string& aParameterName )
GLint location = glGetUniformLocation( programNumber, aParameterName.c_str() );
if( location != -1 )
{
parameterLocation.push_back( location );
}
return location;
}
@ -167,7 +163,7 @@ void SHADER::programInfo( GLuint aProgram )
GLchar* glInfoLog = new GLchar[glInfoLogLength];
glGetProgramInfoLog( aProgram, glInfoLogLength, &writtenChars, glInfoLog );
wxLogInfo( wxString::FromUTF8( (char*) glInfoLog ) );
std::cerr << glInfoLog << std::endl;
delete[] glInfoLog;
}
@ -188,7 +184,7 @@ void SHADER::shaderInfo( GLuint aShader )
GLchar* glInfoLog = new GLchar[glInfoLogLength];
glGetShaderInfoLog( aShader, glInfoLogLength, &writtenChars, glInfoLog );
wxLogInfo( wxString::FromUTF8( (char*) glInfoLog ) );
std::cerr << glInfoLog << std::endl;
delete[] glInfoLog;
}
@ -202,11 +198,7 @@ std::string SHADER::readSource( std::string aShaderSourceName )
std::string shaderSource;
if( !inputFile )
{
DisplayError( NULL, wxString::FromUTF8( "Can't read the shader source: " ) +
wxString( aShaderSourceName.c_str(), wxConvUTF8 ) );
exit( 1 );
}
throw std::runtime_error( "Can't read the shader source: " + aShaderSourceName );
std::string shaderSourceLine;
@ -223,10 +215,7 @@ std::string SHADER::readSource( std::string aShaderSourceName )
bool SHADER::addSource( const std::string& aShaderSource, SHADER_TYPE aShaderType )
{
if( isShaderLinked )
{
wxLogDebug( wxT( "Shader is already linked!" ) );
}
assert( !isShaderLinked );
// Create the program
if( !isProgramCreated )
@ -244,7 +233,7 @@ bool SHADER::addSource( const std::string& aShaderSource, SHADER_TYPE aShaderTyp
// Copy to char array
char* source = new char[aShaderSource.size() + 1];
strcpy( source, aShaderSource.c_str() );
strncpy( source, aShaderSource.c_str(), aShaderSource.size() + 1 );
const char** source_ = (const char**) ( &source );
// Attach the source
@ -261,11 +250,8 @@ bool SHADER::addSource( const std::string& aShaderSource, SHADER_TYPE aShaderTyp
if( status != GL_TRUE )
{
DisplayError( NULL, wxT( "Shader compilation error" ) );
shaderInfo( shaderNumber );
return false;
throw std::runtime_error( "Shader compilation error" );
}
glAttachShader( programNumber, shaderNumber );

View File

@ -66,7 +66,7 @@ public:
* Switches method of rendering graphics.
* @param aGalType is a type of rendering engine that you want to use.
*/
void SwitchBackend( GalType aGalType );
bool SwitchBackend( GalType aGalType );
/**
* Function GetBackend

View File

@ -277,9 +277,7 @@ private:
SHADER shader; ///< There is only one shader used for different objects
// Internal flags
bool isGlewInitialized; ///< Is GLEW initialized?
bool isFramebufferInitialized; ///< Are the framebuffers initialized?
bool isShaderInitialized; ///< Was the shader initialized?
bool isGrouping; ///< Was a group started?
// Polygon tesselation

View File

@ -694,24 +694,26 @@ void PCB_EDIT_FRAME::UseGalCanvas( bool aEnable )
void PCB_EDIT_FRAME::SwitchCanvas( wxCommandEvent& aEvent )
{
int id = aEvent.GetId();
bool use_gal = false;
switch( id )
{
case ID_MENU_CANVAS_DEFAULT:
Compile_Ratsnest( NULL, true );
UseGalCanvas( false );
break;
case ID_MENU_CANVAS_CAIRO:
GetGalCanvas()->SwitchBackend( EDA_DRAW_PANEL_GAL::GAL_TYPE_CAIRO );
UseGalCanvas( true );
use_gal = GetGalCanvas()->SwitchBackend( EDA_DRAW_PANEL_GAL::GAL_TYPE_CAIRO );
break;
case ID_MENU_CANVAS_OPENGL:
GetGalCanvas()->SwitchBackend( EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL );
UseGalCanvas( true );
use_gal = GetGalCanvas()->SwitchBackend( EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL );
break;
}
if( !use_gal )
Compile_Ratsnest( NULL, true );
UseGalCanvas( use_gal );
}