renderer/native-gles3: Add fallback using a shader
The EGL context can only import and blit an EGLImage if the backing DMA buffer has a format modifier combination that is advertised as supported and not marked as "external_only". When the context can't blit the imported image, we can still paint using it GL_OES_EGL_image_external using the texture target GL_TEXTURE_EXTERNAL_OES. However, treat drivers who doesn't support modifiers at all as if they do support blitting, if the modifier is 'linear', to avoid regressions. [jadahl: Make shader path a fallback to allow hardware to utilize copy engines via blitting] Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6221 Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2247 Related: https://launchpad.net/bugs/1970291 now only falls back if modifiers are supported, and they mark linear as export only. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3304>
This commit is contained in:
parent
b065dce194
commit
2d3a2a52b8
@ -3,6 +3,7 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Red Hat
|
||||
* Copyright (c) 2018 DisplayLink (UK) Ltd.
|
||||
* Copyright (c) 2023 Canonical Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@ -44,11 +45,232 @@
|
||||
#error "Somehow included OpenGL headers when we shouldn't have"
|
||||
#endif
|
||||
|
||||
typedef struct _ContextData
|
||||
{
|
||||
GArray *buffer_support;
|
||||
GLuint shader_program;
|
||||
} ContextData;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t drm_format;
|
||||
uint64_t drm_modifier;
|
||||
gboolean can_blit;
|
||||
} BufferTypeSupport;
|
||||
|
||||
static void
|
||||
paint_egl_image (MetaGles3 *gles3,
|
||||
EGLImageKHR egl_image,
|
||||
int width,
|
||||
int height)
|
||||
context_data_free (ContextData *context_data)
|
||||
{
|
||||
g_array_free (context_data->buffer_support, TRUE);
|
||||
g_free (context_data);
|
||||
}
|
||||
|
||||
static GQuark
|
||||
get_quark_for_egl_context (EGLContext egl_context)
|
||||
{
|
||||
char key[128];
|
||||
|
||||
g_snprintf (key, sizeof key, "EGLContext %p", egl_context);
|
||||
|
||||
return g_quark_from_string (key);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
can_blit_buffer (ContextData *context_data,
|
||||
MetaEgl *egl,
|
||||
EGLDisplay egl_display,
|
||||
uint32_t drm_format,
|
||||
uint64_t drm_modifier)
|
||||
{
|
||||
EGLint num_modifiers;
|
||||
EGLuint64KHR *modifiers;
|
||||
EGLBoolean *external_only;
|
||||
g_autoptr (GError) error = NULL;
|
||||
int i;
|
||||
gboolean can_blit;
|
||||
BufferTypeSupport support;
|
||||
|
||||
can_blit = drm_modifier == DRM_FORMAT_MOD_LINEAR;
|
||||
|
||||
for (i = 0; i < context_data->buffer_support->len; i++)
|
||||
{
|
||||
BufferTypeSupport *support =
|
||||
&g_array_index (context_data->buffer_support, BufferTypeSupport, i);
|
||||
|
||||
if (support->drm_format == drm_format &&
|
||||
support->drm_modifier == drm_modifier)
|
||||
return support->can_blit;
|
||||
}
|
||||
|
||||
if (!meta_egl_has_extensions (egl, egl_display, NULL,
|
||||
"EGL_EXT_image_dma_buf_import_modifiers",
|
||||
NULL))
|
||||
{
|
||||
meta_topic (META_DEBUG_RENDER,
|
||||
"No support for EGL_EXT_image_dma_buf_import_modifiers, "
|
||||
"assuming blitting linearly will still work.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!meta_egl_query_dma_buf_modifiers (egl, egl_display,
|
||||
drm_format, 0, NULL, NULL,
|
||||
&num_modifiers, &error))
|
||||
{
|
||||
meta_topic (META_DEBUG_RENDER,
|
||||
"Failed to query supported DMA buffer modifiers (%s), "
|
||||
"assuming blitting linearly will still work.",
|
||||
error->message);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (num_modifiers == 0)
|
||||
goto out;
|
||||
|
||||
modifiers = g_alloca0 (sizeof (EGLuint64KHR) * num_modifiers);
|
||||
external_only = g_alloca0 (sizeof (EGLBoolean) * num_modifiers);
|
||||
if (!meta_egl_query_dma_buf_modifiers (egl, egl_display,
|
||||
drm_format, num_modifiers,
|
||||
modifiers, external_only,
|
||||
&num_modifiers, &error))
|
||||
{
|
||||
g_warning ("Failed to requery supported DMA buffer modifiers: %s",
|
||||
error->message);
|
||||
can_blit = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
can_blit = FALSE;
|
||||
for (i = 0; i < num_modifiers; i++)
|
||||
{
|
||||
if (drm_modifier == modifiers[i])
|
||||
{
|
||||
can_blit = !external_only[i];
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
support = (BufferTypeSupport) {
|
||||
.drm_format = drm_format,
|
||||
.drm_modifier = drm_modifier,
|
||||
.can_blit = can_blit,
|
||||
};
|
||||
g_array_append_val (context_data->buffer_support, support);
|
||||
return can_blit;
|
||||
}
|
||||
|
||||
static GLuint
|
||||
load_shader (const char *src,
|
||||
GLenum type)
|
||||
{
|
||||
GLuint shader = glCreateShader (type);
|
||||
|
||||
if (shader)
|
||||
{
|
||||
GLint compiled;
|
||||
|
||||
glShaderSource (shader, 1, &src, NULL);
|
||||
glCompileShader (shader);
|
||||
glGetShaderiv (shader, GL_COMPILE_STATUS, &compiled);
|
||||
if (!compiled)
|
||||
{
|
||||
GLchar log[1024];
|
||||
|
||||
glGetShaderInfoLog (shader, sizeof (log) - 1, NULL, log);
|
||||
log[sizeof (log) - 1] = '\0';
|
||||
g_warning ("load_shader compile failed: %s", log);
|
||||
glDeleteShader (shader);
|
||||
shader = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
static void
|
||||
ensure_shader_program (ContextData *context_data,
|
||||
MetaGles3 *gles3)
|
||||
{
|
||||
static const char vertex_shader_source[] =
|
||||
"#version 100\n"
|
||||
"attribute vec2 position;\n"
|
||||
"attribute vec2 texcoord;\n"
|
||||
"varying vec2 v_texcoord;\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" gl_Position = vec4(position, 0.0, 1.0);\n"
|
||||
" v_texcoord = texcoord;\n"
|
||||
"}\n";
|
||||
|
||||
static const char fragment_shader_source[] =
|
||||
"#version 100\n"
|
||||
"#extension GL_OES_EGL_image_external : require\n"
|
||||
"precision mediump float;\n"
|
||||
"uniform samplerExternalOES s_texture;\n"
|
||||
"varying vec2 v_texcoord;\n"
|
||||
"\n"
|
||||
" void main()\n"
|
||||
"{\n"
|
||||
" gl_FragColor = texture2D(s_texture, v_texcoord);\n"
|
||||
"}\n";
|
||||
|
||||
static const GLfloat box[] =
|
||||
{ /* position texcoord */
|
||||
-1.0f, +1.0f, 0.0f, 0.0f,
|
||||
+1.0f, +1.0f, 1.0f, 0.0f,
|
||||
+1.0f, -1.0f, 1.0f, 1.0f,
|
||||
-1.0f, -1.0f, 0.0f, 1.0f,
|
||||
};
|
||||
GLint linked;
|
||||
GLuint vertex_shader, fragment_shader;
|
||||
GLint position_attrib, texcoord_attrib;
|
||||
GLuint shader_program;
|
||||
|
||||
if (context_data->shader_program)
|
||||
return;
|
||||
|
||||
shader_program = glCreateProgram ();
|
||||
g_return_if_fail (shader_program);
|
||||
context_data->shader_program = shader_program;
|
||||
|
||||
vertex_shader = load_shader (vertex_shader_source, GL_VERTEX_SHADER);
|
||||
g_return_if_fail (vertex_shader);
|
||||
fragment_shader = load_shader (fragment_shader_source, GL_FRAGMENT_SHADER);
|
||||
g_return_if_fail (fragment_shader);
|
||||
|
||||
GLBAS (gles3, glAttachShader, (shader_program, vertex_shader));
|
||||
GLBAS (gles3, glAttachShader, (shader_program, fragment_shader));
|
||||
GLBAS (gles3, glLinkProgram, (shader_program));
|
||||
GLBAS (gles3, glGetProgramiv, (shader_program, GL_LINK_STATUS, &linked));
|
||||
if (!linked)
|
||||
{
|
||||
GLchar log[1024];
|
||||
|
||||
glGetProgramInfoLog (shader_program, sizeof (log) - 1, NULL, log);
|
||||
log[sizeof (log) - 1] = '\0';
|
||||
g_warning ("Link failed: %s", log);
|
||||
return;
|
||||
}
|
||||
|
||||
GLBAS (gles3, glUseProgram, (shader_program));
|
||||
|
||||
position_attrib = glGetAttribLocation (shader_program, "position");
|
||||
GLBAS (gles3, glEnableVertexAttribArray, (position_attrib));
|
||||
GLBAS (gles3, glVertexAttribPointer,
|
||||
(position_attrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof (GLfloat), box));
|
||||
|
||||
texcoord_attrib = glGetAttribLocation (shader_program, "texcoord");
|
||||
GLBAS (gles3, glEnableVertexAttribArray, (texcoord_attrib));
|
||||
GLBAS (gles3, glVertexAttribPointer,
|
||||
(texcoord_attrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof (GLfloat), box + 2));
|
||||
}
|
||||
|
||||
static void
|
||||
blit_egl_image (MetaGles3 *gles3,
|
||||
EGLImageKHR egl_image,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
GLuint texture;
|
||||
GLuint framebuffer;
|
||||
@ -88,6 +310,43 @@ paint_egl_image (MetaGles3 *gles3,
|
||||
GLBAS (gles3, glDeleteFramebuffers, (1, &framebuffer));
|
||||
}
|
||||
|
||||
static void
|
||||
paint_egl_image (ContextData *context_data,
|
||||
MetaGles3 *gles3,
|
||||
EGLImageKHR egl_image,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
GLuint texture;
|
||||
|
||||
meta_gles3_clear_error (gles3);
|
||||
ensure_shader_program (context_data, gles3);
|
||||
|
||||
GLBAS (gles3, glViewport, (0, 0, width, height));
|
||||
|
||||
GLBAS (gles3, glActiveTexture, (GL_TEXTURE0));
|
||||
GLBAS (gles3, glGenTextures, (1, &texture));
|
||||
GLBAS (gles3, glBindTexture, (GL_TEXTURE_EXTERNAL_OES, texture));
|
||||
GLEXT (gles3, glEGLImageTargetTexture2DOES, (GL_TEXTURE_EXTERNAL_OES,
|
||||
egl_image));
|
||||
GLBAS (gles3, glTexParameteri, (GL_TEXTURE_EXTERNAL_OES,
|
||||
GL_TEXTURE_MAG_FILTER,
|
||||
GL_NEAREST));
|
||||
GLBAS (gles3, glTexParameteri, (GL_TEXTURE_EXTERNAL_OES,
|
||||
GL_TEXTURE_MIN_FILTER,
|
||||
GL_NEAREST));
|
||||
GLBAS (gles3, glTexParameteri, (GL_TEXTURE_EXTERNAL_OES,
|
||||
GL_TEXTURE_WRAP_S,
|
||||
GL_CLAMP_TO_EDGE));
|
||||
GLBAS (gles3, glTexParameteri, (GL_TEXTURE_EXTERNAL_OES,
|
||||
GL_TEXTURE_WRAP_T,
|
||||
GL_CLAMP_TO_EDGE));
|
||||
|
||||
GLBAS (gles3, glDrawArrays, (GL_TRIANGLE_FAN, 0, 4));
|
||||
|
||||
GLBAS (gles3, glDeleteTextures, (1, &texture));
|
||||
}
|
||||
|
||||
gboolean
|
||||
meta_renderer_native_gles3_blit_shared_bo (MetaEgl *egl,
|
||||
MetaGles3 *gles3,
|
||||
@ -108,6 +367,28 @@ meta_renderer_native_gles3_blit_shared_bo (MetaEgl *egl,
|
||||
uint32_t format;
|
||||
EGLImageKHR egl_image;
|
||||
gboolean use_modifiers;
|
||||
GQuark context_data_quark;
|
||||
ContextData *context_data;
|
||||
gboolean can_blit;
|
||||
|
||||
context_data_quark = get_quark_for_egl_context (egl_context);
|
||||
context_data = g_object_get_qdata (G_OBJECT (gles3), context_data_quark);
|
||||
if (!context_data)
|
||||
{
|
||||
context_data = g_new0 (ContextData, 1);
|
||||
context_data->buffer_support = g_array_new (FALSE, FALSE,
|
||||
sizeof (BufferTypeSupport));
|
||||
|
||||
g_object_set_qdata_full (G_OBJECT (gles3),
|
||||
context_data_quark,
|
||||
context_data,
|
||||
(GDestroyNotify) context_data_free);
|
||||
}
|
||||
|
||||
can_blit = can_blit_buffer (context_data,
|
||||
egl, egl_display,
|
||||
gbm_bo_get_format (shared_bo),
|
||||
gbm_bo_get_modifier (shared_bo));
|
||||
|
||||
shared_bo_fd = gbm_bo_get_fd (shared_bo);
|
||||
if (shared_bo_fd < 0)
|
||||
@ -153,7 +434,10 @@ meta_renderer_native_gles3_blit_shared_bo (MetaEgl *egl,
|
||||
if (!egl_image)
|
||||
return FALSE;
|
||||
|
||||
paint_egl_image (gles3, egl_image, width, height);
|
||||
if (can_blit)
|
||||
blit_egl_image (gles3, egl_image, width, height);
|
||||
else
|
||||
paint_egl_image (context_data, gles3, egl_image, width, height);
|
||||
|
||||
meta_egl_destroy_image (egl, egl_display, egl_image, NULL);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user