0030748: Visualization - Marker displayed in immediate layer ruins QT Quick view...
[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>
d95f5ce1 17#include <Message_Messenger.hxx>
30f0ad28 18#include <OpenGl_Context.hxx>
19#include <OpenGl_ShaderObject.hxx>
d95f5ce1 20#include <OSD_File.hxx>
30f0ad28 21#include <OSD_Path.hxx>
d95f5ce1 22#include <OSD_Process.hxx>
23#include <OSD_Protection.hxx>
30f0ad28 24#include <Standard_Assert.hxx>
25#include <TCollection_AsciiString.hxx>
2bda8346 26#include <TCollection_ExtendedString.hxx>
30f0ad28 27
7c65581d 28#ifdef _WIN32
29 #include <malloc.h> // for alloca()
30#endif
30f0ad28 31
92efcf78 32IMPLEMENT_STANDARD_RTTIEXT(OpenGl_ShaderObject,OpenGl_Resource)
33
2bda8346 34//! Puts line numbers to the output of GLSL program source code.
35static TCollection_AsciiString putLineNumbers (const TCollection_AsciiString& theSource)
36{
37 std::stringstream aStream;
38 theSource.Print (aStream);
39 std::string aLine;
40 Standard_Integer aLineNumber = 1;
41 TCollection_AsciiString aResultSource;
42 while (std::getline (aStream, aLine))
43 {
44 TCollection_AsciiString anAsciiString = TCollection_AsciiString (aLine.c_str());
45 anAsciiString.Prepend (TCollection_AsciiString ("\n") + TCollection_AsciiString (aLineNumber) + ": ");
46 aResultSource += anAsciiString;
47 aLineNumber++;
48 }
49 return aResultSource;
50}
51
737e9a8d 52//! Return GLSL shader stage title.
53static TCollection_AsciiString getShaderTypeString (GLenum theType)
54{
55 switch (theType)
56 {
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";
63 }
64 return "Shader";
65}
66
30f0ad28 67// =======================================================================
3b4c6945 68// function : CreateFromSource
69// purpose :
70// =======================================================================
71Handle(Graphic3d_ShaderObject) OpenGl_ShaderObject::CreateFromSource (TCollection_AsciiString& theSource,
72 Graphic3d_TypeOfShaderObject theType,
73 const ShaderVariableList& theUniforms,
74 const ShaderVariableList& theStageInOuts,
75 const TCollection_AsciiString& theInName,
76 const TCollection_AsciiString& theOutName,
77 Standard_Integer theNbGeomInputVerts)
78{
2a332745 79 if (theSource.IsEmpty())
80 {
81 return Handle(Graphic3d_ShaderObject)();
82 }
83
3b4c6945 84 TCollection_AsciiString aSrcUniforms, aSrcInOuts, aSrcInStructs, aSrcOutStructs;
85 for (ShaderVariableList::Iterator anUniformIter (theUniforms); anUniformIter.More(); anUniformIter.Next())
86 {
87 const ShaderVariable& aVar = anUniformIter.Value();
88 if ((aVar.Stages & theType) != 0)
89 {
90 aSrcUniforms += TCollection_AsciiString("\nuniform ") + aVar.Name + ";";
91 }
92 }
93 for (ShaderVariableList::Iterator aVarListIter (theStageInOuts); aVarListIter.More(); aVarListIter.Next())
94 {
95 const ShaderVariable& aVar = aVarListIter.Value();
96 Standard_Integer aStageLower = IntegerLast(), aStageUpper = IntegerFirst();
97 Standard_Integer aNbStages = 0;
98 for (Standard_Integer aStageIter = Graphic3d_TOS_VERTEX; aStageIter <= (Standard_Integer )Graphic3d_TOS_COMPUTE; aStageIter = aStageIter << 1)
99 {
100 if ((aVar.Stages & aStageIter) != 0)
101 {
102 ++aNbStages;
103 aStageLower = Min (aStageLower, aStageIter);
104 aStageUpper = Max (aStageUpper, aStageIter);
105 }
106 }
107 if ((Standard_Integer )theType < aStageLower
108 || (Standard_Integer )theType > aStageUpper)
109 {
110 continue;
111 }
112
113 const Standard_Boolean hasGeomStage = theNbGeomInputVerts > 0
114 && aStageLower < Graphic3d_TOS_GEOMETRY
115 && aStageUpper >= Graphic3d_TOS_GEOMETRY;
2a332745 116 const Standard_Boolean isAllStagesVar = aStageLower == Graphic3d_TOS_VERTEX
117 && aStageUpper == Graphic3d_TOS_FRAGMENT;
3b4c6945 118 if (hasGeomStage
119 || !theInName.IsEmpty()
120 || !theOutName.IsEmpty())
121 {
122 if (aSrcInStructs.IsEmpty()
2a332745 123 && aSrcOutStructs.IsEmpty()
124 && isAllStagesVar)
3b4c6945 125 {
126 if (theType == aStageLower)
127 {
128 aSrcOutStructs = "\nout VertexData\n{";
129 }
130 else if (theType == aStageUpper)
131 {
132 aSrcInStructs = "\nin VertexData\n{";
133 }
134 else // requires theInName/theOutName
135 {
136 aSrcInStructs = "\nin VertexData\n{";
137 aSrcOutStructs = "\nout VertexData\n{";
138 }
139 }
140 }
141
2a332745 142 if (isAllStagesVar
143 && (!aSrcInStructs.IsEmpty()
144 || !aSrcOutStructs.IsEmpty()))
3b4c6945 145 {
146 if (!aSrcInStructs.IsEmpty())
147 {
148 aSrcInStructs += TCollection_AsciiString("\n ") + aVar.Name + ";";
149 }
150 if (!aSrcOutStructs.IsEmpty())
151 {
152 aSrcOutStructs += TCollection_AsciiString("\n ") + aVar.Name + ";";
153 }
154 }
155 else
156 {
157 if (theType == aStageLower)
158 {
159 aSrcInOuts += TCollection_AsciiString("\nTHE_SHADER_OUT ") + aVar.Name + ";";
160 }
161 else if (theType == aStageUpper)
162 {
163 aSrcInOuts += TCollection_AsciiString("\nTHE_SHADER_IN ") + aVar.Name + ";";
164 }
165 }
166 }
167
2a332745 168 if (theType == Graphic3d_TOS_GEOMETRY)
169 {
170 aSrcUniforms.Prepend (TCollection_AsciiString()
171 + "\nlayout (triangles) in;"
172 "\nlayout (triangle_strip, max_vertices = " + theNbGeomInputVerts + ") out;");
173 }
3b4c6945 174 if (!aSrcInStructs.IsEmpty()
175 && theType == Graphic3d_TOS_GEOMETRY)
176 {
177 aSrcInStructs += TCollection_AsciiString ("\n} ") + theInName + "[" + theNbGeomInputVerts + "];";
178 }
179 else if (!aSrcInStructs.IsEmpty())
180 {
181 aSrcInStructs += "\n}";
182 if (!theInName.IsEmpty())
183 {
184 aSrcInStructs += " ";
185 aSrcInStructs += theInName;
186 }
187 aSrcInStructs += ";";
188 }
2a332745 189 if (!aSrcOutStructs.IsEmpty())
3b4c6945 190 {
191 aSrcOutStructs += "\n}";
2a332745 192 if (!theOutName.IsEmpty())
3b4c6945 193 {
194 aSrcOutStructs += " ";
195 aSrcOutStructs += theOutName;
196 }
197 aSrcOutStructs += ";";
198 }
199
200 theSource.Prepend (aSrcUniforms + aSrcInStructs + aSrcOutStructs + aSrcInOuts);
201 return Graphic3d_ShaderObject::CreateFromSource (theType, theSource);
202}
203
204// =======================================================================
30f0ad28 205// function : OpenGl_ShaderObject
206// purpose : Creates uninitialized shader object
207// =======================================================================
208OpenGl_ShaderObject::OpenGl_ShaderObject (GLenum theType)
209: myType (theType),
210 myShaderID (NO_SHADER)
211{
212 //
213}
214
215// =======================================================================
216// function : ~OpenGl_ShaderObject
217// purpose : Releases resources of shader object
218// =======================================================================
219OpenGl_ShaderObject::~OpenGl_ShaderObject()
220{
221 Release (NULL);
222}
223
224// =======================================================================
2bda8346 225// function : LoadAndCompile
226// purpose :
227// =======================================================================
228Standard_Boolean OpenGl_ShaderObject::LoadAndCompile (const Handle(OpenGl_Context)& theCtx,
737e9a8d 229 const TCollection_AsciiString& theId,
2bda8346 230 const TCollection_AsciiString& theSource,
231 bool theIsVerbose,
232 bool theToPrintSource)
233{
234 if (!theIsVerbose)
235 {
236 return LoadSource (theCtx, theSource)
237 && Compile (theCtx);
238 }
239
240 if (!LoadSource (theCtx, theSource))
241 {
242 if (theToPrintSource)
243 {
244 theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, theSource);
245 }
737e9a8d 246 theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
247 TCollection_AsciiString ("Error! Failed to set ") + getShaderTypeString (myType) + " [" + theId + "] source");
2bda8346 248 return false;
249 }
250
251 if (!Compile (theCtx))
252 {
253 if (theToPrintSource)
254 {
255 theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, putLineNumbers (theSource));
256 }
257 TCollection_AsciiString aLog;
258 FetchInfoLog (theCtx, aLog);
259 if (aLog.IsEmpty())
260 {
261 aLog = "Compilation log is empty.";
262 }
263 theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
737e9a8d 264 TCollection_AsciiString ("Failed to compile ") + getShaderTypeString (myType) + " [" + theId + "]. Compilation log:\n" + aLog);
2bda8346 265 return false;
266 }
267 else if (theCtx->caps->glslWarnings)
268 {
269 TCollection_AsciiString aLog;
270 FetchInfoLog (theCtx, aLog);
271 if (!aLog.IsEmpty()
272 && !aLog.IsEqual ("No errors.\n"))
273 {
274 theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_LOW,
737e9a8d 275 getShaderTypeString (myType) + " [" + theId + "] compilation log:\n" + aLog);
2bda8346 276 }
277 }
278 return true;
279}
280
281// =======================================================================
737e9a8d 282// function : DumpSourceCode
283// purpose :
284// =======================================================================
285void OpenGl_ShaderObject::DumpSourceCode (const Handle(OpenGl_Context)& theCtx,
286 const TCollection_AsciiString& theId,
287 const TCollection_AsciiString& theSource) const
288{
289 theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER, 0, GL_DEBUG_SEVERITY_MEDIUM,
290 getShaderTypeString (myType) + " [" + theId + "] source code:\n" + theSource);
291}
292
293// =======================================================================
30f0ad28 294// function : LoadSource
295// purpose : Loads shader source code
296// =======================================================================
297Standard_Boolean OpenGl_ShaderObject::LoadSource (const Handle(OpenGl_Context)& theCtx,
298 const TCollection_AsciiString& theSource)
299{
300 if (myShaderID == NO_SHADER)
301 {
302 return Standard_False;
303 }
304
305 const GLchar* aLines = theSource.ToCString();
4e1523ef 306 theCtx->core20fwd->glShaderSource (myShaderID, 1, &aLines, NULL);
30f0ad28 307 return Standard_True;
308}
309
310// =======================================================================
311// function : Compile
312// purpose : Compiles the shader object
313// =======================================================================
314Standard_Boolean OpenGl_ShaderObject::Compile (const Handle(OpenGl_Context)& theCtx)
315{
316 if (myShaderID == NO_SHADER)
317 {
318 return Standard_False;
319 }
320
321 // Try to compile shader
4e1523ef 322 theCtx->core20fwd->glCompileShader (myShaderID);
30f0ad28 323
324 // Check compile status
325 GLint aStatus = GL_FALSE;
4e1523ef 326 theCtx->core20fwd->glGetShaderiv (myShaderID, GL_COMPILE_STATUS, &aStatus);
30f0ad28 327 return aStatus != GL_FALSE;
328}
329
330// =======================================================================
331// function : FetchInfoLog
332// purpose : Fetches information log of the last compile operation
333// =======================================================================
334Standard_Boolean OpenGl_ShaderObject::FetchInfoLog (const Handle(OpenGl_Context)& theCtx,
335 TCollection_AsciiString& theLog)
336{
337 if (myShaderID == NO_SHADER)
338 {
339 return Standard_False;
340 }
341
342 // Load information log of the compiler
343 GLint aLength = 0;
4e1523ef 344 theCtx->core20fwd->glGetShaderiv (myShaderID, GL_INFO_LOG_LENGTH, &aLength);
30f0ad28 345 if (aLength > 0)
346 {
347 GLchar* aLog = (GLchar*) alloca (aLength);
348 memset (aLog, 0, aLength);
4e1523ef 349 theCtx->core20fwd->glGetShaderInfoLog (myShaderID, aLength, NULL, aLog);
30f0ad28 350 theLog = aLog;
351 }
352
353 return Standard_True;
354}
355
356// =======================================================================
357// function : Create
358// purpose : Creates new empty shader object of specified type
359// =======================================================================
360Standard_Boolean OpenGl_ShaderObject::Create (const Handle(OpenGl_Context)& theCtx)
361{
362 if (myShaderID == NO_SHADER
4e1523ef 363 && theCtx->core20fwd != NULL)
30f0ad28 364 {
4e1523ef 365 myShaderID = theCtx->core20fwd->glCreateShader (myType);
30f0ad28 366 }
367
368 return myShaderID != NO_SHADER;
369}
370
371// =======================================================================
372// function : Release
373// purpose : Destroys shader object
374// =======================================================================
10b9c7df 375void OpenGl_ShaderObject::Release (OpenGl_Context* theCtx)
30f0ad28 376{
377 if (myShaderID == NO_SHADER)
378 {
379 return;
380 }
381
382 Standard_ASSERT_RETURN (theCtx != NULL,
383 "OpenGl_ShaderObject destroyed without GL context! Possible GPU memory leakage...",);
384
4e1523ef 385 if (theCtx->core20fwd != NULL
ec2eeb2d 386 && theCtx->IsValid())
30f0ad28 387 {
4e1523ef 388 theCtx->core20fwd->glDeleteShader (myShaderID);
30f0ad28 389 }
390 myShaderID = NO_SHADER;
391}
d95f5ce1 392
393//! Return GLSL shader stage file extension.
394static const char* getShaderExtension (GLenum theType)
395{
396 switch (theType)
397 {
398 case GL_VERTEX_SHADER: return ".vs";
399 case GL_FRAGMENT_SHADER: return ".fs";
400 case GL_GEOMETRY_SHADER: return ".gs";
401 case GL_TESS_CONTROL_SHADER: return ".tcs";
402 case GL_TESS_EVALUATION_SHADER: return ".tes";
403 case GL_COMPUTE_SHADER: return ".cs";
404 }
405 return ".glsl";
406}
407
408//! Expand substring with additional tail.
409static void insertSubString (TCollection_AsciiString& theString,
410 const char& thePattern,
411 const TCollection_AsciiString& theSubstitution)
412{
413 const int aSubLen = theSubstitution.Length();
414 for (int aCharIter = 1, aNbChars = theString.Length(); aCharIter <= aNbChars; ++aCharIter)
415 {
416 if (theString.Value (aCharIter) == thePattern)
417 {
418 theString.Insert (aCharIter + 1, theSubstitution);
419 aCharIter += aSubLen;
420 aNbChars += aSubLen;
421 }
422 }
423}
424
425//! Dump GLSL shader source code into file.
426static bool dumpShaderSource (const TCollection_AsciiString& theFileName,
427 const TCollection_AsciiString& theSource,
428 bool theToBeautify)
429{
430 OSD_File aFile (theFileName);
431 aFile.Build (OSD_WriteOnly, OSD_Protection());
432 TCollection_AsciiString aSource = theSource;
433 if (theToBeautify)
434 {
435 insertSubString (aSource, ';', "\n");
436 insertSubString (aSource, '{', "\n");
437 insertSubString (aSource, '}', "\n");
438 }
439 if (!aFile.IsOpen())
440 {
441 Message::DefaultMessenger()->Send (TCollection_AsciiString("Error: File '") + theFileName + "' cannot be opened to save shader", Message_Fail);
442 return false;
443 }
444
445 if (aSource.Length() > 0)
446 {
447 aFile.Write (aSource.ToCString(), aSource.Length());
448 }
449 aFile.Close();
450 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Shader source dumped into '") + theFileName + "'", Message_Warning);
451 return true;
452}
453
454//! Read GLSL shader source code from file dump.
455static bool restoreShaderSource (TCollection_AsciiString& theSource,
456 const TCollection_AsciiString& theFileName)
457{
458 OSD_File aFile (theFileName);
459 aFile.Open (OSD_ReadOnly, OSD_Protection());
460 if (!aFile.IsOpen())
461 {
462 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFileName + "' cannot be opened to load shader", Message_Fail);
463 return false;
464 }
465
466 const Standard_Integer aSize = (Standard_Integer )aFile.Size();
467 if (aSize > 0)
468 {
469 theSource = TCollection_AsciiString (aSize, '\0');
470 aFile.Read (theSource, aSize);
471 }
472 aFile.Close();
473 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Restored shader dump from '") + theFileName + "'", Message_Warning);
474 return true;
475}
476
477// =======================================================================
478// function : updateDebugDump
479// purpose :
480// =======================================================================
481Standard_Boolean OpenGl_ShaderObject::updateDebugDump (const Handle(OpenGl_Context)& theCtx,
482 const TCollection_AsciiString& theProgramId,
483 const TCollection_AsciiString& theFolder,
484 Standard_Boolean theToBeautify,
485 Standard_Boolean theToReset)
486{
487 const TCollection_AsciiString aFileName = theFolder + "/" + theProgramId + getShaderExtension (myType);
488 if (!theToReset)
489 {
490 OSD_File aFile (aFileName);
491 if (aFile.Exists())
492 {
493 const Quantity_Date aDate = aFile.AccessMoment();
494 if (aDate > myDumpDate)
495 {
496 TCollection_AsciiString aNewSource;
497 if (restoreShaderSource (aNewSource, aFileName))
498 {
737e9a8d 499 LoadAndCompile (theCtx, theProgramId, aNewSource);
d95f5ce1 500 return Standard_True;
501 }
502 }
503 return Standard_False;
504 }
505 }
506
507 bool isDumped = false;
508 if (myShaderID != NO_SHADER)
509 {
510 GLint aLength = 0;
511 theCtx->core20fwd->glGetShaderiv (myShaderID, GL_SHADER_SOURCE_LENGTH, &aLength);
512 if (aLength > 0)
513 {
514 TCollection_AsciiString aSource (aLength - 1, '\0');
515 theCtx->core20fwd->glGetShaderSource (myShaderID, aLength, NULL, (GLchar* )aSource.ToCString());
516 dumpShaderSource (aFileName, aSource, theToBeautify);
517 isDumped = true;
518 }
519 }
520 if (!isDumped)
521 {
522 dumpShaderSource (aFileName, "", false);
523 }
524 myDumpDate = OSD_Process().SystemDate();
525 return Standard_False;
526}