From 898deb48c4ade5f4fb25799543fd085daa8378e1 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 19 Feb 2017 18:01:03 +0100 Subject: [PATCH] Error reporting on initialization of the Layer Editing OpenGL shaders. The shaders are initialized when the layer editing button is pressed for the first time. If the initialization fails, a message box is shown and the layer editing will stay disabled. --- lib/Slic3r/GUI/3DScene.pm | 62 ++++++++--- lib/Slic3r/GUI/GLShader.pm | 211 +++++++++++++++++++------------------ lib/Slic3r/GUI/Plater.pm | 18 +++- 3 files changed, 174 insertions(+), 117 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 9c8e1a440..f506f01a7 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -19,7 +19,7 @@ package Slic3r::GUI::3DScene::Base; use strict; use warnings; -use Wx qw(:timer :bitmap); +use Wx qw(:timer :bitmap :icon :dialog); use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_TIMER); # must load OpenGL *before* Wx::GLCanvas use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants); @@ -193,24 +193,58 @@ sub layer_editing_enabled { my ($self, $value) = @_; if (@_ == 2) { $self->{layer_editing_enabled} = $value; - if ($value && ! $self->{layer_editing_initialized}) { - # Enabling the layer editing for the first time. This triggers compilation of the necessary OpenGL shaders. - # If compilation fails, the compile log is printed into the console. - $self->{layer_editing_initialized} = 1; - my $shader = $self->{shader} = new Slic3r::GUI::GLShader; - my $info = $shader->Load($self->_fragment_shader, $self->_vertex_shader); - print $info if $info; - ($self->{layer_preview_z_texture_id}) = glGenTextures_p(1); - glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); - glBindTexture(GL_TEXTURE_2D, 0); + if ($value) { + if (! $self->{layer_editing_initialized}) { + # Enabling the layer editing for the first time. This triggers compilation of the necessary OpenGL shaders. + # If compilation fails, a message box is shown with the error codes. + my $shader = $self->{shader} = new Slic3r::GUI::GLShader; + my $error_message; + if (ref($shader)) { + my $info = $shader->Load($self->_fragment_shader, $self->_vertex_shader); + if (defined($info)) { + # Compilation or linking of the shaders failed. + $error_message = "Cannot compile an OpenGL Shader, therefore the Variable Layer Editing will be disabled.\n\n" + . $info; + } else { + ($self->{layer_preview_z_texture_id}) = glGenTextures_p(1); + glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + glBindTexture(GL_TEXTURE_2D, 0); + } + } else { + # Cannot initialize the Shader object, some of the OpenGL capabilities are missing. + $error_message = "Cannot instantiate an OpenGL Shader, therefore the Variable Layer Editing will be disabled.\n\n" + . $shader; + } + if (defined($error_message)) { + # Don't enable the layer editing tool. + $self->{layer_editing_enabled} = 0; + # 2 means failed + $self->{layer_editing_initialized} = 2; + # Show the error message. + Wx::MessageBox($error_message, "Slic3r Error", wxOK | wxICON_EXCLAMATION, $self); + } else { + $self->{layer_editing_initialized} = 1; + } + } elsif ($self->{layer_editing_initialized} == 2) { + # Initilization failed before. Don't try to initialize and disable layer editing. + $self->{layer_editing_enabled} = 0; + } } } return $self->{layer_editing_enabled}; } +sub layer_editing_allowed { + my ($self) = @_; + # Allow layer editing if either the shaders were not initialized yet and we don't know + # whether it will be possible to initialize them, + # or if the initialization was done already and it failed. + return ! (defined($self->{layer_editing_initialized}) && $self->{layer_editing_initialized} == 2); +} + sub _first_selected_object_id { my ($self) = @_; for my $i (0..$#{$self->volumes}) { diff --git a/lib/Slic3r/GUI/GLShader.pm b/lib/Slic3r/GUI/GLShader.pm index 766fe0807..c2af3e38f 100644 --- a/lib/Slic3r/GUI/GLShader.pm +++ b/lib/Slic3r/GUI/GLShader.pm @@ -24,160 +24,171 @@ sub CLONE_SKIP { 1 } # Shader constructor sub new { - # Check for required OpenGL extensions - return undef if (OpenGL::glpCheckExtension('GL_ARB_shader_objects')); - return undef if (OpenGL::glpCheckExtension('GL_ARB_fragment_shader')); - return undef if (OpenGL::glpCheckExtension('GL_ARB_vertex_shader')); - return undef if (OpenGL::glpCheckExtension('GL_ARB_shading_language_100')); -# my $glsl_version = glGetString(GL_SHADING_LANGUAGE_VERSION); -# my $glsl_version_ARB = glGetString(GL_SHADING_LANGUAGE_VERSION_ARB ); -# print "GLSL version: $glsl_version, ARB: $glsl_version_ARB\n"; + # Check for required OpenGL extensions + my $error_message = ''; + my $extensions_valid = 1; + foreach my $i (map "GL_ARB_$_", qw(shader_objects fragment_shader vertex_shader shading_language_100)) { + if (OpenGL::glpCheckExtension($i)) { + $error_message .= "Missing OpenGL extension: $i\n"; + $extensions_valid = 0; + } + } - my $this = shift; - my $class = ref($this) || $this; - my($type) = @_; - my $self = {type => uc($type)}; - bless($self,$class); + if (! $extensions_valid) { + # Cannot create a shader object, because some of the necessary OpenGL extensions are missing. + # Form an error message. + my $gl_version = glGetString(GL_VERSION); + my $gl_vendor = glGetString(GL_VENDOR); + my $gl_renderer = glGetString(GL_RENDERER); + my $glsl_version_ARB = glGetString(GL_SHADING_LANGUAGE_VERSION_ARB) // ''; + my $glsl_version = glGetString(GL_SHADING_LANGUAGE_VERSION) // $glsl_version_ARB; + $glsl_version .= 'ARB(' . $glsl_version_ARB . ')' if ($glsl_version_ARB ne '' && $glsl_version ne $glsl_version_ARB); + my $out = ''; + if ($^O eq 'MSWin32') { + if ($gl_vendor eq 'Microsoft Corporation' && $renderer eq 'GDI Generic') { + $out .= "Windows is using a software OpenGL renderer.\n"; + $out .= "You are either connected over remote desktop,\n"; + $out .= "or a hardware acceleration is not available.\n"; + } + } + $out .= "GL version: ${gl_version}\n"; + $out .= "vendor: ${gl_vendor}\n"; + $out .= "renderer: ${gl_renderer}\n"; + $out .= "GLSL version: ${glsl_version}\n"; + $error_message = $out . $error_message; + return $error_message; + } - # Get GL_SHADING_LANGUAGE_VERSION_ARB - my $shader_ver = glGetString(0x8B8C); - $shader_ver =~ m|([\d\.]+)|; - $self->{version} = $1 || '0'; - print - return $self; + my $this = shift; + my $class = ref($this) || $this; + my($type) = @_; + my $self = {type => uc($type)}; + bless($self, $class); + + # Get GL_SHADING_LANGUAGE_VERSION_ARB + my $shader_ver = glGetString(0x8B8C); + $shader_ver =~ m|([\d\.]+)|; + $self->{version} = $1 || '0'; +# print "Shader version: $self->{version}\n"; + return $self; } # Shader destructor # Must be disabled first sub DESTROY { - my($self) = @_; + my($self) = @_; - if ($self->{program}) - { - glDetachObjectARB($self->{program},$self->{fragment_id}) if ($self->{fragment_id}); - glDetachObjectARB($self->{program},$self->{vertex_id}) if ($self->{vertex_id}); - glDeleteProgramsARB_p($self->{program}); - } + if ($self->{program}) { + glDetachObjectARB($self->{program},$self->{fragment_id}) if ($self->{fragment_id}); + glDetachObjectARB($self->{program},$self->{vertex_id}) if ($self->{vertex_id}); + glDeleteProgramsARB_p($self->{program}); + } - glDeleteProgramsARB_p($self->{fragment_id}) if ($self->{fragment_id}); - glDeleteProgramsARB_p($self->{vertex_id}) if ($self->{vertex_id}); + glDeleteProgramsARB_p($self->{fragment_id}) if ($self->{fragment_id}); + glDeleteProgramsARB_p($self->{vertex_id}) if ($self->{vertex_id}); } - # Load shader strings sub Load { - my($self,$fragment,$vertex) = @_; + my($self, $fragment, $vertex) = @_; - # Load fragment code - if ($fragment) - { - $self->{fragment_id} = glCreateShaderObjectARB(GL_FRAGMENT_SHADER); - return undef if (!$self->{fragment_id}); + # Load fragment code + if ($fragment) { + $self->{fragment_id} = glCreateShaderObjectARB(GL_FRAGMENT_SHADER); + return "glCreateShaderObjectARB failed." if (!$self->{fragment_id}); + glShaderSourceARB_p($self->{fragment_id}, $fragment); + glCompileShaderARB($self->{fragment_id}); + my $stat = glGetInfoLogARB_p($self->{fragment_id}); + return "Fragment shader compilation failed:\n$stat" if ($stat); + } - glShaderSourceARB_p($self->{fragment_id}, $fragment); - #my $frag = glGetShaderSourceARB_p($self->{fragment_id}); - #print STDERR "Loaded fragment:\n$frag\n"; + # Load vertex code + if ($vertex) { + $self->{vertex_id} = glCreateShaderObjectARB(GL_VERTEX_SHADER); + return "glCreateShaderObjectARB failed." if (!$self->{vertex_id}); + glShaderSourceARB_p($self->{vertex_id}, $vertex); + glCompileShaderARB($self->{vertex_id}); + $stat = glGetInfoLogARB_p($self->{vertex_id}); + return "Vertex shader compilation failed:\n$stat" if ($stat); + } - glCompileShaderARB($self->{fragment_id}); - my $stat = glGetInfoLogARB_p($self->{fragment_id}); - return "Fragment shader: $stat" if ($stat); - } + # Link shaders + my $sp = glCreateProgramObjectARB(); + return "glCreateProgramObjectARB failed." if (!sp); + glAttachObjectARB($sp, $self->{fragment_id}) if ($fragment); + glAttachObjectARB($sp, $self->{vertex_id}) if ($vertex); + glLinkProgramARB($sp); + my $linked = glGetObjectParameterivARB_p($sp, GL_OBJECT_LINK_STATUS_ARB); + if (!$linked) { + $stat = glGetInfoLogARB_p($sp); + return "Shader linking failed:\n$stat" if ($stat); + return 'Unable to link the shader.'; + } - # Load vertex code - if ($vertex) - { - $self->{vertex_id} = glCreateShaderObjectARB(GL_VERTEX_SHADER); - return undef if (!$self->{vertex_id}); - - glShaderSourceARB_p($self->{vertex_id}, $vertex); - #my $vert = glGetShaderSourceARB_p($self->{vertex_id}); - #print STDERR "Loaded vertex:\n$vert\n"; - - glCompileShaderARB($self->{vertex_id}); - $stat = glGetInfoLogARB_p($self->{vertex_id}); - return "Vertex shader: $stat" if ($stat); - } - - - # Link shaders - my $sp = glCreateProgramObjectARB(); - glAttachObjectARB($sp, $self->{fragment_id}) if ($fragment); - glAttachObjectARB($sp, $self->{vertex_id}) if ($vertex); - glLinkProgramARB($sp); - my $linked = glGetObjectParameterivARB_p($sp, GL_OBJECT_LINK_STATUS_ARB); - if (!$linked) - { - $stat = glGetInfoLogARB_p($sp); - #print STDERR "Load shader: $stat\n"; - return "Link shader: $stat" if ($stat); - return 'Unable to link shader'; - } - - $self->{program} = $sp; - - return ''; + $self->{program} = $sp; + return undef; } # Enable shader sub Enable { - my($self) = @_; - glUseProgramObjectARB($self->{program}) if ($self->{program}); + my($self) = @_; + glUseProgramObjectARB($self->{program}) if ($self->{program}); } # Disable shader sub Disable { - my($self) = @_; - glUseProgramObjectARB(0) if ($self->{program}); + my($self) = @_; + glUseProgramObjectARB(0) if ($self->{program}); } # Return shader vertex attribute ID sub MapAttr { - my($self,$attr) = @_; - return undef if (!$self->{program}); - my $id = glGetAttribLocationARB_p($self->{program},$attr); - return undef if ($id < 0); - return $id; + my($self,$attr) = @_; + return undef if (!$self->{program}); + my $id = glGetAttribLocationARB_p($self->{program},$attr); + return undef if ($id < 0); + return $id; } # Return shader uniform variable ID sub Map { - my($self,$var) = @_; - return undef if (!$self->{program}); - my $id = glGetUniformLocationARB_p($self->{program},$var); - return undef if ($id < 0); - return $id; + my($self,$var) = @_; + return undef if (!$self->{program}); + my $id = glGetUniformLocationARB_p($self->{program},$var); + return undef if ($id < 0); + return $id; } # Set shader vector sub SetVector { - my($self,$var,@values) = @_; + my($self,$var,@values) = @_; - my $id = $self->Map($var); - return 'Unable to map $var' if (!defined($id)); + my $id = $self->Map($var); + return 'Unable to map $var' if (!defined($id)); - my $count = scalar(@values); - eval('glUniform'.$count.'fARB($id,@values)'); + my $count = scalar(@values); + eval('glUniform'.$count.'fARB($id,@values)'); - return ''; + return ''; } # Set shader 4x4 matrix sub SetMatrix { - my($self,$var,$oga) = @_; + my($self,$var,$oga) = @_; - my $id = $self->Map($var); - return 'Unable to map $var' if (!defined($id)); + my $id = $self->Map($var); + return 'Unable to map $var' if (!defined($id)); - glUniformMatrix4fvARB_c($id,1,0,$oga->ptr()); - return ''; + glUniformMatrix4fvARB_c($id,1,0,$oga->ptr()); + return ''; } 1; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 8d7c96a77..3746ca80f 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -528,6 +528,16 @@ sub _on_select_preset { sub on_layer_editing_toggled { my ($self, $new_state) = @_; $self->{canvas3D}->layer_editing_enabled($new_state); + if ($new_state && ! $self->{canvas3D}->layer_editing_enabled) { + # Initialization of the OpenGL shaders failed. Disable the tool. + if ($self->{htoolbar}) { + $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 0); + $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, 0); + } else { + $self->{"btn_layer_editing"}->Disable; + $self->{"btn_layer_editing"}->SetValue(0); + } + } $self->{canvas3D}->update; } @@ -1661,7 +1671,8 @@ sub on_config_change { } $self->{canvas3D}->layer_editing_enabled(0); $self->{canvas3D}->update; - } else { + } elsif ($self->{canvas3D}->layer_editing_allowed) { + # Want to allow the layer editing, but do it only if the OpenGL supports it. if ($self->{htoolbar}) { $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 1); } else { @@ -1774,17 +1785,18 @@ sub object_list_changed { # Enable/disable buttons depending on whether there are any objects on the platter. my $have_objects = @{$self->{objects}} ? 1 : 0; + my $variable_layer_height_allowed = $self->{config}->variable_layer_height && $self->{canvas3D}->layer_editing_allowed; if ($self->{htoolbar}) { # On OSX or Linux $self->{htoolbar}->EnableTool($_, $have_objects) for (TB_RESET, TB_ARRANGE, TB_LAYER_EDITING); - $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 0) if (! $self->{config}->variable_layer_height); + $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 0) if (! $variable_layer_height_allowed); } else { # On MSW my $method = $have_objects ? 'Enable' : 'Disable'; $self->{"btn_$_"}->$method for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode layer_editing); - $self->{"btn_layer_editing"}->Disable if (! $self->{config}->variable_layer_height); + $self->{"btn_layer_editing"}->Disable if (! $variable_layer_height_allowed); } my $export_in_progress = $self->{export_gcode_output_file} || $self->{send_gcode_file};