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