OXYGENHUB
◆ main openmsx / src / video / GLUtil.hh
10.6 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#ifndef GLUTIL_HH
2
#define GLUTIL_HH
3
4
// Include GLEW headers.
5
#include <GL/glew.h>
6
// Include OpenGL headers.
7
#ifdef __APPLE__
8
#include <OpenGL/gl.h>
9
#else
10
#include <GL/gl.h>
11
#endif
12
13
#include "MemBuffer.hh"
14
#include "gl_vec.hh"
15
16
#include <bit>
17
#include <cassert>
18
#include <cstdint>
19
#include <span>
20
#include <string_view>
21
22
// arbitrary but distinct values, (roughly) ordered according to version number
23
#define OPENGL_ES_2_0 1
24
#define OPENGL_2_1    2
25
#define OPENGL_3_3    3
26
#define OPENGL_VERSION OPENGL_2_1
27
28
namespace gl {
29
30
void checkGLError(std::string_view prefix);
31
32
33
// Dummy object, to be able to construct empty handler objects.
34
struct Null {};
35
36
37
/** Most basic/generic texture: only contains a texture ID.
38
  * Current implementation always assumes 2D textures.
39
  */
40
class Texture
41
{
42
public:
43
	Texture(const Texture&) = delete;
44
	Texture& operator=(const Texture&) = delete;
45
46
	/** Allocate a openGL texture name and enable/disable interpolation. */
47
	explicit Texture(bool interpolation = false, bool wrap = false);
48
49
	/** Create null-handle (not yet allocate an openGL handle). */
50
	explicit Texture(Null) : textureId(0) {}
51
52
	/** Release openGL texture name. */
53
	~Texture() { reset(); }
54
55
	/** Move constructor and assignment. */
56
	Texture(Texture&& other) noexcept
57
		: textureId(other.textureId)
58
	{
59
		other.textureId = 0; // 0 is not a valid openGL texture name
60
	}
61
	Texture& operator=(Texture&& other) noexcept {
62
		std::swap(textureId, other.textureId);
63
		return *this;
64
	}
65
66
	/** Allocate an openGL texture name. */
67
	void allocate();
68
69
	/** Release openGL texture name. */
70
	void reset();
71
72
	/** Returns the underlying openGL handler id.
73
	  * 0 iff no openGL texture is allocated.
74
	  */
75
	[[nodiscard]] GLuint get() const { return textureId; }
76
77
	/** Return as a 'void*' (needed for 'Dear ImGui').
78
	  */
79
	[[nodiscard]] unsigned long long getImGui() const {
80
		assert(textureId);
81
		return uintptr_t(textureId);
82
	}
83
84
	/** Makes this texture the active GL texture.
85
	  * The other methods of this class and its subclasses will implicitly
86
	  * bind the texture, so you only need this method to explicitly bind
87
	  * this texture for use in GL function calls outside of this class.
88
	  */
89
	void bind() const {
90
		glBindTexture(GL_TEXTURE_2D, textureId);
91
	}
92
93
	/** Enable/disable bilinear interpolation for this texture. IOW selects
94
	 * between GL_NEAREST or GL_LINEAR filtering.
95
	  */
96
	void setInterpolation(bool interpolation);
97
98
	void setWrapMode(bool wrap);
99
100
protected:
101
	GLuint textureId;
102
103
	friend class FrameBufferObject;
104
};
105
106
class ColorTexture : public Texture
107
{
108
public:
109
	/** Default constructor, zero-sized texture. */
110
	ColorTexture() = default;
111
112
	/** Create color texture with given size.
113
	  * Initial content is undefined.
114
	  */
115
	ColorTexture(GLsizei width, GLsizei height);
116
	void resize(GLsizei width, GLsizei height);
117
118
	[[nodiscard]] GLsizei getWidth () const { return width;  }
119
	[[nodiscard]] GLsizei getHeight() const { return height; }
120
	[[nodiscard]] gl::ivec2 size() const { return {width, height}; }
121
122
private:
123
	GLsizei width = 0;
124
	GLsizei height = 0;
125
};
126
127
class FrameBufferObject
128
{
129
public:
130
	FrameBufferObject() = default;
131
	explicit FrameBufferObject(Texture& texture);
132
	FrameBufferObject(FrameBufferObject&& other) noexcept
133
		: bufferId(other.bufferId)
134
	{
135
		other.bufferId = 0;
136
	}
137
	FrameBufferObject& operator=(FrameBufferObject&& other) noexcept {
138
		std::swap(bufferId, other.bufferId);
139
		return *this;
140
	}
141
	~FrameBufferObject();
142
143
	void push();
144
	void pop();
145
146
private:
147
	GLuint bufferId = 0; // 0 is not a valid openGL name
148
	GLint previousId = 0;
149
};
150
151
/** Wrapper around a pixel buffer.
152
  * The pixel buffer will be allocated in VRAM if possible, in main RAM
153
  * otherwise.
154
  * The pixel type is templatized T.
155
  *
156
  * Note: openGL ES 2.0 does not yet support this. So for now we always use the
157
  * fallback implementation, maybe we can re-enable this when we switch to
158
  * openGL ES 3.0.
159
  */
160
template<typename T> class PixelBuffer
161
{
162
public:
163
	PixelBuffer();
164
	PixelBuffer(const PixelBuffer& other) = delete;
165
	PixelBuffer(PixelBuffer&& other) noexcept;
166
	PixelBuffer& operator=(const PixelBuffer& other) = delete;
167
	PixelBuffer& operator=(PixelBuffer&& other) noexcept;
168
	~PixelBuffer();
169
170
	/** Allocate the maximum required size for this buffer.
171
	  */
172
	void allocate(GLuint size);
173
174
	/** Bind this PixelBuffer. Must be called before the methods
175
	  * getOffset() or mapWrite() are used. (Is a wrapper around
176
	  * glBindBuffer).
177
	  */
178
	void bind() const;
179
180
	/** Unbind this buffer.
181
	  */
182
	void unbind() const;
183
184
	/** Maps the contents of this buffer into memory. The returned buffer
185
	  * is write-only (reading could be very slow or even result in a
186
	  * segfault).
187
	  * @return Pointer through which you can write pixels to this buffer,
188
	  *         or 0 if the buffer could not be mapped.
189
	  * @pre This PixelBuffer must be bound (see bind()) before calling
190
	  *      this method.
191
	  */
192
	[[nodiscard]] std::span<T> mapWrite();
193
194
	/** Unmaps the contents of this buffer.
195
	  * After this call, you must no longer use the pointer returned by
196
	  * mapWrite.
197
	  */
198
	void unmap() const;
199
200
private:
201
	/** Buffer for main RAM fallback (not allocated in the normal case).
202
	  */
203
	openmsx::MemBuffer<T> allocated;
204
205
	/** Handle of the GL buffer, or 0 if no GL buffer is available.
206
	  */
207
	//GLuint bufferId;
208
};
209
210
// class PixelBuffer
211
212
template<typename T>
213
PixelBuffer<T>::PixelBuffer() = default;
214
//{
215
//	glGenBuffers(1, &bufferId);
216
//}
217
218
template<typename T>
219
PixelBuffer<T>::~PixelBuffer() = default;
220
//{
221
//	glDeleteBuffers(1, &bufferId); // ok to delete '0'
222
//}
223
224
template<typename T>
225
PixelBuffer<T>::PixelBuffer(PixelBuffer<T>&& other) noexcept
226
	: allocated(std::move(other.allocated))
227
	//, bufferId(other.bufferId)
228
{
229
	//other.bufferId = 0;
230
}
231
232
template<typename T>
233
PixelBuffer<T>& PixelBuffer<T>::operator=(PixelBuffer<T>&& other) noexcept
234
{
235
	std::swap(allocated, other.allocated);
236
	//std::swap(bufferId,  other.bufferId);
237
	return *this;
238
}
239
240
template<typename T>
241
void PixelBuffer<T>::allocate(GLuint size)
242
{
243
	//if (bufferId != 0) {
244
	//	bind();
245
	//	// TODO make performance hint configurable?
246
	//	glBufferData(GL_PIXEL_UNPACK_BUFFER,
247
	//	             size * 4,
248
	//	             nullptr, // leave data undefined
249
	//	             GL_STREAM_DRAW); // performance hint
250
	//	unbind();
251
	//} else {
252
		allocated.resize(size);
253
	//}
254
}
255
256
template<typename T>
257
void PixelBuffer<T>::bind() const
258
{
259
	//if (bufferId != 0) {
260
	//	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bufferId);
261
	//}
262
}
263
264
template<typename T>
265
void PixelBuffer<T>::unbind() const
266
{
267
	//if (bufferId != 0) {
268
	//	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
269
	//}
270
}
271
272
template<typename T>
273
std::span<T> PixelBuffer<T>::mapWrite()
274
{
275
	//if (bufferId != 0) {
276
	//	return std::bit_cast<T*>(glMapBuffer(
277
	//		GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY));
278
	//} else {
279
		return std::span{allocated};
280
	//}
281
}
282
283
template<typename T>
284
void PixelBuffer<T>::unmap() const
285
{
286
	//if (bufferId != 0) {
287
	//	glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
288
	//}
289
}
290
291
292
293
/** Wrapper around an OpenGL shader: a program executed on the GPU.
294
  * This class is a base class for vertex and fragment shaders.
295
  */
296
class Shader
297
{
298
public:
299
	Shader(const Shader&) = delete;
300
	Shader(Shader&&) = delete;
301
	Shader& operator=(const Shader&) = delete;
302
	Shader& operator=(Shader&&) = delete;
303
304
protected:
305
	/** Instantiates a shader.
306
	  * @param type The shader type: GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.
307
	  * @param filename The GLSL source code for the shader.
308
	  */
309
	Shader(GLenum type, std::string_view filename) {
310
		init(type, {}, filename);
311
	}
312
	Shader(GLenum type, std::string_view header, std::string_view filename) {
313
		init(type, header, filename);
314
	}
315
	~Shader();
316
317
private:
318
	void init(GLenum type, std::string_view header,
319
	                       std::string_view filename);
320
321
	friend class ShaderProgram;
322
323
private:
324
	GLuint handle;
325
};
326
327
/** Wrapper around an OpenGL vertex shader:
328
  * a program executed on the GPU that computes per-vertex stuff.
329
  */
330
class VertexShader : public Shader
331
{
332
public:
333
	/** Instantiates a vertex shader.
334
	  * @param filename The GLSL source code for the shader.
335
	  */
336
	explicit VertexShader(std::string_view filename)
337
		: Shader(GL_VERTEX_SHADER, filename) {}
338
	VertexShader(std::string_view header, std::string_view filename)
339
		: Shader(GL_VERTEX_SHADER, header, filename) {}
340
};
341
342
/** Wrapper around an OpenGL fragment shader:
343
  * a program executed on the GPU that computes the colors of pixels.
344
  */
345
class FragmentShader : public Shader
346
{
347
public:
348
	/** Instantiates a fragment shader.
349
	  * @param filename The GLSL source code for the shader.
350
	  */
351
	explicit FragmentShader(std::string_view filename)
352
		: Shader(GL_FRAGMENT_SHADER, filename) {}
353
	FragmentShader(std::string_view header, std::string_view filename)
354
		: Shader(GL_FRAGMENT_SHADER, header, filename) {}
355
};
356
357
/** Wrapper around an OpenGL program:
358
  * a collection of vertex and fragment shaders.
359
  */
360
class ShaderProgram
361
{
362
public:
363
	ShaderProgram(const ShaderProgram&) = delete;
364
	ShaderProgram(ShaderProgram&&) = delete;
365
	ShaderProgram& operator=(const ShaderProgram&) = delete;
366
	ShaderProgram& operator=(ShaderProgram&&) = delete;
367
368
	/** Create handler and allocate underlying openGL object. */
369
	ShaderProgram();
370
371
	/** Destroy handler object (release the underlying openGL object). */
372
	~ShaderProgram();
373
374
	/** Returns the underlying openGL handler id.
375
	  */
376
	[[nodiscard]] GLuint get() const { return handle; }
377
378
	/** Returns true iff this program was linked without errors.
379
	  * Note that this will certainly return false until link() is called.
380
	  */
381
	[[nodiscard]] bool isOK() const;
382
383
	/** Adds a given shader to this program.
384
	  */
385
	void attach(const Shader& shader);
386
387
	/** Links all attached shaders together into one program.
388
	  * This should be done before activating the program.
389
	  */
390
	void link();
391
392
	/** Bind the given name for a vertex shader attribute to the given
393
	  * location.
394
	  */
395
	void bindAttribLocation(unsigned index, const char* name);
396
397
	/** Gets a reference to a uniform variable declared in the shader source.
398
	  * Note that you have to activate this program before you can change
399
	  * the uniform variable's value.
400
	  */
401
	[[nodiscard]] GLint getUniformLocation(const char* name) const;
402
403
	/** Makes this program the active shader program.
404
	  * This requires that the program is already linked.
405
	  */
406
	void activate() const;
407
408
	void validate() const;
409
410
private:
411
	GLuint handle;
412
};
413
414
class BufferObject
415
{
416
public:
417
	BufferObject();
418
	BufferObject(const BufferObject&) = delete;
419
	BufferObject& operator=(const BufferObject&) = delete;
420
	~BufferObject();
421
422
	BufferObject(BufferObject&& other) noexcept
423
		: bufferId(other.bufferId)
424
	{
425
		other.bufferId = 0;
426
	}
427
	BufferObject& operator=(BufferObject&& other) noexcept {
428
		std::swap(bufferId, other.bufferId);
429
		return *this;
430
	}
431
432
	[[nodiscard]] GLuint get() const { return bufferId; }
433
434
private:
435
	GLuint bufferId;
436
};
437
438
} // namespace gl
439
440
#endif // GLUTIL_HH