openMSX
GLUtil.cc
Go to the documentation of this file.
1 #include "GLUtil.hh"
2 #include "File.hh"
3 #include "FileContext.hh"
4 #include "FileException.hh"
5 #include "InitException.hh"
6 #include "Math.hh"
7 #include "vla.hh"
8 #include "Version.hh"
9 #include <iostream>
10 #include <vector>
11 #include <memory>
12 #include <cstring>
13 #include <cstdio>
14 
15 #ifdef GL_VERSION_2_0
16 #ifndef glGetShaderiv
17 #warning The version of GLEW you have installed is missing \
18  some OpenGL 2.0 entry points.
19 #warning Please upgrade to GLEW 1.3.2 or higher.
20 #warning Until then, shaders are disabled.
21 #undef GL_VERSION_2_0
22 #endif
23 #endif
24 
25 using std::string;
26 
27 namespace openmsx {
28 
29 /*namespace GLUtil {
30 
31 void checkGLError(const string& prefix)
32 {
33  GLenum error = glGetError();
34  if (error != GL_NO_ERROR) {
35  string err = (char*)gluErrorString(error);
36  std::cerr << "GL error: " << prefix << ": " << err << std::endl;
37  }
38 }
39 
40 }*/
41 
42 
43 // class Texture
44 
46 {
47  glGenTextures(1, &textureId);
49 }
50 
52 {
53  glDeleteTextures(1, &textureId); // ok to delete 0-texture
54 }
55 
57 {
58  bind();
59  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
60  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
61 }
62 
64 {
65  bind();
66  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
67  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
68 }
69 
70 void Texture::setWrapMode(bool wrap)
71 {
72  bind();
73  int mode = wrap ? GL_REPEAT : GL_CLAMP;
74  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mode);
75  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mode);
76  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, mode);
77 }
78 
79 void Texture::drawRect(GLfloat tx, GLfloat ty, GLfloat twidth, GLfloat theight,
80  GLint x, GLint y, GLint width, GLint height)
81 {
82  const GLint x2 = x + width;
83  const GLint y2 = y + height;
84  const GLfloat tx2 = tx + twidth;
85  const GLfloat ty2 = ty + theight;
86  bind();
87  glEnable(GL_TEXTURE_2D);
88  glBegin(GL_QUADS);
89  glTexCoord2f(tx, ty ); glVertex2i(x , y );
90  glTexCoord2f(tx2, ty ); glVertex2i(x2, y );
91  glTexCoord2f(tx2, ty2); glVertex2i(x2, y2);
92  glTexCoord2f(tx, ty2); glVertex2i(x, y2);
93  glEnd();
94  glDisable(GL_TEXTURE_2D);
95 }
96 
97 
98 // class ColorTexture
99 
100 ColorTexture::ColorTexture(GLsizei width_, GLsizei height_)
101 {
102  resize(width_, height_);
103 }
104 
105 void ColorTexture::resize(GLsizei width_, GLsizei height_)
106 {
107  width = width_;
108  height = height_;
109  bind();
110  glTexImage2D(
111  GL_TEXTURE_2D, // target
112  0, // level
113  GL_RGBA8, // internal format
114  width, // width
115  height, // height
116  0, // border
117  GL_BGRA, // format
118  GL_UNSIGNED_BYTE, // type
119  nullptr); // data
120 }
121 
122 
123 // class LuminanceTexture
124 
125 LuminanceTexture::LuminanceTexture(GLsizei width, GLsizei height)
126 {
127  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
128  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
129  glTexImage2D(
130  GL_TEXTURE_2D, // target
131  0, // level
132  GL_LUMINANCE8, // internal format
133  width, // width
134  height, // height
135  0, // border
136  GL_LUMINANCE, // format
137  GL_UNSIGNED_BYTE, // type
138  nullptr); // data
139 }
140 
142  GLint x, GLint y, GLsizei width, GLsizei height, GLbyte* data)
143 {
144  bind();
145  glTexSubImage2D(
146  GL_TEXTURE_2D, // target
147  0, // level
148  x, // offset x
149  y, // offset y
150  width, // width
151  height, // height
152  GL_LUMINANCE, // format
153  GL_UNSIGNED_BYTE, // type
154  data); // data
155 }
156 
157 
158 // class FrameBufferObject
159 
160 static GLuint currentId = 0;
161 static std::vector<GLuint> stack;
162 
164  : bufferId(0) // 0 is not a valid openGL name
165 {
166 }
167 
169 {
170  glGenFramebuffersEXT(1, &bufferId);
171  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bufferId);
172  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
173  GL_COLOR_ATTACHMENT0_EXT,
174  GL_TEXTURE_2D, texture.textureId, 0);
175  bool success = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) ==
176  GL_FRAMEBUFFER_COMPLETE_EXT;
177  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, currentId);
178  if (!success) {
179  throw InitException(
180  "Your OpenGL implementation support for "
181  "framebuffer objects is too limited.");
182  }
183 }
184 
186 {
187  // It's ok to delete '0' (it's a NOP), but we anyway have to check
188  // for pop().
189  if (!bufferId) return;
190 
191  if (currentId == bufferId) {
192  pop();
193  }
194  glDeleteFramebuffersEXT(1, &bufferId);
195 }
196 
198 {
199  stack.push_back(currentId);
200  currentId = bufferId;
201  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, currentId);
202 }
203 
205 {
206  assert(currentId == bufferId);
207  assert(!stack.empty());
208  currentId = stack.back();
209  stack.pop_back();
210  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, currentId);
211 }
212 
213 
214 bool PixelBuffers::enabled = true;
215 
216 // Utility function used by Shader.
217 // Although this is not GL 2.0 dependent in itself, it is only used by GL 2.0
218 // specific routines.
219 
220 #ifdef GL_VERSION_2_0
221 static string readTextFile(const string& filename)
222 {
223  File file(SystemFileContext().resolve(filename));
224  size_t size;
225  const byte* data = file.mmap(size);
226  return string(reinterpret_cast<const char*>(data), size);
227 }
228 #endif
229 
230 
231 // class Shader
232 
233 #ifdef GL_VERSION_2_0
234 Shader::Shader(GLenum type, const string& filename)
235 {
236  init(type, "", filename);
237 }
238 
239 Shader::Shader(GLenum type, const string& header, const string& filename)
240 {
241  init(type, header, filename);
242 }
243 
244 void Shader::init(GLenum type, const string& header, const string& filename)
245 {
246  // Check if GL 2.0 is present on this machine.
247  if (!GLEW_VERSION_2_0) {
248  handle = 0;
249  return;
250  }
251 
252  // Load shader source.
253  string source = header;
254  try {
255  source += readTextFile("shaders/" + filename);
256  } catch (FileException& e) {
257  std::cerr << "Cannot find shader: " << e.getMessage() << std::endl;
258  handle = 0;
259  return;
260  }
261 
262  // Allocate shader handle.
263  handle = glCreateShader(type);
264  if (handle == 0) {
265  std::cerr << "Failed to allocate shader" << std::endl;
266  return;
267  }
268 
269  // Set shader source.
270  const char* sourcePtr = source.c_str();
271  glShaderSource(handle, 1, &sourcePtr, nullptr);
272 
273  // Compile shader and print any errors and warnings.
274  glCompileShader(handle);
275  const bool ok = isOK();
276  GLint infoLogLength = 0;
277  glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
278  // note: the null terminator is included, so empty string has length 1
279  if (!ok || (!Version::RELEASE && infoLogLength > 1)) {
280  VLA(GLchar, infoLog, infoLogLength);
281  glGetShaderInfoLog(handle, infoLogLength, nullptr, infoLog);
282  fprintf(stderr, "%s(s) compiling shader \"%s\":\n%s",
283  ok ? "Warning" : "Error", filename.c_str(),
284  infoLogLength > 1 ? infoLog : "(no details available)\n");
285  }
286 }
287 #else
288 Shader::Shader(GLenum /*type*/, const string& /*filename*/)
289 {
290  handle = 0;
291 }
292 #endif
293 
295 {
296 #ifdef GL_VERSION_2_0
297  if (handle != 0) {
298  glDeleteShader(handle);
299  }
300 #endif
301 }
302 
303 bool Shader::isOK() const
304 {
305 #ifdef GL_VERSION_2_0
306  if (handle == 0) {
307  return false;
308  }
309  GLint compileStatus = GL_FALSE;
310  glGetShaderiv(handle, GL_COMPILE_STATUS, &compileStatus);
311  return compileStatus == GL_TRUE;
312 #else
313  return false;
314 #endif
315 }
316 
317 
318 // class VertexShader
319 
320 #ifndef GL_VERSION_2_0
321 #ifndef GL_VERTEX_SHADER
322 #define GL_VERTEX_SHADER 0
323 #endif
324 #endif
325 VertexShader::VertexShader(const string& filename)
326  : Shader(GL_VERTEX_SHADER, filename)
327 {
328 }
329 
330 VertexShader::VertexShader(const string& header, const string& filename)
331  : Shader(GL_VERTEX_SHADER, header, filename)
332 {
333 }
334 
335 
336 // class FragmentShader
337 
338 #ifndef GL_VERSION_2_0
339 #ifndef GL_FRAGMENT_SHADER
340 #define GL_FRAGMENT_SHADER 0
341 #endif
342 #endif
343 FragmentShader::FragmentShader(const string& filename)
344  : Shader(GL_FRAGMENT_SHADER, filename)
345 {
346 }
347 
348 FragmentShader::FragmentShader(const string& header, const string& filename)
349  : Shader(GL_FRAGMENT_SHADER, header, filename)
350 {
351 }
352 
353 
354 // class ShaderProgram
355 
357 {
358 #ifdef GL_VERSION_2_0
359  // Check if GL 2.0 is present on this machine.
360  if (!GLEW_VERSION_2_0) {
361  //std::cerr << "Shaders not supported by installed OpenGL" << std::endl;
362  handle = 0;
363  return;
364  }
365 
366  // Allocate program handle.
367  handle = glCreateProgram();
368  if (handle == 0) {
369  std::cerr << "Failed to allocate program" << std::endl;
370  return;
371  }
372 #else
373  handle = 0;
374 #endif
375 }
376 
378 {
379 #ifdef GL_VERSION_2_0
380  if (handle != 0) {
381  glDeleteProgram(handle);
382  }
383 #endif
384 }
385 
387 {
388 #ifdef GL_VERSION_2_0
389  if (handle == 0) {
390  return false;
391  }
392  GLint linkStatus = GL_FALSE;
393  glGetProgramiv(handle, GL_LINK_STATUS, &linkStatus);
394  return linkStatus == GL_TRUE;
395 #else
396  return false;
397 #endif
398 }
399 
400 #ifdef GL_VERSION_2_0
401 void ShaderProgram::attach(const Shader& shader)
402 {
403  // Sanity check on this program.
404  if (handle == 0) {
405  return;
406  }
407  // Sanity check on the shader.
408  if (!shader.isOK()) {
409  return;
410  }
411  // Attach it.
412  glAttachShader(handle, shader.handle);
413 }
414 #else
415 void ShaderProgram::attach(const Shader& /*shader*/)
416 {
417 }
418 #endif
419 
421 {
422 #ifdef GL_VERSION_2_0
423  // Sanity check on this program.
424  if (handle == 0) {
425  return;
426  }
427  // Link the program and print any errors and warnings.
428  glLinkProgram(handle);
429  const bool ok = isOK();
430  GLint infoLogLength = 0;
431  glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
432  // note: the null terminator is included, so empty string has length 1
433  if (!ok || (!Version::RELEASE && infoLogLength > 1)) {
434  VLA(GLchar, infoLog, infoLogLength);
435  glGetProgramInfoLog(handle, infoLogLength, nullptr, infoLog);
436  fprintf(stderr, "%s(s) linking shader program:\n%s\n",
437  ok ? "Warning" : "Error",
438  infoLogLength > 1 ? infoLog : "(no details available)\n");
439  }
440 #endif
441 }
442 
443 #ifdef GL_VERSION_2_0
444 GLint ShaderProgram::getUniformLocation(const char* name) const
445 {
446  // Sanity check on this program.
447  if (!isOK()) {
448  return -1;
449  }
450  // Get location and verify returned value.
451  GLint location = glGetUniformLocation(handle, name);
452  if (location == -1) {
453  fprintf(stderr, "%s: \"%s\"\n",
454  strncmp(name, "gl_", 3) == 0
455  ? "Accessing built-in shader variables is not possible"
456  : "Could not find shader variable",
457  name);
458  }
459  return location;
460 }
461 #else
462 GLint ShaderProgram::getUniformLocation(const char* /*name*/) const
463 {
464  return -1;
465 }
466 #endif
467 
469 {
470 #ifdef GL_VERSION_2_0
471  if (GLEW_VERSION_2_0) {
472  glUseProgram(handle);
473  }
474 #endif
475 }
476 
478 {
479 #ifdef GL_VERSION_2_0
480  if (GLEW_VERSION_2_0) {
481  glUseProgram(0);
482  }
483 #endif
484 }
485 
486 // only useful for debugging
488 {
489  glValidateProgram(handle);
490  GLint validateStatus = GL_FALSE;
491  glGetProgramiv(handle, GL_VALIDATE_STATUS, &validateStatus);
492  GLint infoLogLength = 0;
493  glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
494  // note: the null terminator is included, so empty string has length 1
495  VLA(GLchar, infoLog, infoLogLength);
496  glGetProgramInfoLog(handle, infoLogLength, nullptr, infoLog);
497  std::cout << "Validate "
498  << ((validateStatus == GL_TRUE) ? string("OK") : string("FAIL"))
499  << ": " << infoLog << std::endl;
500 }
501 
502 } // namespace openmsx