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