0033661: Data Exchange, Step Import - Tessellated GDTs are not imported
[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>
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 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
737e9a8d 49//! Return GLSL shader stage title.
50static 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// =======================================================================
68OpenGl_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// =======================================================================
79OpenGl_ShaderObject::~OpenGl_ShaderObject()
80{
81 Release (NULL);
82}
83
2bda8346 84// =======================================================================
85// function : LoadAndCompile
86// purpose :
87// =======================================================================
88Standard_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// =======================================================================
145void 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// =======================================================================
157Standard_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// =======================================================================
174Standard_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// =======================================================================
194Standard_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// =======================================================================
220Standard_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 235void 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.
254static 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.
269static 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.
286static 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.
315static 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// =======================================================================
341Standard_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}