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