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 void checkGLError(std::string_view prefix)
18 {
19  GLenum error = glGetError();
20  if (error != GL_NO_ERROR) {
21  // TODO this needs glu, but atm we don't link against glu (in windows)
22  //string err = (const char*)gluErrorString(error);
23  std::cerr << "GL error: " << prefix << ": " << int(error) << '\n';
24  assert(false);
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_RGBA, // internal format
82  width, // width
83  height, // height
84  0, // border
85  GL_RGBA, // 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 100\n";
144  if (type == GL_FRAGMENT_SHADER) {
145  source += "#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
146  " precision highp float;\n"
147  "#else\n"
148  " precision mediump float;\n"
149  "#endif\n";
150  }
151  source += header;
152  try {
153  File file(systemFileContext().resolve("shaders/" + filename));
154  auto mmap = file.mmap();
155  source.append(reinterpret_cast<const char*>(mmap.data()),
156  mmap.size());
157  } catch (FileException& e) {
158  std::cerr << "Cannot find shader: " << e.getMessage() << '\n';
159  handle = 0;
160  return;
161  }
162 
163  // Allocate shader handle.
164  handle = glCreateShader(type);
165  if (handle == 0) {
166  std::cerr << "Failed to allocate shader\n";
167  return;
168  }
169 
170  // Set shader source.
171  const char* sourcePtr = source.c_str();
172  glShaderSource(handle, 1, &sourcePtr, nullptr);
173 
174  // Compile shader and print any errors and warnings.
175  glCompileShader(handle);
176  const bool ok = isOK();
177  GLint infoLogLength = 0;
178  glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
179  // note: the null terminator is included, so empty string has length 1
180  if (!ok || (!Version::RELEASE && infoLogLength > 1)) {
181  VLA(GLchar, infoLog, infoLogLength);
182  glGetShaderInfoLog(handle, infoLogLength, nullptr, infoLog);
183  fprintf(stderr, "%s(s) compiling shader \"%s\":\n%s",
184  ok ? "Warning" : "Error", filename.c_str(),
185  infoLogLength > 1 ? infoLog : "(no details available)\n");
186  }
187 }
188 
189 Shader::~Shader()
190 {
191  glDeleteShader(handle); // ok to delete '0'
192 }
193 
194 bool Shader::isOK() const
195 {
196  if (handle == 0) return false;
197  GLint compileStatus = GL_FALSE;
198  glGetShaderiv(handle, GL_COMPILE_STATUS, &compileStatus);
199  return compileStatus == GL_TRUE;
200 }
201 
202 
203 // class VertexShader
204 
205 VertexShader::VertexShader(const string& filename)
206  : Shader(GL_VERTEX_SHADER, filename)
207 {
208 }
209 
210 VertexShader::VertexShader(const string& header, const string& filename)
211  : Shader(GL_VERTEX_SHADER, header, filename)
212 {
213 }
214 
215 
216 // class FragmentShader
217 
219  : Shader(GL_FRAGMENT_SHADER, filename)
220 {
221 }
222 
223 FragmentShader::FragmentShader(const string& header, const string& filename)
224  : Shader(GL_FRAGMENT_SHADER, header, filename)
225 {
226 }
227 
228 
229 // class ShaderProgram
230 
232 {
233  handle = glCreateProgram();
234  if (handle == 0) {
235  std::cerr << "Failed to allocate program\n";
236  }
237 }
238 
240 {
241  glDeleteProgram(handle); // ok to delete '0'
242  handle = 0;
243 }
244 
246 {
247  if (handle == 0) return false;
248  GLint linkStatus = GL_FALSE;
249  glGetProgramiv(handle, GL_LINK_STATUS, &linkStatus);
250  return linkStatus == GL_TRUE;
251 }
252 
253 void ShaderProgram::attach(const Shader& shader)
254 {
255  // Sanity check on this program.
256  if (handle == 0) return;
257 
258  // Sanity check on the shader.
259  if (!shader.isOK()) return;
260 
261  // Attach it.
262  glAttachShader(handle, shader.handle);
263 }
264 
266 {
267  // Sanity check on this program.
268  if (handle == 0) return;
269 
270  // Link the program and print any errors and warnings.
271  glLinkProgram(handle);
272  const bool ok = isOK();
273  GLint infoLogLength = 0;
274  glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
275  // note: the null terminator is included, so empty string has length 1
276  if (!ok || (!Version::RELEASE && infoLogLength > 1)) {
277  VLA(GLchar, infoLog, infoLogLength);
278  glGetProgramInfoLog(handle, infoLogLength, nullptr, infoLog);
279  fprintf(stderr, "%s(s) linking shader program:\n%s\n",
280  ok ? "Warning" : "Error",
281  infoLogLength > 1 ? infoLog : "(no details available)\n");
282  }
283 }
284 
285 void ShaderProgram::bindAttribLocation(unsigned index, const char* name)
286 {
287  glBindAttribLocation(handle, index, name);
288 }
289 
290 GLint ShaderProgram::getUniformLocation(const char* name) const
291 {
292  // Sanity check on this program.
293  if (!isOK()) return -1;
294 
295  return glGetUniformLocation(handle, name);
296 }
297 
299 {
300  glUseProgram(handle);
301 }
302 
303 // only useful for debugging
305 {
306  glValidateProgram(handle);
307  GLint validateStatus = GL_FALSE;
308  glGetProgramiv(handle, GL_VALIDATE_STATUS, &validateStatus);
309  GLint infoLogLength = 0;
310  glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
311  // note: the null terminator is included, so empty string has length 1
312  VLA(GLchar, infoLog, infoLogLength);
313  glGetProgramInfoLog(handle, infoLogLength, nullptr, infoLog);
314  std::cout << "Validate "
315  << ((validateStatus == GL_TRUE) ? "OK" : "FAIL")
316  << ": " << infoLog << '\n';
317 }
318 
319 
320 // class BufferObject
321 
323 {
324  glGenBuffers(1, &bufferId);
325 }
326 
328 {
329  glDeleteBuffers(1, &bufferId); // ok to delete 0-buffer
330 }
331 
332 } // namespace gl
FileException.hh
gl::Texture
Most basic/generic texture: only contains a texture ID.
Definition: GLUtil.hh:41
gl::ShaderProgram::reset
void reset()
Release the shader program handle.
Definition: GLUtil.cc:239
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:285
gl::ShaderProgram::activate
void activate() const
Makes this program the active shader program.
Definition: GLUtil.cc:298
gl::ShaderProgram::validate
void validate() const
Definition: GLUtil.cc:304
gl::Texture::textureId
GLuint textureId
Definition: GLUtil.hh:94
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:290
vla.hh
gl::Shader
Wrapper around an OpenGL shader: a program executed on the GPU.
Definition: GLUtil.hh:324
File.hh
gl::BufferObject::~BufferObject
~BufferObject()
Definition: GLUtil.cc:327
openmsx::filename
constexpr const char *const filename
Definition: FirmwareSwitch.cc:10
openmsx::FileException
Definition: FileException.hh:9
openmsx::InitException
Thrown when a subsystem initialisation fails.
Definition: InitException.hh:12
gl::Shader::isOK
bool isOK() const
Returns true iff this shader is loaded and compiled without errors.
Definition: GLUtil.cc:194
FileContext.hh
openmsx::YM2413Okazaki::RELEASE
@ RELEASE
Definition: YM2413Okazaki.hh:17
gl::ShaderProgram::allocate
void allocate()
Allocate a shader program handle.
Definition: GLUtil.cc:231
gl::FragmentShader::FragmentShader
FragmentShader(const std::string &filename)
Instantiates a fragment shader.
Definition: GLUtil.cc:218
openmsx::File
Definition: File.hh:16
gl::ShaderProgram::attach
void attach(const Shader &shader)
Adds a given shader to this program.
Definition: GLUtil.cc:253
InitException.hh
gl::VertexShader::VertexShader
VertexShader(const std::string &filename)
Instantiates a vertex shader.
Definition: GLUtil.cc:205
gl::BufferObject::BufferObject
BufferObject()
Definition: GLUtil.cc:322
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:265
VLA
#define VLA(TYPE, NAME, LENGTH)
Definition: vla.hh:10
gl::checkGLError
void checkGLError(std::string_view prefix)
Definition: GLUtil.cc:17
openmsx
This file implemented 3 utility functions:
Definition: Autofire.cc:5
gl::ShaderProgram::isOK
bool isOK() const
Returns true iff this program was linked without errors.
Definition: GLUtil.cc:245
openmsx::systemFileContext
FileContext systemFileContext()
Definition: FileContext.cc:152