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 using namespace openmsx;
20 
21 namespace gl {
22 
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 // class Texture
36 
37 Texture::Texture(bool interpolation, bool wrap)
38 {
39  allocate();
40  setInterpolation(interpolation);
41  setWrapMode(wrap);
42 }
43 
44 void Texture::allocate()
45 {
46  glGenTextures(1, &textureId);
47 }
48 
49 void Texture::reset()
50 {
51  glDeleteTextures(1, &textureId); // ok to delete 0-texture
52  textureId = 0;
53 }
54 
55 void Texture::setInterpolation(bool interpolation)
56 {
57  bind();
58  int mode = interpolation ? GL_LINEAR : GL_NEAREST;
59  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mode);
60  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mode);
61 }
62 
63 void Texture::setWrapMode(bool wrap)
64 {
65  bind();
66  int mode = wrap ? GL_REPEAT : GL_CLAMP_TO_EDGE;
67  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mode);
68  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mode);
69 }
70 
71 
72 // class ColorTexture
73 
74 ColorTexture::ColorTexture(GLsizei width_, GLsizei height_)
75 {
76  resize(width_, height_);
77 }
78 
79 void ColorTexture::resize(GLsizei width_, GLsizei height_)
80 {
81  width = width_;
82  height = height_;
83  bind();
84  glTexImage2D(
85  GL_TEXTURE_2D, // target
86  0, // level
87  GL_RGBA8, // internal format
88  width, // width
89  height, // height
90  0, // border
91  GL_BGRA, // format
92  GL_UNSIGNED_BYTE, // type
93  nullptr); // data
94 }
95 
96 
97 // class FrameBufferObject
98 
99 static GLuint currentId = 0;
100 static std::vector<GLuint> stack;
101 
102 FrameBufferObject::FrameBufferObject()
103  : bufferId(0) // 0 is not a valid openGL name
104 {
105 }
106 
108 {
109  glGenFramebuffersEXT(1, &bufferId);
110  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bufferId);
111  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
112  GL_COLOR_ATTACHMENT0_EXT,
113  GL_TEXTURE_2D, texture.textureId, 0);
114  bool success = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) ==
115  GL_FRAMEBUFFER_COMPLETE_EXT;
116  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, currentId);
117  if (!success) {
118  throw InitException(
119  "Your OpenGL implementation support for "
120  "framebuffer objects is too limited.");
121  }
122 }
123 
125 {
126  // It's ok to delete '0' (it's a NOP), but we anyway have to check
127  // for pop().
128  if (!bufferId) return;
129 
130  if (currentId == bufferId) {
131  pop();
132  }
133  glDeleteFramebuffersEXT(1, &bufferId);
134 }
135 
137 {
138  stack.push_back(currentId);
139  currentId = bufferId;
140  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, currentId);
141 }
142 
144 {
145  assert(currentId == bufferId);
146  assert(!stack.empty());
147  currentId = stack.back();
148  stack.pop_back();
149  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, currentId);
150 }
151 
152 
153 bool PixelBuffers::enabled = true;
154 
155 // Utility function used by Shader.
156 static string readTextFile(const string& filename)
157 {
158  File file(SystemFileContext().resolve(filename));
159  size_t size;
160  const byte* data = file.mmap(size);
161  return string(reinterpret_cast<const char*>(data), size);
162 }
163 
164 
165 // class Shader
166 
167 Shader::Shader(GLenum type, const string& filename)
168 {
169  init(type, "", filename);
170 }
171 
172 Shader::Shader(GLenum type, const string& header, const string& filename)
173 {
174  init(type, header, filename);
175 }
176 
177 void Shader::init(GLenum type, const string& header, const string& filename)
178 {
179  // Load shader source.
180  string source = header;
181  try {
182  source += readTextFile("shaders/" + filename);
183  } catch (FileException& e) {
184  std::cerr << "Cannot find shader: " << e.getMessage() << std::endl;
185  handle = 0;
186  return;
187  }
188 
189  // Allocate shader handle.
190  handle = glCreateShader(type);
191  if (handle == 0) {
192  std::cerr << "Failed to allocate shader" << std::endl;
193  return;
194  }
195 
196  // Set shader source.
197  const char* sourcePtr = source.c_str();
198  glShaderSource(handle, 1, &sourcePtr, nullptr);
199 
200  // Compile shader and print any errors and warnings.
201  glCompileShader(handle);
202  const bool ok = isOK();
203  GLint infoLogLength = 0;
204  glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
205  // note: the null terminator is included, so empty string has length 1
206  if (!ok || (!Version::RELEASE && infoLogLength > 1)) {
207  VLA(GLchar, infoLog, infoLogLength);
208  glGetShaderInfoLog(handle, infoLogLength, nullptr, infoLog);
209  fprintf(stderr, "%s(s) compiling shader \"%s\":\n%s",
210  ok ? "Warning" : "Error", filename.c_str(),
211  infoLogLength > 1 ? infoLog : "(no details available)\n");
212  }
213 }
214 
216 {
217  glDeleteShader(handle); // ok to delete '0'
218 }
219 
220 bool Shader::isOK() const
221 {
222  if (handle == 0) return false;
223  GLint compileStatus = GL_FALSE;
224  glGetShaderiv(handle, GL_COMPILE_STATUS, &compileStatus);
225  return compileStatus == GL_TRUE;
226 }
227 
228 
229 // class VertexShader
230 
231 VertexShader::VertexShader(const string& filename)
232  : Shader(GL_VERTEX_SHADER, filename)
233 {
234 }
235 
236 VertexShader::VertexShader(const string& header, const string& filename)
237  : Shader(GL_VERTEX_SHADER, header, filename)
238 {
239 }
240 
241 
242 // class FragmentShader
243 
244 FragmentShader::FragmentShader(const string& filename)
245  : Shader(GL_FRAGMENT_SHADER, filename)
246 {
247 }
248 
249 FragmentShader::FragmentShader(const string& header, const string& filename)
250  : Shader(GL_FRAGMENT_SHADER, header, filename)
251 {
252 }
253 
254 
255 // class ShaderProgram
256 
258 {
259  handle = glCreateProgram();
260  if (handle == 0) {
261  std::cerr << "Failed to allocate program" << std::endl;
262  }
263 }
264 
266 {
267  glDeleteProgram(handle); // ok to delete '0'
268  handle = 0;
269 }
270 
272 {
273  if (handle == 0) return false;
274  GLint linkStatus = GL_FALSE;
275  glGetProgramiv(handle, GL_LINK_STATUS, &linkStatus);
276  return linkStatus == GL_TRUE;
277 }
278 
279 void ShaderProgram::attach(const Shader& shader)
280 {
281  // Sanity check on this program.
282  if (handle == 0) return;
283 
284  // Sanity check on the shader.
285  if (!shader.isOK()) return;
286 
287  // Attach it.
288  glAttachShader(handle, shader.handle);
289 }
290 
292 {
293  // Sanity check on this program.
294  if (handle == 0) return;
295 
296  // Link the program and print any errors and warnings.
297  glLinkProgram(handle);
298  const bool ok = isOK();
299  GLint infoLogLength = 0;
300  glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
301  // note: the null terminator is included, so empty string has length 1
302  if (!ok || (!Version::RELEASE && infoLogLength > 1)) {
303  VLA(GLchar, infoLog, infoLogLength);
304  glGetProgramInfoLog(handle, infoLogLength, nullptr, infoLog);
305  fprintf(stderr, "%s(s) linking shader program:\n%s\n",
306  ok ? "Warning" : "Error",
307  infoLogLength > 1 ? infoLog : "(no details available)\n");
308  }
309 }
310 
311 void ShaderProgram::bindAttribLocation(unsigned index, const char* name)
312 {
313  glBindAttribLocation(handle, index, name);
314 }
315 
316 GLint ShaderProgram::getUniformLocation(const char* name) const
317 {
318  // Sanity check on this program.
319  if (!isOK()) return -1;
320 
321  return glGetUniformLocation(handle, name);
322 }
323 
325 {
326  glUseProgram(handle);
327 }
328 
329 // only useful for debugging
331 {
332  glValidateProgram(handle);
333  GLint validateStatus = GL_FALSE;
334  glGetProgramiv(handle, GL_VALIDATE_STATUS, &validateStatus);
335  GLint infoLogLength = 0;
336  glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
337  // note: the null terminator is included, so empty string has length 1
338  VLA(GLchar, infoLog, infoLogLength);
339  glGetProgramInfoLog(handle, infoLogLength, nullptr, infoLog);
340  std::cout << "Validate "
341  << ((validateStatus == GL_TRUE) ? string("OK") : string("FAIL"))
342  << ": " << infoLog << std::endl;
343 }
344 
345 
346 // class BufferObject
347 
349 {
350  glGenBuffers(1, &bufferId);
351 }
352 
354 {
355  glDeleteBuffers(1, &bufferId); // ok to delete 0-buffer
356 }
357 
358 } // namespace gl
Shader(GLenum type, const std::string &filename)
Instantiates a shader.
Definition: GLUtil.cc:167
void allocate()
Allocate a shader program handle.
Definition: GLUtil.cc:257
void activate() const
Makes this program the active shader program.
Definition: GLUtil.cc:324
Most basic/generic texture: only contains a texture ID.
Definition: GLUtil.hh:36
unsigned char byte
8 bit unsigned integer
Definition: openmsx.hh:33
void validate()
Definition: GLUtil.cc:330
FragmentShader(const std::string &filename)
Instantiates a fragment shader.
Definition: GLUtil.cc:244
bool isOK() const
Returns true iff this shader is loaded and compiled without errors.
Definition: GLUtil.cc:220
void attach(const Shader &shader)
Adds a given shader to this program.
Definition: GLUtil.cc:279
const std::string & getMessage() const
Definition: MSXException.hh:14
static bool enabled
Global switch to disable pixel buffers using the "-nopbo" option.
Definition: GLUtil.hh:162
GLint getUniformLocation(const char *name) const
Gets a reference to a uniform variable declared in the shader source.
Definition: GLUtil.cc:316
void bindAttribLocation(unsigned index, const char *name)
Bind the given name for a vertex shader attribute to the given location.
Definition: GLUtil.cc:311
Thrown when a subsystem initialisation fails.
void link()
Links all attached shaders together into one program.
Definition: GLUtil.cc:291
uint8_t * data()
bool isOK() const
Returns true iff this program was linked without errors.
Definition: GLUtil.cc:271
size_t size() const
GLuint textureId
Definition: GLUtil.hh:87
VertexShader(const std::string &filename)
Instantiates a vertex shader.
Definition: GLUtil.cc:231
#define VLA(TYPE, NAME, LENGTH)
Definition: vla.hh:10
void reset()
Release the shader program handle.
Definition: GLUtil.cc:265
Wrapper around an OpenGL shader: a program executed on the GPU.
Definition: GLUtil.hh:359