From 5485d806c53ae12d9526543fda202e1aeb4f226c Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 7 Dec 2023 05:45:59 +0100 Subject: [PATCH] glcontext: Add gdk_gl_context_get_memory_flags() Checks which features of a given memory format are supported by the current GL implementation. We check: * usable: Can be used as a texture with NEAREST filter * renderable: Can be used as a render target * filterable: Can be used with GL_LINEAR In normal GL, all formats are all of these things, but GLES is a lot more picky. So far nobody uses this. --- gdk/gdkglcontext.c | 146 +++++++++++++++++++++++++++++++++++++- gdk/gdkglcontextprivate.h | 13 ++++ 2 files changed, 157 insertions(+), 2 deletions(-) diff --git a/gdk/gdkglcontext.c b/gdk/gdkglcontext.c index 489fdb7a5c..e67bee79b6 100644 --- a/gdk/gdkglcontext.c +++ b/gdk/gdkglcontext.c @@ -104,10 +104,15 @@ #define DEFAULT_ALLOWED_APIS GDK_GL_API_GL | GDK_GL_API_GLES -typedef struct { +typedef struct _GdkGLContextPrivate GdkGLContextPrivate; + +struct _GdkGLContextPrivate +{ GdkGLVersion required; GdkGLVersion gl_version; + GdkGLMemoryFlags memory_flags[GDK_MEMORY_N_FORMATS]; + guint has_khr_debug : 1; guint use_khr_debug : 1; guint has_half_float : 1; @@ -129,7 +134,7 @@ typedef struct { EGLContext egl_context; EGLBoolean (*eglSwapBuffersWithDamage) (EGLDisplay, EGLSurface, const EGLint *, EGLint); #endif -} GdkGLContextPrivate; +}; enum { PROP_0, @@ -1524,6 +1529,132 @@ gdk_gl_context_realize (GdkGLContext *context, return priv->api; } +static void +gdk_gl_context_init_memory_flags (GdkGLContext *self) +{ + GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self); + gsize i; + + if (!gdk_gl_context_get_use_es (self)) + { + for (i = 0; i < G_N_ELEMENTS (priv->memory_flags); i++) + { + priv->memory_flags[i] = GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + } + return; + } + + /* GLES 2.0 spec, tables 3.2 and 3.3 */ + priv->memory_flags[GDK_MEMORY_R8G8B8] = GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_R8G8B8A8_PREMULTIPLIED] = GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_R8G8B8A8] = GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE; +#if 0 + /* GLES2 can do these, but GTK can't */ + priv->memory_flags[GDK_MEMORY_A8] = GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_G8] = GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_G8A8_PREMULTIPLIED] = GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_G8A8] = GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE; +#endif + + if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (3, 0))) + { + /* GLES 3.0.6 spec, table 3.13 */ + priv->memory_flags[GDK_MEMORY_G8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_A8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_G8A8_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_G8A8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_R8G8B8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_R8G8B8A8_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_R8G8B8A8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_R8G8B8X8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_R16G16B16_FLOAT] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE; + priv->memory_flags[GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE; + priv->memory_flags[GDK_MEMORY_R16G16B16A16_FLOAT] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE; + priv->memory_flags[GDK_MEMORY_A16_FLOAT] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE; + priv->memory_flags[GDK_MEMORY_R32G32B32_FLOAT] |= GDK_GL_FORMAT_USABLE; + priv->memory_flags[GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE; + priv->memory_flags[GDK_MEMORY_R32G32B32A32_FLOAT] |= GDK_GL_FORMAT_USABLE; + priv->memory_flags[GDK_MEMORY_A32_FLOAT] |= GDK_GL_FORMAT_USABLE; + + /* no changes in GLES 3.1 spec, table 8.13 */ + + if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (3, 2))) + { + /* GLES 3.2 spec, table 8.10 */ + priv->memory_flags[GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED] |= GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_R16G16B16A16_FLOAT] |= GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_A16_FLOAT] |= GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED] |= GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_R32G32B32A32_FLOAT] |= GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_A32_FLOAT] |= GDK_GL_FORMAT_FILTERABLE; + } + } + + if (epoxy_has_gl_extension ("GL_OES_rgb8_rgba8")) + { + priv->memory_flags[GDK_MEMORY_R8G8B8A8_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_R8G8B8A8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_R8G8B8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (3, 0))) + priv->memory_flags[GDK_MEMORY_R8G8B8X8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + } + if (epoxy_has_gl_extension ("GL_EXT_abgr")) + { + priv->memory_flags[GDK_MEMORY_A8B8G8R8_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_A8B8G8R8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (3, 0))) + priv->memory_flags[GDK_MEMORY_X8B8G8R8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + } + if (epoxy_has_gl_extension ("GL_EXT_texture_format_BGRA8888")) + { + priv->memory_flags[GDK_MEMORY_B8G8R8A8_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_B8G8R8A8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (3, 0))) + priv->memory_flags[GDK_MEMORY_B8G8R8X8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + } + + /* Technically, those extensions are supported on GLES2. + * However, GTK uses the wrong format/type pairs with them, so we don't enable them. + */ + if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (3, 0))) + { + if (epoxy_has_gl_extension ("GL_EXT_texture_norm16")) + { + priv->memory_flags[GDK_MEMORY_R16G16B16A16_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_R16G16B16A16] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_R16G16B16] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_G16A16_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_G16A16] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_G16] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_A16] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE; + } + if (epoxy_has_gl_extension ("GL_OES_texture_half_float")) + { + GdkGLMemoryFlags flags = GDK_GL_FORMAT_USABLE; + if (epoxy_has_gl_extension ("GL_EXT_color_buffer_half_float")) + flags |= GDK_GL_FORMAT_RENDERABLE; + if (epoxy_has_gl_extension ("GL_OES_texture_half_float_linear")) + flags |= GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED] |= flags; + priv->memory_flags[GDK_MEMORY_R16G16B16A16_FLOAT] |= flags; + priv->memory_flags[GDK_MEMORY_R16G16B16_FLOAT] |= flags; + priv->memory_flags[GDK_MEMORY_A16_FLOAT] |= flags; + } + if (epoxy_has_gl_extension ("GL_OES_texture_float")) + { + GdkGLMemoryFlags flags = GDK_GL_FORMAT_USABLE; + if (epoxy_has_gl_extension ("GL_EXT_color_buffer_float")) + flags |= GDK_GL_FORMAT_RENDERABLE; + if (epoxy_has_gl_extension ("GL_OES_texture_float_linear")) + flags |= GDK_GL_FORMAT_FILTERABLE; + priv->memory_flags[GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED] |= flags; + priv->memory_flags[GDK_MEMORY_R32G32B32A32_FLOAT] |= flags; + priv->memory_flags[GDK_MEMORY_R32G32B32_FLOAT] |= flags; + priv->memory_flags[GDK_MEMORY_A32_FLOAT] |= flags; + } + } +} + void gdk_gl_version_init_epoxy (GdkGLVersion *version) { @@ -1590,6 +1721,8 @@ gdk_gl_context_check_extensions (GdkGLContext *context) epoxy_has_gl_extension ("GL_ARB_sync") || epoxy_has_gl_extension ("GL_APPLE_sync"); + gdk_gl_context_init_memory_flags (context); + { int max_texture_size; glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size); @@ -1873,6 +2006,15 @@ gdk_gl_context_get_current (void) return context; } +GdkGLMemoryFlags +gdk_gl_context_get_format_flags (GdkGLContext *self, + GdkMemoryFormat format) +{ + GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self); + + return priv->memory_flags[format]; +} + gboolean gdk_gl_context_has_debug (GdkGLContext *self) { diff --git a/gdk/gdkglcontextprivate.h b/gdk/gdkglcontextprivate.h index 9f3ff82ff8..7c8363c659 100644 --- a/gdk/gdkglcontextprivate.h +++ b/gdk/gdkglcontextprivate.h @@ -35,6 +35,17 @@ typedef enum { GDK_GL_CGL } GdkGLBackend; +typedef enum { + /* The format is supported for glTexImage2D() */ + GDK_GL_FORMAT_USABLE = 1 << 0, + /* The format can be rendered to. + * GL/GLES spec term: "color-renderable" */ + GDK_GL_FORMAT_RENDERABLE = 1 << 1, + /* GL_LINEAR/GL_MIPMAP_LINEAR can be used for textures in this format. + * GLES spec term: "texture-filterable" */ + GDK_GL_FORMAT_FILTERABLE = 1 << 2 +} GdkGLMemoryFlags; + /* The maximum amount of buffers we track update regions for. * Note that this is equal to the max buffer age value we * can provide a damage region for */ @@ -150,6 +161,8 @@ void gdk_gl_context_label_object_printf (GdkGLContext const char * gdk_gl_context_get_glsl_version_string (GdkGLContext *self); +GdkGLMemoryFlags gdk_gl_context_get_format_flags (GdkGLContext *self, + GdkMemoryFormat format) G_GNUC_PURE; gboolean gdk_gl_context_has_debug (GdkGLContext *self) G_GNUC_PURE; gboolean gdk_gl_context_use_es_bgra (GdkGLContext *context);