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 "vla.hh"
7 #include "Version.hh"
8 #include <iostream>
9 #include <vector>
10 #include <cstring>
11 #include <cstdio>
12 
13 #ifndef glGetShaderiv
14 #error The version of GLEW you have installed is missing some OpenGL 2.0 entry points. \
15  Please upgrade to GLEW 1.3.2 or higher.
16 #endif
17 
18 using std::string;
19 
20 namespace openmsx {
21 
22 /*namespace GLUtil {
23 
24 void checkGLError(const string& prefix)
25 {
26  GLenum error = glGetError();
27  if (error != GL_NO_ERROR) {
28  string err = (char*)gluErrorString(error);
29  std::cerr << "GL error: " << prefix << ": " << err << std::endl;
30  }
31 }
32 
33 }*/
34 
35 
36 // class Texture
37 
39 {
40  glGenTextures(1, &textureId);
42 }
43 
45 {
46  glDeleteTextures(1, &textureId); // ok to delete 0-texture
47 }
48 
50 {
51  bind();
52  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
53  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
54 }
55 
57 {
58  bind();
59  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
60  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
61 }
62 
63 void Texture::setWrapMode(bool wrap)
64 {
65  bind();
66  int mode = wrap ? GL_REPEAT : GL_CLAMP;
67  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mode);
68  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mode);
69  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, mode);
70 }
71 
72 void Texture::drawRect(GLfloat tx, GLfloat ty, GLfloat twidth, GLfloat theight,
73  GLint x, GLint y, GLint width, GLint height)
74 {
75  const GLint x2 = x + width;
76  const GLint y2 = y + height;
77  const GLfloat tx2 = tx + twidth;
78  const GLfloat ty2 = ty + theight;
79  bind();
80  glEnable(GL_TEXTURE_2D);
81  glBegin(GL_QUADS);
82  glTexCoord2f(tx, ty ); glVertex2i(x , y );
83  glTexCoord2f(tx2, ty ); glVertex2i(x2, y );
84  glTexCoord2f(tx2, ty2); glVertex2i(x2, y2);
85  glTexCoord2f(tx, ty2); glVertex2i(x, y2);
86  glEnd();
87  glDisable(GL_TEXTURE_2D);
88 }
89 
90 
91 // class ColorTexture
92 
93 ColorTexture::ColorTexture(GLsizei width_, GLsizei height_)
94 {
95  resize(width_, height_);
96 }
97 
98 void ColorTexture::resize(GLsizei width_, GLsizei height_)
99 {
100  width = width_;
101  height = height_;
102  bind();
103  glTexImage2D(
104  GL_TEXTURE_2D, // target
105  0, // level
106  GL_RGBA8, // internal format
107  width, // width
108  height, // height
109  0, // border
110  GL_BGRA, // format
111  GL_UNSIGNED_BYTE, // type
112  nullptr); // data
113 }
114 
115 
116 // class LuminanceTexture
117 
118 LuminanceTexture::LuminanceTexture(GLsizei width, GLsizei height)
119 {
120  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
121  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
122  glTexImage2D(
123  GL_TEXTURE_2D, // target
124  0, // level
125  GL_LUMINANCE8, // internal format
126  width, // width
127  height, // height
128  0, // border
129  GL_LUMINANCE, // format
130  GL_UNSIGNED_BYTE, // type
131  nullptr); // data
132 }
133 
135  GLint x, GLint y, GLsizei width, GLsizei height, GLbyte* data)
136 {
137  bind();
138  glTexSubImage2D(
139  GL_TEXTURE_2D, // target
140  0, // level
141  x, // offset x
142  y, // offset y
143  width, // width
144  height, // height
145  GL_LUMINANCE, // format
146  GL_UNSIGNED_BYTE, // type
147  data); // data
148 }
149 
150 
151 // class FrameBufferObject
152 
153 static GLuint currentId = 0;
154 static std::vector<GLuint> stack;
155 
157  : bufferId(0) // 0 is not a valid openGL name
158 {
159 }
160 
162 {
163  glGenFramebuffersEXT(1, &bufferId);
164  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bufferId);
165  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
166  GL_COLOR_ATTACHMENT0_EXT,
167  GL_TEXTURE_2D, texture.textureId, 0);
168  bool success = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) ==
169  GL_FRAMEBUFFER_COMPLETE_EXT;
170  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, currentId);
171  if (!success) {
172  throw InitException(
173  "Your OpenGL implementation support for "
174  "framebuffer objects is too limited.");
175  }
176 }
177 
179 {
180  // It's ok to delete '0' (it's a NOP), but we anyway have to check
181  // for pop().
182  if (!bufferId) return;
183 
184  if (currentId == bufferId) {
185  pop();
186  }
187  glDeleteFramebuffersEXT(1, &bufferId);
188 }
189 
191 {
192  stack.push_back(currentId);
193  currentId = bufferId;
194  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, currentId);
195 }
196 
198 {
199  assert(currentId == bufferId);
200  assert(!stack.empty());
201  currentId = stack.back();
202  stack.pop_back();
203  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, currentId);
204 }
205 
206 
207 bool PixelBuffers::enabled = true;
208 
209 // Utility function used by Shader.
210 static string readTextFile(const string& filename)
211 {
212  File file(SystemFileContext().resolve(filename));
213  size_t size;
214  const byte* data = file.mmap(size);
215  return string(reinterpret_cast<const char*>(data), size);
216 }
217 
218 
219 // class Shader
220 
221 Shader::Shader(GLenum type, const string& filename)
222 {
223  init(type, "", filename);
224 }
225 
226 Shader::Shader(GLenum type, const string& header, const string& filename)
227 {
228  init(type, header, filename);
229 }
230 
231 void Shader::init(GLenum type, const string& header, const string& filename)
232 {
233  // Load shader source.
234  string source = header;
235  try {
236  source += readTextFile("shaders/" + filename);
237  } catch (FileException& e) {
238  std::cerr << "Cannot find shader: " << e.getMessage() << std::endl;
239  handle = 0;
240  return;
241  }
242 
243  // Allocate shader handle.
244  handle = glCreateShader(type);
245  if (handle == 0) {
246  std::cerr << "Failed to allocate shader" << std::endl;
247  return;
248  }
249 
250  // Set shader source.
251  const char* sourcePtr = source.c_str();
252  glShaderSource(handle, 1, &sourcePtr, nullptr);
253 
254  // Compile shader and print any errors and warnings.
255  glCompileShader(handle);
256  const bool ok = isOK();
257  GLint infoLogLength = 0;
258  glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
259  // note: the null terminator is included, so empty string has length 1
260  if (!ok || (!Version::RELEASE && infoLogLength > 1)) {
261  VLA(GLchar, infoLog, infoLogLength);
262  glGetShaderInfoLog(handle, infoLogLength, nullptr, infoLog);
263  fprintf(stderr, "%s(s) compiling shader \"%s\":\n%s",
264  ok ? "Warning" : "Error", filename.c_str(),
265  infoLogLength > 1 ? infoLog : "(no details available)\n");
266  }
267 }
268 
270 {
271  glDeleteShader(handle); // ok to delete '0'
272 }
273 
274 bool Shader::isOK() const
275 {
276  if (handle == 0) return false;
277  GLint compileStatus = GL_FALSE;
278  glGetShaderiv(handle, GL_COMPILE_STATUS, &compileStatus);
279  return compileStatus == GL_TRUE;
280 }
281 
282 
283 // class VertexShader
284 
285 VertexShader::VertexShader(const string& filename)
286  : Shader(GL_VERTEX_SHADER, filename)
287 {
288 }
289 
290 VertexShader::VertexShader(const string& header, const string& filename)
291  : Shader(GL_VERTEX_SHADER, header, filename)
292 {
293 }
294 
295 
296 // class FragmentShader
297 
298 FragmentShader::FragmentShader(const string& filename)
299  : Shader(GL_FRAGMENT_SHADER, filename)
300 {
301 }
302 
303 FragmentShader::FragmentShader(const string& header, const string& filename)
304  : Shader(GL_FRAGMENT_SHADER, header, filename)
305 {
306 }
307 
308 
309 // class ShaderProgram
310 
312 {
313  // Allocate program handle.
314  handle = glCreateProgram();
315  if (handle == 0) {
316  std::cerr << "Failed to allocate program" << std::endl;
317  return;
318  }
319 }
320 
322 {
323  glDeleteProgram(handle); // ok to delete '0'
324 }
325 
327 {
328  if (handle == 0) return false;
329  GLint linkStatus = GL_FALSE;
330  glGetProgramiv(handle, GL_LINK_STATUS, &linkStatus);
331  return linkStatus == GL_TRUE;
332 }
333 
334 void ShaderProgram::attach(const Shader& shader)
335 {
336  // Sanity check on this program.
337  if (handle == 0) return;
338 
339  // Sanity check on the shader.
340  if (!shader.isOK()) return;
341 
342  // Attach it.
343  glAttachShader(handle, shader.handle);
344 }
345 
347 {
348  // Sanity check on this program.
349  if (handle == 0) return;
350 
351  // Link the program and print any errors and warnings.
352  glLinkProgram(handle);
353  const bool ok = isOK();
354  GLint infoLogLength = 0;
355  glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
356  // note: the null terminator is included, so empty string has length 1
357  if (!ok || (!Version::RELEASE && infoLogLength > 1)) {
358  VLA(GLchar, infoLog, infoLogLength);
359  glGetProgramInfoLog(handle, infoLogLength, nullptr, infoLog);
360  fprintf(stderr, "%s(s) linking shader program:\n%s\n",
361  ok ? "Warning" : "Error",
362  infoLogLength > 1 ? infoLog : "(no details available)\n");
363  }
364 }
365 
366 GLint ShaderProgram::getUniformLocation(const char* name) const
367 {
368  // Sanity check on this program.
369  if (!isOK()) return -1;
370 
371  // Get location and verify returned value.
372  GLint location = glGetUniformLocation(handle, name);
373  if (location == -1) {
374  fprintf(stderr, "%s: \"%s\"\n",
375  strncmp(name, "gl_", 3) == 0
376  ? "Accessing built-in shader variables is not possible"
377  : "Could not find shader variable",
378  name);
379  }
380  return location;
381 }
382 
384 {
385  glUseProgram(handle);
386 }
387 
389 {
390  glUseProgram(0);
391 }
392 
393 // only useful for debugging
395 {
396  glValidateProgram(handle);
397  GLint validateStatus = GL_FALSE;
398  glGetProgramiv(handle, GL_VALIDATE_STATUS, &validateStatus);
399  GLint infoLogLength = 0;
400  glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
401  // note: the null terminator is included, so empty string has length 1
402  VLA(GLchar, infoLog, infoLogLength);
403  glGetProgramInfoLog(handle, infoLogLength, nullptr, infoLog);
404  std::cout << "Validate "
405  << ((validateStatus == GL_TRUE) ? string("OK") : string("FAIL"))
406  << ": " << infoLog << std::endl;
407 }
408 
409 } // namespace openmsx
Wrapper around an OpenGL shader: a program executed on the GPU.
Definition: GLUtil.hh:384
VertexShader(const std::string &filename)
Instantiates a vertex shader.
Definition: GLUtil.cc:285
Texture()
Default constructor, allocate a openGL texture name.
Definition: GLUtil.cc:38
unsigned char byte
8 bit unsigned integer
Definition: openmsx.hh:33
void drawRect(GLfloat tx, GLfloat ty, GLfloat twidth, GLfloat theight, GLint x, GLint y, GLint width, GLint height)
Draws this texture as a rectangle on the frame buffer.
Definition: GLUtil.cc:72
void activate() const
Makes this program the active shader program.
Definition: GLUtil.cc:383
void link()
Links all attached shaders together into one program.
Definition: GLUtil.cc:346
LuminanceTexture(LuminanceTexture &&other)
Move constructor and assignment.
Definition: GLUtil.hh:134
bool isOK() const
Returns true iff this program was linked without errors.
Definition: GLUtil.cc:326
static const bool RELEASE
Definition: Version.hh:12
GLint getUniformLocation(const char *name) const
Gets a reference to a uniform variable declared in the shader source.
Definition: GLUtil.cc:366
void disableInterpolation()
Disables bilinear interpolation for this texture and uses nearest neighbour instead.
Definition: GLUtil.cc:56
void bind()
Makes this texture the active GL texture.
Definition: GLUtil.hh:61
static void deactivate()
Deactivates all shader programs.
Definition: GLUtil.cc:388
FragmentShader(const std::string &filename)
Instantiates a fragment shader.
Definition: GLUtil.cc:298
void updateImage(GLint x, GLint y, GLsizei width, GLsizei height, GLbyte *data)
Redefines (part of) the image for this texture.
Definition: GLUtil.cc:134
void attach(const Shader &shader)
Adds a given shader to this program.
Definition: GLUtil.cc:334
ColorTexture()
Default constructor, zero-sized texture.
Definition: GLUtil.hh:96
static bool enabled
Global switch to disable pixel buffers using the "-nopbo" option.
Definition: GLUtil.hh:187
Most basic/generic texture: only contains a texture ID.
Definition: GLUtil.hh:36
Shader(GLenum type, const std::string &filename)
Instantiates a shader.
Definition: GLUtil.cc:221
void resize(GLsizei width, GLsizei height)
Definition: GLUtil.cc:98
Thrown when a subsystem initialisation fails.
bool isOK() const
Returns true iff this shader is loaded and compiled without errors.
Definition: GLUtil.cc:274
uint8_t * data()
size_t size() const
void setWrapMode(bool wrap)
Definition: GLUtil.cc:63
void enableInterpolation()
Enables bilinear interpolation for this texture.
Definition: GLUtil.cc:49
#define VLA(TYPE, NAME, LENGTH)
Definition: vla.hh:10
GLuint textureId
Definition: GLUtil.hh:82
~Texture()
Release openGL texture name.
Definition: GLUtil.cc:44