SLProject  4.2.000
A platform independent 3D computer graphics framework for desktop OS, Android, iOS and online in web browsers
SLGLShader.cpp
Go to the documentation of this file.
1 /**
2  * \file SLGLShader.cpp
3  * \date July 2014
4  * \authors Marcus Hudritsch
5  * \copyright http://opensource.org/licenses/GPL-3.0
6  * \remarks Please use clangformat to format the code. See more code style on
7  * https://github.com/cpvrlab/SLProject4/wiki/SLProject-Coding-Style
8 */
9 
10 #include <Utils.h>
11 #include <SLGLState.h>
12 #include <SLGLProgram.h>
13 #include <SLGLShader.h>
14 #include "SLFileStorage.h"
15 
16 //-----------------------------------------------------------------------------
17 // Error Strings
18 const SLchar* aGLSLErrorString[] = {(const SLchar*)"(e0000) GLSL not enabled",
19  (const SLchar*)"(e0001) not a valid program object",
20  (const SLchar*)"(e0002) not a valid object",
21  (const SLchar*)"(e0003) out of memory",
22  (const SLchar*)"(e0004) unknown compiler error"};
23 
24 //-----------------------------------------------------------------------------
25 //! Default constructor
27 {
28  _type = ST_none;
29  _code = "";
30  _shaderID = 0;
31  _file = "";
32 }
33 //-----------------------------------------------------------------------------
34 //! Ctor with shader filename & shader type
35 /*! If the shader filename does not belong to an existing file the shader code
36  * will be generated at a later stage by SLGLProgramGenerated.
37  * \param filename Path and filename of the shader to be loaded or generated.
38  * \param shaderType Shader type: ST_vertex, ST_fragment and ST_geometry.
39  */
40 SLGLShader::SLGLShader(const SLstring& filename, SLShaderType shaderType)
41  : SLObject(Utils::getFileName(filename), filename)
42 {
43  _type = shaderType;
44  _code = "";
45  _shaderID = 0;
46  _file = filename;
47 
48  // Only load file at this moment, don't compile it.
49  if (SLFileStorage::exists(filename, IOK_shader))
50  load(filename);
51 }
52 //-----------------------------------------------------------------------------
53 //! SLGLShader::load loads a shader file into string _shaderSource
54 void SLGLShader::load(const SLstring& filename)
55 {
57  _code = SLstring(buffer.data, buffer.data + buffer.size);
58  buffer.deallocate();
59 
60  // Expand include pragmas. This has to be done here because we are not
61  // allowed to perform I/O later on when compiling the shader.
63 
64  // remove comments because some stupid ARM compiler can't handle GLSL comments
66 }
67 //-----------------------------------------------------------------------------
68 //! SLGLShader::load loads a shader file from memory into memory
69 void SLGLShader::loadFromMemory(const SLstring& shaderSource)
70 {
71  _code = shaderSource;
72 }
73 //-----------------------------------------------------------------------------
75 {
76  // SL_LOG("~SLGLShader(%s)", name().c_str());
77  if (_shaderID)
78  glDeleteShader(_shaderID);
80 }
81 //-----------------------------------------------------------------------------
82 //! SLGLShader::createAndCompile creates & compiles the OpenGL shader object
83 /*!
84 @return true if compilation was successful
85 */
87 {
88  // delete if object already exits
89  if (_shaderID)
90  glDeleteShader(_shaderID);
91 
92  if (_code.empty())
93  {
94  SL_WARN_MSG("SLGLShader::createAndCompile: Nothing to compile!");
95  std::cout << "file: " << _file << std::endl;
96  return false;
97  }
98 
99  switch (_type)
100  {
101  case ST_vertex:
102  _shaderID = glCreateShader(GL_VERTEX_SHADER);
103  break;
104  case ST_geometry:
105  /*
106  * 0x8DD9 == GL_GEOMETRY_SHADER would be defined in <GLES3/gl32.h>
107  * So far this header is not included by default in Android SDK and therefore
108  * it is not guaranteed that an Android device hat OpenGL ES 3.2 and this header.
109  * The app knows the OpenGL version when it is running. App that are below OpenGL ES 3.2
110  * should not try to create geometry shaders as used for particles.
111  */
112  _shaderID = glCreateShader(0x8DD9);
113  break;
114  case ST_fragment:
115  _shaderID = glCreateShader(GL_FRAGMENT_SHADER);
116  break;
117  default:
118  SL_EXIT_MSG("SLGLShader::load: Unknown shader type.");
119  }
120  GET_GL_ERROR;
121 
122  // Build version string as the first statement
123  SLGLState* state = SLGLState::instance();
124  SLstring verGLSL = state->glSLVersionNO();
125  SLstring srcVersion = "#version " + verGLSL;
126  if (state->glIsES3()) srcVersion += " es";
127  srcVersion += "\n";
128 
130 
131  // Concatenate final code string
132  _code = srcVersion + _code;
133 
134  const char* src = _code.c_str();
135  glShaderSource(_shaderID, 1, &src, nullptr);
136  GET_GL_ERROR;
137 
138  glCompileShader(_shaderID);
139  GET_GL_ERROR;
140 
141  // Check compiler log
142  SLint compileSuccess = 0;
143  glGetShaderiv(_shaderID, GL_COMPILE_STATUS, &compileSuccess);
144  GET_GL_ERROR;
145  if (compileSuccess == GL_FALSE)
146  {
147  GLchar log[1024];
149  glGetShaderInfoLog(_shaderID, sizeof(log), nullptr, &log[0]);
150  SL_LOG("*** COMPILER ERROR ***");
151  SL_LOG("Source file: %s\n", _file.c_str());
152  SL_LOG("%s---", log);
154  SLint lineNum = 1;
155  for (string& line : lines)
156  SL_LOG("%4d: %s", lineNum++, line.c_str());
157  SL_LOG("\n");
158  return false;
159  }
160 
161 #ifndef SL_EMSCRIPTEN
162  // Write generated shader out
163  if (!_file.empty())
164  {
165 # if defined(DEBUG) || defined(_DEBUG)
166  string filename = Utils::getFileName(_file);
167  string path = Utils::getDirName(_file);
168  if (Utils::dirExists(path))
169  {
170  if (Utils::containsString(path, "generatedShaders"))
171  {
173  SL_LOG("Exported Shader : %s", filename.c_str());
174  }
175  }
176  else
177  SL_WARN_MSG("**** No path to write shader ***");
178 # else
180  {
181  string filename = Utils::getFileName(_file);
182  string path = Utils::getDirName(_file);
183  if (Utils::dirExists(path))
184  {
186  SL_LOG("Exported Shader : %s", filename.c_str());
187  }
188  else
189  SL_WARN_MSG("**** No path to write shader ***");
190  }
191 # endif
192  }
193  else
194  SL_WARN_MSG("**** No shader path and filename for shader ***");
195 #endif
196 
197  return true;
198 }
199 //-----------------------------------------------------------------------------
200 //! SLGLShader::removeComments for C/C++ comments removal from shader code
202 {
203  SLstring dst;
204  SLuint len = (SLuint)src.length();
205  SLuint i = 0;
206  SLint column = 0;
207 
208  while (i < len)
209  {
210  if (src[i] == '/' && src[i + 1] == '/')
211  {
212  if (column > 0)
213  dst += '\n';
214  while (i < len && src[i] != '\n')
215  i++;
216  i++;
217  }
218  else if (src[i] == '/' && src[i + 1] == '*')
219  {
220  while (i < len && !(src[i] == '*' && src[i + 1] == '/'))
221  {
222  if (src[i] == '\n') dst += '\n';
223  i++;
224  }
225  i += 2;
226  }
227  else
228  {
229  if (src[i] == '\n')
230  column = 0;
231  else
232  column++;
233 
234  dst += src[i++];
235  }
236  }
237  return dst;
238 }
239 //-----------------------------------------------------------------------------
240 //! Returns the shader type as string
242 {
243  switch (_type)
244  {
245  case ST_vertex: return "Vertex";
246  case ST_fragment: return "Fragment";
247  case ST_geometry: return "Geometry";
248  case ST_tesselation: return "Tesselation";
249  default: return "Unknown";
250  }
251 }
252 // ----------------------------------------------------------------------------
253 //! Replaces our custom `pragma include` directives in GLSL code
255 {
256  // Check first if #pragma exists at all
257  size_t pragmaStart = inCode.find("#pragma");
258  if (pragmaStart == string::npos)
259  return inCode;
260 
261  SLVstring codeLines = Utils::getStringLines(inCode);
262 
263  string outCode;
264 
265  for (string& line : codeLines)
266  {
267  pragmaStart = line.find("#pragma");
268  if (pragmaStart == string::npos)
269  outCode += line + '\n';
270  else
271  {
272  SLVstring pragmaParts;
273  Utils::splitString(line, ' ', pragmaParts);
274 
275  for (auto& part : pragmaParts)
276  part = Utils::trimString(part);
277 
278  if (pragmaParts[1] == "include") //................................
279  {
280  string filename = Utils::trimString(pragmaParts[2], "\"");
281  string path = Utils::getPath(_file);
282  string pathFile = path + filename;
283  if (SLFileStorage::exists(pathFile, IOK_shader))
284  {
285  string includeCode = SLFileStorage::readIntoString(pathFile, IOK_shader);
286  includeCode = removeComments(includeCode);
287  outCode += includeCode + '\n';
288  }
289  else
290  {
291  SL_LOG("SLGLShader::preprocessPragmas: File doesn't exist: %s",
292  pathFile.c_str());
293  outCode += line + '\n';
294  }
295  }
296  else
297  outCode += line + '\n';
298  }
299  }
300  return outCode;
301 }
302 // ----------------------------------------------------------------------------
303 //! Replaces our custom `pragma define` directives in GLSL code
305 {
306  // Check first if #pragma exists at all
307  size_t pragmaStart = inCode.find("#pragma");
308  if (pragmaStart == string::npos)
309  return inCode;
310 
311  SLVstring codeLines = Utils::getStringLines(inCode);
312 
313  string outCode;
314 
315  for (string& line : codeLines)
316  {
317  pragmaStart = line.find("#pragma");
318  if (pragmaStart == string::npos)
319  outCode += line + '\n';
320  else
321  {
322  SLVstring pragmaParts;
323  Utils::splitString(line, ' ', pragmaParts);
324 
325  for (auto& part : pragmaParts)
326  part = Utils::trimString(part);
327 
328  if (pragmaParts[1] == "define") //............................
329  {
330  if (pragmaParts[2] == "NUM_LIGHTS")
331  {
332  outCode += "#define NUM_LIGHTS " +
333  std::to_string(lights->size()) + "\n";
334  }
335  else
336  outCode += line + '\n';
337  } //...............................................................
338  else
339  outCode += line + '\n';
340  }
341  }
342  return outCode;
343 }
344 //-----------------------------------------------------------------------------
#define SL_LOG(...)
Definition: SL.h:233
unsigned int SLuint
Definition: SL.h:171
char SLchar
Definition: SL.h:162
#define SL_WARN_MSG(message)
Definition: SL.h:241
bool SLbool
Definition: SL.h:175
vector< SLstring > SLVstring
Definition: SL.h:201
#define SL_EXIT_MSG(message)
Definition: SL.h:240
string SLstring
Definition: SL.h:158
int SLint
Definition: SL.h:170
SLShaderType
Shader type enumeration for vertex or fragment (pixel) shader.
Definition: SLEnums.h:222
@ ST_tesselation
Definition: SLEnums.h:227
@ ST_vertex
Definition: SLEnums.h:224
@ ST_none
Definition: SLEnums.h:223
@ ST_geometry
Definition: SLEnums.h:226
@ ST_fragment
Definition: SLEnums.h:225
@ IOK_shader
Definition: SLFileStorage.h:42
const SLchar * aGLSLErrorString[]
Definition: SLGLShader.cpp:18
Singleton class for global render state.
#define GET_GL_ERROR
Definition: SLGLState.h:56
vector< SLLight * > SLVLight
STL vector of light pointers.
Definition: SLLight.h:232
SLstring preprocessIncludePragmas(SLstring inCode)
Replaces our custom pragma include directives in GLSL code.
Definition: SLGLShader.cpp:254
SLuint _shaderID
Program Object.
Definition: SLGLShader.h:55
void load(const SLstring &filename)
SLGLShader::load loads a shader file into string _shaderSource.
Definition: SLGLShader.cpp:54
SLShaderType _type
Shader type enumeration.
Definition: SLGLShader.h:54
SLstring _file
Path & filename of shader.
Definition: SLGLShader.h:57
SLstring typeName()
Returns the shader type as string.
Definition: SLGLShader.cpp:241
SLstring _code
ASCII Source-Code.
Definition: SLGLShader.h:56
SLbool createAndCompile(SLVLight *lights)
SLGLShader::createAndCompile creates & compiles the OpenGL shader object.
Definition: SLGLShader.cpp:86
void loadFromMemory(const SLstring &program)
SLGLShader::load loads a shader file from memory into memory.
Definition: SLGLShader.cpp:69
SLstring preprocessDefinePragmas(SLstring inCode, SLVLight *lights)
Replaces our custom pragma define directives in GLSL code.
Definition: SLGLShader.cpp:304
~SLGLShader() override
Definition: SLGLShader.cpp:74
static SLstring removeComments(SLstring src)
SLGLShader::removeComments for C/C++ comments removal from shader code.
Definition: SLGLShader.cpp:201
SLGLShader()
Default constructor.
Definition: SLGLShader.cpp:26
Singleton class holding all OpenGL states.
Definition: SLGLState.h:71
static SLGLState * instance()
Public static instance getter for singleton pattern.
Definition: SLGLState.h:74
SLbool glIsES3() const
Definition: SLGLState.h:136
SLstring glSLVersionNO()
Definition: SLGLState.h:133
Base class for all other classes.
Definition: SLObject.h:23
SLIOBuffer readIntoBuffer(std::string path, SLIOStreamKind kind)
Reads an entire file into memory.
bool exists(std::string path, SLIOStreamKind kind)
Checks whether a given file exists.
std::string readIntoString(std::string path, SLIOStreamKind kind)
Reads an entire file into a string.
void writeString(std::string path, SLIOStreamKind kind, const std::string &string)
Writes a string to a file.
Utils provides utilities for string & file handling, logging and math functions.
Definition: Averaged.h:22
bool dirExists(const string &path)
Returns true if a directory exists.
Definition: Utils.cpp:790
bool containsString(const string &container, const string &search)
Returns true if container contains the search string.
Definition: Utils.cpp:345
vector< string > getStringLines(const string &multiLineString)
Returns a vector of string one per line of a multiline string.
Definition: Utils.cpp:195
string getFileName(const string &pathFilename)
Returns the filename of path-filename string.
Definition: Utils.cpp:580
void splitString(const string &s, char delimiter, vector< string > &splits)
Splits an input string at a delimiter character into a string vector.
Definition: Utils.cpp:152
string getPath(const string &pathFilename)
Returns the path w. '\' of path-filename string.
Definition: Utils.cpp:392
string getDirName(const string &pathFilename)
Strip last component from file name.
Definition: Utils.cpp:598
string trimString(const string &s, const string &drop)
Trims a string at both end.
Definition: Utils.cpp:128
bool onlyErrorLogs
if this flag is set to true all calls to log get ignored
Definition: Utils.cpp:84
void log(const char *tag, const char *format,...)
logs a formatted string platform independently
Definition: Utils.cpp:1103
Utility struct that holds a pointer and its length.
Definition: SLFileStorage.h:28
void deallocate()
Deallocates the data owned by the buffer.
size_t size
Definition: SLFileStorage.h:30
unsigned char * data
Definition: SLFileStorage.h:29