gl-renderer: Add texture utilities

Add gl_texture_is_format_supported(), gl_texture_2d_init(),
gl_texture_2d_upload() and gl_texture_fini() utilities. The creation
function accepts all the coloured sized internal format from OpenGL ES
3 using glTexStorage2D() to create immutable textures and reduce API
overhead.

Co-authored-by: Daniel Stone <daniels@collabora.com>
Signed-off-by: Loïc Molinari <loic.molinari@collabora.com>
This commit is contained in:
Loïc Molinari 2024-09-06 16:07:34 +02:00 committed by Daniel Stone
parent 8c74b48493
commit a7611efce9
2 changed files with 461 additions and 0 deletions

View file

@ -487,6 +487,32 @@ gl_features_has(struct gl_renderer *gr,
return (bool) (gr->features & ((uint64_t) flag));
}
bool
gl_texture_is_format_supported(struct gl_renderer *gr,
GLenum format);
bool
gl_texture_2d_init(struct gl_renderer *gr,
int levels,
GLenum format,
int width,
int height,
GLuint *tex_out);
void
gl_texture_2d_store(struct gl_renderer *gr,
int level,
int x,
int y,
int width,
int height,
GLenum format,
GLenum type,
const void *data);
void
gl_texture_fini(GLuint *tex);
bool
gl_fbo_is_format_supported(struct gl_renderer *gr,
GLenum format);

View file

