weston/libweston/renderer-gl/gl-utils.c

394 lines
16 KiB
C
Raw Normal View History

/*
* Copyright © 2024 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include "shared/xalloc.h"
#include "gl-renderer.h"
#include "gl-renderer-internal.h"
/*
* Table 1: List of OpenGL ES 3 sized internal colour formats allowed for
* texture and FBO creation. Built from Table 3.13 in the OpenGL ES 3.0 and 3.1
* specs and from Table 8.10 in the OpenGL ES 3.2 spec.
*
*
* Internal fmt¹ T² F³ R External fmt External type(s)
*
* GL_R8 3.0 3.0 3.0 GL_RED GL_UNSIGNED_BYTE
* GL_R8_SNORM 3.0 3.0 GL_RED GL_BYTE
* GL_R16F 3.0 3.0 3.2 GL_RED GL_HALF_FLOAT,
* GL_FLOAT
* GL_R32F 3.0 3.2 GL_RED GL_FLOAT
* GL_R8UI 3.0 3.0 GL_RED_INTEGER GL_UNSIGNED_BYTE
* GL_R8I 3.0 3.0 GL_RED_INTEGER GL_BYTE
* GL_R16UI 3.0 3.0 GL_RED_INTEGER GL_UNSIGNED_SHORT
* GL_R16I 3.0 3.0 GL_RED_INTEGER GL_SHORT
* GL_R32UI 3.0 3.0 GL_RED_INTEGER GL_UNSIGNED_INT
* GL_R32I 3.0 3.0 GL_RED_INTEGER GL_INT
* GL_RG8 3.0 3.0 3.0 GL_RG GL_UNSIGNED_BYTE
* GL_RG8_SNORM 3.0 3.0 GL_RG GL_BYTE
* GL_RG16F 3.0 3.0 3.2 GL_RG GL_HALF_FLOAT,
* GL_FLOAT
* GL_RG32F 3.0 3.2 GL_RG GL_FLOAT
* GL_RG8UI 3.0 3.0 GL_RG_INTEGER GL_UNSIGNED_BYTE
* GL_RG8I 3.0 3.0 GL_RG_INTEGER GL_BYTE
* GL_RG16UI 3.0 3.0 GL_RG_INTEGER GL_UNSIGNED_SHORT
* GL_RG16I 3.0 3.0 GL_RG_INTEGER GL_SHORT
* GL_RG32UI 3.0 3.0 GL_RG_INTEGER GL_UNSIGNED_INT
* GL_RG32I 3.0 3.0 GL_RG_INTEGER GL_INT
* GL_RGB8 3.0 3.0 3.0 GL_RGB GL_UNSIGNED_BYTE
* GL_SRGB8 3.0 3.0 GL_RGB GL_UNSIGNED_BYTE
* GL_RGB565 3.0 3.0 3.0 GL_RGB GL_UNSIGNED_BYTE,
* GL_UNSIGNED_SHORT_5_6_5
* GL_RGB8_SNORM 3.0 3.0 GL_RGB GL_BYTE
* GL_R11F_G11F_B10F 3.0 3.0 3.2 GL_RGB GL_UNSIGNED_INT_10F_11F_11F_REV,
* GL_HALF_FLOAT,
* GL_FLOAT
* GL_RGB9_E5 3.0 3.0 GL_RGB GL_UNSIGNED_INT_5_9_9_9_REV,
* GL_HALF_FLOAT,
* GL_FLOAT
* GL_RGB16F 3.0 3.0 GL_RGB GL_HALF_FLOAT,
* GL_FLOAT
* GL_RGB32F 3.0 GL_RGB GL_FLOAT
* GL_RGB8UI 3.0 GL_RGB_INTEGER GL_UNSIGNED_BYTE
* GL_RGB8I 3.0 GL_RGB_INTEGER GL_BYTE
* GL_RGB16UI 3.0 GL_RGB_INTEGER GL_UNSIGNED_SHORT
* GL_RGB16I 3.0 GL_RGB_INTEGER GL_SHORT
* GL_RGB32UI 3.0 GL_RGB_INTEGER GL_UNSIGNED_INT
* GL_RGB32I 3.0 GL_RGB_INTEGER GL_INT
* GL_RGBA8 3.0 3.0 3.0 GL_RGBA GL_UNSIGNED_BYTE
* GL_SRGB8_ALPHA8 3.0 3.0 3.0 GL_RGBA GL_UNSIGNED_BYTE
* GL_RGBA8_SNORM 3.0 3.0 GL_RGBA GL_BYTE
* GL_RGB5_A1 3.0 3.0 3.0 GL_RGBA GL_UNSIGNED_BYTE,
* GL_UNSIGNED_SHORT_5_5_5_1,
* GL_UNSIGNED_INT_2_10_10_10_REV
* GL_RGBA4 3.0 3.0 3.0 GL_RGBA GL_UNSIGNED_BYTE,
* GL_UNSIGNED_SHORT_4_4_4_4
* GL_RGB10_A2 3.0 3.0 3.0 GL_RGBA GL_UNSIGNED_INT_2_10_10_10_REV
* GL_RGBA16F 3.0 3.0 3.2 GL_RGBA GL_HALF_FLOAT,
* GL_FLOAT
* GL_RGBA32F 3.0 3.2 GL_RGBA GL_FLOAT
* GL_RGBA8UI 3.0 3.0 GL_RGBA_INTEGER GL_UNSIGNED_BYTE
* GL_RGBA8I 3.0 3.0 GL_RGBA_INTEGER GL_BYTE
* GL_RGB10_A2UI 3.0 3.0 GL_RGBA_INTEGER GL_UNSIGNED_INT_2_10_10_10_REV
* GL_RGBA16UI 3.0 3.0 GL_RGBA_INTEGER GL_UNSIGNED_SHORT
* GL_RGBA16I 3.0 3.0 GL_RGBA_INTEGER GL_SHORT
* GL_RGBA32I 3.0 3.0 GL_RGBA_INTEGER GL_INT
* GL_RGBA32UI 3.0 3.0 GL_RGBA_INTEGER GL_UNSIGNED_INT
*
*
* ¹ Sized internal format.
* ² Texturable since.
* ³ Texture-filterable (GL_LINEAR support) since.
* Renderable (FBO support) since.
* External format and type combination(s).
*/
/* Check whether gl_fbo_init() supports FBO creation for a given
* colour-renderable sized internal 'format' or not.
*/
bool
gl_fbo_is_format_supported(struct gl_renderer *gr,
GLenum format)
{
switch (format) {
case GL_RGBA4:
case GL_RGB5_A1:
case GL_RGB565:
return true; /* From OpenGL ES 2.0 (Table 4.5 in spec). */
case GL_RGB8:
case GL_RGBA8:
return gr->gl_version >= gl_version(3, 0) ||
gl_extensions_has(gr, EXTENSION_OES_RGB8_RGBA8);
case GL_R8:
case GL_RG8:
case GL_SRGB8_ALPHA8:
case GL_R8I:
case GL_R8UI:
case GL_R16I:
case GL_R16UI:
case GL_R32I:
case GL_R32UI:
case GL_RG8I:
case GL_RG8UI:
case GL_RG16I:
case GL_RG16UI:
case GL_RG32I:
case GL_RG32UI:
case GL_RGBA8I:
case GL_RGBA8UI:
case GL_RGBA16I:
case GL_RGBA16UI:
case GL_RGBA32I:
case GL_RGBA32UI:
case GL_RGB10_A2:
case GL_RGB10_A2UI:
return gr->gl_version >= gl_version(3, 0);
case GL_R16F:
case GL_RG16F:
case GL_RGBA16F:
case GL_R32F:
case GL_RG32F:
case GL_RGBA32F:
case GL_R11F_G11F_B10F:
return gr->gl_version >= gl_version(3, 2);
case GL_R8_SNORM:
case GL_RG8_SNORM:
case GL_SRGB8:
case GL_RGB9_E5:
case GL_RGB16F:
case GL_RGB32F:
case GL_RGB8_SNORM:
case GL_RGB8I:
case GL_RGB8UI:
case GL_RGB16I:
case GL_RGB16UI:
case GL_RGB32I:
case GL_RGB32UI:
case GL_RGBA8_SNORM:
return false;
default:
unreachable("Unsupported sized internal format!");
return false;
}
}
/* Initialise a pair of framebuffer and renderbuffer objects. 'format' is a
* colour-renderable sized internal format listed in Table 1 above with the
* Renderable column filled. The framebuffer object is left bound on success.
* Use gl_fbo_fini() to finalise.
*
* OpenGL ES 2 notes:
*
* Implementations support at least these formats: GL_RGBA4, GL_RGB5_A1 and
* GL_RGB565. Additional formats are supported depending on extensions: GL_RGB8
* and GL_RGBA8.
*
* See gl_fbo_is_format_supported().
*/
bool
gl_fbo_init(struct gl_renderer *gr,
GLenum format,
int width,
int height,
GLuint *fb_out,
GLuint *rb_out)
{
GLuint fb, rb;
GLenum status;
if (!gl_fbo_is_format_supported(gr, format)) {
weston_log("Error: FBO format not supported.\n");
return false;
}
glGenFramebuffers(1, &fb);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
glGenRenderbuffers(1, &rb);
glBindRenderbuffer(GL_RENDERBUFFER, rb);
glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, rb);
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
if (status != GL_FRAMEBUFFER_COMPLETE) {
weston_log("Error: FBO incomplete.\n");
goto error;
}
*fb_out = fb;
*rb_out = rb;
return true;
error:
glDeleteFramebuffers(1, &fb);
glDeleteRenderbuffers(1, &rb);
return false;
}
/* Finalise a pair of framebuffer and renderbuffer objects.
*/
void
gl_fbo_fini(GLuint *fb,
GLuint *rb)
{
glDeleteFramebuffers(1, fb);
glDeleteRenderbuffers(1, rb);
*fb = 0;
*rb = 0;
}
/* Initialise a pair of framebuffer and renderbuffer objects to render into an
* EGL image. The framebuffer object is left bound on success. Use gl_fbo_fini()
* to finalise.
*/
bool
gl_fbo_image_init(struct gl_renderer *gr,
EGLImageKHR image,
GLuint *fb_out,
GLuint *rb_out)
{
GLuint fb, rb;
GLenum status;
glGenFramebuffers(1, &fb);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
glGenRenderbuffers(1, &rb);
glBindRenderbuffer(GL_RENDERBUFFER, rb);
gr->image_target_renderbuffer_storage(GL_RENDERBUFFER, image);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, rb);
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
if (status != GL_FRAMEBUFFER_COMPLETE) {
weston_log("Error: FBO incomplete.\n");
goto error;
}
*fb_out = fb;
*rb_out = rb;
return true;
error:
glDeleteFramebuffers(1, &fb);
glDeleteRenderbuffers(1, &rb);
return false;
}
/* Initialise a pair of framebuffer and texture objects to render into a
* texture. The framebuffer object is left bound on success. Use
* gl_fbo_texture_fini() to finalise.
*/
bool
gl_fbo_texture_init(GLenum internal_format,
int width,
int height,
GLenum format,
GLenum type,
GLuint *fb_out,
GLuint *tex_out)
{
GLenum status;
GLuint fb, tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0,
format, type, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glGenFramebuffers(1, &fb);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, tex, 0);
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
weston_log("Error: FBO incomplete.\n");
goto error;
}
*fb_out = fb;
*tex_out = tex;
return true;
error:
glDeleteFramebuffers(1, &fb);
glDeleteTextures(1, &tex);
return false;
}
/* Finalise a pair of framebuffer and texture objects.
*/
void
gl_fbo_texture_fini(GLuint *fb,
GLuint *tex)
{
glDeleteFramebuffers(1, fb);
glDeleteTextures(1, tex);
*fb = 0;
*tex = 0;
}
/* Add extension flags to the bitfield that 'flags_out' points to. 'table'
* stores extension names and flags to check for and 'extensions' is the list
* usually returned by the EGL or GL implementation. New flags are stored using
* a binary OR in order to keep flags set from a previous call. Caller must
* ensure the bitfield is set to 0 at first call.
*/
void
gl_extensions_add(const struct gl_extension_table *table,
const char *extensions,
uint64_t *flags_out)
{
struct { const char *str; size_t len; } *map;
size_t i = 0, n = 0;
uint64_t flags = 0;
char prev_char = ' ';
/* Get number of extensions. */
while (extensions[i]) {
if (prev_char == ' ' && extensions[i] != ' ')
n++;
prev_char = extensions[i++];
}
if (n == 0)
return;
/* Allocate data structure mapping each extension with their length. */
map = xmalloc(n * sizeof *map);
prev_char = ' ';
i = n = 0;
while (prev_char) {
if (extensions[i] != ' ' && extensions[i] != '\0') {
if (prev_char == ' ')
map[n].str = &extensions[i];
} else if (prev_char != ' ') {
map[n].len = &extensions[i] - map[n].str;
n++;
}
prev_char = extensions[i++];
}
/* Match extensions with table. */
for (; table->str; table++) {
for (i = 0; i < n; i++) {
if (table->len == map[i].len &&
!strncmp(table->str, map[i].str, table->len)) {
flags |= table->flag;
break;
}
}
}
*flags_out |= flags;
free(map);
}