0029810: Visualization - Tool for debugging shaders
authorasl <asl@opencascade.com>
Tue, 26 Feb 2019 10:56:03 +0000 (13:56 +0300)
committerapn <apn@opencascade.com>
Wed, 27 Feb 2019 16:54:26 +0000 (19:54 +0300)
A new tool for debugging shaders has been introduced.
The new method OpenGl_ShaderProgram::UpdateDebugDump() allows dynamically
dumping/restoring certain shader program (vertex and fragment shaders) to/from external file.

The file name is generated from the program's name with suffix ".vs" or ".fs" for shader type.
The environment variable CSF_ShadersDirectoryDump specifies the folder for dumps.

If the file does not exist (first frame), then it is automatically saved.
When the file date/time is changed in comparison with recent cached date,
then the file will be automatically loaded from external file,
thus this file can be modified in any editor.

OpenGl_ShaderManager now generates a human-readable resource ids for standard GLSL programs.

Draw Harness command vshader has been extended with arguments -list,-dump and -reload
for debugging shaders within OpenGl_Context.

14 files changed:
src/Graphic3d/Graphic3d_ShaderProgram.hxx
src/OpenGl/OpenGl_ShaderManager.cxx
src/OpenGl/OpenGl_ShaderManager.hxx
src/OpenGl/OpenGl_ShaderObject.cxx
src/OpenGl/OpenGl_ShaderObject.hxx
src/OpenGl/OpenGl_ShaderProgram.cxx
src/OpenGl/OpenGl_ShaderProgram.hxx
src/OpenGl/OpenGl_View.hxx
src/OpenGl/OpenGl_View_Raytrace.cxx
src/Shaders/Declarations.glsl
src/Shaders/DeclarationsImpl.glsl
src/Shaders/Shaders_DeclarationsImpl_glsl.pxx
src/Shaders/Shaders_Declarations_glsl.pxx
src/ViewerTest/ViewerTest_OpenGlCommands.cxx

index 0e2cd15..96d13e8 100755 (executable)
@@ -60,6 +60,11 @@ public:
   //! Returns unique ID used to manage resource in graphic driver.
   const TCollection_AsciiString& GetId() const { return myID; }
 
+  //! Sets unique ID used to manage resource in graphic driver.
+  //! WARNING! Graphic3d_ShaderProgram constructor generates a unique id for proper resource management;
+  //! however if application overrides it, it is responsibility of application to avoid name collisions.
+  void SetId (const TCollection_AsciiString& theId) { myID = theId; }
+
   //! Returns GLSL header (version code and extensions).
   const TCollection_AsciiString& Header() const { return myHeader; }
 
index a6f689b..b2f4ea9 100644 (file)
@@ -377,6 +377,17 @@ EOL"}";
   }
 #endif
 
+  //! Generate map key for light sources configuration.
+  static TCollection_AsciiString genLightKey (const Handle(Graphic3d_LightSet)& theLights)
+  {
+    if (theLights->NbEnabled() <= THE_NB_UNROLLED_LIGHTS_MAX)
+    {
+      return TCollection_AsciiString ("l_") + theLights->KeyEnabledLong();
+    }
+
+    const Standard_Integer aMaxLimit = roundUpMaxLightSources (theLights->NbEnabled());
+    return TCollection_AsciiString ("l_") + theLights->KeyEnabledShort() + aMaxLimit;
+  }
 }
 
 // =======================================================================
@@ -529,18 +540,7 @@ void OpenGl_ShaderManager::switchLightPrograms()
     return;
   }
 
-  TCollection_AsciiString aKey ("l_");
-  if (aLights->NbEnabled() <= THE_NB_UNROLLED_LIGHTS_MAX)
-  {
-    aKey += aLights->KeyEnabledLong();
-  }
-  else
-  {
-    const Standard_Integer aMaxLimit = roundUpMaxLightSources (aLights->NbEnabled());
-    aKey += aLights->KeyEnabledShort();
-    aKey += aMaxLimit;
-  }
-
+  const TCollection_AsciiString aKey = genLightKey (aLights);
   if (!myMapOfLightPrograms.Find (aKey, myLightPrograms))
   {
     myLightPrograms = new OpenGl_SetOfShaderPrograms();
@@ -1315,7 +1315,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramFont()
        EOL"}";
 
   Handle(Graphic3d_ShaderProgram) aProgramSrc = new Graphic3d_ShaderProgram();
-  defaultGlslVersion (aProgramSrc, 0);
+  defaultGlslVersion (aProgramSrc, "font", 0);
   aProgramSrc->SetNbLightsMax (0);
   aProgramSrc->SetNbClipPlanesMax (0);
   aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcVert, Graphic3d_TOS_VERTEX,   aUniforms, aStageInOuts));
@@ -1380,6 +1380,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramFboBlit()
     aProgramSrc->SetHeader ("#version 150");
   }
 #endif
+  aProgramSrc->SetId ("occt_blit");
   aProgramSrc->SetNbLightsMax (0);
   aProgramSrc->SetNbClipPlanesMax (0);
   aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcVert, Graphic3d_TOS_VERTEX,   aUniforms, aStageInOuts));
@@ -1470,6 +1471,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramOitCompositing (const St
   #endif
   }
 
