0030488: Visualization, Ray Tracing - empty error message on GLSL program compilation
[occt.git] / src / OpenGl / OpenGl_ShaderObject.cxx
CommitLineData
30f0ad28 1// Created on: 2013-09-19
2// Created by: Denis BOGOLEPOV
d5f74e42 3// Copyright (c) 2013-2014 OPEN CASCADE SAS
30f0ad28 4//
973c2be1 5// This file is part of Open CASCADE Technology software library.
30f0ad28 6//
d5f74e42 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
973c2be1 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.
30f0ad28 12//
973c2be1 13// Alternatively, this file may be used under the terms of Open CASCADE
14// commercial license or contractual agreement.
30f0ad28 15
16#include <Graphic3d_ShaderObject.hxx>
2bda8346 17
30f0ad28 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>
2bda8346 23#include <TCollection_ExtendedString.hxx>
30f0ad28 24
7c65581d 25#ifdef _WIN32
26 #include <malloc.h> // for alloca()
27#endif
30f0ad28 28
92efcf78 29IMPLEMENT_STANDARD_RTTIEXT(OpenGl_ShaderObject,OpenGl_Resource)
30
2bda8346 31//! Puts line numbers to the output of GLSL program source code.
32static 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
30f0ad28 49// =======================================================================
3b4c6945 50// function : CreateFromSource
51// purpose :
52// =======================================================================
53Handle(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// =======================================================================
30f0ad28 172// function : OpenGl_ShaderObject
173// purpose : Creates uninitialized shader object
174// =======================================================================
175OpenGl_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// =======================================================================
186OpenGl_ShaderObject::~OpenGl_ShaderObject()
187{
188 Release (NULL);
189}
190
191// =======================================================================
2bda8346 192// function : LoadAndCompile
193// purpose :
194// =======================================================================
195Standard_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// =======================================================================
30f0ad28 249// function : LoadSource
250// purpose : Loads shader source code
251// =======================================================================
252Standard_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();
4e1523ef 261 theCtx->core20fwd->glShaderSource (myShaderID, 1, &aLines, NULL);
30f0ad28 262 return Standard_True;
263}
264
265// =======================================================================
266// function : Compile
267// purpose : Compiles the shader object
268// =======================================================================
269Standard_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
4e1523ef 277 theCtx->core20fwd->glCompileShader (myShaderID);
30f0ad28 278
279 // Check compile status
280 GLint aStatus = GL_FALSE;
4e1523ef 281 theCtx->core20fwd->glGetShaderiv (myShaderID, GL_COMPILE_STATUS, &aStatus);
30f0ad28 282 return aStatus != GL_FALSE;
283}
284
285// =======================================================================
286// function : FetchInfoLog
287// purpose : Fetches information log of the last compile operation
288// =======================================================================
289Standard_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;
4e1523ef 299 theCtx->core20fwd->glGetShaderiv (myShaderID, GL_INFO_LOG_LENGTH, &aLength);
30f0ad28 300 if (aLength > 0)
301 {
302 GLchar* aLog = (GLchar*) alloca (aLength);
303 memset (aLog, 0, aLength);
4e1523ef 304 theCtx->core20fwd->glGetShaderInfoLog (myShaderID, aLength, NULL, aLog);
30f0ad28 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// =======================================================================
315Standard_Boolean OpenGl_ShaderObject::Create (const Handle(OpenGl_Context)& theCtx)
316{
317 if (myShaderID == NO_SHADER
4e1523ef 318 && theCtx->core20fwd != NULL)
30f0ad28 319 {
4e1523ef 320 myShaderID = theCtx->core20fwd->glCreateShader (myType);
30f0ad28 321 }
322
323 return myShaderID != NO_SHADER;
324}
325
326// =======================================================================
327// function : Release
328// purpose : Destroys shader object
329// =======================================================================
10b9c7df 330void OpenGl_ShaderObject::Release (OpenGl_Context* theCtx)
30f0ad28 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
4e1523ef 340 if (theCtx->core20fwd != NULL
ec2eeb2d 341 && theCtx->IsValid())
30f0ad28 342 {
4e1523ef 343 theCtx->core20fwd->glDeleteShader (myShaderID);
30f0ad28 344 }
345 myShaderID = NO_SHADER;
346}