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