GLVis  v4.2
Accurate and flexible finite element visualization
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
shader.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-443271.
4 //
5 // This file is part of the GLVis visualization tool and library. For more
6 // information and source code availability see https://glvis.org.
7 //
8 // GLVis is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 #include "shader.hpp"
13 #include <regex>
14 
15 namespace gl3
16 {
17 
18 bool ShaderProgram::create(std::string vertexShader,
19  std::string fragmentShader,
20  std::unordered_map<int, std::string> inAttributes,
21  int numOutputs)
22 {
23  attrib_idx = inAttributes;
24  num_outputs = numOutputs;
25  is_compiled = false;
26  GetGLSLVersion();
27 
28  std::string fmtVS = formatShader(vertexShader, GL_VERTEX_SHADER);
29  vertex_shader = compileShader(fmtVS, GL_VERTEX_SHADER);
30  if (vertex_shader == 0)
31  {
32  return false;
33  }
34 
35  std::string fmtFS = formatShader(fragmentShader, GL_FRAGMENT_SHADER);
36  fragment_shader = compileShader(fmtFS, GL_FRAGMENT_SHADER);
37  if (fragment_shader == 0)
38  {
39  return false;
40  }
41 
42  if (!linkShaders({vertex_shader, fragment_shader}))
43  {
44  std::cerr << "Failed to link shaders for program." << std::endl;
45  return false;
46  }
47 
48  mapShaderUniforms();
49 
50  is_compiled = true;
51  return is_compiled;
52 }
53 
54 int ShaderProgram::glsl_version = -1;
55 #ifndef __EMSCRIPTEN__
56 const bool ShaderProgram::glsl_is_es = false;
57 #else
58 const bool ShaderProgram::glsl_is_es = true;
59 #endif
60 
61 void ShaderProgram::GetGLSLVersion()
62 {
63  if (glsl_version == -1)
64  {
65  std::string verStr = (char*)glGetString(GL_VERSION);
66  int ver_major, ver_minor;
67  int vs_idx = verStr.find_first_of(".");
68  ver_major = std::stoi(verStr.substr(vs_idx - 1, vs_idx));
69  ver_minor = std::stoi(verStr.substr(vs_idx + 1, 1));
70  int opengl_ver = ver_major * 100 + ver_minor * 10;
71 
72 #ifndef __EMSCRIPTEN__
73  // The GLSL version is the same as the OpenGL version when the OpenGL
74  // version is >= 3.30, otherwise it is:
75  //
76  // GL Version | GLSL Version
77  // -------------------------
78  // 2.0 | 1.10
79  // 2.1 | 1.20
80  // 3.0 | 1.30
81  // 3.1 | 1.40
82  // 3.2 | 1.50
83 
84  if (opengl_ver < 330)
85  {
86  if (ver_major == 2)
87  {
88  glsl_version = opengl_ver - 90;
89  }
90  else if (ver_major == 3)
91  {
92  glsl_version = opengl_ver - 170;
93  }
94  else
95  {
96  std::cerr << "fatal: unsupported OpenGL version " << opengl_ver << std::endl;
97  glsl_version = 100;
98  }
99  }
100  else
101  {
102  glsl_version = opengl_ver;
103  }
104 #else
105  // GL Version | WebGL Version | GLSL Version
106  // 2.0 | 1.0 | 1.00 ES
107  // 3.0 | 2.0 | 3.00 ES
108  // 3.1 | | 3.10 ES
109  if (opengl_ver < 300)
110  {
111  glsl_version = 100;
112  }
113  else
114  {
115  glsl_version = 300;
116  }
117 #endif
118  std::cerr << "Using GLSL " << glsl_version;
119  if (glsl_is_es) { std::cerr << " ES"; }
120  std::cerr << std::endl;
121  }
122 }
123 
124 std::string ShaderProgram::formatShader(const std::string& inShader,
125  GLenum shaderType)
126 {
127  std::string formatted = inShader;
128 
129  // replace some identifiers depending on the version of glsl we're using
130  if (glsl_version >= 130)
131  {
132  if (shaderType == GL_VERTEX_SHADER)
133  {
134  formatted = std::regex_replace(formatted, std::regex("attribute"), "in");
135  formatted = std::regex_replace(formatted, std::regex("varying"), "out");
136  }
137  else if (shaderType == GL_FRAGMENT_SHADER)
138  {
139  formatted = std::regex_replace(formatted, std::regex("varying"), "in");
140  for (int i = 0; i < num_outputs; i++)
141  {
142  std::string indexString = "gl_FragData[";
143  indexString += std::to_string(i) + "]";
144  std::string outputString = "out vec4 fragColor_";
145  outputString += std::to_string(i) + ";\n";
146  if (glsl_version >= 300)
147  {
148  // GLSL/OpenGL 3.30+ or WebGL 2 (GLSL 3.00 ES):
149  // Prefer in-shader output index setting.
150  std::string layoutString = "layout(location = ";
151  layoutString += std::to_string(i) + ") ";
152  formatted = layoutString + outputString + formatted;
153  }
154  else
155  {
156  // GLSL 1.30-1.50 (OpenGL 3.0-3.2):
157  // No in-shader output indexing.
158  // Output locations will be set using glBindFragDataLocation.
159  formatted = outputString + formatted;
160  }
161  std::string indexStringRgx = "gl_FragData\\[";
162  indexStringRgx += std::to_string(i) + "\\]";
163  formatted = std::regex_replace(formatted, std::regex(indexStringRgx),
164  "fragColor_" + std::to_string(i));
165  if (i == 0)
166  {
167  formatted = std::regex_replace(formatted, std::regex("gl_FragColor"),
168  "fragColor_0");
169  }
170  }
171  }
172  else
173  {
174  std::cerr << "buildShaderString: unknown shader type" << std::endl;
175  return {};
176  }
177 
178  formatted = std::regex_replace(formatted, std::regex("texture2D"), "texture");
179  }
180 
182  {
183  formatted = "#define USE_ALPHA\n" + formatted;
184  }
185 
186  if (glsl_is_es)
187  {
188  if (shaderType == GL_FRAGMENT_SHADER)
189  {
190  // Add precision specifier - required for WebGL fragment shaders
191  formatted = "precision mediump float;\n" + formatted;
192  }
193  if (num_outputs > 1 && glsl_version == 100)
194  {
195  // Enable WEBGL_draw_buffers in the shader
196  formatted = "#extension GL_EXT_draw_buffers : require\n" + formatted;
197  }
198  if (glsl_version == 300)
199  {
200  // WebGL 2 shaders require explicit version setting
201  formatted = "#version 300 es\n" + formatted;
202  }
203  }
204  else
205  {
206  // Append version setting for all desktop shaders
207  formatted = std::regex_replace("#version GLSL_VER\n", std::regex("GLSL_VER"),
208  std::to_string(glsl_version)) + formatted;
209  }
210 
211  return formatted;
212 }
213 
214 GLuint ShaderProgram::compileShader(const std::string& inShader,
215  GLenum shaderType)
216 {
217  int shader_len = inShader.length();
218  const char *shader_cstr = inShader.c_str();
219 
220  GLuint shader = glCreateShader(shaderType);
221  glShaderSource(shader, 1, &shader_cstr, &shader_len);
222  glCompileShader(shader);
223 
224  GLint stat;
225  glGetShaderiv(shader, GL_COMPILE_STATUS, &stat);
226  // glGetObjectParameteriv
227  if (stat == GL_FALSE)
228  {
229  std::cerr << "failed to compile shader" << std::endl;
230  int err_len;
231  glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &err_len);
232  char *error_text = new char[err_len];
233  glGetShaderInfoLog(shader, err_len, &err_len, error_text);
234  std::cerr << error_text << std::endl;
235  delete[] error_text;
236  return 0;
237  }
238  return shader;
239 }
240 
241 bool ShaderProgram::linkShaders(const std::vector<GLuint>& shaders)
242 {
243  // Bind all incoming attributes to their VAO indices.
244  for (auto attrib_pair : attrib_idx)
245  {
246  glBindAttribLocation(program_id, attrib_pair.first,
247  attrib_pair.second.c_str());
248  }
249 
250 #ifndef __EMSCRIPTEN__
251  if (glsl_version >= 130 && glsl_version < 300)
252  {
253  // Bind fragment output variables to MRT indices.
254  for (int i = 0; i < num_outputs; i++)
255  {
256  std::string fragOutVar = "fragColor_" + std::to_string(i);
257  glBindFragDataLocation(program_id, i, fragOutVar.c_str());
258  }
259  }
260 #endif
261 
262  for (GLuint i : shaders)
263  {
264  glAttachShader(program_id, i);
265  }
266  glLinkProgram(program_id);
267 
268  GLint stat;
269  glGetProgramiv(program_id, GL_LINK_STATUS, &stat);
270  if (stat == GL_FALSE)
271  {
272  cerr << "fatal: Shader linking failed" << endl;
273  }
274  return (stat == GL_TRUE);
275 }
276 
277 void ShaderProgram::mapShaderUniforms()
278 {
279  int num_unifs;
280  glGetProgramiv(program_id, GL_ACTIVE_UNIFORMS, &num_unifs);
281  int max_unif_len;
282  glGetProgramiv(program_id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_unif_len);
283 
284  for (int i = 0; i < num_unifs; i++)
285  {
286  vector<char> unif_buf(max_unif_len+1);
287  GLsizei name_length;
288  GLint gl_size;
289  GLenum gl_type;
290  glGetActiveUniform(program_id, i, max_unif_len, &name_length, &gl_size,
291  &gl_type,
292  unif_buf.data());
293  std::string unif_name(unif_buf.data(), name_length);
294  GLuint location = glGetUniformLocation(program_id, unif_name.c_str());
295  uniform_idx[unif_name] = location;
296  }
297 }
298 
299 }
bool create(std::string vertexShader, std::string fragmentShader, std::unordered_map< int, std::string > inAttributes, int numOutputs)
Definition: shader.cpp:18
static bool useLegacyTextureFmts()
Definition: renderer.cpp:27