0033661: Data Exchange, Step Import - Tessellated GDTs are not imported
[occt.git] / src / OpenGl / OpenGl_ShaderObject.cxx
1 // Created on: 2013-09-19
2 // Created by: Denis BOGOLEPOV
3 // Copyright (c) 2013-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <Graphic3d_ShaderObject.hxx>
17 #include <Message_Messenger.hxx>
18 #include <OpenGl_Context.hxx>
19 #include <OpenGl_ShaderObject.hxx>
20 #include <OSD_File.hxx>
21 #include <OSD_Path.hxx>
22 #include <OSD_Process.hxx>
23 #include <OSD_Protection.hxx>
24 #include <Standard_Assert.hxx>
25 #include <TCollection_AsciiString.hxx>
26 #include <TCollection_ExtendedString.hxx>
27
28 #ifdef _WIN32
29   #include <malloc.h> // for alloca()
30 #endif
31
32 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_ShaderObject,OpenGl_Resource)
33
34 //! Puts line numbers to the output of GLSL program source code.
35 static TCollection_AsciiString putLineNumbers (const TCollection_AsciiString& theSource)
36 {
37   std::stringstream aStream;
38   theSource.Print (aStream);
39   std::string aLine;
40   Standard_Integer aLineNumber = 1;
41   TCollection_AsciiString aResultSource;
42   while (std::getline (aStream, aLine))
43   {
44     TCollection_AsciiString anAsciiString = TCollection_AsciiString (aLine.c_str());
45     anAsciiString.Prepend (TCollection_AsciiString ("\n") + TCollection_AsciiString (aLineNumber) + ": ");
46     aResultSource += anAsciiString;
47     aLineNumber++;
48   }
49   return aResultSource;
50 }
51
52 //! Return GLSL shader stage title.
53 static TCollection_AsciiString getShaderTypeString (GLenum theType)
54 {
55   switch (theType)
56   {
57     case GL_VERTEX_SHADER:          return "Vertex Shader";
58     case GL_FRAGMENT_SHADER:        return "Fragment Shader";
59     case GL_GEOMETRY_SHADER:        return "Geometry Shader";
60     case GL_TESS_CONTROL_SHADER:    return "Tessellation Control Shader";
61     case GL_TESS_EVALUATION_SHADER: return "Tessellation Evaluation Shader";
62     case GL_COMPUTE_SHADER:         return "Compute Shader";
63   }
64   return "Shader";
65 }
66
67 // =======================================================================
68 // function : CreateFromSource
69 // purpose  :
70 // =======================================================================
71 Handle(Graphic3d_ShaderObject) OpenGl_ShaderObject::CreateFromSource (TCollection_AsciiString& theSource,
72                                                                       Graphic3d_TypeOfShaderObject theType,
73                                                                       const ShaderVariableList& theUniforms,
74                                                                       const ShaderVariableList& theStageInOuts,
75                                                                       const TCollection_AsciiString& theInName,
76                                                                       const TCollection_AsciiString& theOutName,
77                                                                       Standard_Integer theNbGeomInputVerts)
78 {
79   if (theSource.IsEmpty())
80   {
81     return Handle(Graphic3d_ShaderObject)();
82   }
83
84   TCollection_AsciiString aSrcUniforms, aSrcInOuts, aSrcInStructs, aSrcOutStructs;
85   for (ShaderVariableList::Iterator anUniformIter (theUniforms); anUniformIter.More(); anUniformIter.Next())
86   {
87     const ShaderVariable& aVar = anUniformIter.Value();
88     if ((aVar.Stages & theType) != 0)
89     {
90       aSrcUniforms += TCollection_AsciiString("\nuniform ") + aVar.Name + ";";
91     }
92   }
93   for (ShaderVariableList::Iterator aVarListIter (theStageInOuts); aVarListIter.More(); aVarListIter.Next())
94   {
95     const ShaderVariable& aVar = aVarListIter.Value();
96     Standard_Integer aStageLower = IntegerLast(), aStageUpper = IntegerFirst();
97     Standard_Integer aNbStages = 0;
98     for (Standard_Integer aStageIter = Graphic3d_TOS_VERTEX; aStageIter <= (Standard_Integer )Graphic3d_TOS_COMPUTE; aStageIter = aStageIter << 1)
99     {
100       if ((aVar.Stages & aStageIter) != 0)
101       {
102         ++aNbStages;
103         aStageLower = Min (aStageLower, aStageIter);
104         aStageUpper = Max (aStageUpper, aStageIter);
105       }
106     }
107     if ((Standard_Integer )theType < aStageLower
108      || (Standard_Integer )theType > aStageUpper)
109     {
110       continue;
111     }
112
113     const Standard_Boolean hasGeomStage = theNbGeomInputVerts > 0
114                                        && aStageLower <  Graphic3d_TOS_GEOMETRY
115                                        && aStageUpper >= Graphic3d_TOS_GEOMETRY;
116     const Standard_Boolean isAllStagesVar = aStageLower == Graphic3d_TOS_VERTEX
117                                          && aStageUpper == Graphic3d_TOS_FRAGMENT;
118     if (hasGeomStage
119     || !theInName.IsEmpty()
120     || !theOutName.IsEmpty())
121     {
122       if (aSrcInStructs.IsEmpty()
123        && aSrcOutStructs.IsEmpty()
124        && isAllStagesVar)
125       {
126         if (theType == aStageLower)
127         {
128           aSrcOutStructs = "\nout VertexData\n{";
129         }
130         else if (theType == aStageUpper)
131         {
132           aSrcInStructs = "\nin VertexData\n{";
133         }
134         else // requires theInName/theOutName
135         {
136           aSrcInStructs  = "\nin  VertexData\n{";
137           aSrcOutStructs = "\nout VertexData\n{";
138         }
139       }
140     }
141
142     if (isAllStagesVar
143      && (!aSrcInStructs.IsEmpty()
144       || !aSrcOutStructs.IsEmpty()))
145     {
146       if (!aSrcInStructs.IsEmpty())
147       {
148         aSrcInStructs  += TCollection_AsciiString("\n  ") + aVar.Name + ";";
149       }
150       if (!aSrcOutStructs.IsEmpty())
151       {
152         aSrcOutStructs += TCollection_AsciiString("\n  ") + aVar.Name + ";";
153       }
154     }
155     else
156     {
157       if (theType == aStageLower)
158       {
159         aSrcInOuts += TCollection_AsciiString("\nTHE_SHADER_OUT ") + aVar.Name + ";";
160       }
161       else if (theType == aStageUpper)
162       {
163         aSrcInOuts += TCollection_AsciiString("\nTHE_SHADER_IN ") + aVar.Name + ";";
164       }
165     }
166   }
167
168   if (theType == Graphic3d_TOS_GEOMETRY)
169   {
170     aSrcUniforms.Prepend (TCollection_AsciiString()
171                         + "\nlayout (triangles) in;"
172                           "\nlayout (triangle_strip, max_vertices = " + theNbGeomInputVerts + ") out;");
173   }
174   if (!aSrcInStructs.IsEmpty()
175    && theType == Graphic3d_TOS_GEOMETRY)
176   {
177     aSrcInStructs  += TCollection_AsciiString ("\n} ") + theInName  + "[" + theNbGeomInputVerts + "];";
178   }
179   else if (!aSrcInStructs.IsEmpty())
180   {
181     aSrcInStructs += "\n}";
182     if (!theInName.IsEmpty())
183     {
184       aSrcInStructs += " ";
185       aSrcInStructs += theInName;
186     }
187     aSrcInStructs += ";";
188   }
189   if (!aSrcOutStructs.IsEmpty())
190   {
191     aSrcOutStructs += "\n}";
192     if (!theOutName.IsEmpty())
193     {
194       aSrcOutStructs += " ";
195       aSrcOutStructs += theOutName;
196     }
197     aSrcOutStructs += ";";
198   }
199
200   theSource.Prepend (aSrcUniforms + aSrcInStructs + aSrcOutStructs + aSrcInOuts);
201   return Graphic3d_ShaderObject::CreateFromSource (theType, theSource);
202 }
203
204 // =======================================================================
205 // function : OpenGl_ShaderObject
206 // purpose  : Creates uninitialized shader object
207 // =======================================================================
208 OpenGl_ShaderObject::OpenGl_ShaderObject (GLenum theType)
209 : myType     (theType),
210   myShaderID (NO_SHADER)
211 {
212   //
213 }
214
215 // =======================================================================
216 // function : ~OpenGl_ShaderObject
217 // purpose  : Releases resources of shader object
218 // =======================================================================
219 OpenGl_ShaderObject::~OpenGl_ShaderObject()
220 {
221   Release (NULL);
222 }
223
224 // =======================================================================
225 // function : LoadAndCompile
226 // purpose  :
227 // =======================================================================
228 Standard_Boolean OpenGl_ShaderObject::LoadAndCompile (const Handle(OpenGl_Context)& theCtx,
229                                                       const TCollection_AsciiString& theId,
230                                                       const TCollection_AsciiString& theSource,
231                                                       bool theIsVerbose,
232                                                       bool theToPrintSource)
233 {
234   if (!theIsVerbose)
235   {
236     return LoadSource (theCtx, theSource)
237         && Compile (theCtx);
238   }
239
240   if (!LoadSource (theCtx, theSource))
241   {
242     if (theToPrintSource)
243     {
244       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, theSource);
245     }
246     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
247                          TCollection_AsciiString ("Error! Failed to set ") + getShaderTypeString (myType) + " [" + theId + "] source");
248     return false;
249   }
250
251   if (!Compile (theCtx))
252   {
253     if (theToPrintSource)
254     {
255       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, putLineNumbers (theSource));
256     }
257     TCollection_AsciiString aLog;
258     FetchInfoLog (theCtx, aLog);
259     if (aLog.IsEmpty())
260     {
261       aLog = "Compilation log is empty.";
262     }
263     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
264                          TCollection_AsciiString ("Failed to compile ") + getShaderTypeString (myType) + " [" + theId + "]. Compilation log:\n" + aLog);
265     return false;
266   }
267   else if (theCtx->caps->glslWarnings)
268   {
269     TCollection_AsciiString aLog;
270     FetchInfoLog (theCtx, aLog);
271     if (!aLog.IsEmpty()
272      && !aLog.IsEqual ("No errors.\n"))
273     {
274       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_LOW,
275                            getShaderTypeString (myType) + " [" + theId + "] compilation log:\n" + aLog);
276     }
277   }
278   return true;
279 }
280
281 // =======================================================================
282 // function : DumpSourceCode
283 // purpose  :
284 // =======================================================================
285 void OpenGl_ShaderObject::DumpSourceCode (const Handle(OpenGl_Context)& theCtx,
286                                           const TCollection_AsciiString& theId,
287                                           const TCollection_AsciiString& theSource) const
288 {
289   theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER, 0, GL_DEBUG_SEVERITY_MEDIUM,
290                        getShaderTypeString (myType) + " [" + theId + "] source code:\n"  + theSource);
291 }
292
293 // =======================================================================
294 // function : LoadSource
295 // purpose  : Loads shader source code
296 // =======================================================================
297 Standard_Boolean OpenGl_ShaderObject::LoadSource (const Handle(OpenGl_Context)&  theCtx,
298                                                   const TCollection_AsciiString& theSource)
299 {
300   if (myShaderID == NO_SHADER)
301   {
302     return Standard_False;
303   }
304
305   const GLchar* aLines = theSource.ToCString();
306   theCtx->core20fwd->glShaderSource (myShaderID, 1, &aLines, NULL);
307   return Standard_True;
308 }
309
310 // =======================================================================
311 // function : Compile
312 // purpose  : Compiles the shader object
313 // =======================================================================
314 Standard_Boolean OpenGl_ShaderObject::Compile (const Handle(OpenGl_Context)& theCtx)
315 {
316   if (myShaderID == NO_SHADER)
317   {
318     return Standard_False;
319   }
320
321   // Try to compile shader
322   theCtx->core20fwd->glCompileShader (myShaderID);
323
324   // Check compile status
325   GLint aStatus = GL_FALSE;
326   theCtx->core20fwd->glGetShaderiv (myShaderID, GL_COMPILE_STATUS, &aStatus);
327   return aStatus != GL_FALSE;
328 }
329
330 // =======================================================================
331 // function : FetchInfoLog
332 // purpose  : Fetches information log of the last compile operation
333 // =======================================================================
334 Standard_Boolean OpenGl_ShaderObject::FetchInfoLog (const Handle(OpenGl_Context)& theCtx,
335                                                     TCollection_AsciiString&      theLog)
336 {
337   if (myShaderID == NO_SHADER)
338   {
339     return Standard_False;
340   }
341
342   // Load information log of the compiler
343   GLint aLength = 0;
344   theCtx->core20fwd->glGetShaderiv (myShaderID, GL_INFO_LOG_LENGTH, &aLength);
345   if (aLength > 0)
346   {
347     GLchar* aLog = (GLchar*) alloca (aLength);
348     memset (aLog, 0, aLength);
349     theCtx->core20fwd->glGetShaderInfoLog (myShaderID, aLength, NULL, aLog);
350     theLog = aLog;
351   }
352
353   return Standard_True;
354 }
355
356 // =======================================================================
357 // function : Create
358 // purpose  : Creates new empty shader object of specified type
359 // =======================================================================
360 Standard_Boolean OpenGl_ShaderObject::Create (const Handle(OpenGl_Context)& theCtx)
361 {
362   if (myShaderID == NO_SHADER
363    && theCtx->core20fwd != NULL)
364   {
365     myShaderID = theCtx->core20fwd->glCreateShader (myType);
366   }
367
368   return myShaderID != NO_SHADER;
369 }
370
371 // =======================================================================
372 // function : Release
373 // purpose  : Destroys shader object
374 // =======================================================================
375 void OpenGl_ShaderObject::Release (OpenGl_Context* theCtx)
376 {
377   if (myShaderID == NO_SHADER)
378   {
379     return;
380   }
381
382   Standard_ASSERT_RETURN (theCtx != NULL,
383     "OpenGl_ShaderObject destroyed without GL context! Possible GPU memory leakage...",);
384
385   if (theCtx->core20fwd != NULL
386    && theCtx->IsValid())
387   {
388     theCtx->core20fwd->glDeleteShader (myShaderID);
389   }
390   myShaderID = NO_SHADER;
391 }
392
393 //! Return GLSL shader stage file extension.
394 static const char* getShaderExtension (GLenum theType)
395 {
396   switch (theType)
397   {
398     case GL_VERTEX_SHADER:          return ".vs";
399     case GL_FRAGMENT_SHADER:        return ".fs";
400     case GL_GEOMETRY_SHADER:        return ".gs";
401     case GL_TESS_CONTROL_SHADER:    return ".tcs";
402     case GL_TESS_EVALUATION_SHADER: return ".tes";
403     case GL_COMPUTE_SHADER:         return ".cs";
404   }
405   return ".glsl";
406 }
407
408 //! Expand substring with additional tail.
409 static void insertSubString (TCollection_AsciiString& theString,
410                              const char& thePattern,
411                              const TCollection_AsciiString& theSubstitution)
412 {
413   const int aSubLen = theSubstitution.Length();
414   for (int aCharIter = 1, aNbChars = theString.Length(); aCharIter <= aNbChars; ++aCharIter)
415   {
416     if (theString.Value (aCharIter) == thePattern)
417     {
418       theString.Insert (aCharIter + 1, theSubstitution);
419       aCharIter += aSubLen;
420       aNbChars  += aSubLen;
421     }
422   }
423 }
424
425 //! Dump GLSL shader source code into file.
426 static bool dumpShaderSource (const TCollection_AsciiString& theFileName,
427                               const TCollection_AsciiString& theSource,
428                               bool theToBeautify)
429 {
430   OSD_File aFile (theFileName);
431   aFile.Build (OSD_WriteOnly, OSD_Protection());
432   TCollection_AsciiString aSource = theSource;
433   if (theToBeautify)
434   {
435     insertSubString (aSource, ';', "\n");
436     insertSubString (aSource, '{', "\n");
437     insertSubString (aSource, '}', "\n");
438   }
439   if (!aFile.IsOpen())
440   {
441     Message::SendFail (TCollection_AsciiString("Error: File '") + theFileName + "' cannot be opened to save shader");
442     return false;
443   }
444
445   if (aSource.Length() > 0)
446   {
447     aFile.Write (aSource.ToCString(), aSource.Length());
448   }
449   aFile.Close();
450   Message::SendWarning (TCollection_AsciiString ("Shader source dumped into '") + theFileName + "'");
451   return true;
452 }
453
454 //! Read GLSL shader source code from file dump.
455 static bool restoreShaderSource (TCollection_AsciiString& theSource,
456                                  const TCollection_AsciiString& theFileName)
457 {
458   OSD_File aFile (theFileName);
459   aFile.Open (OSD_ReadOnly, OSD_Protection());
460   if (!aFile.IsOpen())
461   {
462     Message::SendFail (TCollection_AsciiString ("File '") + theFileName + "' cannot be opened to load shader");
463     return false;
464   }
465
466   const Standard_Integer aSize = (Standard_Integer )aFile.Size();
467   if (aSize > 0)
468   {
469     theSource = TCollection_AsciiString (aSize, '\0');
470     aFile.Read (theSource, aSize);
471   }
472   aFile.Close();
473   Message::SendWarning (TCollection_AsciiString ("Restored shader dump from '") + theFileName + "'");
474   return true;
475 }
476
477 // =======================================================================
478 // function : updateDebugDump
479 // purpose  :
480 // =======================================================================
481 Standard_Boolean OpenGl_ShaderObject::updateDebugDump (const Handle(OpenGl_Context)& theCtx,
482                                                        const TCollection_AsciiString& theProgramId,
483                                                        const TCollection_AsciiString& theFolder,
484                                                        Standard_Boolean theToBeautify,
485                                                        Standard_Boolean theToReset)
486 {
487   const TCollection_AsciiString aFileName = theFolder + "/" + theProgramId + getShaderExtension (myType);
488   if (!theToReset)
489   {
490     OSD_File aFile (aFileName);
491     if (aFile.Exists())
492     {
493       const Quantity_Date aDate = aFile.AccessMoment();
494       if (aDate > myDumpDate)
495       {
496         TCollection_AsciiString aNewSource;
497         if (restoreShaderSource (aNewSource, aFileName))
498         {
499           LoadAndCompile (theCtx, theProgramId, aNewSource);
500           return Standard_True;
501         }
502       }
503       return Standard_False;
504     }
505   }
506
507   bool isDumped = false;
508   if (myShaderID != NO_SHADER)
509   {
510     GLint aLength = 0;
511     theCtx->core20fwd->glGetShaderiv (myShaderID, GL_SHADER_SOURCE_LENGTH, &aLength);
512     if (aLength > 0)
513     {
514       TCollection_AsciiString aSource (aLength - 1, '\0');
515       theCtx->core20fwd->glGetShaderSource (myShaderID, aLength, NULL, (GLchar* )aSource.ToCString());
516       dumpShaderSource (aFileName, aSource, theToBeautify);
517       isDumped = true;
518     }
519   }
520   if (!isDumped)
521   {
522     dumpShaderSource (aFileName, "", false);
523   }
524   myDumpDate = OSD_Process().SystemDate();
525   return Standard_False;
526 }