+  aProgramSrc->SetId (theMsaa ? "occt_weight-oit-msaa" : "occt_weight-oit");
   aProgramSrc->SetNbLightsMax (0);
   aProgramSrc->SetNbClipPlanesMax (0);
   aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcVert, Graphic3d_TOS_VERTEX,   aUniforms, aStageInOuts));
@@ -1526,6 +1528,7 @@ namespace
 // purpose  :
 // =======================================================================
 int OpenGl_ShaderManager::defaultGlslVersion (const Handle(Graphic3d_ShaderProgram)& theProgram,
+                                              const TCollection_AsciiString& theName,
                                               int theBits,
                                               bool theUsesDerivates) const
 {
@@ -1596,6 +1599,11 @@ int OpenGl_ShaderManager::defaultGlslVersion (const Handle(Graphic3d_ShaderProgr
     }
   }
 #endif
+
+  // should fit OpenGl_PO_NB
+  char aBitsStr[64];
+  Sprintf (aBitsStr, "%04x", aBits);
+  theProgram->SetId (TCollection_AsciiString ("occt_") + theName + aBitsStr);
   return aBits;
 }
 
@@ -1804,7 +1812,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramUnlit (Handle(OpenGl_Sha
 
   if ((theBits & OpenGl_PO_StippleLine) != 0)
   {
-    const Standard_Integer aBits = defaultGlslVersion (aProgramSrc, theBits);
+    const Standard_Integer aBits = defaultGlslVersion (aProgramSrc, "unlit", theBits);
     if ((aBits & OpenGl_PO_StippleLine) != 0)
     {
       aUniforms.Append (OpenGl_ShaderObject::ShaderVariable ("int   uPattern", Graphic3d_TOS_FRAGMENT));
@@ -1850,7 +1858,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramUnlit (Handle(OpenGl_Sha
     + aSrcFragMainGetColor
     + EOL"}";
 
-  defaultGlslVersion (aProgramSrc, theBits);
+  defaultGlslVersion (aProgramSrc, "unlit", theBits);
   aProgramSrc->SetNbLightsMax (0);
   aProgramSrc->SetNbClipPlanesMax (aNbClipPlanes);
   aProgramSrc->SetAlphaTest ((theBits & OpenGl_PO_AlphaTest) != 0);
@@ -2160,7 +2168,8 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramGouraud (Handle(OpenGl_S
     + EOL"  occSetFragColor (getFinalColor());"
     + EOL"}";
 
-  defaultGlslVersion (aProgramSrc, theBits);
+  const TCollection_AsciiString aProgId = TCollection_AsciiString ("gouraud-") + genLightKey (myLightSourceState.LightSources()) + "-";
+  defaultGlslVersion (aProgramSrc, aProgId, theBits);
   aProgramSrc->SetNbLightsMax (aNbLights);
   aProgramSrc->SetNbClipPlanesMax (aNbClipPlanes);
   aProgramSrc->SetAlphaTest ((theBits & OpenGl_PO_AlphaTest) != 0);
@@ -2330,7 +2339,8 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramPhong (Handle(OpenGl_Sha
     + EOL"  occSetFragColor (getFinalColor());"
     + EOL"}";
 
-  defaultGlslVersion (aProgramSrc, theBits, isFlatNormal);
+  const TCollection_AsciiString aProgId = TCollection_AsciiString (theIsFlatNormal ? "flat-" : "phong-") + genLightKey (myLightSourceState.LightSources()) + "-";
+  defaultGlslVersion (aProgramSrc, aProgId, theBits, isFlatNormal);
   aProgramSrc->SetNbLightsMax (aNbLights);
   aProgramSrc->SetNbClipPlanesMax (aNbClipPlanes);
   aProgramSrc->SetAlphaTest ((theBits & OpenGl_PO_AlphaTest) != 0);
@@ -2368,10 +2378,12 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramStereo (Handle(OpenGl_Sh
   TCollection_AsciiString aSrcFrag;
   aUniforms.Append (OpenGl_ShaderObject::ShaderVariable ("sampler2D uLeftSampler",  Graphic3d_TOS_FRAGMENT));
   aUniforms.Append (OpenGl_ShaderObject::ShaderVariable ("sampler2D uRightSampler", Graphic3d_TOS_FRAGMENT));
+  const char* aName = "stereo";
   switch (theStereoMode)
   {
     case Graphic3d_StereoMode_Anaglyph:
     {
+      aName = "anaglyph";
       aUniforms.Append (OpenGl_ShaderObject::ShaderVariable ("mat4 uMultL", Graphic3d_TOS_FRAGMENT));
       aUniforms.Append (OpenGl_ShaderObject::ShaderVariable ("mat4 uMultR", Graphic3d_TOS_FRAGMENT));
       aSrcFrag =
@@ -2391,6 +2403,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramStereo (Handle(OpenGl_Sh
     }
     case Graphic3d_StereoMode_RowInterlaced:
     {
+      aName = "row-interlaced";
       aSrcFrag =
           EOL"void main()"
           EOL"{"
@@ -2409,6 +2422,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramStereo (Handle(OpenGl_Sh
     }
     case Graphic3d_StereoMode_ColumnInterlaced:
     {
+      aName = "column-interlaced";
       aSrcFrag =
           EOL"void main()"
           EOL"{"
@@ -2427,6 +2441,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramStereo (Handle(OpenGl_Sh
     }
     case Graphic3d_StereoMode_ChessBoard:
     {
+      aName = "chessboard";
       aSrcFrag =
           EOL"void main()"
           EOL"{"
@@ -2447,6 +2462,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramStereo (Handle(OpenGl_Sh
     }
     case Graphic3d_StereoMode_SideBySide:
     {
+      aName = "sidebyside";
       aSrcFrag =
           EOL"void main()"
           EOL"{"
@@ -2470,6 +2486,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramStereo (Handle(OpenGl_Sh
     }
     case Graphic3d_StereoMode_OverUnder:
     {
+      aName = "overunder";
       aSrcFrag =
           EOL"void main()"
           EOL"{"
@@ -2514,7 +2531,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramStereo (Handle(OpenGl_Sh
     }
   }
 
-  defaultGlslVersion (aProgramSrc, 0);
+  defaultGlslVersion (aProgramSrc, aName, 0);
   aProgramSrc->SetNbLightsMax (0);
   aProgramSrc->SetNbClipPlanesMax (0);
   aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcVert, Graphic3d_TOS_VERTEX,   aUniforms, aStageInOuts));
@@ -2559,7 +2576,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramBoundBox()
     EOL"  occSetFragColor (occColor);"
     EOL"}";
 
-  defaultGlslVersion (aProgramSrc, 0);
+  defaultGlslVersion (aProgramSrc, "bndbox", 0);
   aProgramSrc->SetNbLightsMax (0);
   aProgramSrc->SetNbClipPlanesMax (0);
   aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcVert, Graphic3d_TOS_VERTEX,   aUniforms, aStageInOuts));
index 131005c..71e5ebc 100644 (file)
@@ -600,6 +600,7 @@ protected:
 
   //! Prepare GLSL version header.
   Standard_EXPORT Standard_Integer defaultGlslVersion (const Handle(Graphic3d_ShaderProgram)& theProgram,
+                                                       const TCollection_AsciiString& theName,
                                                        Standard_Integer theBits,
                                                        bool theUsesDerivates = false) const;
 
index 2e0c2b3..783a032 100755 (executable)
 // commercial license or contractual agreement.
 
 #include <Graphic3d_ShaderObject.hxx>
-
+#include <Message_Messenger.hxx>
 #include <OpenGl_Context.hxx>
 #include <OpenGl_ShaderObject.hxx>
+#include <OSD_File.hxx>
 #include <OSD_Path.hxx>
+#include <OSD_Process.hxx>
+#include <OSD_Protection.hxx>
 #include <Standard_Assert.hxx>
 #include <TCollection_AsciiString.hxx>
 #include <TCollection_ExtendedString.hxx>
@@ -225,7 +228,6 @@ Standard_Boolean OpenGl_ShaderObject::LoadAndCompile (const Handle(OpenGl_Contex
       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, theSource);
     }
     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, "Error! Failed to set shader source");
-    Release (theCtx.operator->());
     return false;
   }
 
@@ -243,7 +245,6 @@ Standard_Boolean OpenGl_ShaderObject::LoadAndCompile (const Handle(OpenGl_Contex
     }
     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
                          TCollection_AsciiString ("Failed to compile shader object. Compilation log:\n") + aLog);
-    Release (theCtx.operator->());
     return false;
   }
   else if (theCtx->caps->glslWarnings)
@@ -359,3 +360,138 @@ void OpenGl_ShaderObject::Release (OpenGl_Context* theCtx)
   }
   myShaderID = NO_SHADER;
 }
+
+//! Return GLSL shader stage file extension.
+static const char* getShaderExtension (GLenum theType)
+{
+  switch (theType)
+  {
+    case GL_VERTEX_SHADER:          return ".vs";
+    case GL_FRAGMENT_SHADER:        return ".fs";
+    case GL_GEOMETRY_SHADER:        return ".gs";
+    case GL_TESS_CONTROL_SHADER:    return ".tcs";
+    case GL_TESS_EVALUATION_SHADER: return ".tes";
+    case GL_COMPUTE_SHADER:         return ".cs";
+  }
+  return ".glsl";
+}
+
+//! Expand substring with additional tail.
+static void insertSubString (TCollection_AsciiString& theString,
+                             const char& thePattern,
+                             const TCollection_AsciiString& theSubstitution)
+{
+  const int aSubLen = theSubstitution.Length();
+  for (int aCharIter = 1, aNbChars = theString.Length(); aCharIter <= aNbChars; ++aCharIter)
+  {
+    if (theString.Value (aCharIter) == thePattern)
+    {
+      theString.Insert (aCharIter + 1, theSubstitution);
+      aCharIter += aSubLen;
+      aNbChars  += aSubLen;
+    }
+  }
+}
+
+//! Dump GLSL shader source code into file.
+static bool dumpShaderSource (const TCollection_AsciiString& theFileName,
+                              const TCollection_AsciiString& theSource,
+                              bool theToBeautify)
+{
+  OSD_File aFile (theFileName);
+  aFile.Build (OSD_WriteOnly, OSD_Protection());
+  TCollection_AsciiString aSource = theSource;
+  if (theToBeautify)
+  {
+    insertSubString (aSource, ';', "\n");
+    insertSubString (aSource, '{', "\n");
+    insertSubString (aSource, '}', "\n");
+  }
+  if (!aFile.IsOpen())
+  {
+    Message::DefaultMessenger()->Send (TCollection_AsciiString("Error: File '") + theFileName + "' cannot be opened to save shader", Message_Fail);
+    return false;
+  }
+
+  if (aSource.Length() > 0)
+  {
+    aFile.Write (aSource.ToCString(), aSource.Length());
+  }
+  aFile.Close();
+  Message::DefaultMessenger()->Send (TCollection_AsciiString ("Shader source dumped into '") + theFileName + "'", Message_Warning);
+  return true;
+}
+
+//! Read GLSL shader source code from file dump.
+static bool restoreShaderSource (TCollection_AsciiString& theSource,
+                                 const TCollection_AsciiString& theFileName)
+{
+  OSD_File aFile (theFileName);
+  aFile.Open (OSD_ReadOnly, OSD_Protection());
+  if (!aFile.IsOpen())
+  {
+    Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFileName + "' cannot be opened to load shader", Message_Fail);
+    return false;
+  }
+
+  const Standard_Integer aSize = (Standard_Integer )aFile.Size();
+  if (aSize > 0)
+  {
+    theSource = TCollection_AsciiString (aSize, '\0');
+    aFile.Read (theSource, aSize);
+  }
+  aFile.Close();
+  Message::DefaultMessenger()->Send (TCollection_AsciiString ("Restored shader dump from '") + theFileName + "'", Message_Warning);
+  return true;
+}
+
+// =======================================================================
+// function : updateDebugDump
+// purpose  :
+// =======================================================================
+Standard_Boolean OpenGl_ShaderObject::updateDebugDump (const Handle(OpenGl_Context)& theCtx,
+                                                       const TCollection_AsciiString& theProgramId,
+                                                       const TCollection_AsciiString& theFolder,
+                                                       Standard_Boolean theToBeautify,
+                                                       Standard_Boolean theToReset)
+{
+  const TCollection_AsciiString aFileName = theFolder + "/" + theProgramId + getShaderExtension (myType);
+  if (!theToReset)
+  {
+    OSD_File aFile (aFileName);
+    if (aFile.Exists())
+    {
+      const Quantity_Date aDate = aFile.AccessMoment();
+      if (aDate > myDumpDate)
+      {
+        TCollection_AsciiString aNewSource;
+        if (restoreShaderSource (aNewSource, aFileName))
+        {
+          LoadAndCompile (theCtx, aNewSource);
+          return Standard_True;
+        }
+      }
+      return Standard_False;
+    }
+  }
+
+  bool isDumped = false;
+  if (myShaderID != NO_SHADER)
+  {
+    GLint aLength = 0;
+    theCtx->core20fwd->glGetShaderiv (myShaderID, GL_SHADER_SOURCE_LENGTH, &aLength);
+    if (aLength > 0)
+    {
+      TCollection_AsciiString aSource (aLength - 1, '\0');
+      theCtx->core20fwd->glGetShaderSource (myShaderID, aLength, NULL, (GLchar* )aSource.ToCString());
+      dumpShaderSource (aFileName, aSource, theToBeautify);
+      isDumped = true;
+    }
+  }
+  if (!isDumped)
+  {
+    dumpShaderSource (aFileName, "", false);
+  }
+  myDumpDate = OSD_Process().SystemDate();
+  return Standard_False;
+}
index 36f8b9e..5a2650a 100755 (executable)
@@ -19,6 +19,7 @@
 #include <Graphic3d_ShaderObject.hxx>
 #include <OpenGl_GlCore20.hxx>
 #include <OpenGl_Resource.hxx>
+#include <Quantity_Date.hxx>
 
 //! Wrapper for OpenGL shader object.
 class OpenGl_ShaderObject : public OpenGl_Resource
@@ -110,11 +111,29 @@ public:
 
   //! Returns type of shader object.
   GLenum Type() const { return myType; }
-  
+
+public:
+
+  //! Update the shader object from external file in the following way:
+  //! 1) If external file does not exist, then it will be created (current source code will be dumped, no recompilation) and FALSE will be returned.
+  //! 2) If external file exists and it has the same timestamp as   myDumpDate, nothing will be done      and FALSE will be returned.
+  //! 3) If external file exists and it has    newer timestamp than myDumpDate, shader  will be recompiled and TRUE will be returned.
+  //! @param theCtx OpenGL context bound to this working thread
+  //! @param theId  GLSL program id to define file name
+  //! @param theFolder folder to store files
+  //! @param theToBeautify flag improving formatting (add extra newlines)
+  //! @param theToReset when TRUE, existing dumps will be overridden
+  Standard_EXPORT Standard_Boolean updateDebugDump (const Handle(OpenGl_Context)& theCtx,
+                                                    const TCollection_AsciiString& theId,
+                                                    const TCollection_AsciiString& theFolder,
+                                                    Standard_Boolean theToBeautify,
+                                                    Standard_Boolean theToReset);
+
 protected:
 
-  GLenum myType;     //!< Type of OpenGL shader object
-  GLuint myShaderID; //!< Handle of OpenGL shader object
+  Quantity_Date myDumpDate; //!< The recent date of the shader dump
+  GLenum        myType;     //!< Type of OpenGL shader object
+  GLuint        myShaderID; //!< Handle of OpenGL shader object
 
 };
 
index 334d05b..26dc423 100755 (executable)
@@ -14,6 +14,7 @@
 // commercial license or contractual agreement.
 
 #include <OSD_File.hxx>
+#include <OSD_Environment.hxx>
 #include <OSD_Protection.hxx>
 
 #include <Graphic3d_Buffer.hxx>
@@ -152,8 +153,9 @@ void OpenGl_VariableSetterSelector::Set (const Handle(OpenGl_Context)&
 // function : OpenGl_ShaderProgram
 // purpose  : Creates uninitialized shader program
 // =======================================================================
-OpenGl_ShaderProgram::OpenGl_ShaderProgram (const Handle(Graphic3d_ShaderProgram)& theProxy)
-: OpenGl_NamedResource (!theProxy.IsNull() ? theProxy->GetId() : ""),
+OpenGl_ShaderProgram::OpenGl_ShaderProgram (const Handle(Graphic3d_ShaderProgram)& theProxy,
+                                            const TCollection_AsciiString& theId)
+: OpenGl_NamedResource (!theProxy.IsNull() ? theProxy->GetId() : theId),
   myProgramID (NO_PROGRAM),
   myProxy     (theProxy),
   myShareCount(1),
@@ -409,6 +411,7 @@ Standard_Boolean OpenGl_ShaderProgram::Initialize (const Handle(OpenGl_Context)&
                                           + anIter.Value()->Source();      // the source code itself (defining main() function)
     if (!aShader->LoadAndCompile (theCtx, aSource))
     {
+      aShader->Release (theCtx.operator->());
       return Standard_False;
     }
 
@@ -581,6 +584,7 @@ Standard_Boolean OpenGl_ShaderProgram::link (const Handle(OpenGl_Context)& theCt
     return Standard_False;
   }
 
+  memset (myCurrentState, 0, sizeof (myCurrentState));
   for (GLint aVar = 0; aVar < OpenGl_OCCT_NUMBER_OF_STATE_VARIABLES; ++aVar)
   {
     myStateLocations[aVar] = GetUniformLocation (theCtx, PredefinedKeywords[aVar]);
@@ -1495,3 +1499,45 @@ void OpenGl_ShaderProgram::Release (OpenGl_Context* theCtx)
 
   myProgramID = NO_PROGRAM;
 }
+
+// =======================================================================
+// function : UpdateDebugDump
+// purpose  :
+// =======================================================================
+Standard_Boolean OpenGl_ShaderProgram::UpdateDebugDump (const Handle(OpenGl_Context)& theCtx,
+                                                        const TCollection_AsciiString& theFolder,
+                                                        Standard_Boolean theToBeautify,
+                                                        Standard_Boolean theToReset)
+{
+  if (myProgramID == NO_PROGRAM)
+  {
+    return Standard_False;
+  }
+
+  TCollection_AsciiString aFolder = theFolder;
+  if (aFolder.IsEmpty())
+  {
+    OSD_Environment aShaderVar ("CSF_ShadersDirectoryDump");
+    aFolder = aShaderVar.Value();
+    if (aFolder.IsEmpty())
+    {
+      aFolder = ".";
+    }
+  }
+
+  bool hasUpdates = false;
+  for (OpenGl_ShaderList::Iterator anIter (myShaderObjects); anIter.More(); anIter.Next())
+  {
+    if (!anIter.Value().IsNull())
+    {
+      // desktop OpenGL (but not OpenGL ES) allows multiple shaders of the same stage to be attached,
+      // but here we expect only single source per stage
+      hasUpdates = anIter.ChangeValue()->updateDebugDump (theCtx, myResourceId, aFolder, theToBeautify, theToReset) || hasUpdates;
+    }
+  }
+  if (hasUpdates)
+  {
+    return Link (theCtx);
+  }
+  return Standard_False;
+}
index 261a2f1..dc33347 100755 (executable)
@@ -202,7 +202,8 @@ public:
   //!
   //! This constructor has been made public to provide more flexibility to re-use OCCT OpenGL classes without OCCT Viewer itself.
   //! If this is not the case - create the program using shared OpenGl_ShaderManager instance instead.
-  Standard_EXPORT OpenGl_ShaderProgram (const Handle(Graphic3d_ShaderProgram)& theProxy = NULL);
+  Standard_EXPORT OpenGl_ShaderProgram (const Handle(Graphic3d_ShaderProgram)& theProxy = NULL,
+                                        const TCollection_AsciiString& theId = "");
 
 protected:
 
@@ -599,6 +600,21 @@ public:
                                                GLint                         theLocation,
                                                const Graphic3d_TextureUnit   theTextureUnit);
 
+public:
+
+  //! Update the shader program from external files (per shader stage) in the following way:
+  //! 1) If external file does not exist, then it will be created (current source code will be dumped, no recompilation) and FALSE will be returned.
+  //! 2) If external file exists and it has the same timestamp as   myDumpDate, nothing will be done and FALSE will be returned.
+  //! 3) If external file exists and it has    newer timestamp than myDumpDate, shader  will be recompiled and relinked and TRUE will be returned.
+  //! @param theCtx OpenGL context bound to this working thread
+  //! @param theFolder folder to store files; when unspecified, $CSF_ShadersDirectoryDump or current folder will be used instead
+  //! @param theToBeautify flag improving formatting (add extra newlines)
+  //! @param theToReset when TRUE, existing dumps will be overridden
+  Standard_EXPORT Standard_Boolean UpdateDebugDump (const Handle(OpenGl_Context)& theCtx,
+                                                    const TCollection_AsciiString& theFolder = "",
+                                                    Standard_Boolean theToBeautify = Standard_False,
+                                                    Standard_Boolean theToReset = Standard_False);
+
 protected:
 
   //! Increments counter of users.
index 4155221..9098cfd 100644 (file)
@@ -848,7 +848,8 @@ protected: //! @name methods related to ray-tracing
   //! Creates shader program from the given vertex and fragment shaders.
   Handle(OpenGl_ShaderProgram) initProgram (const Handle(OpenGl_Context)&      theGlContext,
                                             const Handle(OpenGl_ShaderObject)& theVertShader,
-                                            const Handle(OpenGl_ShaderObject)& theFragShader);
+                                            const Handle(OpenGl_ShaderObject)& theFragShader,
+                                            const TCollection_AsciiString& theName);
 
   //! Initializes OpenGL/GLSL shader programs.
   Standard_Boolean initRaytraceResources (const Standard_Integer theSizeX,
index e2f15e6..2f4f92e 100644 (file)
@@ -1270,9 +1270,11 @@ Handle(OpenGl_ShaderObject) OpenGl_View::initShader (const GLenum
 // =======================================================================
 Handle(OpenGl_ShaderProgram) OpenGl_View::initProgram (const Handle(OpenGl_Context)&      theGlContext,
                                                        const Handle(OpenGl_ShaderObject)& theVertShader,
-                                                       const Handle(OpenGl_ShaderObject)& theFragShader)
+                                                       const Handle(OpenGl_ShaderObject)& theFragShader,
+                                                       const TCollection_AsciiString& theName)
 {
-  Handle(OpenGl_ShaderProgram) aProgram = new OpenGl_ShaderProgram;
+  const TCollection_AsciiString anId = TCollection_AsciiString("occt_rt_") + theName;
+  Handle(OpenGl_ShaderProgram) aProgram = new OpenGl_ShaderProgram(Handle(Graphic3d_ShaderProgram)(), anId);
 
   if (!aProgram->Create (theGlContext))
   {
@@ -1553,7 +1555,7 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Standard_Integer theS
         return safeFailBack ("Failed to initialize ray-trace fragment shader", theGlContext);
       }
 
-      myRaytraceProgram = initProgram (theGlContext, aBasicVertShader, myRaytraceShader);
+      myRaytraceProgram = initProgram (theGlContext, aBasicVertShader, myRaytraceShader, "main");
       if (myRaytraceProgram.IsNull())
       {
         return safeFailBack ("Failed to initialize ray-trace shader program", theGlContext);
@@ -1588,7 +1590,7 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Standard_Integer theS
         return safeFailBack ("Failed to initialize FSAA fragment shader", theGlContext);
       }
 
-      myPostFSAAProgram = initProgram (theGlContext, aBasicVertShader, myPostFSAAShader);
+      myPostFSAAProgram = initProgram (theGlContext, aBasicVertShader, myPostFSAAShader, "fsaa");
       if (myPostFSAAProgram.IsNull())
       {
         return safeFailBack ("Failed to initialize FSAA shader program", theGlContext);
@@ -1623,7 +1625,7 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Standard_Integer theS
         return safeFailBack ("Failed to set display fragment shader source", theGlContext);
       }
 
-      myOutImageProgram = initProgram (theGlContext, aBasicVertShader, myOutImageShader);
+      myOutImageProgram = initProgram (theGlContext, aBasicVertShader, myOutImageShader, "out");
       if (myOutImageProgram.IsNull())
       {
         return safeFailBack ("Failed to initialize display shader program", theGlContext);
index 5cd1ef0..d214e4a 100644 (file)
@@ -1,21 +1,5 @@
-// Created on: 2013-10-10
-// Created by: Denis BOGOLEPOV
-// Copyright (c) 2013-2014 OPEN CASCADE SAS
-//
-// This file is part of Open CASCADE Technology software library.
-//
-// This library is free software; you can redistribute it and/or modify it under
-// the terms of the GNU Lesser General Public License version 2.1 as published
-// by the Free Software Foundation, with special exception defined in the file
-// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
-// distribution for complete text of the license and disclaimer of any warranty.
-//
-// Alternatively, this file may be used under the terms of Open CASCADE
-// commercial license or contractual agreement.
-
-//! @file Declarations.glsl
-//! This files includes definition of common uniform variables in OCCT GLSL programs
 
+//! @file Declarations.glsl includes definition of common uniform variables in OCCT GLSL programs
 //! @def THE_MAX_LIGHTS
 //! Specifies the length of array of lights, which is 8 by default. Defined by Shader Manager.
 // #define THE_MAX_LIGHTS 8
@@ -176,3 +160,4 @@ uniform               vec4 occClipPlaneEquations[THE_MAX_CLIP_PLANES];
 uniform THE_PREC_ENUM int  occClipPlaneChains[THE_MAX_CLIP_PLANES]; //! Indicating the number of planes in the Chain
 uniform THE_PREC_ENUM int  occClipPlaneCount;   //!< Total number of clip planes
 #endif
+//! @endfile Declarations.glsl
index 34bf584..5fd67ed 100644 (file)
@@ -1,20 +1,5 @@
-// Created on: 2013-10-10
-// Created by: Denis BOGOLEPOV
-// Copyright (c) 2013-2014 OPEN CASCADE SAS
-//
-// This file is part of Open CASCADE Technology software library.
-//
-// This library is free software; you can redistribute it and/or modify it under
-// the terms of the GNU Lesser General Public License version 2.1 as published
-// by the Free Software Foundation, with special exception defined in the file
-// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
-// distribution for complete text of the license and disclaimer of any warranty.
-//
-// Alternatively, this file may be used under the terms of Open CASCADE
-// commercial license or contractual agreement.
-
-// This file includes implementation of common functions and properties accessors
 
+//! @file DeclarationsImpl.glsl includes implementation of common functions and properties accessors
 #if defined(FRAGMENT_SHADER)
 //! Output color (and coverage for accumulation by OIT algorithm).
 void occSetFragColor (in vec4 theColor)
@@ -75,3 +60,4 @@ vec2  occTextureTrsf_Translation(void) { return occTexTrsf2d[0].xy; }
 vec2  occTextureTrsf_Scale(void)       { return occTexTrsf2d[0].zw; }
 float occTextureTrsf_RotationSin(void) { return occTexTrsf2d[1].x; }
 float occTextureTrsf_RotationCos(void) { return occTexTrsf2d[1].y; }
+//! @endfile DeclarationsImpl.glsl
index 796b4e7..6b454b0 100644 (file)
@@ -1,23 +1,8 @@
 // This file has been automatically generated from resource file src/Shaders/DeclarationsImpl.glsl
 
 static const char Shaders_DeclarationsImpl_glsl[] =
-  "// Created on: 2013-10-10\n"
-  "// Created by: Denis BOGOLEPOV\n"
-  "// Copyright (c) 2013-2014 OPEN CASCADE SAS\n"
-  "//\n"
-  "// This file is part of Open CASCADE Technology software library.\n"
-  "//\n"
-  "// This library is free software; you can redistribute it and/or modify it under\n"
-  "// the terms of the GNU Lesser General Public License version 2.1 as published\n"
-  "// by the Free Software Foundation, with special exception defined in the file\n"
-  "// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT\n"
-  "// distribution for complete text of the license and disclaimer of any warranty.\n"
-  "//\n"
-  "// Alternatively, this file may be used under the terms of Open CASCADE\n"
-  "// commercial license or contractual agreement.\n"
-  "\n"
-  "// This file includes implementation of common functions and properties accessors\n"
   "\n"
+  "//! @file DeclarationsImpl.glsl includes implementation of common functions and properties accessors\n"
   "#if defined(FRAGMENT_SHADER)\n"
   "//! Output color (and coverage for accumulation by OIT algorithm).\n"
   "void occSetFragColor (in vec4 theColor)\n"
@@ -77,4 +62,5 @@ static const char Shaders_DeclarationsImpl_glsl[] =
   "vec2  occTextureTrsf_Translation(void) { return occTexTrsf2d[0].xy; }\n"
   "vec2  occTextureTrsf_Scale(void)       { return occTexTrsf2d[0].zw; }\n"
   "float occTextureTrsf_RotationSin(void) { return occTexTrsf2d[1].x; }\n"
-  "float occTextureTrsf_RotationCos(void) { return occTexTrsf2d[1].y; }\n";
+  "float occTextureTrsf_RotationCos(void) { return occTexTrsf2d[1].y; }\n"
+  "//! @endfile DeclarationsImpl.glsl\n";
index 74663c4..f8de26c 100644 (file)
@@ -1,24 +1,8 @@
 // This file has been automatically generated from resource file src/Shaders/Declarations.glsl
 
 static const char Shaders_Declarations_glsl[] =
-  "// Created on: 2013-10-10\n"
-  "// Created by: Denis BOGOLEPOV\n"
-  "// Copyright (c) 2013-2014 OPEN CASCADE SAS\n"
-  "//\n"
-  "// This file is part of Open CASCADE Technology software library.\n"
-  "//\n"
-  "// This library is free software; you can redistribute it and/or modify it under\n"
-  "// the terms of the GNU Lesser General Public License version 2.1 as published\n"
-  "// by the Free Software Foundation, with special exception defined in the file\n"
-  "// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT\n"
-  "// distribution for complete text of the license and disclaimer of any warranty.\n"
-  "//\n"
-  "// Alternatively, this file may be used under the terms of Open CASCADE\n"
-  "// commercial license or contractual agreement.\n"
-  "\n"
-  "//! @file Declarations.glsl\n"
-  "//! This files includes definition of common uniform variables in OCCT GLSL programs\n"
   "\n"
+  "//! @file Declarations.glsl includes definition of common uniform variables in OCCT GLSL programs\n"
   "//! @def THE_MAX_LIGHTS\n"
   "//! Specifies the length of array of lights, which is 8 by default. Defined by Shader Manager.\n"
   "// #define THE_MAX_LIGHTS 8\n"
@@ -178,4 +162,5 @@ static const char Shaders_Declarations_glsl[] =
   "uniform               vec4 occClipPlaneEquations[THE_MAX_CLIP_PLANES];\n"
   "uniform THE_PREC_ENUM int  occClipPlaneChains[THE_MAX_CLIP_PLANES]; //! Indicating the number of planes in the Chain\n"
   "uniform THE_PREC_ENUM int  occClipPlaneCount;   //!< Total number of clip planes\n"
-  "#endif\n";
+  "#endif\n"
+  "//! @endfile Declarations.glsl\n";
index 3a614a3..7045379 100644 (file)
@@ -585,7 +585,7 @@ static bool parseShaderTypeArg (Graphic3d_TypeOfShaderObject& theType,
 //function : VShaderProg
 //purpose  : Sets the pair of vertex and fragment shaders for the object
 //==============================================================================
-static Standard_Integer VShaderProg (Draw_Interpretor& /*theDI*/,
+static Standard_Integer VShaderProg (Draw_Interpretor& theDI,
                                      Standard_Integer  theArgNb,
                                      const char**      theArgVec)
 {
@@ -611,10 +611,60 @@ static Standard_Integer VShaderProg (Draw_Interpretor& /*theDI*/,
     TCollection_AsciiString anArg (theArgVec[anArgIter]);
     anArg.LowerCase();
     Graphic3d_TypeOfShaderObject aShaderTypeArg = Graphic3d_TypeOfShaderObject(-1);
-    if (!aProgram.IsNull()
-     &&  aProgram->ShaderObjects().IsEmpty()
-     && (anArg == "-off"
-      || anArg ==  "off"))
+    if (anArg == "-list"
+     || ((anArg == "-update"
+       || anArg == "-dump"
+       || anArg == "-debug"
+       || anArg == "-reload"
+       || anArg == "-load")
+      && anArgIter + 1 < theArgNb))
+    {
+      Handle(OpenGl_Context) aGlCtx;
+      if (Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast (aCtx->CurrentViewer()->Driver()))
+      {
+        aGlCtx = aDriver->GetSharedContext();
+      }
+      if (aGlCtx.IsNull())
+      {
+        std::cout << "Error: no OpenGl_Context\n";
+        return 1;
+      }
+
+      if (anArg == "-list")
+      {
+        for (OpenGl_Context::OpenGl_ResourcesMap::Iterator aResIter (aGlCtx->SharedResources()); aResIter.More(); aResIter.Next())
+        {
+          if (Handle(OpenGl_ShaderProgram) aResProg = Handle(OpenGl_ShaderProgram)::DownCast (aResIter.Value()))
+          {
+            theDI << aResProg->ResourceId() << " ";
+          }
+        }
+      }
+      else
+      {
+        TCollection_AsciiString aShaderName = theArgVec[++anArgIter];
+        Handle(OpenGl_ShaderProgram) aResProg;
+        if (!aGlCtx->GetResource (aShaderName, aResProg))
+        {
+          std::cout << "Syntax error: shader resource '" << aShaderName << "' is not found\n";
+          return 1;
+        }
+        if (aResProg->UpdateDebugDump (aGlCtx, "", false, anArg == "-dump"))
+        {
+          aCtx->UpdateCurrentViewer();
+        }
+      }
+      if (anArgIter + 1 < theArgNb)
+      {
+        std::cout << "Syntax error: wrong number of arguments\n";
+        return 1;
+      }
+      return 0;
+    }
+    else if (!aProgram.IsNull()
+          &&  aProgram->ShaderObjects().IsEmpty()
+          && (anArg == "-off"
+           || anArg ==  "off"))
     {
       aProgram.Nullify();
     }
@@ -850,7 +900,11 @@ void ViewerTest::OpenGlCommands(Draw_Interpretor& theCommands)
                   "\n\t\t:   [-off] [-phong] [-aspect {shading|line|point|text}=shading]"
                   "\n\t\t:   [-header VersionHeader]"
                   "\n\t\t:   [-tessControl TessControlShader -tesseval TessEvaluationShader]"
-                  "\n\t\t: Assign custom GLSL program to presentation aspects.",
+                  "\n\t\t: Assign custom GLSL program to presentation aspects."
+                  "\nvshader [-list] [-dump] [-reload] ShaderId"
+                  "\n\t\t:  -list   prints the list of registered GLSL programs"
+                  "\n\t\t:  -dump   dumps specified GLSL program (for debugging)"
+                  "\n\t\t:  -reload restores dump of specified GLSL program",
     __FILE__, VShaderProg, aGroup);
   theCommands.Add("vshaderprog", "Alias for vshader", __FILE__, VShaderProg, aGroup);
 }