mirror of
https://gitlab.freedesktop.org/wayland/weston.git
synced 2025-12-22 11:30:13 +01:00
Validate the colour-renderable sized internal formats passed to gl_fbo_init() using a new gl_fbo_is_format_supported() utility. Sized internal formats are now required. More formats will progressively be added depending on the OpenGL ES version and the supported extensions. Support for RGBA8 FBOs is now required by the GL renderer. Signed-off-by: Loïc Molinari <loic.molinari@collabora.com>
393 lines
16 KiB
C
393 lines
16 KiB
C
/*
|
|
* 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);
|
|
}
|