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> |
17 | #include <OpenGl_Context.hxx> |
18 | #include <OpenGl_ShaderObject.hxx> |
d95f5ce1 |
19 | #include <OSD_File.hxx> |
d95f5ce1 |
20 | #include <OSD_Process.hxx> |
21 | #include <OSD_Protection.hxx> |
30f0ad28 |
22 | #include <Standard_Assert.hxx> |
23 | #include <TCollection_AsciiString.hxx> |
24 | |
7c65581d |
25 | #ifdef _WIN32 |
26 | #include <malloc.h> // for alloca() |
27 | #endif |
30f0ad28 |
28 | |
92efcf78 |
29 | IMPLEMENT_STANDARD_RTTIEXT(OpenGl_ShaderObject,OpenGl_Resource) |
30 | |
2bda8346 |
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 | |
737e9a8d |
49 | //! Return GLSL shader stage title. |
50 | static TCollection_AsciiString getShaderTypeString (GLenum theType) |
51 | { |
52 | switch (theType) |
53 | { |
54 | case GL_VERTEX_SHADER: return "Vertex Shader"; |
55 | case GL_FRAGMENT_SHADER: return "Fragment Shader"; |
56 | case GL_GEOMETRY_SHADER: return "Geometry Shader"; |
57 | case GL_TESS_CONTROL_SHADER: return "Tessellation Control Shader"; |
58 | case GL_TESS_EVALUATION_SHADER: return "Tessellation Evaluation Shader"; |
59 | case GL_COMPUTE_SHADER: return "Compute Shader"; |
60 | } |
61 | return "Shader"; |
62 | } |
63 | |
30f0ad28 |
64 | // ======================================================================= |
65 | // function : OpenGl_ShaderObject |
66 | // purpose : Creates uninitialized shader object |
67 | // ======================================================================= |
68 | OpenGl_ShaderObject::OpenGl_ShaderObject (GLenum theType) |
69 | : myType (theType), |
70 | myShaderID (NO_SHADER) |
71 | { |
72 | // |
73 | } |
74 | |
75 | // ======================================================================= |
76 | // function : ~OpenGl_ShaderObject |
77 | // purpose : Releases resources of shader object |
78 | // ======================================================================= |
79 | OpenGl_ShaderObject::~OpenGl_ShaderObject() |
80 | { |
81 | Release (NULL); |
82 | } |
83 | |
2bda8346 |
84 | // ======================================================================= |
85 | // function : LoadAndCompile |
86 | // purpose : |
87 | // ======================================================================= |
88 | Standard_Boolean OpenGl_ShaderObject::LoadAndCompile (const Handle(OpenGl_Context)& theCtx, |
737e9a8d |
89 | const TCollection_AsciiString& theId, |
2bda8346 |
90 | const TCollection_AsciiString& theSource, |
91 | bool theIsVerbose, |
92 | bool theToPrintSource) |
93 | { |
94 | if (!theIsVerbose) |
95 | { |
96 | return LoadSource (theCtx, theSource) |
97 | && Compile (theCtx); |
98 | } |
99 | |
100 | if (!LoadSource (theCtx, theSource)) |
101 | { |
102 | if (theToPrintSource) |
103 | { |
104 | theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, theSource); |
105 | } |
737e9a8d |
106 | theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, |
107 | TCollection_AsciiString ("Error! Failed to set ") + getShaderTypeString (myType) + " [" + theId + "] source"); |
2bda8346 |
108 | return false; |
109 | } |
110 | |
111 | if (!Compile (theCtx)) |
112 | { |
113 | if (theToPrintSource) |
114 | { |
115 | theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, putLineNumbers (theSource)); |
116 | } |
117 | TCollection_AsciiString aLog; |
118 | FetchInfoLog (theCtx, aLog); |
119 | if (aLog.IsEmpty()) |
120 | { |
121 | aLog = "Compilation log is empty."; |
122 | } |
123 | theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, |
737e9a8d |
124 | TCollection_AsciiString ("Failed to compile ") + getShaderTypeString (myType) + " [" + theId + "]. Compilation log:\n" + aLog); |
2bda8346 |
125 | return false; |
126 | } |
127 | else if (theCtx->caps->glslWarnings) |
128 | { |
129 | TCollection_AsciiString aLog; |
130 | FetchInfoLog (theCtx, aLog); |
131 | if (!aLog.IsEmpty() |
132 | && !aLog.IsEqual ("No errors.\n")) |
133 | { |
134 | theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_LOW, |
737e9a8d |
135 | getShaderTypeString (myType) + " [" + theId + "] compilation log:\n" + aLog); |
2bda8346 |
136 | } |
137 | } |
138 | return true; |
139 | } |
140 | |
737e9a8d |
141 | // ======================================================================= |
142 | // function : DumpSourceCode |
143 | // purpose : |
144 | // ======================================================================= |
145 | void OpenGl_ShaderObject::DumpSourceCode (const Handle(OpenGl_Context)& theCtx, |
146 | const TCollection_AsciiString& theId, |
147 | const TCollection_AsciiString& theSource) const |
148 | { |
149 | theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER, 0, GL_DEBUG_SEVERITY_MEDIUM, |
150 | getShaderTypeString (myType) + " [" + theId + "] source code:\n" + theSource); |
151 | } |
152 | |
30f0ad28 |
153 | // ======================================================================= |
154 | // function : LoadSource |
155 | // purpose : Loads shader source code |
156 | // ======================================================================= |
157 | Standard_Boolean OpenGl_ShaderObject::LoadSource (const Handle(OpenGl_Context)& theCtx, |
158 | const TCollection_AsciiString& theSource) |
159 | { |
160 | if (myShaderID == NO_SHADER) |
161 | { |
162 | return Standard_False; |
163 | } |
164 | |
165 | const GLchar* aLines = theSource.ToCString(); |
4e1523ef |
166 | theCtx->core20fwd->glShaderSource (myShaderID, 1, &aLines, NULL); |
30f0ad28 |
167 | return Standard_True; |
168 | } |
169 | |
170 | // ======================================================================= |
171 | // function : Compile |
172 | // purpose : Compiles the shader object |
173 | // ======================================================================= |
174 | Standard_Boolean OpenGl_ShaderObject::Compile (const Handle(OpenGl_Context)& theCtx) |
175 | { |
176 | if (myShaderID == NO_SHADER) |
177 | { |
178 | return Standard_False; |
179 | } |
180 | |
181 | // Try to compile shader |
4e1523ef |
182 | theCtx->core20fwd->glCompileShader (myShaderID); |
30f0ad28 |
183 | |
184 | // Check compile status |
185 | GLint aStatus = GL_FALSE; |
4e1523ef |
186 | theCtx->core20fwd->glGetShaderiv (myShaderID, GL_COMPILE_STATUS, &aStatus); |
30f0ad28 |
187 | return aStatus != GL_FALSE; |
188 | } |
189 | |
190 | // ======================================================================= |
191 | // function : FetchInfoLog |
192 | // purpose : Fetches information log of the last compile operation |
193 | // ======================================================================= |
194 | Standard_Boolean OpenGl_ShaderObject::FetchInfoLog (const Handle(OpenGl_Context)& theCtx, |
195 | TCollection_AsciiString& theLog) |
196 | { |
197 | if (myShaderID == NO_SHADER) |
198 | { |
199 | return Standard_False; |
200 | } |
201 | |
202 | // Load information log of the compiler |
203 | GLint aLength = 0; |
4e1523ef |
204 | theCtx->core20fwd->glGetShaderiv (myShaderID, GL_INFO_LOG_LENGTH, &aLength); |
30f0ad28 |
205 | if (aLength > 0) |
206 | { |
207 | GLchar* aLog = (GLchar*) alloca (aLength); |
208 | memset (aLog, 0, aLength); |
4e1523ef |
209 | theCtx->core20fwd->glGetShaderInfoLog (myShaderID, aLength, NULL, aLog); |
30f0ad28 |
210 | theLog = aLog; |
211 | } |
212 | |
213 | return Standard_True; |
214 | } |
215 | |
216 | // ======================================================================= |
217 | // function : Create |
218 | // purpose : Creates new empty shader object of specified type |
219 | // ======================================================================= |
220 | Standard_Boolean OpenGl_ShaderObject::Create (const Handle(OpenGl_Context)& theCtx) |
221 | { |
222 | if (myShaderID == NO_SHADER |
4e1523ef |
223 | && theCtx->core20fwd != NULL) |
30f0ad28 |
224 | { |
4e1523ef |
225 | myShaderID = theCtx->core20fwd->glCreateShader (myType); |
30f0ad28 |
226 | } |
227 | |
228 | return myShaderID != NO_SHADER; |
229 | } |
230 | |
231 | // ======================================================================= |
232 | // function : Release |
233 | // purpose : Destroys shader object |
234 | // ======================================================================= |
10b9c7df |
235 | void OpenGl_ShaderObject::Release (OpenGl_Context* theCtx) |
30f0ad28 |
236 | { |
237 | if (myShaderID == NO_SHADER) |
238 | { |
239 | return; |
240 | } |
241 | |
242 | Standard_ASSERT_RETURN (theCtx != NULL, |
243 | "OpenGl_ShaderObject destroyed without GL context! Possible GPU memory leakage...",); |
244 | |
4e1523ef |
245 | if (theCtx->core20fwd != NULL |
ec2eeb2d |
246 | && theCtx->IsValid()) |
30f0ad28 |
247 | { |
4e1523ef |
248 | theCtx->core20fwd->glDeleteShader (myShaderID); |
30f0ad28 |
249 | } |
250 | myShaderID = NO_SHADER; |
251 | } |
d95f5ce1 |
252 | |
253 | //! Return GLSL shader stage file extension. |
254 | static const char* getShaderExtension (GLenum theType) |
255 | { |
256 | switch (theType) |
257 | { |
258 | case GL_VERTEX_SHADER: return ".vs"; |
259 | case GL_FRAGMENT_SHADER: return ".fs"; |
260 | case GL_GEOMETRY_SHADER: return ".gs"; |
261 | case GL_TESS_CONTROL_SHADER: return ".tcs"; |
262 | case GL_TESS_EVALUATION_SHADER: return ".tes"; |
263 | case GL_COMPUTE_SHADER: return ".cs"; |
264 | } |
265 | return ".glsl"; |
266 | } |
267 | |
268 | //! Expand substring with additional tail. |
269 | static void insertSubString (TCollection_AsciiString& theString, |
270 | const char& thePattern, |
271 | const TCollection_AsciiString& theSubstitution) |
272 | { |
273 | const int aSubLen = theSubstitution.Length(); |
274 | for (int aCharIter = 1, aNbChars = theString.Length(); aCharIter <= aNbChars; ++aCharIter) |
275 | { |
276 | if (theString.Value (aCharIter) == thePattern) |
277 | { |
278 | theString.Insert (aCharIter + 1, theSubstitution); |
279 | aCharIter += aSubLen; |
280 | aNbChars += aSubLen; |
281 | } |
282 | } |
283 | } |
284 | |
285 | //! Dump GLSL shader source code into file. |
286 | static bool dumpShaderSource (const TCollection_AsciiString& theFileName, |
287 | const TCollection_AsciiString& theSource, |
288 | bool theToBeautify) |
289 | { |
290 | OSD_File aFile (theFileName); |
291 | aFile.Build (OSD_WriteOnly, OSD_Protection()); |
292 | TCollection_AsciiString aSource = theSource; |
293 | if (theToBeautify) |
294 | { |
295 | insertSubString (aSource, ';', "\n"); |
296 | insertSubString (aSource, '{', "\n"); |
297 | insertSubString (aSource, '}', "\n"); |
298 | } |
299 | if (!aFile.IsOpen()) |
300 | { |
a87b1b37 |
301 | Message::SendFail (TCollection_AsciiString("Error: File '") + theFileName + "' cannot be opened to save shader"); |
d95f5ce1 |
302 | return false; |
303 | } |
304 | |
305 | if (aSource.Length() > 0) |
306 | { |
307 | aFile.Write (aSource.ToCString(), aSource.Length()); |
308 | } |
309 | aFile.Close(); |
a87b1b37 |
310 | Message::SendWarning (TCollection_AsciiString ("Shader source dumped into '") + theFileName + "'"); |
d95f5ce1 |
311 | return true; |
312 | } |
313 | |
314 | //! Read GLSL shader source code from file dump. |
315 | static bool restoreShaderSource (TCollection_AsciiString& theSource, |
316 | const TCollection_AsciiString& theFileName) |
317 | { |
318 | OSD_File aFile (theFileName); |
319 | aFile.Open (OSD_ReadOnly, OSD_Protection()); |
320 | if (!aFile.IsOpen()) |
321 | { |
a87b1b37 |
322 | Message::SendFail (TCollection_AsciiString ("File '") + theFileName + "' cannot be opened to load shader"); |
d95f5ce1 |
323 | return false; |
324 | } |
325 | |
326 | const Standard_Integer aSize = (Standard_Integer )aFile.Size(); |
327 | if (aSize > 0) |
328 | { |
329 | theSource = TCollection_AsciiString (aSize, '\0'); |
330 | aFile.Read (theSource, aSize); |
331 | } |
332 | aFile.Close(); |
a87b1b37 |
333 | Message::SendWarning (TCollection_AsciiString ("Restored shader dump from '") + theFileName + "'"); |
d95f5ce1 |
334 | return true; |
335 | } |
336 | |
337 | // ======================================================================= |
338 | // function : updateDebugDump |
339 | // purpose : |
340 | // ======================================================================= |
341 | Standard_Boolean OpenGl_ShaderObject::updateDebugDump (const Handle(OpenGl_Context)& theCtx, |
342 | const TCollection_AsciiString& theProgramId, |
343 | const TCollection_AsciiString& theFolder, |
344 | Standard_Boolean theToBeautify, |
345 | Standard_Boolean theToReset) |
346 | { |
347 | const TCollection_AsciiString aFileName = theFolder + "/" + theProgramId + getShaderExtension (myType); |
348 | if (!theToReset) |
349 | { |
350 | OSD_File aFile (aFileName); |
351 | if (aFile.Exists()) |
352 | { |
353 | const Quantity_Date aDate = aFile.AccessMoment(); |
354 | if (aDate > myDumpDate) |
355 | { |
356 | TCollection_AsciiString aNewSource; |
357 | if (restoreShaderSource (aNewSource, aFileName)) |
358 | { |
737e9a8d |
359 | LoadAndCompile (theCtx, theProgramId, aNewSource); |
d95f5ce1 |
360 | return Standard_True; |
361 | } |
362 | } |
363 | return Standard_False; |
364 | } |
365 | } |
366 | |
367 | bool isDumped = false; |
368 | if (myShaderID != NO_SHADER) |
369 | { |
370 | GLint aLength = 0; |
371 | theCtx->core20fwd->glGetShaderiv (myShaderID, GL_SHADER_SOURCE_LENGTH, &aLength); |
372 | if (aLength > 0) |
373 | { |
374 | TCollection_AsciiString aSource (aLength - 1, '\0'); |
375 | theCtx->core20fwd->glGetShaderSource (myShaderID, aLength, NULL, (GLchar* )aSource.ToCString()); |
376 | dumpShaderSource (aFileName, aSource, theToBeautify); |
377 | isDumped = true; |
378 | } |
379 | } |
380 | if (!isDumped) |
381 | { |
382 | dumpShaderSource (aFileName, "", false); |
383 | } |
384 | myDumpDate = OSD_Process().SystemDate(); |
385 | return Standard_False; |
386 | } |