1/**
2 * Copyright (c) 2006-2023 LOVE Development Team
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software
14 * in a product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 * misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 **/
20
21// LOVE
22#include "common/config.h"
23#include "OpenGL.h"
24
25#include "Shader.h"
26#include "Canvas.h"
27#include "common/Exception.h"
28
29#include "graphics/Graphics.h"
30#include "graphics/Buffer.h"
31
32// C++
33#include <algorithm>
34#include <limits>
35
36// C
37#include <cstring>
38#include <cstdio>
39
40// For SDL_GL_GetProcAddress.
41#include <SDL_video.h>
42
43#ifdef LOVE_IOS
44#include <SDL_syswm.h>
45#endif
46
47#ifdef LOVE_ANDROID
48#include <dlfcn.h>
49#endif
50
51namespace love
52{
53namespace graphics
54{
55namespace opengl
56{
57
58static void *LOVEGetProcAddress(const char *name)
59{
60#ifdef LOVE_ANDROID
61 void *proc = dlsym(RTLD_DEFAULT, name);
62 if (proc)
63 return proc;
64#endif
65
66 return SDL_GL_GetProcAddress(name);
67}
68
69OpenGL::TempDebugGroup::TempDebugGroup(const char *name)
70{
71 if (isDebugEnabled())
72 {
73 if (GLAD_VERSION_4_3 || (GLAD_KHR_debug && !GLAD_ES_VERSION_2_0))
74 glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, 0, (const GLchar *) name);
75 else if (GLAD_ES_VERSION_2_0 && GLAD_KHR_debug)
76 glPushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION, 0, 0, (const GLchar *) name);
77 else if (GLAD_EXT_debug_marker)
78 glPushGroupMarkerEXT(0, (const GLchar *) name);
79 }
80}
81
82OpenGL::TempDebugGroup::~TempDebugGroup()
83{
84 if (isDebugEnabled())
85 {
86 if (GLAD_VERSION_4_3 || (GLAD_KHR_debug && !GLAD_ES_VERSION_2_0))
87 glPopDebugGroup();
88 else if (GLAD_ES_VERSION_2_0 && GLAD_KHR_debug)
89 glPopDebugGroupKHR();
90 else if (GLAD_EXT_debug_marker)
91 glPopGroupMarkerEXT();
92 }
93}
94
95OpenGL::OpenGL()
96 : stats()
97 , contextInitialized(false)
98 , pixelShaderHighpSupported(false)
99 , baseVertexSupported(false)
100 , maxAnisotropy(1.0f)
101 , max2DTextureSize(0)
102 , max3DTextureSize(0)
103 , maxCubeTextureSize(0)
104 , maxTextureArrayLayers(0)
105 , maxRenderTargets(1)
106 , maxRenderbufferSamples(0)
107 , maxTextureUnits(1)
108 , maxPointSize(1)
109 , coreProfile(false)
110 , vendor(VENDOR_UNKNOWN)
111 , state()
112{
113 state.constantColor = Colorf(1.0f, 1.0f, 1.0f, 1.0f);
114
115 float nan = std::numeric_limits<float>::quiet_NaN();
116 state.lastConstantColor = Colorf(nan, nan, nan, nan);
117}
118
119bool OpenGL::initContext()
120{
121 if (contextInitialized)
122 return true;
123
124 if (!gladLoadGLLoader(LOVEGetProcAddress))
125 return false;
126
127 initVendor();
128
129 bugs = {};
130
131 if (GLAD_ES_VERSION_3_0 && !GLAD_ES_VERSION_3_1)
132 {
133 const char *device = (const char *) glGetString(GL_RENDERER);
134 if (getVendor() == VENDOR_VIVANTE && strstr(device, "Vivante GC7000UL"))
135 bugs.brokenGLES3 = true;
136 }
137
138 if (bugs.brokenGLES3)
139 GLAD_ES_VERSION_3_0 = false;
140
141 if (GLAD_VERSION_3_2)
142 {
143 GLint profileMask = 0;
144 glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profileMask);
145 coreProfile = (profileMask & GL_CONTEXT_CORE_PROFILE_BIT);
146 }
147 else
148 coreProfile = false;
149
150 initOpenGLFunctions();
151
152#if defined(LOVE_WINDOWS) || defined(LOVE_LINUX)
153 // See the comments in OpenGL.h.
154 if (getVendor() == VENDOR_AMD)
155 {
156 bugs.clearRequiresDriverTextureStateUpdate = true;
157 if (!gl.isCoreProfile() && !GLAD_ES_VERSION_2_0)
158 bugs.generateMipmapsRequiresTexture2DEnable = true;
159 }
160#endif
161
162#ifdef LOVE_WINDOWS
163 if (getVendor() == VENDOR_INTEL && gl.isCoreProfile())
164 {
165 const char *device = (const char *) glGetString(GL_RENDERER);
166 if (strstr(device, "HD Graphics 4000") || strstr(device, "HD Graphics 2500"))
167 bugs.clientWaitSyncStalls = true;
168 }
169
170 if (getVendor() == VENDOR_INTEL)
171 {
172 const char *device = (const char *) glGetString(GL_RENDERER);
173 if (strstr(device, "HD Graphics 3000") || strstr(device, "HD Graphics 2000")
174 || !strcmp(device, "Intel(R) HD Graphics") || !strcmp(device, "Intel(R) HD Graphics Family"))
175 {
176 bugs.brokenSRGB = true;
177 }
178 }
179#endif
180
181#ifdef LOVE_WINDOWS
182 if (getVendor() == VENDOR_AMD)
183 {
184 // Radeon drivers switched from "ATI Radeon" to "AMD Radeon" around
185 // the 7000 series. We'll assume this bug doesn't affect those newer
186 // GPUs / drivers.
187 const char *device = (const char *) glGetString(GL_RENDERER);
188 if (strstr(device, "ATI Radeon") || strstr(device, "ATI Mobility Radeon"))
189 bugs.texStorageBreaksSubImage = true;
190 }
191#endif
192
193 contextInitialized = true;
194
195 return true;
196}
197
198void OpenGL::setupContext()
199{
200 if (!contextInitialized)
201 return;
202
203 initMaxValues();
204
205 GLfloat glcolor[4] = {1.0f, 1.0f, 1.0f, 1.0f};
206 glVertexAttrib4fv(ATTRIB_COLOR, glcolor);
207 glVertexAttrib4fv(ATTRIB_CONSTANTCOLOR, glcolor);
208
209 GLint maxvertexattribs = 1;
210 glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxvertexattribs);
211
212 state.enabledAttribArrays = (uint32) ((1ull << uint32(maxvertexattribs)) - 1);
213 state.instancedAttribArrays = 0;
214
215 setVertexAttributes(vertex::Attributes(), vertex::BufferBindings());
216
217 // Get the current viewport.
218 glGetIntegerv(GL_VIEWPORT, (GLint *) &state.viewport.x);
219
220 // And the current scissor - but we need to compensate for GL scissors
221 // starting at the bottom left instead of top left.
222 glGetIntegerv(GL_SCISSOR_BOX, (GLint *) &state.scissor.x);
223 state.scissor.y = state.viewport.h - (state.scissor.y + state.scissor.h);
224
225 if (GLAD_VERSION_1_0)
226 glGetFloatv(GL_POINT_SIZE, &state.pointSize);
227 else
228 state.pointSize = 1.0f;
229
230 for (int i = 0; i < 2; i++)
231 state.boundFramebuffers[i] = std::numeric_limits<GLuint>::max();
232 bindFramebuffer(FRAMEBUFFER_ALL, getDefaultFBO());
233
234 setEnableState(ENABLE_DEPTH_TEST, state.enableState[ENABLE_DEPTH_TEST]);
235 setEnableState(ENABLE_STENCIL_TEST, state.enableState[ENABLE_STENCIL_TEST]);
236 setEnableState(ENABLE_SCISSOR_TEST, state.enableState[ENABLE_SCISSOR_TEST]);
237 setEnableState(ENABLE_FACE_CULL, state.enableState[ENABLE_FACE_CULL]);
238
239 if (!bugs.brokenSRGB && (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB
240 || GLAD_EXT_framebuffer_sRGB || GLAD_EXT_sRGB_write_control))
241 {
242 setEnableState(ENABLE_FRAMEBUFFER_SRGB, state.enableState[ENABLE_FRAMEBUFFER_SRGB]);
243 }
244 else
245 state.enableState[ENABLE_FRAMEBUFFER_SRGB] = false;
246
247 GLint faceCull = GL_BACK;
248 glGetIntegerv(GL_CULL_FACE_MODE, &faceCull);
249 state.faceCullMode = faceCull;
250
251 for (int i = 0; i < (int) BUFFER_MAX_ENUM; i++)
252 {
253 state.boundBuffers[i] = 0;
254 glBindBuffer(getGLBufferType((BufferType) i), 0);
255 }
256
257 // Initialize multiple texture unit support for shaders.
258 for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
259 {
260 state.boundTextures[i].clear();
261 state.boundTextures[i].resize(maxTextureUnits, 0);
262 }
263
264 for (int i = 0; i < maxTextureUnits; i++)
265 {
266 glActiveTexture(GL_TEXTURE0 + i);
267
268 for (int j = 0; j < TEXTURE_MAX_ENUM; j++)
269 {
270 TextureType textype = (TextureType) j;
271
272 if (isTextureTypeSupported(textype))
273 glBindTexture(getGLTextureType(textype), 0);
274 }
275 }
276
277 glActiveTexture(GL_TEXTURE0);
278 state.curTextureUnit = 0;
279
280 setDepthWrites(state.depthWritesEnabled);
281
282 createDefaultTexture();
283
284 contextInitialized = true;
285
286#ifdef LOVE_ANDROID
287 // This can't be done in initContext with the rest of the bug checks because
288 // Canvas::isFormatSupported relies on state initialized here / after init.
289 if (GLAD_ES_VERSION_3_0 && !Canvas::isFormatSupported(PIXELFORMAT_R8))
290 bugs.brokenR8PixelFormat = true;
291#endif
292}
293
294void OpenGL::deInitContext()
295{
296 if (!contextInitialized)
297 return;
298
299 for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
300 {
301 if (state.defaultTexture[i] != 0)
302 {
303 gl.deleteTexture(state.defaultTexture[i]);
304 state.defaultTexture[i] = 0;
305 }
306 }
307
308 contextInitialized = false;
309}
310
311void OpenGL::initVendor()
312{
313 const char *vstr = (const char *) glGetString(GL_VENDOR);
314 if (!vstr)
315 {
316 vendor = VENDOR_UNKNOWN;
317 return;
318 }
319
320 // http://feedback.wildfiregames.com/report/opengl/feature/GL_VENDOR
321 // http://stackoverflow.com/questions/2093594/opengl-extensions-available-on-different-android-devices
322 // https://opengl.gpuinfo.org/displaycapability.php?name=GL_VENDOR
323 if (strstr(vstr, "ATI Technologies") || strstr(vstr, "AMD") || strstr(vstr, "Advanced Micro Devices"))
324 vendor = VENDOR_AMD;
325 else if (strstr(vstr, "NVIDIA"))
326 vendor = VENDOR_NVIDIA;
327 else if (strstr(vstr, "Intel"))
328 vendor = VENDOR_INTEL;
329 else if (strstr(vstr, "Mesa"))
330 vendor = VENDOR_MESA_SOFT;
331 else if (strstr(vstr, "Apple Computer") || strstr(vstr, "Apple Inc."))
332 vendor = VENDOR_APPLE;
333 else if (strstr(vstr, "Microsoft"))
334 vendor = VENDOR_MICROSOFT;
335 else if (strstr(vstr, "Imagination"))
336 vendor = VENDOR_IMGTEC;
337 else if (strstr(vstr, "ARM"))
338 vendor = VENDOR_ARM;
339 else if (strstr(vstr, "Qualcomm"))
340 vendor = VENDOR_QUALCOMM;
341 else if (strstr(vstr, "Broadcom"))
342 vendor = VENDOR_BROADCOM;
343 else if (strstr(vstr, "Vivante"))
344 vendor = VENDOR_VIVANTE;
345 else
346 vendor = VENDOR_UNKNOWN;
347}
348
349void OpenGL::initOpenGLFunctions()
350{
351 // Alias extension-suffixed framebuffer functions to core versions since
352 // there are so many different-named extensions that do the same things...
353 if (!(GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
354 {
355 if (GLAD_VERSION_1_0 && GLAD_EXT_framebuffer_object)
356 {
357 fp_glBindRenderbuffer = fp_glBindRenderbufferEXT;
358 fp_glDeleteRenderbuffers = fp_glDeleteRenderbuffersEXT;
359 fp_glGenRenderbuffers = fp_glGenRenderbuffersEXT;
360 fp_glRenderbufferStorage = fp_glRenderbufferStorageEXT;
361 fp_glGetRenderbufferParameteriv = fp_glGetRenderbufferParameterivEXT;
362 fp_glBindFramebuffer = fp_glBindFramebufferEXT;
363 fp_glDeleteFramebuffers = fp_glDeleteFramebuffersEXT;
364 fp_glGenFramebuffers = fp_glGenFramebuffersEXT;
365 fp_glCheckFramebufferStatus = fp_glCheckFramebufferStatusEXT;
366 fp_glFramebufferTexture2D = fp_glFramebufferTexture2DEXT;
367 fp_glFramebufferTexture3D = fp_glFramebufferTexture3DEXT;
368 fp_glFramebufferRenderbuffer = fp_glFramebufferRenderbufferEXT;
369 fp_glGetFramebufferAttachmentParameteriv = fp_glGetFramebufferAttachmentParameterivEXT;
370 fp_glGenerateMipmap = fp_glGenerateMipmapEXT;
371 }
372
373 if (GLAD_VERSION_1_0 && GLAD_EXT_texture_array)
374 fp_glFramebufferTextureLayer = fp_glFramebufferTextureLayerEXT;
375
376 if (GLAD_EXT_framebuffer_blit)
377 fp_glBlitFramebuffer = fp_glBlitFramebufferEXT;
378 else if (GLAD_ANGLE_framebuffer_blit)
379 fp_glBlitFramebuffer = fp_glBlitFramebufferANGLE;
380 else if (GLAD_NV_framebuffer_blit)
381 fp_glBlitFramebuffer = fp_glBlitFramebufferNV;
382
383 if (GLAD_EXT_framebuffer_multisample)
384 fp_glRenderbufferStorageMultisample = fp_glRenderbufferStorageMultisampleEXT;
385 else if (GLAD_APPLE_framebuffer_multisample)
386 fp_glRenderbufferStorageMultisample = fp_glRenderbufferStorageMultisampleAPPLE;
387 else if (GLAD_ANGLE_framebuffer_multisample)
388 fp_glRenderbufferStorageMultisample = fp_glRenderbufferStorageMultisampleANGLE;
389 else if (GLAD_NV_framebuffer_multisample)
390 fp_glRenderbufferStorageMultisample = fp_glRenderbufferStorageMultisampleNV;
391 }
392
393 if (isInstancingSupported() && !(GLAD_VERSION_3_3 || GLAD_ES_VERSION_3_0))
394 {
395 if (GLAD_ARB_instanced_arrays)
396 {
397 fp_glDrawArraysInstanced = fp_glDrawArraysInstancedARB;
398 fp_glDrawElementsInstanced = fp_glDrawElementsInstancedARB;
399 fp_glVertexAttribDivisor = fp_glVertexAttribDivisorARB;
400 }
401 else if (GLAD_EXT_instanced_arrays)
402 {
403 fp_glDrawArraysInstanced = fp_glDrawArraysInstancedEXT;
404 fp_glDrawElementsInstanced = fp_glDrawElementsInstancedEXT;
405 fp_glVertexAttribDivisor = fp_glVertexAttribDivisorEXT;
406 }
407 else if (GLAD_ANGLE_instanced_arrays)
408 {
409 fp_glDrawArraysInstanced = fp_glDrawArraysInstancedANGLE;
410 fp_glDrawElementsInstanced = fp_glDrawElementsInstancedANGLE;
411 fp_glVertexAttribDivisor = fp_glVertexAttribDivisorANGLE;
412 }
413 }
414
415 if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
416 {
417 // The Nvidia Tegra 3 driver (used by Ouya) claims to support GL_EXT_texture_array but
418 // segfaults if you actually try to use it. OpenGL ES 2.0 devices should use OES_texture_3D.
419 // GL_EXT_texture_array is for desktops.
420 GLAD_EXT_texture_array = false;
421
422 if (GLAD_OES_texture_3D)
423 {
424 // Function signatures don't match, we'll have to conditionally call it
425 //fp_glTexImage3D = fp_glTexImage3DOES;
426 fp_glTexSubImage3D = fp_glTexSubImage3DOES;
427 fp_glCopyTexSubImage3D = fp_glCopyTexSubImage3DOES;
428 fp_glCompressedTexImage3D = fp_glCompressedTexImage3DOES;
429 fp_glCompressedTexSubImage3D = fp_glCompressedTexSubImage3DOES;
430 fp_glFramebufferTexture3D = fp_glFramebufferTexture3DOES;
431 }
432 }
433
434 if (!GLAD_VERSION_3_2 && !GLAD_ES_VERSION_3_2 && !GLAD_ARB_draw_elements_base_vertex)
435 {
436 if (GLAD_OES_draw_elements_base_vertex)
437 {
438 fp_glDrawElementsBaseVertex = fp_glDrawElementsBaseVertexOES;
439
440 if (GLAD_ES_VERSION_3_0)
441 {
442 fp_glDrawRangeElementsBaseVertex = fp_glDrawRangeElementsBaseVertexOES;
443 fp_glDrawElementsInstancedBaseVertex = fp_glDrawElementsInstancedBaseVertexOES;
444 }
445
446 }
447 else if (GLAD_EXT_draw_elements_base_vertex)
448 {
449 fp_glDrawElementsBaseVertex = fp_glDrawElementsBaseVertexEXT;
450
451 if (GLAD_ES_VERSION_3_0)
452 {
453 fp_glDrawRangeElementsBaseVertex = fp_glDrawRangeElementsBaseVertexEXT;
454 fp_glDrawElementsInstancedBaseVertex = fp_glDrawElementsInstancedBaseVertexEXT;
455 }
456
457 }
458 }
459}
460
461void OpenGL::initMaxValues()
462{
463 if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
464 {
465 GLint range = 0;
466 GLint precision = 0;
467 glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, &range, &precision);
468 pixelShaderHighpSupported = range > 0;
469 }
470 else
471 pixelShaderHighpSupported = true;
472
473 baseVertexSupported = GLAD_VERSION_3_2 || GLAD_ES_VERSION_3_2 || GLAD_ARB_draw_elements_base_vertex
474 || GLAD_OES_draw_elements_base_vertex || GLAD_EXT_draw_elements_base_vertex;
475
476 // We'll need this value to clamp anisotropy.
477 if (GLAD_EXT_texture_filter_anisotropic)
478 glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
479 else
480 maxAnisotropy = 1.0f;
481
482 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max2DTextureSize);
483 glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &maxCubeTextureSize);
484
485 if (isTextureTypeSupported(TEXTURE_VOLUME))
486 glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &max3DTextureSize);
487 else
488 max3DTextureSize = 0;
489
490 if (isTextureTypeSupported(TEXTURE_2D_ARRAY))
491 glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxTextureArrayLayers);
492 else
493 maxTextureArrayLayers = 0;
494
495 int maxattachments = 1;
496 int maxdrawbuffers = 1;
497
498 if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_2_0)
499 {
500 glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxattachments);
501 glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxdrawbuffers);
502 }
503
504 maxRenderTargets = std::max(std::min(maxattachments, maxdrawbuffers), 1);
505
506 if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object
507 || GLAD_EXT_framebuffer_multisample || GLAD_APPLE_framebuffer_multisample
508 || GLAD_ANGLE_framebuffer_multisample)
509 {
510 glGetIntegerv(GL_MAX_SAMPLES, &maxRenderbufferSamples);
511 }
512 else
513 maxRenderbufferSamples = 0;
514
515 glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
516
517 GLfloat limits[2];
518 if (GLAD_VERSION_3_0)
519 glGetFloatv(GL_POINT_SIZE_RANGE, limits);
520 else
521 glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, limits);
522 maxPointSize = limits[1];
523
524 if (isSamplerLODBiasSupported())
525 glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxLODBias);
526 else
527 maxLODBias = 0.0f;
528}
529
530void OpenGL::createDefaultTexture()
531{
532 // Set the 'default' texture as a repeating white pixel. Otherwise, texture
533 // calls inside a shader would return black when drawing graphics primitives
534 // which would create the need to use different "passthrough" shaders for
535 // untextured primitives vs images.
536 const GLubyte pix[] = {255, 255, 255, 255};
537
538 Texture::Filter filter;
539 filter.min = filter.mag = Texture::FILTER_NEAREST;
540
541 Texture::Wrap wrap;
542 wrap.s = wrap.t = wrap.r = Texture::WRAP_CLAMP;
543
544 for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
545 {
546 state.defaultTexture[i] = 0;
547
548 TextureType type = (TextureType) i;
549
550 if (!isTextureTypeSupported(type))
551 continue;
552
553 GLuint curtexture = state.boundTextures[type][0];
554
555 glGenTextures(1, &state.defaultTexture[type]);
556 bindTextureToUnit(type, state.defaultTexture[type], 0, false);
557
558 setTextureWrap(type, wrap);
559 setTextureFilter(type, filter);
560
561 bool isSRGB = false;
562 rawTexStorage(type, 1, PIXELFORMAT_RGBA8, isSRGB, 1, 1);
563
564 TextureFormat fmt = convertPixelFormat(PIXELFORMAT_RGBA8, false, isSRGB);
565
566 int slices = type == TEXTURE_CUBE ? 6 : 1;
567
568 for (int slice = 0; slice < slices; slice++)
569 {
570 GLenum gltarget = getGLTextureType(type);
571
572 if (type == TEXTURE_CUBE)
573 gltarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
574
575 if (type == TEXTURE_2D || type == TEXTURE_CUBE)
576 glTexSubImage2D(gltarget, 0, 0, 0, 1, 1, fmt.externalformat, fmt.type, pix);
577 else if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
578 glTexSubImage3D(gltarget, 0, 0, 0, slice, 1, 1, 1, fmt.externalformat, fmt.type, pix);
579 }
580
581 bindTextureToUnit(type, curtexture, 0, false);
582 }
583}
584
585void OpenGL::prepareDraw()
586{
587 TempDebugGroup debuggroup("Prepare OpenGL draw");
588
589 // Make sure the active shader's love-provided uniforms are up to date.
590 if (Shader::current != nullptr)
591 ((Shader *)Shader::current)->updateBuiltinUniforms();
592
593 if (state.constantColor != state.lastConstantColor)
594 {
595 state.lastConstantColor = state.constantColor;
596 Colorf c = state.constantColor;
597 gammaCorrectColor(c);
598 glVertexAttrib4f(ATTRIB_CONSTANTCOLOR, c.r, c.g, c.b, c.a);
599 }
600}
601
602GLenum OpenGL::getGLPrimitiveType(PrimitiveType type)
603{
604 switch (type)
605 {
606 case PRIMITIVE_TRIANGLES:
607 return GL_TRIANGLES;
608 case PRIMITIVE_TRIANGLE_STRIP:
609 return GL_TRIANGLE_STRIP;
610 case PRIMITIVE_TRIANGLE_FAN:
611 return GL_TRIANGLE_FAN;
612 case PRIMITIVE_POINTS:
613 return GL_POINTS;
614 case PRIMITIVE_MAX_ENUM:
615 return GL_ZERO;
616 }
617
618 return GL_ZERO;
619}
620
621GLenum OpenGL::getGLBufferType(BufferType type)
622{
623 switch (type)
624 {
625 case BUFFER_VERTEX:
626 return GL_ARRAY_BUFFER;
627 case BUFFER_INDEX:
628 return GL_ELEMENT_ARRAY_BUFFER;
629 case BUFFER_MAX_ENUM:
630 return GL_ZERO;
631 }
632
633 return GL_ZERO;
634}
635
636GLenum OpenGL::getGLTextureType(TextureType type)
637{
638 switch (type)
639 {
640 case TEXTURE_2D:
641 return GL_TEXTURE_2D;
642 case TEXTURE_VOLUME:
643 return GL_TEXTURE_3D;
644 case TEXTURE_2D_ARRAY:
645 return GL_TEXTURE_2D_ARRAY;
646 case TEXTURE_CUBE:
647 return GL_TEXTURE_CUBE_MAP;
648 case TEXTURE_MAX_ENUM:
649 return GL_ZERO;
650 }
651
652 return GL_ZERO;
653}
654
655GLenum OpenGL::getGLIndexDataType(IndexDataType type)
656{
657 switch (type)
658 {
659 case INDEX_UINT16:
660 return GL_UNSIGNED_SHORT;
661 case INDEX_UINT32:
662 return GL_UNSIGNED_INT;
663 default:
664 return GL_ZERO;
665 }
666}
667
668GLenum OpenGL::getGLVertexDataType(vertex::DataType type, GLboolean &normalized)
669{
670 normalized = GL_FALSE;
671
672 switch (type)
673 {
674 case vertex::DATA_UNORM8:
675 normalized = GL_TRUE;
676 return GL_UNSIGNED_BYTE;
677 case vertex::DATA_UNORM16:
678 normalized = GL_TRUE;
679 return GL_UNSIGNED_SHORT;
680 case vertex::DATA_FLOAT:
681 normalized = GL_FALSE;
682 return GL_FLOAT;
683 case vertex::DATA_MAX_ENUM:
684 return GL_ZERO;
685 }
686
687 return GL_ZERO;
688}
689
690GLenum OpenGL::getGLBufferUsage(vertex::Usage usage)
691{
692 switch (usage)
693 {
694 case vertex::USAGE_STREAM:
695 return GL_STREAM_DRAW;
696 case vertex::USAGE_DYNAMIC:
697 return GL_DYNAMIC_DRAW;
698 case vertex::USAGE_STATIC:
699 return GL_STATIC_DRAW;
700 default:
701 return 0;
702 }
703}
704
705void OpenGL::bindBuffer(BufferType type, GLuint buffer)
706{
707 if (state.boundBuffers[type] != buffer)
708 {
709 glBindBuffer(getGLBufferType(type), buffer);
710 state.boundBuffers[type] = buffer;
711 }
712}
713
714void OpenGL::deleteBuffer(GLuint buffer)
715{
716 glDeleteBuffers(1, &buffer);
717
718 for (int i = 0; i < (int) BUFFER_MAX_ENUM; i++)
719 {
720 if (state.boundBuffers[i] == buffer)
721 state.boundBuffers[i] = 0;
722 }
723}
724
725void OpenGL::setVertexAttributes(const vertex::Attributes &attributes, const vertex::BufferBindings &buffers)
726{
727 uint32 enablediff = attributes.enableBits ^ state.enabledAttribArrays;
728 uint32 instanceattribbits = 0;
729 uint32 allbits = attributes.enableBits | state.enabledAttribArrays;
730
731 uint32 i = 0;
732 while (allbits)
733 {
734 uint32 bit = 1u << i;
735
736 if (enablediff & bit)
737 {
738 if (attributes.enableBits & bit)
739 glEnableVertexAttribArray(i);
740 else
741 glDisableVertexAttribArray(i);
742 }
743
744 if (attributes.enableBits & bit)
745 {
746 const auto &attrib = attributes.attribs[i];
747 const auto &layout = attributes.bufferLayouts[attrib.bufferIndex];
748 const auto &bufferinfo = buffers.info[attrib.bufferIndex];
749
750 uint32 bufferbit = 1u << attrib.bufferIndex;
751 uint32 divisor = (attributes.instanceBits & bufferbit) != 0 ? 1 : 0;
752 uint32 divisorbit = divisor << i;
753 instanceattribbits |= divisorbit;
754
755 if ((state.instancedAttribArrays & bit) ^ divisorbit)
756 glVertexAttribDivisor(i, divisor);
757
758 GLboolean normalized = GL_FALSE;
759 GLenum gltype = getGLVertexDataType(attrib.type, normalized);
760
761 const void *offsetpointer = reinterpret_cast<void*>(bufferinfo.offset + attrib.offsetFromVertex);
762
763 bindBuffer(BUFFER_VERTEX, (GLuint) bufferinfo.buffer->getHandle());
764 glVertexAttribPointer(i, attrib.components, gltype, normalized, layout.stride, offsetpointer);
765 }
766
767 i++;
768 allbits >>= 1;
769 }
770
771 state.enabledAttribArrays = attributes.enableBits;
772 state.instancedAttribArrays = instanceattribbits | (state.instancedAttribArrays & (~attributes.enableBits));
773
774 // glDisableVertexAttribArray will make the constant value for a vertex
775 // attribute undefined. We rely on the per-vertex color attribute being
776 // white when no per-vertex color is used, so we set it here.
777 // FIXME: Is there a better place to do this?
778 if ((enablediff & ATTRIBFLAG_COLOR) && !(attributes.enableBits & ATTRIBFLAG_COLOR))
779 glVertexAttrib4f(ATTRIB_COLOR, 1.0f, 1.0f, 1.0f, 1.0f);
780}
781
782void OpenGL::setCullMode(CullMode mode)
783{
784 bool enabled = mode != CULL_NONE;
785
786 if (enabled != isStateEnabled(ENABLE_FACE_CULL))
787 setEnableState(ENABLE_FACE_CULL, enabled);
788
789 if (enabled)
790 {
791 GLenum glmode = mode == CULL_BACK ? GL_BACK : GL_FRONT;
792 if (glmode != state.faceCullMode)
793 {
794 glCullFace(glmode);
795 state.faceCullMode = glmode;
796 }
797 }
798}
799
800void OpenGL::clearDepth(double value)
801{
802 if (GLAD_ES_VERSION_2_0)
803 glClearDepthf((GLfloat) value);
804 else
805 glClearDepth(value);
806}
807
808void OpenGL::setViewport(const Rect &v)
809{
810 glViewport(v.x, v.y, v.w, v.h);
811 state.viewport = v;
812}
813
814Rect OpenGL::getViewport() const
815{
816 return state.viewport;
817}
818
819void OpenGL::setScissor(const Rect &v, bool canvasActive)
820{
821 if (canvasActive)
822 glScissor(v.x, v.y, v.w, v.h);
823 else
824 {
825 // With no Canvas active, we need to compensate for glScissor starting
826 // from the lower left of the viewport instead of the top left.
827 glScissor(v.x, state.viewport.h - (v.y + v.h), v.w, v.h);
828 }
829
830 state.scissor = v;
831}
832
833void OpenGL::setConstantColor(const Colorf &color)
834{
835 state.constantColor = color;
836}
837
838const Colorf &OpenGL::getConstantColor() const
839{
840 return state.constantColor;
841}
842
843void OpenGL::setPointSize(float size)
844{
845 if (GLAD_VERSION_1_0)
846 glPointSize(size);
847
848 state.pointSize = size;
849}
850
851float OpenGL::getPointSize() const
852{
853 return state.pointSize;
854}
855
856void OpenGL::setEnableState(EnableState enablestate, bool enable)
857{
858 GLenum glstate = GL_NONE;
859
860 switch (enablestate)
861 {
862 case ENABLE_DEPTH_TEST:
863 glstate = GL_DEPTH_TEST;
864 break;
865 case ENABLE_STENCIL_TEST:
866 glstate = GL_STENCIL_TEST;
867 break;
868 case ENABLE_SCISSOR_TEST:
869 glstate = GL_SCISSOR_TEST;
870 break;
871 case ENABLE_FACE_CULL:
872 glstate = GL_CULL_FACE;
873 break;
874 case ENABLE_FRAMEBUFFER_SRGB:
875 glstate = GL_FRAMEBUFFER_SRGB;
876 break;
877 case ENABLE_MAX_ENUM:
878 break;
879 }
880
881 if (enable)
882 glEnable(glstate);
883 else
884 glDisable(glstate);
885
886 state.enableState[enablestate] = enable;
887}
888
889bool OpenGL::isStateEnabled(EnableState enablestate) const
890{
891 return state.enableState[enablestate];
892}
893
894void OpenGL::bindFramebuffer(FramebufferTarget target, GLuint framebuffer)
895{
896 bool bindingmodified = false;
897
898 if ((target & FRAMEBUFFER_DRAW) && state.boundFramebuffers[0] != framebuffer)
899 {
900 bindingmodified = true;
901 state.boundFramebuffers[0] = framebuffer;
902 }
903
904 if ((target & FRAMEBUFFER_READ) && state.boundFramebuffers[1] != framebuffer)
905 {
906 bindingmodified = true;
907 state.boundFramebuffers[1] = framebuffer;
908 }
909
910 if (bindingmodified)
911 {
912 GLenum gltarget = GL_FRAMEBUFFER;
913 if (target == FRAMEBUFFER_DRAW)
914 gltarget = GL_DRAW_FRAMEBUFFER;
915 else if (target == FRAMEBUFFER_READ)
916 gltarget = GL_READ_FRAMEBUFFER;
917
918 glBindFramebuffer(gltarget, framebuffer);
919 }
920}
921
922GLenum OpenGL::getFramebuffer(FramebufferTarget target) const
923{
924 if (target & FRAMEBUFFER_DRAW)
925 return state.boundFramebuffers[0];
926 else if (target & FRAMEBUFFER_READ)
927 return state.boundFramebuffers[1];
928 else
929 return 0;
930}
931
932void OpenGL::deleteFramebuffer(GLuint framebuffer)
933{
934 glDeleteFramebuffers(1, &framebuffer);
935
936 for (int i = 0; i < 2; i++)
937 {
938 if (state.boundFramebuffers[i] == framebuffer)
939 state.boundFramebuffers[i] = 0;
940 }
941}
942
943void OpenGL::framebufferTexture(GLenum attachment, TextureType texType, GLuint texture, int level, int layer, int face)
944{
945 GLenum textarget = getGLTextureType(texType);
946
947 switch (texType)
948 {
949 case TEXTURE_2D:
950 glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, textarget, texture, level);
951 break;
952 case TEXTURE_VOLUME:
953 glFramebufferTexture3D(GL_FRAMEBUFFER, attachment, textarget, texture, level, layer);
954 break;
955 case TEXTURE_2D_ARRAY:
956 glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, texture, level, layer);
957 break;
958 case TEXTURE_CUBE:
959 glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture, level);
960 break;
961 default:
962 break;
963 }
964}
965
966void OpenGL::setDepthWrites(bool enable)
967{
968 glDepthMask(enable ? GL_TRUE : GL_FALSE);
969 state.depthWritesEnabled = enable;
970}
971
972bool OpenGL::hasDepthWrites() const
973{
974 return state.depthWritesEnabled;
975}
976
977void OpenGL::useProgram(GLuint program)
978{
979 glUseProgram(program);
980 ++stats.shaderSwitches;
981}
982
983GLuint OpenGL::getDefaultFBO() const
984{
985#ifdef LOVE_IOS
986 // Hack: iOS uses a custom FBO.
987 SDL_SysWMinfo info = {};
988 SDL_VERSION(&info.version);
989 SDL_GetWindowWMInfo(SDL_GL_GetCurrentWindow(), &info);
990 return info.info.uikit.framebuffer;
991#else
992 return 0;
993#endif
994}
995
996GLuint OpenGL::getDefaultTexture(TextureType type) const
997{
998 return state.defaultTexture[type];
999}
1000
1001void OpenGL::setTextureUnit(int textureunit)
1002{
1003 if (textureunit != state.curTextureUnit)
1004 glActiveTexture(GL_TEXTURE0 + textureunit);
1005
1006 state.curTextureUnit = textureunit;
1007}
1008
1009void OpenGL::bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev, bool bindforedit)
1010{
1011 if (texture != state.boundTextures[target][textureunit])
1012 {
1013 int oldtextureunit = state.curTextureUnit;
1014 if (oldtextureunit != textureunit)
1015 glActiveTexture(GL_TEXTURE0 + textureunit);
1016
1017 state.boundTextures[target][textureunit] = texture;
1018 glBindTexture(getGLTextureType(target), texture);
1019
1020 if (restoreprev && oldtextureunit != textureunit)
1021 glActiveTexture(GL_TEXTURE0 + oldtextureunit);
1022 else
1023 state.curTextureUnit = textureunit;
1024 }
1025 else if (bindforedit && !restoreprev && textureunit != state.curTextureUnit)
1026 {
1027 glActiveTexture(GL_TEXTURE0 + textureunit);
1028 state.curTextureUnit = textureunit;
1029 }
1030}
1031
1032void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev, bool bindforedit)
1033{
1034 TextureType textype = TEXTURE_2D;
1035 GLuint handle = 0;
1036
1037 if (texture != nullptr)
1038 {
1039 textype = texture->getTextureType();
1040 handle = (GLuint) texture->getHandle();
1041 }
1042 else
1043 {
1044 if (textureunit == 0 && Shader::current != nullptr)
1045 {
1046 TextureType shadertex = Shader::current->getMainTextureType();
1047 if (shadertex != TEXTURE_MAX_ENUM)
1048 textype = shadertex;
1049 }
1050
1051 handle = getDefaultTexture(textype);
1052 }
1053
1054 bindTextureToUnit(textype, handle, textureunit, restoreprev, bindforedit);
1055}
1056
1057void OpenGL::deleteTexture(GLuint texture)
1058{
1059 // glDeleteTextures binds texture 0 to all texture units the deleted texture
1060 // was bound to before deletion.
1061 for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
1062 {
1063 for (GLuint &texid : state.boundTextures[i])
1064 {
1065 if (texid == texture)
1066 texid = 0;
1067 }
1068 }
1069
1070 glDeleteTextures(1, &texture);
1071}
1072
1073void OpenGL::setTextureFilter(TextureType target, graphics::Texture::Filter &f)
1074{
1075 GLint gmin = f.min == Texture::FILTER_NEAREST ? GL_NEAREST : GL_LINEAR;
1076 GLint gmag = f.mag == Texture::FILTER_NEAREST ? GL_NEAREST : GL_LINEAR;
1077
1078 if (f.mipmap != Texture::FILTER_NONE)
1079 {
1080 if (f.min == Texture::FILTER_NEAREST && f.mipmap == Texture::FILTER_NEAREST)
1081 gmin = GL_NEAREST_MIPMAP_NEAREST;
1082 else if (f.min == Texture::FILTER_NEAREST && f.mipmap == Texture::FILTER_LINEAR)
1083 gmin = GL_NEAREST_MIPMAP_LINEAR;
1084 else if (f.min == Texture::FILTER_LINEAR && f.mipmap == Texture::FILTER_NEAREST)
1085 gmin = GL_LINEAR_MIPMAP_NEAREST;
1086 else if (f.min == Texture::FILTER_LINEAR && f.mipmap == Texture::FILTER_LINEAR)
1087 gmin = GL_LINEAR_MIPMAP_LINEAR;
1088 else
1089 gmin = GL_LINEAR;
1090 }
1091
1092 GLenum gltarget = getGLTextureType(target);
1093
1094 glTexParameteri(gltarget, GL_TEXTURE_MIN_FILTER, gmin);
1095 glTexParameteri(gltarget, GL_TEXTURE_MAG_FILTER, gmag);
1096
1097 if (GLAD_EXT_texture_filter_anisotropic)
1098 {
1099 f.anisotropy = std::min(std::max(f.anisotropy, 1.0f), maxAnisotropy);
1100 glTexParameterf(gltarget, GL_TEXTURE_MAX_ANISOTROPY_EXT, f.anisotropy);
1101 }
1102 else
1103 f.anisotropy = 1.0f;
1104}
1105
1106GLint OpenGL::getGLWrapMode(Texture::WrapMode wmode)
1107{
1108 switch (wmode)
1109 {
1110 case Texture::WRAP_CLAMP:
1111 default:
1112 return GL_CLAMP_TO_EDGE;
1113 case Texture::WRAP_CLAMP_ZERO:
1114 return GL_CLAMP_TO_BORDER;
1115 case Texture::WRAP_REPEAT:
1116 return GL_REPEAT;
1117 case Texture::WRAP_MIRRORED_REPEAT:
1118 return GL_MIRRORED_REPEAT;
1119 }
1120}
1121
1122GLint OpenGL::getGLCompareMode(CompareMode mode)
1123{
1124 switch (mode)
1125 {
1126 case COMPARE_LESS:
1127 return GL_LESS;
1128 case COMPARE_LEQUAL:
1129 return GL_LEQUAL;
1130 case COMPARE_EQUAL:
1131 return GL_EQUAL;
1132 case COMPARE_GEQUAL:
1133 return GL_GEQUAL;
1134 case COMPARE_GREATER:
1135 return GL_GREATER;
1136 case COMPARE_NOTEQUAL:
1137 return GL_NOTEQUAL;
1138 case COMPARE_ALWAYS:
1139 return GL_ALWAYS;
1140 case COMPARE_NEVER:
1141 return GL_NEVER;
1142 default:
1143 return GL_NEVER;
1144 }
1145}
1146
1147void OpenGL::setTextureWrap(TextureType target, const graphics::Texture::Wrap &w)
1148{
1149 glTexParameteri(getGLTextureType(target), GL_TEXTURE_WRAP_S, getGLWrapMode(w.s));
1150 glTexParameteri(getGLTextureType(target), GL_TEXTURE_WRAP_T, getGLWrapMode(w.t));
1151
1152 if (target == TEXTURE_VOLUME)
1153 glTexParameteri(getGLTextureType(target), GL_TEXTURE_WRAP_R, getGLWrapMode(w.r));
1154}
1155
1156bool OpenGL::rawTexStorage(TextureType target, int levels, PixelFormat pixelformat, bool &isSRGB, int width, int height, int depth)
1157{
1158 GLenum gltarget = getGLTextureType(target);
1159 TextureFormat fmt = convertPixelFormat(pixelformat, false, isSRGB);
1160
1161 if (fmt.swizzled)
1162 {
1163 glTexParameteri(gltarget, GL_TEXTURE_SWIZZLE_R, fmt.swizzle[0]);
1164 glTexParameteri(gltarget, GL_TEXTURE_SWIZZLE_G, fmt.swizzle[1]);
1165 glTexParameteri(gltarget, GL_TEXTURE_SWIZZLE_B, fmt.swizzle[2]);
1166 glTexParameteri(gltarget, GL_TEXTURE_SWIZZLE_A, fmt.swizzle[3]);
1167 }
1168
1169 if (isTexStorageSupported())
1170 {
1171 if (target == TEXTURE_2D || target == TEXTURE_CUBE)
1172 glTexStorage2D(gltarget, levels, fmt.internalformat, width, height);
1173 else if (target == TEXTURE_VOLUME || target == TEXTURE_2D_ARRAY)
1174 glTexStorage3D(gltarget, levels, fmt.internalformat, width, height, depth);
1175 }
1176 else
1177 {
1178 int w = width;
1179 int h = height;
1180 int d = depth;
1181
1182 for (int level = 0; level < levels; level++)
1183 {
1184 if (target == TEXTURE_2D || target == TEXTURE_CUBE)
1185 {
1186 int faces = target == TEXTURE_CUBE ? 6 : 1;
1187 for (int face = 0; face < faces; face++)
1188 {
1189 if (target == TEXTURE_CUBE)
1190 gltarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
1191
1192 glTexImage2D(gltarget, level, fmt.internalformat, w, h, 0,
1193 fmt.externalformat, fmt.type, nullptr);
1194 }
1195 }
1196 else if (target == TEXTURE_2D_ARRAY || target == TEXTURE_VOLUME)
1197 {
1198 if (target == TEXTURE_VOLUME && GLAD_ES_VERSION_2_0 && GLAD_OES_texture_3D && !GLAD_ES_VERSION_3_0)
1199 {
1200 glTexImage3DOES(gltarget, level, fmt.internalformat, w, h,
1201 d, 0, fmt.externalformat, fmt.type, nullptr);
1202 }
1203 else
1204 {
1205 glTexImage3D(gltarget, level, fmt.internalformat, w, h, d,
1206 0, fmt.externalformat, fmt.type, nullptr);
1207 }
1208 }
1209
1210 w = std::max(w / 2, 1);
1211 h = std::max(h / 2, 1);
1212
1213 if (target == TEXTURE_VOLUME)
1214 d = std::max(d / 2, 1);
1215 }
1216 }
1217
1218 return gltarget != GL_ZERO;
1219}
1220
1221bool OpenGL::isTexStorageSupported()
1222{
1223 bool supportsTexStorage = GLAD_VERSION_4_2 || GLAD_ARB_texture_storage;
1224
1225 // Apparently there are bugs with glTexStorage on some Android drivers. I'd
1226 // rather not find out the hard way, so we'll avoid it for now...
1227#ifndef LOVE_ANDROID
1228 if (GLAD_ES_VERSION_3_0)
1229 supportsTexStorage = true;
1230#endif
1231
1232 if (gl.bugs.texStorageBreaksSubImage)
1233 supportsTexStorage = false;
1234
1235 return supportsTexStorage;
1236}
1237
1238bool OpenGL::isTextureTypeSupported(TextureType type) const
1239{
1240 switch (type)
1241 {
1242 case TEXTURE_2D:
1243 return true;
1244 case TEXTURE_VOLUME:
1245 return GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_3D;
1246 case TEXTURE_2D_ARRAY:
1247 return GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0 || GLAD_EXT_texture_array;
1248 case TEXTURE_CUBE:
1249 return GLAD_VERSION_1_3 || GLAD_ES_VERSION_2_0;
1250 default:
1251 return false;
1252 }
1253}
1254
1255bool OpenGL::isClampZeroTextureWrapSupported() const
1256{
1257 return GLAD_VERSION_1_3 || GLAD_EXT_texture_border_clamp || GLAD_NV_texture_border_clamp;
1258}
1259
1260bool OpenGL::isPixelShaderHighpSupported() const
1261{
1262 return pixelShaderHighpSupported;
1263}
1264
1265bool OpenGL::isInstancingSupported() const
1266{
1267 return GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_3
1268 || GLAD_ARB_instanced_arrays || GLAD_EXT_instanced_arrays || GLAD_ANGLE_instanced_arrays;
1269}
1270
1271bool OpenGL::isDepthCompareSampleSupported() const
1272{
1273 // Our official API only supports this in GLSL3 shaders, but unofficially
1274 // the requirements are more lax.
1275 return GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || GLAD_EXT_shadow_samplers;
1276}
1277
1278bool OpenGL::isSamplerLODBiasSupported() const
1279{
1280 return GLAD_VERSION_1_4;
1281}
1282
1283bool OpenGL::isBaseVertexSupported() const
1284{
1285 return baseVertexSupported;
1286}
1287
1288int OpenGL::getMax2DTextureSize() const
1289{
1290 return std::max(max2DTextureSize, 1);
1291}
1292
1293int OpenGL::getMax3DTextureSize() const
1294{
1295 return std::max(max3DTextureSize, 1);
1296}
1297
1298int OpenGL::getMaxCubeTextureSize() const
1299{
1300 return std::max(maxCubeTextureSize, 1);
1301}
1302
1303int OpenGL::getMaxTextureLayers() const
1304{
1305 return std::max(maxTextureArrayLayers, 1);
1306}
1307
1308int OpenGL::getMaxRenderTargets() const
1309{
1310 return std::min(maxRenderTargets, MAX_COLOR_RENDER_TARGETS);
1311}
1312
1313int OpenGL::getMaxRenderbufferSamples() const
1314{
1315 return maxRenderbufferSamples;
1316}
1317
1318int OpenGL::getMaxTextureUnits() const
1319{
1320 return maxTextureUnits;
1321}
1322
1323float OpenGL::getMaxPointSize() const
1324{
1325 return maxPointSize;
1326}
1327
1328float OpenGL::getMaxAnisotropy() const
1329{
1330 return maxAnisotropy;
1331}
1332
1333float OpenGL::getMaxLODBias() const
1334{
1335 return maxLODBias;
1336}
1337
1338bool OpenGL::isCoreProfile() const
1339{
1340 return coreProfile;
1341}
1342
1343OpenGL::Vendor OpenGL::getVendor() const
1344{
1345 return vendor;
1346}
1347
1348OpenGL::TextureFormat OpenGL::convertPixelFormat(PixelFormat pixelformat, bool renderbuffer, bool &isSRGB)
1349{
1350 TextureFormat f;
1351
1352 f.framebufferAttachments[0] = GL_COLOR_ATTACHMENT0;
1353 f.framebufferAttachments[1] = GL_NONE;
1354
1355 if (pixelformat == PIXELFORMAT_RGBA8 && isSRGB)
1356 pixelformat = PIXELFORMAT_sRGBA8;
1357 else if (pixelformat == PIXELFORMAT_ETC1)
1358 {
1359 // The ETC2 format can load ETC1 textures.
1360 if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility)
1361 pixelformat = PIXELFORMAT_ETC2_RGB;
1362 }
1363
1364 switch (pixelformat)
1365 {
1366 case PIXELFORMAT_R8:
1367 if ((GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0 || GLAD_ARB_texture_rg || GLAD_EXT_texture_rg)
1368 && !gl.bugs.brokenR8PixelFormat)
1369 {
1370 f.internalformat = GL_R8;
1371 f.externalformat = GL_RED;
1372 }
1373 else
1374 {
1375 f.internalformat = GL_LUMINANCE8;
1376 f.externalformat = GL_LUMINANCE;
1377 }
1378 f.type = GL_UNSIGNED_BYTE;
1379 break;
1380 case PIXELFORMAT_RG8:
1381 f.internalformat = GL_RG8;
1382 f.externalformat = GL_RG;
1383 f.type = GL_UNSIGNED_BYTE;
1384 break;
1385 case PIXELFORMAT_RGBA8:
1386 f.internalformat = GL_RGBA8;
1387 f.externalformat = GL_RGBA;
1388 f.type = GL_UNSIGNED_BYTE;
1389 break;
1390 case PIXELFORMAT_sRGBA8:
1391 f.internalformat = GL_SRGB8_ALPHA8;
1392 f.type = GL_UNSIGNED_BYTE;
1393 if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
1394 f.externalformat = GL_SRGB_ALPHA;
1395 else
1396 f.externalformat = GL_RGBA;
1397 break;
1398 case PIXELFORMAT_R16:
1399 f.internalformat = GL_R16;
1400 f.externalformat = GL_RED;
1401 f.type = GL_UNSIGNED_SHORT;
1402 break;
1403 case PIXELFORMAT_RG16:
1404 f.internalformat = GL_RG16;
1405 f.externalformat = GL_RG;
1406 f.type = GL_UNSIGNED_SHORT;
1407 break;
1408 case PIXELFORMAT_RGBA16:
1409 f.internalformat = GL_RGBA16;
1410 f.externalformat = GL_RGBA;
1411 f.type = GL_UNSIGNED_SHORT;
1412 break;
1413 case PIXELFORMAT_R16F:
1414 f.internalformat = GL_R16F;
1415 f.externalformat = GL_RED;
1416 if (GLAD_OES_texture_half_float)
1417 f.type = GL_HALF_FLOAT_OES;
1418 else
1419 f.type = GL_HALF_FLOAT;
1420 break;
1421 case PIXELFORMAT_RG16F:
1422 f.internalformat = GL_RG16F;
1423 f.externalformat = GL_RG;
1424 if (GLAD_OES_texture_half_float)
1425 f.type = GL_HALF_FLOAT_OES;
1426 else
1427 f.type = GL_HALF_FLOAT;
1428 break;
1429 case PIXELFORMAT_RGBA16F:
1430 f.internalformat = GL_RGBA16F;
1431 f.externalformat = GL_RGBA;
1432 if (GLAD_OES_texture_half_float)
1433 f.type = GL_HALF_FLOAT_OES;
1434 else
1435 f.type = GL_HALF_FLOAT;
1436 break;
1437 case PIXELFORMAT_R32F:
1438 f.internalformat = GL_R32F;
1439 f.externalformat = GL_RED;
1440 f.type = GL_FLOAT;
1441 break;
1442 case PIXELFORMAT_RG32F:
1443 f.internalformat = GL_RG32F;
1444 f.externalformat = GL_RG;
1445 f.type = GL_FLOAT;
1446 break;
1447 case PIXELFORMAT_RGBA32F:
1448 f.internalformat = GL_RGBA32F;
1449 f.externalformat = GL_RGBA;
1450 f.type = GL_FLOAT;
1451 break;
1452
1453 case PIXELFORMAT_LA8:
1454 if (gl.isCoreProfile() || GLAD_ES_VERSION_3_0)
1455 {
1456 f.internalformat = GL_RG8;
1457 f.externalformat = GL_RG;
1458 f.type = GL_UNSIGNED_BYTE;
1459 f.swizzled = true;
1460 f.swizzle[0] = f.swizzle[1] = f.swizzle[2] = GL_RED;
1461 f.swizzle[3] = GL_GREEN;
1462 }
1463 else
1464 {
1465 f.internalformat = GL_LUMINANCE8_ALPHA8;
1466 f.externalformat = GL_LUMINANCE_ALPHA;
1467 f.type = GL_UNSIGNED_BYTE;
1468 }
1469 break;
1470
1471 case PIXELFORMAT_RGBA4:
1472 f.internalformat = GL_RGBA4;
1473 f.externalformat = GL_RGBA;
1474 f.type = GL_UNSIGNED_SHORT_4_4_4_4;
1475 break;
1476 case PIXELFORMAT_RGB5A1:
1477 f.internalformat = GL_RGB5_A1;
1478 f.externalformat = GL_RGBA;
1479 f.type = GL_UNSIGNED_SHORT_5_5_5_1;
1480 break;
1481 case PIXELFORMAT_RGB565:
1482 f.internalformat = GL_RGB565;
1483 f.externalformat = GL_RGB;
1484 f.type = GL_UNSIGNED_SHORT_5_6_5;
1485 break;
1486 case PIXELFORMAT_RGB10A2:
1487 f.internalformat = GL_RGB10_A2;
1488 f.externalformat = GL_RGBA;
1489 f.type = GL_UNSIGNED_INT_2_10_10_10_REV;
1490 break;
1491 case PIXELFORMAT_RG11B10F:
1492 f.internalformat = GL_R11F_G11F_B10F;
1493 f.externalformat = GL_RGB;
1494 f.type = GL_UNSIGNED_INT_10F_11F_11F_REV;
1495 break;
1496
1497 case PIXELFORMAT_STENCIL8:
1498 // Prefer a combined depth/stencil buffer due to driver issues.
1499 if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object)
1500 {
1501 f.internalformat = GL_DEPTH24_STENCIL8;
1502 f.externalformat = GL_DEPTH_STENCIL;
1503 f.type = GL_UNSIGNED_INT_24_8;
1504 f.framebufferAttachments[0] = GL_DEPTH_STENCIL_ATTACHMENT;
1505 }
1506 else if (GLAD_EXT_packed_depth_stencil || GLAD_OES_packed_depth_stencil)
1507 {
1508 f.internalformat = GL_DEPTH24_STENCIL8;
1509 f.externalformat = GL_DEPTH_STENCIL;
1510 f.type = GL_UNSIGNED_INT_24_8;
1511 f.framebufferAttachments[0] = GL_DEPTH_ATTACHMENT;
1512 f.framebufferAttachments[1] = GL_STENCIL_ATTACHMENT;
1513 }
1514 else
1515 {
1516 f.internalformat = GL_STENCIL_INDEX8;
1517 f.externalformat = GL_STENCIL;
1518 f.type = GL_UNSIGNED_BYTE;
1519 f.framebufferAttachments[0] = GL_STENCIL_ATTACHMENT;
1520 }
1521 break;
1522
1523 case PIXELFORMAT_DEPTH16:
1524 f.internalformat = GL_DEPTH_COMPONENT16;
1525 f.externalformat = GL_DEPTH_COMPONENT;
1526 f.type = GL_UNSIGNED_SHORT;
1527 f.framebufferAttachments[0] = GL_DEPTH_ATTACHMENT;
1528 break;
1529
1530 case PIXELFORMAT_DEPTH24:
1531 if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0 && !GLAD_OES_depth24 && GLAD_OES_packed_depth_stencil)
1532 {
1533 f.internalformat = GL_DEPTH24_STENCIL8;
1534 f.externalformat = GL_DEPTH_STENCIL;
1535 f.type = GL_UNSIGNED_INT_24_8;
1536 f.framebufferAttachments[0] = GL_DEPTH_ATTACHMENT;
1537 f.framebufferAttachments[1] = GL_STENCIL_ATTACHMENT;
1538 }
1539 else
1540 {
1541 f.internalformat = GL_DEPTH_COMPONENT24;
1542 f.externalformat = GL_DEPTH_COMPONENT;
1543 f.type = GL_UNSIGNED_INT;
1544 f.framebufferAttachments[0] = GL_DEPTH_ATTACHMENT;
1545 }
1546 break;
1547
1548 case PIXELFORMAT_DEPTH32F:
1549 f.internalformat = GL_DEPTH_COMPONENT32F;
1550 f.externalformat = GL_DEPTH_COMPONENT;
1551 f.type = GL_FLOAT;
1552 f.framebufferAttachments[0] = GL_DEPTH_ATTACHMENT;
1553 break;
1554
1555 case PIXELFORMAT_DEPTH24_STENCIL8:
1556 f.internalformat = GL_DEPTH24_STENCIL8;
1557 f.externalformat = GL_DEPTH_STENCIL;
1558 f.type = GL_UNSIGNED_INT_24_8;
1559 if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object)
1560 {
1561 f.framebufferAttachments[0] = GL_DEPTH_STENCIL_ATTACHMENT;
1562 }
1563 else if (GLAD_EXT_packed_depth_stencil || GLAD_OES_packed_depth_stencil)
1564 {
1565 f.framebufferAttachments[0] = GL_DEPTH_ATTACHMENT;
1566 f.framebufferAttachments[1] = GL_STENCIL_ATTACHMENT;
1567 }
1568 break;
1569
1570 case PIXELFORMAT_DEPTH32F_STENCIL8:
1571 f.internalformat = GL_DEPTH32F_STENCIL8;
1572 f.externalformat = GL_DEPTH_STENCIL;
1573 f.type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
1574 f.framebufferAttachments[0] = GL_DEPTH_STENCIL_ATTACHMENT;
1575 break;
1576
1577 case PIXELFORMAT_DXT1:
1578 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT : GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
1579 break;
1580 case PIXELFORMAT_DXT3:
1581 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
1582 break;
1583 case PIXELFORMAT_DXT5:
1584 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
1585 break;
1586 case PIXELFORMAT_BC4:
1587 isSRGB = false;
1588 f.internalformat = GL_COMPRESSED_RED_RGTC1;
1589 break;
1590 case PIXELFORMAT_BC4s:
1591 isSRGB = false;
1592 f.internalformat = GL_COMPRESSED_SIGNED_RED_RGTC1;
1593 break;
1594 case PIXELFORMAT_BC5:
1595 isSRGB = false;
1596 f.internalformat = GL_COMPRESSED_RG_RGTC2;
1597 break;
1598 case PIXELFORMAT_BC5s:
1599 isSRGB = false;
1600 f.internalformat = GL_COMPRESSED_SIGNED_RG_RGTC2;
1601 break;
1602 case PIXELFORMAT_BC6H:
1603 isSRGB = false;
1604 f.internalformat = GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
1605 break;
1606 case PIXELFORMAT_BC6Hs:
1607 isSRGB = false;
1608 f.internalformat = GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT;
1609 break;
1610 case PIXELFORMAT_BC7:
1611 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM : GL_COMPRESSED_RGBA_BPTC_UNORM;
1612 break;
1613 case PIXELFORMAT_PVR1_RGB2:
1614 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT : GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
1615 break;
1616 case PIXELFORMAT_PVR1_RGB4:
1617 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT : GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
1618 break;
1619 case PIXELFORMAT_PVR1_RGBA2:
1620 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT : GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
1621 break;
1622 case PIXELFORMAT_PVR1_RGBA4:
1623 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT : GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
1624 break;
1625 case PIXELFORMAT_ETC1:
1626 isSRGB = false;
1627 f.internalformat = GL_ETC1_RGB8_OES;
1628 break;
1629 case PIXELFORMAT_ETC2_RGB:
1630 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB8_ETC2 : GL_COMPRESSED_RGB8_ETC2;
1631 break;
1632 case PIXELFORMAT_ETC2_RGBA:
1633 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : GL_COMPRESSED_RGBA8_ETC2_EAC;
1634 break;
1635 case PIXELFORMAT_ETC2_RGBA1:
1636 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 : GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;
1637 break;
1638 case PIXELFORMAT_EAC_R:
1639 isSRGB = false;
1640 f.internalformat = GL_COMPRESSED_R11_EAC;
1641 break;
1642 case PIXELFORMAT_EAC_Rs:
1643 isSRGB = false;
1644 f.internalformat = GL_COMPRESSED_SIGNED_R11_EAC;
1645 break;
1646 case PIXELFORMAT_EAC_RG:
1647 isSRGB = false;
1648 f.internalformat = GL_COMPRESSED_RG11_EAC;
1649 break;
1650 case PIXELFORMAT_EAC_RGs:
1651 isSRGB = false;
1652 f.internalformat = GL_COMPRESSED_SIGNED_RG11_EAC;
1653 break;
1654 case PIXELFORMAT_ASTC_4x4:
1655 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : GL_COMPRESSED_RGBA_ASTC_4x4_KHR;
1656 break;
1657 case PIXELFORMAT_ASTC_5x4:
1658 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : GL_COMPRESSED_RGBA_ASTC_5x4_KHR;
1659 break;
1660 case PIXELFORMAT_ASTC_5x5:
1661 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : GL_COMPRESSED_RGBA_ASTC_5x5_KHR;
1662 break;
1663 case PIXELFORMAT_ASTC_6x5:
1664 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : GL_COMPRESSED_RGBA_ASTC_6x5_KHR;
1665 break;
1666 case PIXELFORMAT_ASTC_6x6:
1667 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : GL_COMPRESSED_RGBA_ASTC_6x6_KHR;
1668 break;
1669 case PIXELFORMAT_ASTC_8x5:
1670 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : GL_COMPRESSED_RGBA_ASTC_8x5_KHR;
1671 break;
1672 case PIXELFORMAT_ASTC_8x6:
1673 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : GL_COMPRESSED_RGBA_ASTC_8x6_KHR;
1674 break;
1675 case PIXELFORMAT_ASTC_8x8:
1676 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : GL_COMPRESSED_RGBA_ASTC_8x8_KHR;
1677 break;
1678 case PIXELFORMAT_ASTC_10x5:
1679 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : GL_COMPRESSED_RGBA_ASTC_10x5_KHR;
1680 break;
1681 case PIXELFORMAT_ASTC_10x6:
1682 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : GL_COMPRESSED_RGBA_ASTC_10x6_KHR;
1683 break;
1684 case PIXELFORMAT_ASTC_10x8:
1685 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : GL_COMPRESSED_RGBA_ASTC_10x8_KHR;
1686 break;
1687 case PIXELFORMAT_ASTC_10x10:
1688 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : GL_COMPRESSED_RGBA_ASTC_10x10_KHR;
1689 break;
1690 case PIXELFORMAT_ASTC_12x10:
1691 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : GL_COMPRESSED_RGBA_ASTC_12x10_KHR;
1692 break;
1693 case PIXELFORMAT_ASTC_12x12:
1694 f.internalformat = isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : GL_COMPRESSED_RGBA_ASTC_12x12_KHR;
1695 break;
1696
1697 default:
1698 printf("Unhandled pixel format %d when converting to OpenGL enums!", pixelformat);
1699 break;
1700 }
1701
1702 if (!isPixelFormatCompressed(pixelformat))
1703 {
1704 // glTexImage in OpenGL ES 2 only accepts internal format enums that
1705 // match the external format. GLES3 doesn't have that restriction -
1706 // except for GL_LUMINANCE_ALPHA which doesn't have a sized version in
1707 // ES3. However we always use RG8 for PIXELFORMAT_LA8 on GLES3 so it
1708 // doesn't matter there.
1709 // Also note that GLES2+extension sRGB format enums are different from
1710 // desktop GL and GLES3+ (this is handled above).
1711 if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0
1712 && !renderbuffer && !isTexStorageSupported())
1713 {
1714 f.internalformat = f.externalformat;
1715 }
1716
1717 if (pixelformat != PIXELFORMAT_sRGBA8)
1718 isSRGB = false;
1719 }
1720
1721 return f;
1722}
1723
1724bool OpenGL::isPixelFormatSupported(PixelFormat pixelformat, bool rendertarget, bool readable, bool isSRGB)
1725{
1726 if (rendertarget && isPixelFormatCompressed(pixelformat))
1727 return false;
1728
1729 if (pixelformat == PIXELFORMAT_RGBA8 && isSRGB)
1730 pixelformat = PIXELFORMAT_sRGBA8;
1731
1732 switch (pixelformat)
1733 {
1734 case PIXELFORMAT_R8:
1735 case PIXELFORMAT_RG8:
1736 if (GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0 || GLAD_ARB_texture_rg || GLAD_EXT_texture_rg)
1737 return true;
1738 else if (pixelformat == PIXELFORMAT_R8 && !rendertarget && (GLAD_ES_VERSION_2_0 || GLAD_VERSION_1_1))
1739 return true; // We'll use OpenGL's luminance format internally.
1740 else
1741 return false;
1742 case PIXELFORMAT_RGBA8:
1743 if (rendertarget)
1744 return GLAD_VERSION_1_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_rgb8_rgba8 || GLAD_ARM_rgba8;
1745 else
1746 return true;
1747 case PIXELFORMAT_sRGBA8:
1748 if (gl.bugs.brokenSRGB)
1749 return false;
1750 if (rendertarget)
1751 {
1752 if (GLAD_VERSION_1_0)
1753 {
1754 return GLAD_VERSION_3_0 || ((GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB)
1755 && (GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB));
1756 }
1757 else
1758 return GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB;
1759 }
1760 else
1761 return GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB || GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB;
1762 case PIXELFORMAT_R16:
1763 case PIXELFORMAT_RG16:
1764 return GLAD_VERSION_3_0
1765 || (GLAD_VERSION_1_1 && GLAD_ARB_texture_rg)
1766 || (GLAD_EXT_texture_norm16 && (GLAD_ES_VERSION_3_0 || GLAD_EXT_texture_rg));
1767 case PIXELFORMAT_RGBA16:
1768 return GLAD_VERSION_1_1 || GLAD_EXT_texture_norm16;
1769 case PIXELFORMAT_R16F:
1770 case PIXELFORMAT_RG16F:
1771 if (GLAD_VERSION_1_0)
1772 return GLAD_VERSION_3_0 || (GLAD_ARB_texture_float && GLAD_ARB_half_float_pixel && GLAD_ARB_texture_rg);
1773 else if (rendertarget && !GLAD_EXT_color_buffer_half_float)
1774 return false;
1775 else
1776 return GLAD_ES_VERSION_3_0 || (GLAD_OES_texture_half_float && GLAD_EXT_texture_rg);
1777 case PIXELFORMAT_RGBA16F:
1778 if (GLAD_VERSION_1_0)
1779 return GLAD_VERSION_3_0 || (GLAD_ARB_texture_float && GLAD_ARB_half_float_pixel);
1780 else if (rendertarget && !GLAD_EXT_color_buffer_half_float)
1781 return false;
1782 else
1783 return GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float;
1784 case PIXELFORMAT_R32F:
1785 case PIXELFORMAT_RG32F:
1786 if (GLAD_VERSION_1_0)
1787 return GLAD_VERSION_3_0 || (GLAD_ARB_texture_float && GLAD_ARB_texture_rg);
1788 else if (!rendertarget)
1789 return GLAD_ES_VERSION_3_0 || (GLAD_OES_texture_float && GLAD_EXT_texture_rg);
1790 else
1791 return false;
1792 case PIXELFORMAT_RGBA32F:
1793 if (GLAD_VERSION_1_0)
1794 return GLAD_VERSION_3_0 || GLAD_ARB_texture_float;
1795 else if (!rendertarget)
1796 return GLAD_ES_VERSION_3_0 || GLAD_OES_texture_float;
1797 else
1798 return false;
1799
1800 case PIXELFORMAT_LA8:
1801 return !rendertarget;
1802
1803 case PIXELFORMAT_RGBA4:
1804 case PIXELFORMAT_RGB5A1:
1805 return true;
1806 case PIXELFORMAT_RGB565:
1807 return GLAD_ES_VERSION_2_0 || GLAD_VERSION_4_2 || GLAD_ARB_ES2_compatibility;
1808 case PIXELFORMAT_RGB10A2:
1809 return GLAD_ES_VERSION_3_0 || GLAD_VERSION_1_0;
1810 case PIXELFORMAT_RG11B10F:
1811 if (rendertarget)
1812 return GLAD_VERSION_3_0 || GLAD_EXT_packed_float || GLAD_APPLE_color_buffer_packed_float;
1813 else
1814 return GLAD_VERSION_3_0 || GLAD_EXT_packed_float || GLAD_APPLE_texture_packed_float;
1815
1816 case PIXELFORMAT_STENCIL8:
1817 return rendertarget && !readable;
1818
1819 case PIXELFORMAT_DEPTH16:
1820 if (!rendertarget)
1821 return false;
1822 else if (readable)
1823 return GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_depth_texture;
1824 else
1825 return true;
1826
1827 case PIXELFORMAT_DEPTH24:
1828 if (!rendertarget)
1829 return false;
1830 else if (readable)
1831 return GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || (GLAD_OES_depth_texture && (GLAD_OES_depth24 || GLAD_OES_depth_texture));
1832 else
1833 return GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_depth24 || GLAD_OES_depth_texture;
1834
1835 case PIXELFORMAT_DEPTH24_STENCIL8:
1836 if (!rendertarget)
1837 return false;
1838 else if (readable)
1839 return GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0 || GLAD_EXT_packed_depth_stencil || (GLAD_OES_depth_texture && GLAD_OES_packed_depth_stencil);
1840 else
1841 return GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0 || GLAD_EXT_packed_depth_stencil || GLAD_OES_packed_depth_stencil;
1842
1843 case PIXELFORMAT_DEPTH32F:
1844 case PIXELFORMAT_DEPTH32F_STENCIL8:
1845 return rendertarget && (GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0 || GLAD_ARB_depth_buffer_float);
1846
1847 case PIXELFORMAT_DXT1:
1848 return GLAD_EXT_texture_compression_s3tc || GLAD_EXT_texture_compression_dxt1;
1849 case PIXELFORMAT_DXT3:
1850 return GLAD_EXT_texture_compression_s3tc || GLAD_ANGLE_texture_compression_dxt3;
1851 case PIXELFORMAT_DXT5:
1852 return GLAD_EXT_texture_compression_s3tc || GLAD_ANGLE_texture_compression_dxt5;
1853 case PIXELFORMAT_BC4:
1854 case PIXELFORMAT_BC4s:
1855 case PIXELFORMAT_BC5:
1856 case PIXELFORMAT_BC5s:
1857 return (GLAD_VERSION_3_0 || GLAD_ARB_texture_compression_rgtc || GLAD_EXT_texture_compression_rgtc);
1858 case PIXELFORMAT_BC6H:
1859 case PIXELFORMAT_BC6Hs:
1860 case PIXELFORMAT_BC7:
1861 return GLAD_VERSION_4_2 || GLAD_ARB_texture_compression_bptc;
1862 case PIXELFORMAT_PVR1_RGB2:
1863 case PIXELFORMAT_PVR1_RGB4:
1864 case PIXELFORMAT_PVR1_RGBA2:
1865 case PIXELFORMAT_PVR1_RGBA4:
1866 return isSRGB ? GLAD_EXT_pvrtc_sRGB : GLAD_IMG_texture_compression_pvrtc;
1867 case PIXELFORMAT_ETC1:
1868 // ETC2 support guarantees ETC1 support as well.
1869 return GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility || GLAD_OES_compressed_ETC1_RGB8_texture;
1870 case PIXELFORMAT_ETC2_RGB:
1871 case PIXELFORMAT_ETC2_RGBA:
1872 case PIXELFORMAT_ETC2_RGBA1:
1873 case PIXELFORMAT_EAC_R:
1874 case PIXELFORMAT_EAC_Rs:
1875 case PIXELFORMAT_EAC_RG:
1876 case PIXELFORMAT_EAC_RGs:
1877 return GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility;
1878 case PIXELFORMAT_ASTC_4x4:
1879 case PIXELFORMAT_ASTC_5x4:
1880 case PIXELFORMAT_ASTC_5x5:
1881 case PIXELFORMAT_ASTC_6x5:
1882 case PIXELFORMAT_ASTC_6x6:
1883 case PIXELFORMAT_ASTC_8x5:
1884 case PIXELFORMAT_ASTC_8x6:
1885 case PIXELFORMAT_ASTC_8x8:
1886 case PIXELFORMAT_ASTC_10x5:
1887 case PIXELFORMAT_ASTC_10x6:
1888 case PIXELFORMAT_ASTC_10x8:
1889 case PIXELFORMAT_ASTC_10x10:
1890 case PIXELFORMAT_ASTC_12x10:
1891 case PIXELFORMAT_ASTC_12x12:
1892 return GLAD_ES_VERSION_3_2 || GLAD_KHR_texture_compression_astc_ldr;
1893
1894 default:
1895 return false;
1896 }
1897}
1898
1899bool OpenGL::hasTextureFilteringSupport(PixelFormat pixelformat)
1900{
1901 switch (pixelformat)
1902 {
1903 case PIXELFORMAT_R16F:
1904 case PIXELFORMAT_RG16F:
1905 case PIXELFORMAT_RGBA16F:
1906 return GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float_linear;
1907 case PIXELFORMAT_R32F:
1908 case PIXELFORMAT_RG32F:
1909 case PIXELFORMAT_RGBA32F:
1910 return GLAD_VERSION_1_1 || GLAD_OES_texture_float_linear;
1911 default:
1912 return true;
1913 }
1914}
1915
1916const char *OpenGL::errorString(GLenum errorcode)
1917{
1918 switch (errorcode)
1919 {
1920 case GL_NO_ERROR:
1921 return "no error";
1922 case GL_INVALID_ENUM:
1923 return "invalid enum";
1924 case GL_INVALID_VALUE:
1925 return "invalid value";
1926 case GL_INVALID_OPERATION:
1927 return "invalid operation";
1928 case GL_OUT_OF_MEMORY:
1929 return "out of memory";
1930 case GL_INVALID_FRAMEBUFFER_OPERATION:
1931 return "invalid framebuffer operation";
1932 case GL_CONTEXT_LOST:
1933 return "OpenGL context has been lost";
1934 default:
1935 break;
1936 }
1937
1938 static char text[64] = {};
1939
1940 memset(text, 0, sizeof(text));
1941 sprintf(text, "0x%x", errorcode);
1942
1943 return text;
1944}
1945
1946const char *OpenGL::framebufferStatusString(GLenum status)
1947{
1948 switch (status)
1949 {
1950 case GL_FRAMEBUFFER_COMPLETE:
1951 return "complete (success)";
1952 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
1953 return "Texture format cannot be rendered to on this system.";
1954 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
1955 return "Error in graphics driver (missing render texture attachment)";
1956 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
1957 return "Error in graphics driver (incomplete draw buffer)";
1958 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
1959 return "Error in graphics driver (incomplete read buffer)";
1960 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
1961 return "Canvas with the specified MSAA count cannot be rendered to on this system.";
1962 case GL_FRAMEBUFFER_UNSUPPORTED:
1963 return "Renderable textures are unsupported";
1964 default:
1965 break;
1966 }
1967
1968 static char text[64] = {};
1969
1970 memset(text, 0, sizeof(text));
1971 sprintf(text, "0x%x", status);
1972
1973 return text;
1974}
1975
1976const char *OpenGL::debugSeverityString(GLenum severity)
1977{
1978 switch (severity)
1979 {
1980 case GL_DEBUG_SEVERITY_HIGH:
1981 return "high";
1982 case GL_DEBUG_SEVERITY_MEDIUM:
1983 return "medium";
1984 case GL_DEBUG_SEVERITY_LOW:
1985 return "low";
1986 default:
1987 return "unknown";
1988 }
1989}
1990
1991const char *OpenGL::debugSourceString(GLenum source)
1992{
1993 switch (source)
1994 {
1995 case GL_DEBUG_SOURCE_API:
1996 return "API";
1997 case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
1998 return "window";
1999 case GL_DEBUG_SOURCE_SHADER_COMPILER:
2000 return "shader";
2001 case GL_DEBUG_SOURCE_THIRD_PARTY:
2002 return "external";
2003 case GL_DEBUG_SOURCE_APPLICATION:
2004 return "LOVE";
2005 case GL_DEBUG_SOURCE_OTHER:
2006 return "other";
2007 default:
2008 return "unknown";
2009 }
2010}
2011
2012const char *OpenGL::debugTypeString(GLenum type)
2013{
2014 switch (type)
2015 {
2016 case GL_DEBUG_TYPE_ERROR:
2017 return "error";
2018 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
2019 return "deprecated behavior";
2020 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
2021 return "undefined behavior";
2022 case GL_DEBUG_TYPE_PERFORMANCE:
2023 return "performance";
2024 case GL_DEBUG_TYPE_PORTABILITY:
2025 return "portability";
2026 case GL_DEBUG_TYPE_OTHER:
2027 return "other";
2028 default:
2029 return "unknown";
2030 }
2031}
2032
2033
2034// OpenGL class instance singleton.
2035OpenGL gl;
2036
2037} // opengl
2038} // graphics
2039} // love
2040