0029076: Visualization - implement element shrinking Shader
[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
18 #include <OpenGl_Context.hxx>
19 #include <OpenGl_ShaderObject.hxx>
20 #include <OSD_Path.hxx>
21 #include <Standard_Assert.hxx>
22 #include <TCollection_AsciiString.hxx>
23 #include <TCollection_ExtendedString.hxx>
24
25 #ifdef _WIN32
26   #include <malloc.h> // for alloca()
27 #endif
28
29 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_ShaderObject,OpenGl_Resource)
30
31 //! Puts line numbers to the output of GLSL program source code.
32 static TCollection_AsciiString putLineNumbers (const TCollection_AsciiString& theSource)
33 {
34   std::stringstream aStream;
35   theSource.Print (aStream);
36   std::string aLine;
37   Standard_Integer aLineNumber = 1;
38   TCollection_AsciiString aResultSource;
39   while (std::getline (aStream, aLine))
40   {
41     TCollection_AsciiString anAsciiString = TCollection_AsciiString (aLine.c_str());
42     anAsciiString.Prepend (TCollection_AsciiString ("\n") + TCollection_AsciiString (aLineNumber) + ": ");
43     aResultSource += anAsciiString;
44     aLineNumber++;
45   }
46   return aResultSource;
47 }
48
49 // =======================================================================
50 // function : CreateFromSource
51 // purpose  :
52 // =======================================================================
53 Handle(Graphic3d_ShaderObject) OpenGl_ShaderObject::CreateFromSource (TCollection_AsciiString& theSource,
54                                                                       Graphic3d_TypeOfShaderObject theType,
55                                                                       const ShaderVariableList& theUniforms,
56                                                                       const ShaderVariableList& theStageInOuts,
57                                                                       const TCollection_AsciiString& theInName,
58                                                                       const TCollection_AsciiString& theOutName,
59                                                                       Standard_Integer theNbGeomInputVerts)
60 {
61   if (theSource.IsEmpty())
62   {
63     return Handle(Graphic3d_ShaderObject)();
64   }
65
66   TCollection_AsciiString aSrcUniforms, aSrcInOuts, aSrcInStructs, aSrcOutStructs;
67   for (ShaderVariableList::Iterator anUniformIter (theUniforms); anUniformIter.More(); anUniformIter.Next())
68   {
69     const ShaderVariable& aVar = anUniformIter.Value();
70     if ((aVar.Stages & theType) != 0)
71     {
72       aSrcUniforms += TCollection_AsciiString("\nuniform ") + aVar.Name + ";";
73     }
74   }
75   for (ShaderVariableList::Iterator aVarListIter (theStageInOuts); aVarListIter.More(); aVarListIter.Next())
76   {
77     const ShaderVariable& aVar = aVarListIter.Value();
78     Standard_Integer aStageLower = IntegerLast(), aStageUpper = IntegerFirst();
79     Standard_Integer aNbStages = 0;
80     for (Standard_Integer aStageIter = Graphic3d_TOS_VERTEX; aStageIter <= (Standard_Integer )Graphic3d_TOS_COMPUTE; aStageIter = aStageIter << 1)
81     {
82       if ((aVar.Stages & aStageIter) != 0)
83       {
84         ++aNbStages;
85         aStageLower = Min (aStageLower, aStageIter);
86         aStageUpper = Max (aStageUpper, aStageIter);
87       }
88     }
89     if ((Standard_Integer )theType < aStageLower
90      || (Standard_Integer )theType > aStageUpper)
91     {
92       continue;
93     }
94
95     const Standard_Boolean hasGeomStage = theNbGeomInputVerts > 0
96                                        && aStageLower <  Graphic3d_TOS_GEOMETRY
97                                        && aStageUpper >= Graphic3d_TOS_GEOMETRY;
98     const Standard_Boolean isAllStagesVar = aStageLower == Graphic3d_TOS_VERTEX
99                                          && aStageUpper == Graphic3d_TOS_FRAGMENT;
100     if (hasGeomStage
101     || !theInName.IsEmpty()
102     || !theOutName.IsEmpty())
103     {
104       if (aSrcInStructs.IsEmpty()
105        && aSrcOutStructs.IsEmpty()
106        && isAllStagesVar)
107       {
108         if (theType == aStageLower)
109         {
110           aSrcOutStructs = "\nout VertexData\n{";
111         }
112         else if (theType == aStageUpper)
113         {
114           aSrcInStructs = "\nin VertexData\n{";
115         }
116         else // requires theInName/theOutName
117         {
118           aSrcInStructs  = "\nin  VertexData\n{";
119           aSrcOutStructs = "\nout VertexData\n{";
120         }
121       }
122     }
123
124     if (isAllStagesVar
125      && (!aSrcInStructs.IsEmpty()
126       || !aSrcOutStructs.IsEmpty()))
127     {
128       if (!aSrcInStructs.IsEmpty())
129       {
130         aSrcInStructs  += TCollection_AsciiString("\n  ") + aVar.Name + ";";
131       }
132       if (!aSrcOutStructs.IsEmpty())
133       {
134         aSrcOutStructs += TCollection_AsciiString("\n  ") + aVar.Name + ";";
135       }
136     }
137     else
138     {
139       if (theType == aStageLower)
140       {
141         aSrcInOuts += TCollection_AsciiString("\nTHE_SHADER_OUT ") + aVar.Name + ";";
142       }
143       else if (theType == aStageUpper)
144       {
145         aSrcInOuts += TCollection_AsciiString("\nTHE_SHADER_IN ") + aVar.Name + ";";
146       }
147     }
148   }
149
150   if (theType == Graphic3d_TOS_GEOMETRY)
151   {
152     aSrcUniforms.Prepend (TCollection_AsciiString()
153                         + "\nlayout (triangles) in;"
154                           "\nlayout (triangle_strip, max_vertices = " + theNbGeomInputVerts + ") out;");
155   }
156   if (!aSrcInStructs.IsEmpty()
157    && theType == Graphic3d_TOS_GEOMETRY)
158   {
159     aSrcInStructs  += TCollection_AsciiString ("\n} ") + theInName  + "[" + theNbGeomInputVerts + "];";
160   }
161   else if (!aSrcInStructs.IsEmpty())
162   {
163     aSrcInStructs += "\n}";
164     if (!theInName.IsEmpty())
165     {
166       aSrcInStructs += " ";
167       aSrcInStructs += theInName;
168     }
169     aSrcInStructs += ";";
170   }
171   if (!aSrcOutStructs.IsEmpty())
172   {
173     aSrcOutStructs += "\n}";
174     if (!theOutName.IsEmpty())
175     {
176       aSrcOutStructs += " ";
177       aSrcOutStructs += theOutName;
178     }
179     aSrcOutStructs += ";";
180   }
181
182   theSource.Prepend (aSrcUniforms + aSrcInStructs + aSrcOutStructs + aSrcInOuts);
183   return Graphic3d_ShaderObject::CreateFromSource (theType, theSource);
184 }
185
186 // =======================================================================
187 // function : OpenGl_ShaderObject
188 // purpose  : Creates uninitialized shader object
189 // =======================================================================
190 OpenGl_ShaderObject::OpenGl_ShaderObject (GLenum theType)
191 : myType     (theType),
192   myShaderID (NO_SHADER)
193 {
194   //
195 }
196
197 // =======================================================================
198 // function : ~OpenGl_ShaderObject
199 // purpose  : Releases resources of shader object
200 // =======================================================================
201 OpenGl_ShaderObject::~OpenGl_ShaderObject()
202 {
203   Release (NULL);
204 }
205
206 // =======================================================================
207 // function : LoadAndCompile
208 // purpose  :
209 // =======================================================================
210 Standard_Boolean OpenGl_ShaderObject::LoadAndCompile (const Handle(OpenGl_Context)& theCtx,
211                                                       const TCollection_AsciiString& theSource,
212                                                       bool theIsVerbose,
213                                                       bool theToPrintSource)
214 {
215   if (!theIsVerbose)
216   {
217     return LoadSource (theCtx, theSource)
218         && Compile (theCtx);
219   }
220
221   if (!LoadSource (theCtx, theSource))
222   {
223     if (theToPrintSource)
224     {
225       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, theSource);
226     }
227     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, "Error! Failed to set shader source");
228     Release (theCtx.operator->());
229     return false;
230   }
231
232   if (!Compile (theCtx))
233   {
234     if (theToPrintSource)
235     {
236       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, putLineNumbers (theSource));
237     }
238     TCollection_AsciiString aLog;
239     FetchInfoLog (theCtx, aLog);
240     if (aLog.IsEmpty())
241     {
242       aLog = "Compilation log is empty.";
243     }
244     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
245                          TCollection_AsciiString ("Failed to compile shader object. Compilation log:\n") + aLog);
246     Release (theCtx.operator->());
247     return false;
248   }
249   else if (theCtx->caps->glslWarnings)
250   {
251     TCollection_AsciiString aLog;
252     FetchInfoLog (theCtx, aLog);
253     if (!aLog.IsEmpty()
254      && !aLog.IsEqual ("No errors.\n"))
255     {
256       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_LOW,
257                            TCollection_AsciiString ("Shader compilation log:\n") + aLog);
258     }
259   }
260   return true;
261 }
262
263 // =======================================================================
264 // function : LoadSource
265 // purpose  : Loads shader source code
266 // =======================================================================
267 Standard_Boolean OpenGl_ShaderObject::LoadSource (const Handle(OpenGl_Context)&  theCtx,
268                                                   const TCollection_AsciiString& theSource)
269 {
270   if (myShaderID == NO_SHADER)
271   {
272     return Standard_False;
273   }
274
275   const GLchar* aLines = theSource.ToCString();
276   theCtx->core20fwd->glShaderSource (myShaderID, 1, &aLines, NULL);
277   return Standard_True;
278 }
279
280 // =======================================================================
281 // function : Compile
282 // purpose  : Compiles the shader object
283 // =======================================================================
284 Standard_Boolean OpenGl_ShaderObject::Compile (const Handle(OpenGl_Context)& theCtx)
285 {
286   if (myShaderID == NO_SHADER)
287   {
288     return Standard_False;
289   }
290
291   // Try to compile shader
292   theCtx->core20fwd->glCompileShader (myShaderID);
293
294   // Check compile status
295   GLint aStatus = GL_FALSE;
296   theCtx->core20fwd->glGetShaderiv (myShaderID, GL_COMPILE_STATUS, &aStatus);
297   return aStatus != GL_FALSE;
298 }
299
300 // =======================================================================
301 // function : FetchInfoLog
302 // purpose  : Fetches information log of the last compile operation
303 // =======================================================================
304 Standard_Boolean OpenGl_ShaderObject::FetchInfoLog (const Handle(OpenGl_Context)& theCtx,
305                                                     TCollection_AsciiString&      theLog)
306 {
307   if (myShaderID == NO_SHADER)
308   {
309     return Standard_False;
310   }
311
312   // Load information log of the compiler
313   GLint aLength = 0;
314   theCtx->core20fwd->glGetShaderiv (myShaderID, GL_INFO_LOG_LENGTH, &aLength);
315   if (aLength > 0)
316   {
317     GLchar* aLog = (GLchar*) alloca (aLength);
318     memset (aLog, 0, aLength);
319     theCtx->core20fwd->glGetShaderInfoLog (myShaderID, aLength, NULL, aLog);
320     theLog = aLog;
321   }
322
323   return Standard_True;
324 }
325
326 // =======================================================================
327 // function : Create
328 // purpose  : Creates new empty shader object of specified type
329 // =======================================================================
330 Standard_Boolean OpenGl_ShaderObject::Create (const Handle(OpenGl_Context)& theCtx)
331 {
332   if (myShaderID == NO_SHADER
333    && theCtx->core20fwd != NULL)
334   {
335     myShaderID = theCtx->core20fwd->glCreateShader (myType);
336   }
337
338   return myShaderID != NO_SHADER;
339 }
340
341 // =======================================================================
342 // function : Release
343 // purpose  : Destroys shader object
344 // =======================================================================
345 void OpenGl_ShaderObject::Release (OpenGl_Context* theCtx)
346 {
347   if (myShaderID == NO_SHADER)
348   {
349     return;
350   }
351
352   Standard_ASSERT_RETURN (theCtx != NULL,
353     "OpenGl_ShaderObject destroyed without GL context! Possible GPU memory leakage...",);
354
355   if (theCtx->core20fwd != NULL
356    && theCtx->IsValid())
357   {
358     theCtx->core20fwd->glDeleteShader (myShaderID);
359   }
360   myShaderID = NO_SHADER;
361 }