Fix DoRePaint routine in EDA_3D_CANVAS

- Check that we aren't already painting (return if we are)
 - Check that GLEW functions exist before calling them in 3D canvas and throw exception if they are no longer available
 - Catch above exceptions in paint routine and show an infobar message to the user
Fixes https://gitlab.com/kicad/code/kicad/-/issues/6246
This commit is contained in:
Roberto Fernandez Bautista 2020-11-03 00:13:31 +00:00 committed by Mark Roszko
parent 2d33b23530
commit 1bbc569151
4 changed files with 58 additions and 15 deletions

View File

@ -48,6 +48,7 @@
#include <settings/settings_manager.h> #include <settings/settings_manager.h>
#include <tool/tool_dispatcher.h> #include <tool/tool_dispatcher.h>
#include <confirm.h>
#include <widgets/wx_busy_indicator.h> #include <widgets/wx_busy_indicator.h>
@ -134,6 +135,8 @@ EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow* aParent, const int* aAttribList, BOARD*
NULL, NULL,
this ); this );
m_is_currently_painting.clear();
m_3d_render_raytracing = new C3D_RENDER_RAYTRACING( m_boardAdapter, m_camera ); m_3d_render_raytracing = new C3D_RENDER_RAYTRACING( m_boardAdapter, m_camera );
m_3d_render_ogl_legacy = new C3D_RENDER_OGL_LEGACY( m_boardAdapter, m_camera ); m_3d_render_ogl_legacy = new C3D_RENDER_OGL_LEGACY( m_boardAdapter, m_camera );
@ -361,10 +364,15 @@ void EDA_3D_CANVAS::OnPaint( wxPaintEvent& aEvent )
void EDA_3D_CANVAS::DoRePaint() void EDA_3D_CANVAS::DoRePaint()
{ {
if( m_is_currently_painting.test_and_set() )
return;
// SwapBuffer requires the window to be shown before calling // SwapBuffer requires the window to be shown before calling
if( !IsShownOnScreen() ) if( !IsShownOnScreen() )
{ {
wxLogTrace( m_logTrace, "EDA_3D_CANVAS::DoRePaint !IsShown" ); wxLogTrace( m_logTrace, "EDA_3D_CANVAS::DoRePaint !IsShown" );
m_is_currently_painting.clear();
return; return;
} }
@ -414,6 +422,7 @@ void EDA_3D_CANVAS::DoRePaint()
if( !initializeOpenGL() ) if( !initializeOpenGL() )
{ {
GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC ); GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC );
m_is_currently_painting.clear();
return; return;
} }
@ -435,6 +444,7 @@ void EDA_3D_CANVAS::DoRePaint()
SwapBuffers(); SwapBuffers();
GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC ); GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC );
m_is_currently_painting.clear();
return; return;
} }
@ -493,19 +503,32 @@ void EDA_3D_CANVAS::DoRePaint()
if( m_3d_render ) if( m_3d_render )
{ {
m_3d_render->SetCurWindowSize( clientSize ); try
{
m_3d_render->SetCurWindowSize( clientSize );
bool reloadRaytracingForIntersectionCalculations = false; bool reloadRaytracingForIntersectionCalculations = false;
if( ( m_boardAdapter.RenderEngineGet() == RENDER_ENGINE::OPENGL_LEGACY ) && if( ( m_boardAdapter.RenderEngineGet() == RENDER_ENGINE::OPENGL_LEGACY )
m_3d_render_ogl_legacy->IsReloadRequestPending() ) && m_3d_render_ogl_legacy->IsReloadRequestPending() )
reloadRaytracingForIntersectionCalculations = true; reloadRaytracingForIntersectionCalculations = true;
requested_redraw = m_3d_render->Redraw( m_mouse_was_moved || m_camera_is_moving, requested_redraw = m_3d_render->Redraw(
&activityReporter, &warningReporter ); m_mouse_was_moved || m_camera_is_moving, &activityReporter, &warningReporter );
if( reloadRaytracingForIntersectionCalculations ) if( reloadRaytracingForIntersectionCalculations )
m_3d_render_raytracing->Reload( nullptr, nullptr, true ); m_3d_render_raytracing->Reload( nullptr, nullptr, true );
}
catch( std::runtime_error& err )
{
DisplayInfoMessage( m_parent, _( "Unable to use OpenGL" ), wxString( err.what() ) );
m_is_opengl_version_supported = false;
m_opengl_supports_raytracing = false;
m_is_opengl_initialized = false;
GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC );
m_is_currently_painting.clear();
return;
}
} }
if( m_render_pivot ) if( m_render_pivot )
@ -546,6 +569,8 @@ void EDA_3D_CANVAS::DoRePaint()
m_mouse_was_moved = false; m_mouse_was_moved = false;
Request_refresh( false ); Request_refresh( false );
} }
m_is_currently_painting.clear();
} }
@ -872,7 +897,8 @@ void EDA_3D_CANVAS::stop_editingTimeOut_Timer()
void EDA_3D_CANVAS::restart_editingTimeOut_Timer() void EDA_3D_CANVAS::restart_editingTimeOut_Timer()
{ {
m_editing_timeout_timer.Start( m_3d_render->GetWaitForEditingTimeOut() , wxTIMER_ONE_SHOT ); if( m_3d_render )
m_editing_timeout_timer.Start( m_3d_render->GetWaitForEditingTimeOut(), wxTIMER_ONE_SHOT );
} }

