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