gl-renderer: Add 3D support to texture utilities

Signed-off-by: Loïc Molinari <loic.molinari@collabora.com>
This commit is contained in:
Loïc Molinari 2024-10-15 18:11:18 +02:00 committed by Daniel Stone
parent 7e73234313
commit 0c8b6ce945
3 changed files with 271 additions and 91 deletions

View file

@ -143,6 +143,7 @@ enum gl_extension_flag {
EXTENSION_OES_MAPBUFFER = 1ull << 24,
EXTENSION_OES_REQUIRED_INTERNALFORMAT = 1ull << 25,
EXTENSION_OES_RGB8_RGBA8 = 1ull << 26,
EXTENSION_OES_TEXTURE_3D = 1ull << 27,
EXTENSION_OES_TEXTURE_FLOAT = 1ull << 28,
EXTENSION_OES_TEXTURE_FLOAT_LINEAR = 1ull << 29,
EXTENSION_OES_TEXTURE_HALF_FLOAT = 1ull << 30,
@ -170,7 +171,9 @@ enum gl_feature_flag {
FEATURE_ASYNC_READBACK = 1ull << 3,
/* GL renderer can create 16-bit floating-point framebuffers and
* transform colours using linearly interpolated 3D look-up tables. */
* transform colours using linearly interpolated 3D look-up tables.
*
* Strong dependency on the texture 3D feature. */
FEATURE_COLOR_TRANSFORMS = 1ull << 4,
/* GL renderer can instrument output repaint time and report it through
@ -178,7 +181,10 @@ enum gl_feature_flag {
FEATURE_GPU_TIMELINE = 1ull << 5,
/* GL renderer can specify the entire structure of a texture in a single
* call. Once specified, format and dimensions can't be changed. */
* call. Once specified, format and dimensions can't be changed.
*
* Weak dependency on the texture 3D feature, enabling immutability of
* of 3D textures. */
FEATURE_TEXTURE_IMMUTABILITY = 1ull << 6,
/* GL renderer can create two-component red-green textures. */
@ -188,6 +194,9 @@ enum gl_feature_flag {
* with the GL_BGRA8_EXT sized internal format revision (23/06/2024) for
* renderbuffer objects. */
FEATURE_SIZED_BGRA8_RENDERBUFFER = 1ull << 8,
/* GL renderer can create 3D textures. */
FEATURE_TEXTURE_3D = 1ull << 9,
};
/* Keep the following in sync with vertex.glsl. */
@ -424,6 +433,7 @@ struct gl_renderer {
/* GL_OES_texture_3d */
PFNGLTEXIMAGE3DOESPROC tex_image_3d;
PFNGLTEXSUBIMAGE3DOESPROC tex_sub_image_3d;
/* GL_EXT_disjoint_timer_query */
PFNGLGENQUERIESEXTPROC gen_queries;
@ -437,6 +447,7 @@ struct gl_renderer {
/* GL_EXT_texture_storage */
PFNGLTEXSTORAGE2DEXTPROC tex_storage_2d;
PFNGLTEXSTORAGE3DEXTPROC tex_storage_3d;
uint64_t features;
@ -549,6 +560,28 @@ gl_texture_2d_store(struct gl_renderer *gr,
GLenum type,
const void *data);
bool
gl_texture_3d_init(struct gl_renderer *gr,
int levels,
GLenum format,
int width,
int height,
int depth,
GLuint *tex_out);
bool
gl_texture_3d_store(struct gl_renderer *gr,
int level,
int x,
int y,
int z,
int width,
int height,
int depth,
GLenum format,
GLenum type,
const void *data);
void
gl_texture_fini(GLuint *tex);

View file

@ -301,6 +301,7 @@ static const struct gl_extension_table extension_table[] = {
EXT("GL_OES_mapbuffer", EXTENSION_OES_MAPBUFFER),
EXT("GL_OES_required_internalformat", EXTENSION_OES_REQUIRED_INTERNALFORMAT),
EXT("GL_OES_rgb8_rgba8", EXTENSION_OES_RGB8_RGBA8),
EXT("GL_OES_texture_3D", EXTENSION_OES_TEXTURE_3D),
EXT("GL_OES_texture_float", EXTENSION_OES_TEXTURE_FLOAT),
EXT("GL_OES_texture_float_linear", EXTENSION_OES_TEXTURE_FLOAT_LINEAR),
EXT("GL_OES_texture_half_float", EXTENSION_OES_TEXTURE_HALF_FLOAT),
@ -4789,8 +4790,17 @@ gl_renderer_setup(struct weston_compositor *ec)
~EXTENSION_EXT_DISJOINT_TIMER_QUERY;
}
if (gl_extensions_has(gr, EXTENSION_EXT_TEXTURE_STORAGE))
if (gl_extensions_has(gr, EXTENSION_EXT_TEXTURE_STORAGE)) {
GET_PROC_ADDRESS(gr->tex_storage_2d, "glTexStorage2DEXT");
if (gl_extensions_has(gr, EXTENSION_OES_TEXTURE_3D))
GET_PROC_ADDRESS(gr->tex_storage_3d,
"glTexStorage3DEXT");
}
if (gl_extensions_has(gr, EXTENSION_OES_TEXTURE_3D)) {
GET_PROC_ADDRESS(gr->tex_image_3d, "glTexImage3DOES");
GET_PROC_ADDRESS(gr->tex_sub_image_3d, "glTexSubImage3DOES");
}
/* Async read-back feature. */
if (gr->gl_version >= gl_version(3, 0) &&
@ -4815,15 +4825,24 @@ gl_renderer_setup(struct weston_compositor *ec)
gr->features |= FEATURE_ASYNC_READBACK;
}
/* Texture 3D feature. */
if (gr->gl_version >= gl_version(3, 0) &&
egl_display_has(gr, EXTENSION_KHR_GET_ALL_PROC_ADDRESSES)) {
GET_PROC_ADDRESS(gr->tex_image_3d, "glTexImage3D");
GET_PROC_ADDRESS(gr->tex_sub_image_3d, "glTexSubImage3D");
gr->features |= FEATURE_TEXTURE_3D;
} else if (gl_extensions_has(gr, EXTENSION_OES_TEXTURE_3D)) {
gr->features |= FEATURE_TEXTURE_3D;
}
/* Color transforms feature. */
if ((gr->gl_version >= gl_version(3, 2) &&
egl_display_has(gr, EXTENSION_KHR_GET_ALL_PROC_ADDRESSES) &&
gl_extensions_has(gr, EXTENSION_OES_TEXTURE_FLOAT_LINEAR)) ||
(gr->gl_version >= gl_version(3, 0) &&
egl_display_has(gr, EXTENSION_KHR_GET_ALL_PROC_ADDRESSES) &&
gl_extensions_has(gr, EXTENSION_OES_TEXTURE_FLOAT_LINEAR) &&
gl_extensions_has(gr, EXTENSION_EXT_COLOR_BUFFER_HALF_FLOAT))) {
GET_PROC_ADDRESS(gr->tex_image_3d, "glTexImage3D");
gl_features_has(gr, FEATURE_TEXTURE_3D)) ||
(gr->gl_version >= gl_version(3, 0) &&
gl_extensions_has(gr, EXTENSION_OES_TEXTURE_FLOAT_LINEAR) &&
gl_extensions_has(gr, EXTENSION_EXT_COLOR_BUFFER_HALF_FLOAT) &&
gl_features_has(gr, FEATURE_TEXTURE_3D))) {
gr->features |= FEATURE_COLOR_TRANSFORMS;
}
@ -4836,6 +4855,7 @@ gl_renderer_setup(struct weston_compositor *ec)
if (gr->gl_version >= gl_version(3, 0) &&
egl_display_has(gr, EXTENSION_KHR_GET_ALL_PROC_ADDRESSES)) {
GET_PROC_ADDRESS(gr->tex_storage_2d, "glTexStorage2D");
GET_PROC_ADDRESS(gr->tex_storage_3d, "glTexStorage3D");
gr->features |= FEATURE_TEXTURE_IMMUTABILITY;
} else if (gl_extensions_has(gr, EXTENSION_EXT_TEXTURE_STORAGE)) {
gr->features |= FEATURE_TEXTURE_IMMUTABILITY;

View file

@ -760,50 +760,27 @@ gl_texture_is_format_supported(struct gl_renderer *gr,
}
}
/* 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.
*
* OpenGL ES 2 notes:
*
* Implementations support at least this subset of formats: GL_R8, GL_RG8,
* GL_RGB8, GL_RGB565, GL_RGBA8, GL_RGBA4 and GL_RGB5_A1. Additional formats are
* supported depending on extensions: GL_R16F, GL_RG16F, GL_RGB16F, GL_RGBA16F,
* GL_R32F, GL_RG32F, GL_RGB32F, GL_RGBA32F, GL_R11F_G11F_B10F, GL_RGB9_E5,
* GL_RGB10_A2 and GL_BGRA8_EXT.
*
* This is implemented by implicitly converting 'format' into an external
* format. If the red and red-green texture formats aren't supported
* (FEATURE_TEXTURE_RG flag not set), GL_R8 is converted into a luminance format
* and GL_RG8 into a luminance alpha format. Care must be taken in the latter
* case in order to access the green component in the shader: "c.a" (or "c[3]")
* must be used instead of "c.g" (or "c[1]").
*
* 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)
static void
texture_init(struct gl_renderer *gr,
GLenum target,
int levels,
GLenum format,
int width,
int height,
int depth,
GLuint *tex_out)
{
bool bgra_fallback;
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;
}
assert(levels <= ((int) log2(MAX(width, height)) + 1));
assert(target == GL_TEXTURE_2D ||
target == GL_TEXTURE_3D);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glBindTexture(target, tex);
/* Fallback to TexImage*D() when GL_BGRA8_EXT isn't supported by
* TexStorage*D(). */
@ -843,8 +820,12 @@ gl_texture_2d_init(struct gl_renderer *gr,
}
}
gr->tex_storage_2d(GL_TEXTURE_2D, levels, format, width,
height);
if (target == GL_TEXTURE_2D)
gr->tex_storage_2d(GL_TEXTURE_2D, levels, format, width,
height);
else
gr->tex_storage_3d(GL_TEXTURE_3D, levels, format, width,
height, depth);
} else {
GLenum external_format, type;
int i;
@ -981,22 +962,177 @@ gl_texture_2d_init(struct gl_renderer *gr,
default:
unreachable("Missing conversion to external format!");
return false;
return;
}
/* Allocate storage. */
for (i = 0; i < levels; i++) {
glTexImage2D(GL_TEXTURE_2D, i, format, width, height, 0,
external_format, type, NULL);
width = MAX(width / 2, 1);
height = MAX(height / 2, 1);
if (target == GL_TEXTURE_2D) {
for (i = 0; i < levels; i++) {
glTexImage2D(GL_TEXTURE_2D, i, format, width,
height, 0, external_format, type,
NULL);
width = MAX(width / 2, 1);
height = MAX(height / 2, 1);
}
} else {
for (i = 0; i < levels; i++) {
gr->tex_image_3d(GL_TEXTURE_3D, i, format,
width, height, depth, 0,
external_format, type, NULL);
width = MAX(width / 2, 1);
height = MAX(height / 2, 1);
depth = MAX(depth / 2, 1);
}
}
}
*tex_out = tex;
}
/* 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.
*
* OpenGL ES 2 notes:
*
* Implementations support at least this subset of formats: GL_R8, GL_RG8,
* GL_RGB8, GL_RGB565, GL_RGBA8, GL_RGBA4 and GL_RGB5_A1. Additional formats are
* supported depending on extensions: GL_R16F, GL_RG16F, GL_RGB16F, GL_RGBA16F,
* GL_R32F, GL_RG32F, GL_RGB32F, GL_RGBA32F, GL_R11F_G11F_B10F, GL_RGB9_E5,
* GL_RGB10_A2 and GL_BGRA8_EXT.
*
* This is implemented by implicitly converting 'format' into an external
* format. If the red and red-green texture formats aren't supported
* (FEATURE_TEXTURE_RG flag not set), GL_R8 is converted into a luminance format
* and GL_RG8 into a luminance alpha format. Care must be taken in the latter
* case in order to access the green component in the shader: "c.a" (or "c[3]")
* must be used instead of "c.g" (or "c[1]").
*
* 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)
{
if (!gl_texture_is_format_supported(gr, format)) {
weston_log("Error: texture format not supported.\n");
return false;
}
texture_init(gr, GL_TEXTURE_2D, levels, format, width, height, 1,
tex_out);
return true;
}
/* Initialise a 3D texture object. The texture object is left bound on the 3D
* texture target of the current texture unit on success. The accepted formats
* and OpenGL ES 2 notes are exactly the same as for the 2D init function.
*
* See gl_texture_2d_init().
*/
bool
gl_texture_3d_init(struct gl_renderer *gr,
int levels,
GLenum format,
int width,
int height,
int depth,
GLuint *tex_out)
{
if (!gl_features_has(gr, FEATURE_TEXTURE_3D)) {
weston_log("Error: texture 3D not supported.\n");
return false;
}
if (!gl_texture_is_format_supported(gr, format)) {
weston_log("Error: texture format not supported.\n");
return false;
}
texture_init(gr, GL_TEXTURE_3D, levels, format, width, height, depth,
tex_out);
return true;
}
static void
texture_store(struct gl_renderer *gr,
GLenum target,
int level,
int x,
int y,
int z,
int width,
int height,
int depth,
GLenum format,
GLenum type,
const void *data)
{
#if !defined(NDEBUG)
GLint tex, tex_width, tex_height, tex_depth, tex_internal_format;
#endif
if (!gl_features_has(gr, FEATURE_TEXTURE_RG)) {
if (format == GL_RED)
format = GL_LUMINANCE;
else if (format == GL_RG)
format = GL_LUMINANCE_ALPHA;
}
if (type == GL_HALF_FLOAT && gr->gl_version == gl_version(2, 0))
type = GL_HALF_FLOAT_OES;
#if !defined(NDEBUG)
assert(target == GL_TEXTURE_2D ||
target == GL_TEXTURE_3D);
glGetIntegerv(target == GL_TEXTURE_2D ?
GL_TEXTURE_BINDING_2D :
GL_TEXTURE_BINDING_3D, &tex);
assert(tex != 0);
if (gr->gl_version == gl_version(2, 0)) {
assert(is_valid_combination_es2(gr, format, type));
} else 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(target, level,
GL_TEXTURE_WIDTH, &tex_width);
glGetTexLevelParameteriv(target, level,
GL_TEXTURE_HEIGHT, &tex_height);
if (target == GL_TEXTURE_3D)
glGetTexLevelParameteriv(target, level,
GL_TEXTURE_DEPTH, &tex_depth);
glGetTexLevelParameteriv(target, level,
GL_TEXTURE_INTERNAL_FORMAT,
&tex_internal_format);
assert(level >= 0);
assert(x >= 0);
assert(y >= 0);
assert(z >= 0);
assert(x + width <= tex_width);
assert(y + height <= tex_height);
if (target == GL_TEXTURE_3D)
assert(z + depth <= tex_depth);
assert(is_valid_format_es3(gr, tex_internal_format, format));
assert(is_valid_type_es3(gr, tex_internal_format, type));
}
#endif
if (target == GL_TEXTURE_3D)
gr->tex_sub_image_3d(target, level, x, y, z, width, height,
depth, format, type, data);
else
glTexSubImage2D(target, level, x, y, width, height, format,
type, data);
}
/* 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
@ -1034,48 +1170,39 @@ gl_texture_2d_store(struct gl_renderer *gr,
GLenum type,
const void *data)
{
#if !defined(NDEBUG)
GLint tex, tex_width, tex_height, tex_internal_format;
#endif
texture_store(gr, GL_TEXTURE_2D, level, x, y, 0, width, height, 1,
format, type, data);
}
if (!gl_features_has(gr, FEATURE_TEXTURE_RG)) {
if (format == GL_RED)
format = GL_LUMINANCE;
else if (format == GL_RG)
format = GL_LUMINANCE_ALPHA;
/* Store data into the texture object bound to the 3D texture target of the
* current texture unit. The texture object is left bound. No texture parameters
* are set. The accepted external format and type combination and the OpenGL ES
* 2 notes are exactly the same as for the 2D store function.
*
* See gl_texture_store_2d() and gl_texture_3d_init().
*/
bool
gl_texture_3d_store(struct gl_renderer *gr,
int level,
int x,
int y,
int z,
int width,
int height,
int depth,
GLenum format,
GLenum type,
const void *data)
{
if (!gl_features_has(gr, FEATURE_TEXTURE_3D)) {
weston_log("Error: texture 3D not supported.\n");
return false;
}
if (type == GL_HALF_FLOAT && gr->gl_version == gl_version(2, 0))
type = GL_HALF_FLOAT_OES;
texture_store(gr, GL_TEXTURE_3D, level, x, y, z, width, height, depth,
format, type, data);
#if !defined(NDEBUG)
glGetIntegerv(GL_TEXTURE_BINDING_2D, &tex);
assert(tex != 0);
if (gr->gl_version == gl_version(2, 0)) {
assert(is_valid_combination_es2(gr, format, type));
} else 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);
return true;
}
/* Finalise a texture object.