1 // Created on: 2013-09-19
2 // Created by: Denis BOGOLEPOV
3 // Copyright (c) 2013-2014 OPEN CASCADE SAS
5 // This file is part of Open CASCADE Technology software library.
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.
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
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>
29 #include <malloc.h> // for alloca()
32 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_ShaderObject,OpenGl_Resource)
34 //! Puts line numbers to the output of GLSL program source code.
35 static TCollection_AsciiString putLineNumbers (const TCollection_AsciiString& theSource)
37 std::stringstream aStream;
38 theSource.Print (aStream);
40 Standard_Integer aLineNumber = 1;
41 TCollection_AsciiString aResultSource;
42 while (std::getline (aStream, aLine))
44 TCollection_AsciiString anAsciiString = TCollection_AsciiString (aLine.c_str());
45 anAsciiString.Prepend (TCollection_AsciiString ("\n") + TCollection_AsciiString (aLineNumber) + ": ");
46 aResultSource += anAsciiString;
52 //! Return GLSL shader stage title.
53 static TCollection_AsciiString getShaderTypeString (GLenum theType)
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";
67 // =======================================================================
68 // function : OpenGl_ShaderObject
69 // purpose : Creates uninitialized shader object
70 // =======================================================================
71 OpenGl_ShaderObject::OpenGl_ShaderObject (GLenum theType)
73 myShaderID (NO_SHADER)
78 // =======================================================================
79 // function : ~OpenGl_ShaderObject
80 // purpose : Releases resources of shader object
81 // =======================================================================
82 OpenGl_ShaderObject::~OpenGl_ShaderObject()
87 // =======================================================================
88 // function : LoadAndCompile
90 // =======================================================================
91 Standard_Boolean OpenGl_ShaderObject::LoadAndCompile (const Handle(OpenGl_Context)& theCtx,
92 const TCollection_AsciiString& theId,
93 const TCollection_AsciiString& theSource,
95 bool theToPrintSource)
99 return LoadSource (theCtx, theSource)
103 if (!LoadSource (theCtx, theSource))
105 if (theToPrintSource)
107 theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, theSource);
109 theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
110 TCollection_AsciiString ("Error! Failed to set ") + getShaderTypeString (myType) + " [" + theId + "] source");
114 if (!Compile (theCtx))
116 if (theToPrintSource)
118 theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, putLineNumbers (theSource));
120 TCollection_AsciiString aLog;
121 FetchInfoLog (theCtx, aLog);
124 aLog = "Compilation log is empty.";
126 theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
127 TCollection_AsciiString ("Failed to compile ") + getShaderTypeString (myType) + " [" + theId + "]. Compilation log:\n" + aLog);
130 else if (theCtx->caps->glslWarnings)
132 TCollection_AsciiString aLog;
133 FetchInfoLog (theCtx, aLog);
135 && !aLog.IsEqual ("No errors.\n"))
137 theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_LOW,
138 getShaderTypeString (myType) + " [" + theId + "] compilation log:\n" + aLog);
144 // =======================================================================
145 // function : DumpSourceCode
147 // =======================================================================
148 void OpenGl_ShaderObject::DumpSourceCode (const Handle(OpenGl_Context)& theCtx,
149 const TCollection_AsciiString& theId,
150 const TCollection_AsciiString& theSource) const
152 theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER, 0, GL_DEBUG_SEVERITY_MEDIUM,
153 getShaderTypeString (myType) + " [" + theId + "] source code:\n" + theSource);
156 // =======================================================================
157 // function : LoadSource
158 // purpose : Loads shader source code
159 // =======================================================================
160 Standard_Boolean OpenGl_ShaderObject::LoadSource (const Handle(OpenGl_Context)& theCtx,
161 const TCollection_AsciiString& theSource)
163 if (myShaderID == NO_SHADER)
165 return Standard_False;
168 const GLchar* aLines = theSource.ToCString();
169 theCtx->core20fwd->glShaderSource (myShaderID, 1, &aLines, NULL);
170 return Standard_True;
173 // =======================================================================
174 // function : Compile
175 // purpose : Compiles the shader object
176 // =======================================================================
177 Standard_Boolean OpenGl_ShaderObject::Compile (const Handle(OpenGl_Context)& theCtx)
179 if (myShaderID == NO_SHADER)
181 return Standard_False;
184 // Try to compile shader
185 theCtx->core20fwd->glCompileShader (myShaderID);
187 // Check compile status
188 GLint aStatus = GL_FALSE;
189 theCtx->core20fwd->glGetShaderiv (myShaderID, GL_COMPILE_STATUS, &aStatus);
190 return aStatus != GL_FALSE;
193 // =======================================================================
194 // function : FetchInfoLog
195 // purpose : Fetches information log of the last compile operation
196 // =======================================================================
197 Standard_Boolean OpenGl_ShaderObject::FetchInfoLog (const Handle(OpenGl_Context)& theCtx,
198 TCollection_AsciiString& theLog)
200 if (myShaderID == NO_SHADER)
202 return Standard_False;
205 // Load information log of the compiler
207 theCtx->core20fwd->glGetShaderiv (myShaderID, GL_INFO_LOG_LENGTH, &aLength);
210 GLchar* aLog = (GLchar*) alloca (aLength);
211 memset (aLog, 0, aLength);
212 theCtx->core20fwd->glGetShaderInfoLog (myShaderID, aLength, NULL, aLog);
216 return Standard_True;
219 // =======================================================================
221 // purpose : Creates new empty shader object of specified type
222 // =======================================================================
223 Standard_Boolean OpenGl_ShaderObject::Create (const Handle(OpenGl_Context)& theCtx)
225 if (myShaderID == NO_SHADER
226 && theCtx->core20fwd != NULL)
228 myShaderID = theCtx->core20fwd->glCreateShader (myType);
231 return myShaderID != NO_SHADER;
234 // =======================================================================
235 // function : Release
236 // purpose : Destroys shader object
237 // =======================================================================
238 void OpenGl_ShaderObject::Release (OpenGl_Context* theCtx)
240 if (myShaderID == NO_SHADER)
245 Standard_ASSERT_RETURN (theCtx != NULL,
246 "OpenGl_ShaderObject destroyed without GL context! Possible GPU memory leakage...",);
248 if (theCtx->core20fwd != NULL
249 && theCtx->IsValid())
251 theCtx->core20fwd->glDeleteShader (myShaderID);
253 myShaderID = NO_SHADER;
256 //! Return GLSL shader stage file extension.
257 static const char* getShaderExtension (GLenum theType)
261 case GL_VERTEX_SHADER: return ".vs";
262 case GL_FRAGMENT_SHADER: return ".fs";
263 case GL_GEOMETRY_SHADER: return ".gs";
264 case GL_TESS_CONTROL_SHADER: return ".tcs";
265 case GL_TESS_EVALUATION_SHADER: return ".tes";
266 case GL_COMPUTE_SHADER: return ".cs";
271 //! Expand substring with additional tail.
272 static void insertSubString (TCollection_AsciiString& theString,
273 const char& thePattern,
274 const TCollection_AsciiString& theSubstitution)
276 const int aSubLen = theSubstitution.Length();
277 for (int aCharIter = 1, aNbChars = theString.Length(); aCharIter <= aNbChars; ++aCharIter)
279 if (theString.Value (aCharIter) == thePattern)
281 theString.Insert (aCharIter + 1, theSubstitution);
282 aCharIter += aSubLen;
288 //! Dump GLSL shader source code into file.
289 static bool dumpShaderSource (const TCollection_AsciiString& theFileName,
290 const TCollection_AsciiString& theSource,
293 OSD_File aFile (theFileName);
294 aFile.Build (OSD_WriteOnly, OSD_Protection());
295 TCollection_AsciiString aSource = theSource;
298 insertSubString (aSource, ';', "\n");
299 insertSubString (aSource, '{', "\n");
300 insertSubString (aSource, '}', "\n");
304 Message::SendFail (TCollection_AsciiString("Error: File '") + theFileName + "' cannot be opened to save shader");
308 if (aSource.Length() > 0)
310 aFile.Write (aSource.ToCString(), aSource.Length());
313 Message::SendWarning (TCollection_AsciiString ("Shader source dumped into '") + theFileName + "'");
317 //! Read GLSL shader source code from file dump.
318 static bool restoreShaderSource (TCollection_AsciiString& theSource,
319 const TCollection_AsciiString& theFileName)
321 OSD_File aFile (theFileName);
322 aFile.Open (OSD_ReadOnly, OSD_Protection());
325 Message::SendFail (TCollection_AsciiString ("File '") + theFileName + "' cannot be opened to load shader");
329 const Standard_Integer aSize = (Standard_Integer )aFile.Size();
332 theSource = TCollection_AsciiString (aSize, '\0');
333 aFile.Read (theSource, aSize);
336 Message::SendWarning (TCollection_AsciiString ("Restored shader dump from '") + theFileName + "'");
340 // =======================================================================
341 // function : updateDebugDump
343 // =======================================================================
344 Standard_Boolean OpenGl_ShaderObject::updateDebugDump (const Handle(OpenGl_Context)& theCtx,
345 const TCollection_AsciiString& theProgramId,
346 const TCollection_AsciiString& theFolder,
347 Standard_Boolean theToBeautify,
348 Standard_Boolean theToReset)
350 const TCollection_AsciiString aFileName = theFolder + "/" + theProgramId + getShaderExtension (myType);
353 OSD_File aFile (aFileName);
356 const Quantity_Date aDate = aFile.AccessMoment();
357 if (aDate > myDumpDate)
359 TCollection_AsciiString aNewSource;
360 if (restoreShaderSource (aNewSource, aFileName))
362 LoadAndCompile (theCtx, theProgramId, aNewSource);
363 return Standard_True;
366 return Standard_False;
370 bool isDumped = false;
371 if (myShaderID != NO_SHADER)
374 theCtx->core20fwd->glGetShaderiv (myShaderID, GL_SHADER_SOURCE_LENGTH, &aLength);
377 TCollection_AsciiString aSource (aLength - 1, '\0');
378 theCtx->core20fwd->glGetShaderSource (myShaderID, aLength, NULL, (GLchar* )aSource.ToCString());
379 dumpShaderSource (aFileName, aSource, theToBeautify);
385 dumpShaderSource (aFileName, "", false);
387 myDumpDate = OSD_Process().SystemDate();
388 return Standard_False;