View File

@ -25,7 +25,7 @@
#ifndef EDA_3D_CANVAS_H #ifndef EDA_3D_CANVAS_H
#define EDA_3D_CANVAS_H #define EDA_3D_CANVAS_H
#include <atomic>
#include "board_adapter.h" #include "board_adapter.h"
#include "3d_rendering/3d_render_raytracing/accelerators/caccelerator.h" #include "3d_rendering/3d_render_raytracing/accelerators/caccelerator.h"
#include "3d_rendering/c3d_render_base.h" #include "3d_rendering/c3d_render_base.h"
@ -249,6 +249,7 @@ private:
wxTimer m_editing_timeout_timer; // Expires after some time signalling that wxTimer m_editing_timeout_timer; // Expires after some time signalling that
// the mouse / keyboard movements are over // the mouse / keyboard movements are over
wxTimer m_redraw_trigger_timer; // Used to schedule a redraw event wxTimer m_redraw_trigger_timer; // Used to schedule a redraw event
std::atomic_flag m_is_currently_painting; // Avoid drawing twice at the same time
bool m_mouse_is_moving; // Mouse activity is in progress bool m_mouse_is_moving; // Mouse activity is in progress
bool m_mouse_was_moved; bool m_mouse_was_moved;

View File

@ -400,6 +400,10 @@ void C_OGL_3DMODEL::Draw(bool aTransparent, float aOpacity, bool aUseSelectedMat
if( aOpacity <= FLT_EPSILON ) if( aOpacity <= FLT_EPSILON )
return; return;
if( !glBindBuffer )
throw std::runtime_error( "The OpenGL context no longer exists: unable to draw" );
glBindBuffer( GL_ARRAY_BUFFER, m_vertex_buffer ); glBindBuffer( GL_ARRAY_BUFFER, m_vertex_buffer );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_index_buffer ); glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_index_buffer );
@ -457,15 +461,21 @@ void C_OGL_3DMODEL::Draw(bool aTransparent, float aOpacity, bool aUseSelectedMat
C_OGL_3DMODEL::~C_OGL_3DMODEL() C_OGL_3DMODEL::~C_OGL_3DMODEL()
{ {
glDeleteBuffers( 1, &m_vertex_buffer ); if( glDeleteBuffers )
glDeleteBuffers( 1, &m_index_buffer ); {
glDeleteBuffers( 1, &m_bbox_vertex_buffer ); glDeleteBuffers( 1, &m_vertex_buffer );
glDeleteBuffers( 1, &m_bbox_index_buffer ); glDeleteBuffers( 1, &m_index_buffer );
glDeleteBuffers( 1, &m_bbox_vertex_buffer );
glDeleteBuffers( 1, &m_bbox_index_buffer );
}
} }
void C_OGL_3DMODEL::Draw_bbox() const void C_OGL_3DMODEL::Draw_bbox() const
{ {
if( !glBindBuffer )
throw std::runtime_error( "The OpenGL context no longer exists: unable to draw bbox" );
glBindBuffer( GL_ARRAY_BUFFER, m_bbox_vertex_buffer ); glBindBuffer( GL_ARRAY_BUFFER, m_bbox_vertex_buffer );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_bbox_index_buffer ); glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_bbox_index_buffer );
@ -482,6 +492,9 @@ void C_OGL_3DMODEL::Draw_bbox() const
void C_OGL_3DMODEL::Draw_bboxes() const void C_OGL_3DMODEL::Draw_bboxes() const
{ {
if( !glBindBuffer )
throw std::runtime_error( "The OpenGL context no longer exists: unable to draw bboxes" );
glBindBuffer( GL_ARRAY_BUFFER, m_bbox_vertex_buffer ); glBindBuffer( GL_ARRAY_BUFFER, m_bbox_vertex_buffer );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_bbox_index_buffer ); glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_bbox_index_buffer );

View File

@ -212,6 +212,9 @@ void OGL_DrawBackground( const SFVEC3F &aTopColor, const SFVEC3F &aBotColor )
void OGL_ResetTextureStateDefaults() void OGL_ResetTextureStateDefaults()
{ {
if( !glActiveTexture || !glClientActiveTexture )
throw std::runtime_error( "The OpenGL context no longer exists: unable to Reset Textures" );
glActiveTexture( GL_TEXTURE0 ); glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, 0 ); glBindTexture( GL_TEXTURE_2D, 0 );
glClientActiveTexture( GL_TEXTURE0 ); glClientActiveTexture( GL_TEXTURE0 );