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