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
11using namespace openmsx;
12
13namespace gl {
14
15void 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
29Texture::Texture(bool interpolation, bool wrap)
30{
31 allocate();
32 setInterpolation(interpolation);
33 setWrapMode(wrap);
34}
35
37{
38 glGenTextures(1, &textureId);
39}
40
42{
43 // Calling glDeleteTextures with a 0-texture-id is OK, it doesn't do
44 // anything. So from that point of view we don't need this test. However
45 // during initialization, before the openGL context is created, we
46 // already create some Texture(null) objects. These can also be moved
47 // around (std::move()), and then when the moved-from object gets
48 // deleted that also calls this method. That should be fine as calling
49 // glDeleteTextures() with 0 does nothing. EXCEPT that on macOS it does
50 // crash when calling an openGL function before an openGL context is
51 // created (it works fine on Linux and Windows).
52 if (textureId) {
53 glDeleteTextures(1, &textureId);
54 textureId = 0;
55 }
56}
57
58void Texture::setInterpolation(bool interpolation)
59{
60 bind();
61 int mode = interpolation ? GL_LINEAR : GL_NEAREST;
62 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mode);
63 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mode);
64}
65
66void Texture::setWrapMode(bool wrap)
67{
68 bind();
69 int mode = wrap ? GL_REPEAT : GL_CLAMP_TO_EDGE;
70 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mode);
71 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mode);
72}
73
74
75// class ColorTexture
76
77ColorTexture::ColorTexture(GLsizei width_, GLsizei height_)
78{
79 resize(width_, height_);
80}
81
82void ColorTexture::resize(GLsizei width_, GLsizei height_)
83{
84 width = width_;
85 height = height_;
86 bind();
87 glTexImage2D(
88 GL_TEXTURE_2D, // target
89 0, // level
90 GL_RGBA, // internal format
91 width, // width
92 height, // height
93 0, // border
94 GL_RGBA, // format
95 GL_UNSIGNED_BYTE, // type
96 nullptr); // data
97}
98
99
100// class FrameBufferObject
101
103{
104 glGenFramebuffers(1, &bufferId);
105 push();
106 glFramebufferTexture2D(GL_FRAMEBUFFER,
107 GL_COLOR_ATTACHMENT0,
108 GL_TEXTURE_2D, texture.textureId, 0);
109 bool success = glCheckFramebufferStatus(GL_FRAMEBUFFER) ==
110 GL_FRAMEBUFFER_COMPLETE;
111 pop();
112 if (!success) {
113 throw InitException(
114 "Your OpenGL implementation support for "
115 "framebuffer objects is too limited.");
116 }
117}
118
120{
121 pop();
122 glDeleteFramebuffers(1, &bufferId);
123}
124
126{
127 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &previousId);
128 glBindFramebuffer(GL_FRAMEBUFFER, bufferId);
129}
130
132{
133 glBindFramebuffer(GL_FRAMEBUFFER, GLuint(previousId));
134}
135
136
137// class Shader
138
139void Shader::init(GLenum type, std::string_view header, std::string_view filename)
140{
141 // Load shader source.
142 std::string source;
143 if constexpr (OPENGL_VERSION == OPENGL_ES_2_0) {
144 source += "#version 100\n";
145 if (type == GL_FRAGMENT_SHADER) {
146 source += "#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
147 " precision highp float;\n"
148 "#else\n"
149 " precision mediump float;\n"
150 "#endif\n";
151 }
152 } else {
153 source += "#version 110\n";
154 }
155 source += header;
156 try {
157 File file(systemFileContext().resolve(tmpStrCat("shaders/", filename)));
158 auto mmap = file.mmap();
159 source.append(reinterpret_cast<const char*>(mmap.data()),
160 mmap.size());
161 } catch (FileException& e) {
162 std::cerr << "Cannot find shader: " << e.getMessage() << '\n';
163 handle = 0;
164 return;
165 }
166
167 // Allocate shader handle.
168 handle = glCreateShader(type);
169 if (handle == 0) {
170 std::cerr << "Failed to allocate shader\n";
171 return;
172 }
173
174 // Set shader source.
175 const char* sourcePtr = source.c_str();
176 glShaderSource(handle, 1, &sourcePtr, nullptr);
177
178 // Compile shader and print any errors and warnings.
179 glCompileShader(handle);
180 const bool ok = isOK();
181 GLint infoLogLength = 0;
182 glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
183 // note: the null terminator is included, so empty string has length 1
184 if (!ok || (!Version::RELEASE && infoLogLength > 1)) {
185 VLA(GLchar, infoLog, infoLogLength);
186 glGetShaderInfoLog(handle, infoLogLength, nullptr, infoLog.data());
187 std::cerr << (ok ? "Warning" : "Error") << "(s) compiling shader \""
188 << filename << "\":\n"
189 << (infoLogLength > 1 ? infoLog.data() : "(no details available)\n");
190 }
191}
192
194{
195 glDeleteShader(handle); // ok to delete '0'
196}
197
198bool Shader::isOK() const
199{
200 if (handle == 0) return false;
201 GLint compileStatus = GL_FALSE;
202 glGetShaderiv(handle, GL_COMPILE_STATUS, &compileStatus);
203 return compileStatus == GL_TRUE;
204}
205
206
207// class ShaderProgram
208
210{
211 handle = glCreateProgram();
212 if (handle == 0) {
213 std::cerr << "Failed to allocate program\n";
214 }
215}
216
218{
219 // ok to delete '0', but see comment in Texture::reset()
220 if (handle) {
221 glDeleteProgram(handle);
222 handle = 0;
223 }
224}
225
227{
228 if (handle == 0) return false;
229 GLint linkStatus = GL_FALSE;
230 glGetProgramiv(handle, GL_LINK_STATUS, &linkStatus);
231 return linkStatus == GL_TRUE;
232}
233
234void ShaderProgram::attach(const Shader& shader)
235{
236 // Sanity check on this program.
237 if (handle == 0) return;
238
239 // Sanity check on the shader.
240 if (!shader.isOK()) return;
241
242 // Attach it.
243 glAttachShader(handle, shader.handle);
244}
245
247{
248 // Sanity check on this program.
249 if (handle == 0) return;
250
251 // Link the program and print any errors and warnings.
252 glLinkProgram(handle);
253 const bool ok = isOK();
254 GLint infoLogLength = 0;
255 glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
256 // note: the null terminator is included, so empty string has length 1
257 if (!ok || (!Version::RELEASE && infoLogLength > 1)) {
258 VLA(GLchar, infoLog, infoLogLength);
259 glGetProgramInfoLog(handle, infoLogLength, nullptr, infoLog.data());
260 fprintf(stderr, "%s(s) linking shader program:\n%s\n",
261 ok ? "Warning" : "Error",
262 infoLogLength > 1 ? infoLog.data() : "(no details available)\n");
263 }
264}
265
266void ShaderProgram::bindAttribLocation(unsigned index, const char* name)
267{
268 glBindAttribLocation(handle, index, name);
269}
270
271GLint ShaderProgram::getUniformLocation(const char* name) const
272{
273 // Sanity check on this program.
274 if (!isOK()) return -1;
275
276 return glGetUniformLocation(handle, name);
277}
278
280{
281 glUseProgram(handle);
282}
283
284// only useful for debugging
286{
287 glValidateProgram(handle);
288 GLint validateStatus = GL_FALSE;
289 glGetProgramiv(handle, GL_VALIDATE_STATUS, &validateStatus);
290 GLint infoLogLength = 0;
291 glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &infoLogLength);
292 // note: the null terminator is included, so empty string has length 1
293 VLA(GLchar, infoLog, infoLogLength);
294 glGetProgramInfoLog(handle, infoLogLength, nullptr, infoLog.data());
295 std::cout << "Validate "
296 << ((validateStatus == GL_TRUE) ? "OK" : "FAIL")
297 << ": " << infoLog.data() << '\n';
298}
299
300
301// class BufferObject
302
304{
305 glGenBuffers(1, &bufferId);
306}
307
309{
310 glDeleteBuffers(1, &bufferId); // ok to delete 0-buffer
311}
312
313} // namespace gl
#define OPENGL_ES_2_0
Definition GLUtil.hh:18
#define OPENGL_VERSION
Definition GLUtil.hh:21
void resize(GLsizei width, GLsizei height)
Definition GLUtil.cc:82
ColorTexture()=default
Default constructor, zero-sized texture.
FrameBufferObject()=default
void activate() const
Makes this program the active shader program.
Definition GLUtil.cc:279
bool isOK() const
Returns true iff this program was linked without errors.
Definition GLUtil.cc:226
void attach(const Shader &shader)
Adds a given shader to this program.
Definition GLUtil.cc:234
void validate() const
Definition GLUtil.cc:285
void allocate()
Allocate a shader program handle.
Definition GLUtil.cc:209
void reset()
Release the shader program handle.
Definition GLUtil.cc:217
void link()
Links all attached shaders together into one program.
Definition GLUtil.cc:246
void bindAttribLocation(unsigned index, const char *name)
Bind the given name for a vertex shader attribute to the given location.
Definition GLUtil.cc:266
GLint getUniformLocation(const char *name) const
Gets a reference to a uniform variable declared in the shader source.
Definition GLUtil.cc:271
Wrapper around an OpenGL shader: a program executed on the GPU.
Definition GLUtil.hh:319
bool isOK() const
Returns true iff this shader is loaded and compiled without errors.
Definition GLUtil.cc:198
Most basic/generic texture: only contains a texture ID.
Definition GLUtil.hh:36
void reset()
Release openGL texture name.
Definition GLUtil.cc:41
Texture(const Texture &)=delete
void setWrapMode(bool wrap)
Definition GLUtil.cc:66
void setInterpolation(bool interpolation)
Enable/disable bilinear interpolation for this texture.
Definition GLUtil.cc:58
void bind()
Makes this texture the active GL texture.
Definition GLUtil.hh:77
void allocate()
Allocate an openGL texture name.
Definition GLUtil.cc:36
GLuint textureId
Definition GLUtil.hh:89
Thrown when a subsystem initialisation fails.
static const bool RELEASE
Definition Version.hh:12
constexpr double e
Definition Math.hh:21
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()
TemporaryString tmpStrCat(Ts &&... ts)
Definition strCat.hh:742
#define VLA(TYPE, NAME, LENGTH)
Definition vla.hh:12