@ -107,6 +107,441 @@
* External format and type combination(s).
*/
#if !defined(NDEBUG)
/* Validate an external format for a given OpenGL ES 3 sized internal colour
* format. Based on Table 1 above.
*/
static bool
is_valid_format_es3(struct gl_renderer *gr,
GLenum internal_format,
GLenum external_format)
{
assert(gr->gl_version >= gl_version(3, 0));
switch (internal_format) {
case GL_R8I:
case GL_R8UI:
case GL_R16I:
case GL_R16UI:
case GL_R32I:
case GL_R32UI:
return external_format == GL_RED_INTEGER;
case GL_R8:
case GL_R8_SNORM:
case GL_R16F:
case GL_R32F:
return external_format == GL_RED;
case GL_RG8I:
case GL_RG8UI:
case GL_RG16I:
case GL_RG16UI:
case GL_RG32I:
case GL_RG32UI:
return external_format == GL_RG_INTEGER;
case GL_RG8:
case GL_RG8_SNORM:
case GL_RG16F:
case GL_RG32F:
return external_format == GL_RG;
case GL_RGB8I:
case GL_RGB8UI:
case GL_RGB16I:
case GL_RGB16UI:
case GL_RGB32I:
case GL_RGB32UI:
return external_format == GL_RGB_INTEGER;
case GL_RGB8:
case GL_RGB8_SNORM:
case GL_RGB16F:
case GL_RGB32F:
case GL_R11F_G11F_B10F:
case GL_RGB9_E5:
case GL_RGB565:
case GL_SRGB8:
return external_format == GL_RGB;
case GL_RGBA8I:
case GL_RGBA8UI:
case GL_RGBA16I:
case GL_RGBA16UI:
case GL_RGBA32I:
case GL_RGBA32UI:
case GL_RGB10_A2UI:
return external_format == GL_RGBA_INTEGER;
case GL_RGBA8:
case GL_RGBA8_SNORM:
case GL_RGBA16F:
case GL_RGBA32F:
case GL_RGB10_A2:
case GL_SRGB8_ALPHA8:
case GL_RGB5_A1:
case GL_RGBA4:
return external_format == GL_RGBA;
default:
return false;
}
}
/* Validate an external type for a given OpenGL ES 3 sized internal colour
* format. Based on Table 1 above.
*/
static bool
is_valid_type_es3(struct gl_renderer *gr,
GLenum internal_format,
GLenum type)
{
assert(gr->gl_version >= gl_version(3, 0));
switch (internal_format) {
case GL_R8:
case GL_R8UI:
case GL_RG8:
case GL_RG8UI:
case GL_RGB8:
case GL_RGB8UI:
case GL_RGBA8:
case GL_RGBA8UI:
case GL_SRGB8:
case GL_SRGB8_ALPHA8:
return type == GL_UNSIGNED_BYTE;
case GL_R8I:
case GL_R8_SNORM:
case GL_RG8I:
case GL_RG8_SNORM:
case GL_RGB8I:
case GL_RGB8_SNORM:
case GL_RGBA8I:
case GL_RGBA8_SNORM:
return type == GL_BYTE;
case GL_R16UI:
case GL_RG16UI:
case GL_RGB16UI:
case GL_RGBA16UI:
return type == GL_UNSIGNED_SHORT;
case GL_R16I:
case GL_RG16I:
case GL_RGB16I:
case GL_RGBA16I:
return type == GL_SHORT;
case GL_R32UI:
case GL_RG32UI:
case GL_RGB32UI:
case GL_RGBA32UI:
return type == GL_UNSIGNED_INT;
case GL_RGB10_A2UI:
return type == GL_UNSIGNED_INT_2_10_10_10_REV;
case GL_R32I:
case GL_RG32I:
case GL_RGB32I:
case GL_RGBA32I:
return type == GL_INT;
case GL_R32F:
case GL_RG32F:
case GL_RGB32F:
case GL_RGBA32F:
return type == GL_FLOAT;
case GL_R16F:
case GL_RG16F:
case GL_RGB16F:
case GL_RGBA16F:
return type == GL_HALF_FLOAT ||
type == GL_FLOAT;
case GL_RGB565:
return type == GL_UNSIGNED_BYTE ||
type == GL_UNSIGNED_SHORT_5_6_5;
case GL_R11F_G11F_B10F:
return type == GL_UNSIGNED_INT_10F_11F_11F_REV ||
type == GL_HALF_FLOAT ||
type == GL_FLOAT;
case GL_RGB9_E5:
return type == GL_UNSIGNED_INT_5_9_9_9_REV ||
type == GL_HALF_FLOAT ||
type == GL_FLOAT;
case GL_RGB5_A1:
return type == GL_UNSIGNED_BYTE ||
type == GL_UNSIGNED_SHORT_5_5_5_1 ||
type == GL_UNSIGNED_INT_2_10_10_10_REV;
case GL_RGBA4:
return type == GL_UNSIGNED_BYTE ||
type == GL_UNSIGNED_SHORT_4_4_4_4;
case GL_RGB10_A2:
return type == GL_UNSIGNED_INT_2_10_10_10_REV;
default:
return false;
}
}
/* Validate an external format and type combination for OpenGL ES 3.
*/
static bool
is_valid_combination_es3(struct gl_renderer *gr,
GLenum external_format,
GLenum type)
{
assert(gr->gl_version >= gl_version(3, 0));
switch (external_format) {
case GL_RED:
case GL_RG:
switch (type) {
case GL_UNSIGNED_BYTE:
case GL_BYTE:
case GL_HALF_FLOAT:
case GL_FLOAT:
return true;
default:
return false;
}
case GL_RED_INTEGER:
case GL_RG_INTEGER:
case GL_RGB_INTEGER:
switch (type) {
case GL_UNSIGNED_BYTE:
case GL_BYTE:
case GL_UNSIGNED_SHORT:
case GL_SHORT:
case GL_UNSIGNED_INT:
case GL_INT:
return true;
default:
return false;
}
case GL_RGB:
switch (type) {
case GL_UNSIGNED_BYTE:
case GL_BYTE:
case GL_UNSIGNED_SHORT_5_6_5:
case GL_UNSIGNED_INT_10F_11F_11F_REV:
case GL_UNSIGNED_INT_5_9_9_9_REV:
case GL_HALF_FLOAT:
case GL_FLOAT:
return true;
default:
return false;
}
case GL_RGBA:
switch (type) {
case GL_UNSIGNED_BYTE:
case GL_BYTE:
case GL_UNSIGNED_SHORT_4_4_4_4:
case GL_UNSIGNED_SHORT_5_5_5_1:
case GL_UNSIGNED_INT_2_10_10_10_REV:
case GL_HALF_FLOAT:
case GL_FLOAT:
return true;
default:
return false;
}
case GL_RGBA_INTEGER:
switch (type) {
case GL_UNSIGNED_BYTE:
case GL_BYTE:
case GL_UNSIGNED_SHORT:
case GL_SHORT:
case GL_UNSIGNED_INT:
case GL_INT:
case GL_UNSIGNED_INT_2_10_10_10_REV:
return true;
default:
return false;
}
default:
return false;
}
}
#endif /* !defined(NDEBUG) */
/* Check whether gl_texture_2d_init() supports texture creation for a given
* coloured sized internal format or not.
*/
bool
gl_texture_is_format_supported(struct gl_renderer *gr,
GLenum format)
{
switch (format) {
case GL_R8I:
case GL_R8UI:
case GL_R8:
case GL_R8_SNORM:
case GL_R16I:
case GL_R16UI:
case GL_R16F:
case GL_R32I:
case GL_R32UI:
case GL_R32F:
case GL_RG8I:
case GL_RG8UI:
case GL_RG8:
case GL_RG8_SNORM:
case GL_RG16I:
case GL_RG16UI:
case GL_RG16F:
case GL_RG32I:
case GL_RG32UI:
case GL_RG32F:
case GL_RGB8I:
case GL_RGB8UI:
case GL_RGB8:
case GL_RGB8_SNORM:
case GL_RGB16I:
case GL_RGB16UI:
case GL_RGB16F:
case GL_RGB32I:
case GL_RGB32UI:
case GL_RGB32F:
case GL_R11F_G11F_B10F:
case GL_RGB9_E5:
case GL_RGB565:
case GL_SRGB8:
case GL_RGBA8I:
case GL_RGBA8UI:
case GL_RGBA8:
case GL_RGBA8_SNORM:
case GL_RGBA16I:
case GL_RGBA16UI:
case GL_RGBA16F:
case GL_RGBA32I:
case GL_RGBA32UI:
case GL_RGBA32F:
case GL_RGB10_A2:
case GL_RGB10_A2UI:
case GL_SRGB8_ALPHA8:
case GL_RGB5_A1:
case GL_RGBA4:
return gr->gl_version >= gl_version(3, 0);
default:
unreachable("Unsupported sized internal format!");
return false;
}
}
/* Initialise a 2D texture object. 'format' is a coloured sized internal format
* listed in Table 1 above with the Texturable column filled. The texture object
* is left bound on the 2D texture target of the current texture unit on
* success. No texture parameters are set. Use gl_texture_fini() to finalise.
*
* See gl_texture_is_format_supported().
*/
bool
gl_texture_2d_init(struct gl_renderer *gr,
int levels,
GLenum format,
int width,
int height,
GLuint *tex_out)
{
GLuint tex;
assert(width > 0);
assert(height > 0);
assert(levels <= (int) log2(MAX(width, height)) + 1);
if (!gl_texture_is_format_supported(gr, format)) {
weston_log("Error: texture format not supported.\n");
return false;
}
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexStorage2D(GL_TEXTURE_2D, levels, format, width, height);
*tex_out = tex;
return true;
}
/* Store data into the texture object bound to the 2D texture target of the
* current texture unit. 'format' and 'type' must be a valid external format and
* type combination for the internal format of the texture object as listed in
* Table 1 above. The texture object is left bound. No texture parameters are
* set.
*
* See gl_texture_2d_init().
*/
void
gl_texture_2d_store(struct gl_renderer *gr,
int level,
int x,
int y,
int width,
int height,
GLenum format,
GLenum type,
const void *data)
{
#if !defined(NDEBUG)
GLint tex, tex_width, tex_height, tex_internal_format;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &tex);
assert(tex != 0);
if (gr->gl_version == gl_version(3, 0)) {
assert(is_valid_combination_es3(gr, format, type));
} else if (gr->gl_version >= gl_version(3, 1)) {
glGetTexLevelParameteriv(GL_TEXTURE_2D, level,
GL_TEXTURE_WIDTH, &tex_width);
glGetTexLevelParameteriv(GL_TEXTURE_2D, level,
GL_TEXTURE_HEIGHT, &tex_height);
glGetTexLevelParameteriv(GL_TEXTURE_2D, level,
GL_TEXTURE_INTERNAL_FORMAT,
&tex_internal_format);
assert(level >= 0);
assert(x >= 0);
assert(y >= 0);
assert(x + width <= tex_width);
assert(y + height <= tex_height);
assert(is_valid_format_es3(gr, tex_internal_format, format));
assert(is_valid_type_es3(gr, tex_internal_format, type));
}
#endif
glTexSubImage2D(GL_TEXTURE_2D, level, x, y, width, height, format, type,
data);
}
/* Finalise a texture object.
*/
void
gl_texture_fini(GLuint *tex)
{
glDeleteTextures(1, tex);
*tex = 0;
}
/* Check whether gl_fbo_init() supports FBO creation for a given
* colour-renderable sized internal 'format' or not.
*/