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 void Shader::init(GLenum type, std::string_view header, std::string_view filename)
131 {
132  // Load shader source.
133  string source;
134  if constexpr (OPENGL_VERSION == OPENGL_ES_2_0) {
135  source += "#version 100\n";
136  if (type == GL_FRAGMENT_SHADER) {
137  source += "#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
138  " precision highp float;\n"
139  "#else\n"
140  " precision mediump float;\n"
141  "#endif\n";
142  }
143  } else {
144  source += "#version 110\n";
145  }
146  source += header;
147  try {
148  File file(systemFileContext().resolve(tmpStrCat("shaders/", filename)));
149  auto mmap = file.mmap();
150  source.append(reinterpret_cast<const char*>(mmap.data()),
151  mmap.size());
152  } catch (FileException& e) {
153  std::cerr << "Cannot find shader: " << e.getMessage() << '\n';
154  handle = 0;
155  return;
156  }
157 
158  // Allocate shader handle.
159  handle = glCreateShader(type);
160  if (handle == 0) {
161  std::cerr << "Failed to allocate shader\n";
162  return;
163  }
164 
165  // Set shader source.
166  const char* sourcePtr = source.c_str();
167  glShaderSource(handle, 1, &sourcePtr, nullptr);
168 
169  // Compile shader and print any errors and warnings.
170  glCompileShader(handle);
171  const bool ok = isOK();
172  GLint infoLogLength = 0;
173  glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
174  // note: the null terminator is included, so empty string has length 1
175  if (!ok || (!Version::RELEASE && infoLogLength > 1)) {
176  VLA(GLchar, infoLog, infoLogLength);
177  glGetShaderInfoLog(handle, infoLogLength, nullptr, infoLog);
178  std::cerr << (ok ? "Warning" : "Error") << "(s) compiling shader \""
179  << filename << "\":\n"
180  << (infoLogLength > 1 ? infoLog : "(no details available)\n");
181  }
182 }
183 
184 Shader::~Shader()
185 {
186  glDeleteShader(handle); // ok to delete '0'
187 }
188 
189 bool Shader::isOK() const
190 {
191  if (handle == 0) return false;
192  GLint compileStatus = GL_FALSE;
193  glGetShaderiv(handle, GL_COMPILE_STATUS, &compileStatus);
194  return compileStatus == GL_TRUE;
195 }
196 
197 
198 // class ShaderProgram
199 
200 void ShaderProgram::allocate()
201 {
202  handle = glCreateProgram();
203  if (handle == 0) {
204  std::cerr << "Failed to allocate program\n";
205  }
206 }
207 
208 void ShaderProgram::reset()
209 {
210  glDeleteProgram(handle); // ok to delete '0'
211  handle = 0;
212 }
213 
214 bool ShaderProgram::isOK() const
215 {
216  if (handle == 0) return false;
217  GLint linkStatus = GL_FALSE;
218  glGetProgramiv(handle, GL_LINK_STATUS, &linkStatus);
219  return linkStatus == GL_TRUE;
220 }
221 
222 void ShaderProgram::attach(const Shader& shader)
223 {
224  // Sanity check on this program.
225  if (handle == 0) return;
226 
227  // Sanity check on the shader.
228  if (!shader.isOK()) return;
229 
230  // Attach it.
231  glAttachShader(handle, shader.handle);
232 }
233 
234 void ShaderProgram::link()
235 {
236  // Sanity check on this program.
237  if (handle == 0) return;
238 
239  // Link the program and print any errors and warnings.
240  glLinkProgram(handle);
241  const bool ok = isOK();
242  GLint infoLogLength = 0;
243  glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
244  // note: the null terminator is included, so empty string has length 1
245  if (!ok || (!Version::RELEASE && infoLogLength > 1)) {
246  VLA(GLchar, infoLog, infoLogLength);
247  glGetProgramInfoLog(handle, infoLogLength, nullptr, infoLog);
248  fprintf(stderr, "%s(s) linking shader program:\n%s\n",
249  ok ? "Warning" : "Error",
250  infoLogLength > 1 ? infoLog : "(no details available)\n");
251  }
252 }
253 
254 void ShaderProgram::bindAttribLocation(unsigned index, const char* name)
255 {
256  glBindAttribLocation(handle, index, name);
257 }
258 
259 GLint ShaderProgram::getUniformLocation(const char* name) const
260 {
261  // Sanity check on this program.
262  if (!isOK()) return -1;
263 
264  return glGetUniformLocation(handle, name);
265 }
266 
267 void ShaderProgram::activate() const
268 {
269  glUseProgram(handle);
270 }
271 
272 // only useful for debugging
273 void ShaderProgram::validate() const
274 {
275  glValidateProgram(handle);
276  GLint validateStatus = GL_FALSE;
277  glGetProgramiv(handle, GL_VALIDATE_STATUS, &validateStatus);
278  GLint infoLogLength = 0;
279  glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
280  // note: the null terminator is included, so empty string has length 1
281  VLA(GLchar, infoLog, infoLogLength);
282  glGetProgramInfoLog(handle, infoLogLength, nullptr, infoLog);
283  std::cout << "Validate "
284  << ((validateStatus == GL_TRUE) ? "OK" : "FAIL")
285  << ": " << infoLog << '\n';
286 }
287 
288 
289 // class BufferObject
290 
291 BufferObject::BufferObject()
292 {
293  glGenBuffers(1, &bufferId);
294 }
295 
296 BufferObject::~BufferObject()
297 {
298  glDeleteBuffers(1, &bufferId); // ok to delete 0-buffer
299 }
300 
301 } // namespace gl
#define OPENGL_ES_2_0
Definition: GLUtil.hh:24
#define OPENGL_VERSION
Definition: GLUtil.hh:27
Wrapper around an OpenGL shader: a program executed on the GPU.
Definition: GLUtil.hh:325
bool isOK() const
Returns true iff this shader is loaded and compiled without errors.
Definition: GLUtil.cc:189
Most basic/generic texture: only contains a texture ID.
Definition: GLUtil.hh:42
GLuint textureId
Definition: GLUtil.hh:95
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:17
This file implemented 3 utility functions:
Definition: Autofire.cc:9
const FileContext & systemFileContext()
Definition: FileContext.cc:156
constexpr const char *const filename
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:659
#define VLA(TYPE, NAME, LENGTH)
Definition: vla.hh:10