1
0
Fork 0

onscreen/native: Provide damage rectangles to secondary GPUs in copy path

This provides damage rectangles to the secondary GPU in the copy path so
it, much like the primary GPU, can avoid updating parts of the framebuffer
that haven't been changed since the last frame(s). This works in tandem
with EGL_EXT_buffer_age to determine how old the contents of the current
framebuffer are (i.e. to know if there is anything to reuse from the last
frame).

(cherry picked from commit bf4a767ac3a36eac7a2fc612550a218ebfb9480d)
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4073>
Signed-off-by: Mingi Sung <sungmg@saltyming.net>
This commit is contained in:
Gert-dev 2024-10-10 20:36:19 +02:00 committed by Mingi Sung
parent 443366ede5
commit 47293a748d
Signed by: sungmg
GPG key ID: 41BAFD6FFD8036C5
3 changed files with 304 additions and 53 deletions

View file

@ -53,6 +53,18 @@
#include "common/meta-cogl-drm-formats.h"
#include "common/meta-drm-format-helpers.h"
/*
The maximum amount of damage rectangles to maintain. At a certain point there are so many damage rectangles that
individually processing them isn't worth the time they save based on a full redraw, which is determined by this
number.
*/
#define MAX_DAMAGE_RECTANGLES 16
/*
The maximum supported buffer age for secondary GPU surfaces.
*/
#define MAX_SECONDARY_GPU_BUFFER_AGE 4
typedef enum _MetaSharedFramebufferImportStatus
{
/* Not tried importing yet. */
@ -63,6 +75,12 @@ typedef enum _MetaSharedFramebufferImportStatus
META_SHARED_FRAMEBUFFER_IMPORT_STATUS_OK
} MetaSharedFramebufferImportStatus;
typedef struct _MetaOnscreenNativeSecondaryGpuDamageRectangleInfo
{
MtkRectangle rectangles[MAX_DAMAGE_RECTANGLES];
int n_rectangles;
} MetaOnscreenNativeSecondaryGpuDamageRectangleInfo;
typedef struct _MetaOnscreenNativeSecondaryGpuState
{
MetaGpuKms *gpu_kms;
@ -79,6 +97,8 @@ typedef struct _MetaOnscreenNativeSecondaryGpuState
MetaDrmBufferDumb *dumb_fbs[2];
} cpu;
MetaOnscreenNativeSecondaryGpuDamageRectangleInfo damage_rectangles[MAX_SECONDARY_GPU_BUFFER_AGE];
gboolean noted_primary_gpu_copy_ok;
gboolean noted_primary_gpu_copy_failed;
MetaSharedFramebufferImportStatus import_status;
@ -831,11 +851,94 @@ import_shared_framebuffer (CoglOnscreen *onscreen,
return imported_buffer;
}
static void
push_secondary_gpu_damage_rectangles (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state,
const int *rectangles,
int n_rectangles)
{
int i;
if (n_rectangles > MAX_DAMAGE_RECTANGLES)
n_rectangles = 0;
for (i = MAX_SECONDARY_GPU_BUFFER_AGE - 1; i > 0; --i)
{
if (secondary_gpu_state->damage_rectangles[i - 1].n_rectangles > 0)
memcpy (secondary_gpu_state->damage_rectangles[i].rectangles,
secondary_gpu_state->damage_rectangles[i - 1].rectangles,
secondary_gpu_state->damage_rectangles[i - 1].n_rectangles * sizeof (MtkRectangle));
secondary_gpu_state->damage_rectangles[i].n_rectangles = secondary_gpu_state->damage_rectangles[i - 1].n_rectangles;
}
if (n_rectangles > 0)
memcpy (secondary_gpu_state->damage_rectangles[0].rectangles,
rectangles,
n_rectangles * sizeof (MtkRectangle));
secondary_gpu_state->damage_rectangles[0].n_rectangles = n_rectangles;
}
static int
calculate_secondary_gpu_damage_rectangle_count (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state,
int buffer_age)
{
int i;
int union_n_rectangles = 0;
for (i = 0; i <= buffer_age; ++i)
{
/* Everything being damaged in a previous frame means we also need to damage everything now. */
if (secondary_gpu_state->damage_rectangles[i].n_rectangles == 0)
return 0;
union_n_rectangles += secondary_gpu_state->damage_rectangles[i].n_rectangles;
}
return union_n_rectangles;
}
static int
get_secondary_gpu_buffer_age (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state,
MetaRendererNativeGpuData *renderer_gpu_data)
{
MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
MetaRenderDevice *render_device;
EGLDisplay egl_display;
int buffer_age;
g_autoptr (GError) error = NULL;
render_device = renderer_gpu_data->render_device;
egl_display = meta_render_device_get_egl_display (render_device);
if (!meta_egl_query_surface (egl, egl_display, secondary_gpu_state->egl_surface, EGL_BUFFER_AGE_EXT, &buffer_age, &error))
{
g_warning ("Failed to query age of surface, ignoring damage rectangles and fully redrawing, "
"which may cause increased GPU power consumption: %s", error->message);
return 0;
}
if (buffer_age > MAX_SECONDARY_GPU_BUFFER_AGE)
{
g_warning ("Secondary GPU provides buffers of age %i, which is older than supported; "
"ignoring damage rectangles and fully redrawing which may cause incraesed GPU power consumption",
buffer_age);
return 0;
}
return buffer_age;
}
static MetaDrmBuffer *
copy_shared_framebuffer_gpu (CoglOnscreen *onscreen,
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state,
MetaRendererNativeGpuData *renderer_gpu_data,
MetaDrmBuffer *primary_gpu_fb,
const int *rectangles,
int n_rectangles,
GError **error)
{
MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
@ -851,10 +954,15 @@ copy_shared_framebuffer_gpu (CoglOnscreen *onscreen,
MetaDrmBufferFlags flags;
MetaDrmBufferGbm *buffer_gbm = NULL;
struct gbm_bo *bo;
int buffer_age = 0;
int blit_n_rectangles = 0;
MtkRectangle *blit_rectangles = NULL;
COGL_TRACE_BEGIN_SCOPED (CopySharedFramebufferSecondaryGpu,
"copy_shared_framebuffer_gpu()");
push_secondary_gpu_damage_rectangles (secondary_gpu_state, rectangles, n_rectangles);
if (renderer_gpu_data->secondary.needs_explicit_sync)
cogl_framebuffer_finish (COGL_FRAMEBUFFER (onscreen));
@ -874,12 +982,35 @@ copy_shared_framebuffer_gpu (CoglOnscreen *onscreen,
buffer_gbm = META_DRM_BUFFER_GBM (primary_gpu_fb);
bo = meta_drm_buffer_gbm_get_bo (buffer_gbm);
buffer_age = get_secondary_gpu_buffer_age (secondary_gpu_state, renderer_gpu_data);
blit_n_rectangles = calculate_secondary_gpu_damage_rectangle_count (secondary_gpu_state, buffer_age);
if (blit_n_rectangles > 0)
{
int i;
size_t offset = 0;
blit_rectangles = g_newa (MtkRectangle, blit_n_rectangles);
for (i = 0; i <= buffer_age; ++i)
{
memcpy (blit_rectangles + offset,
secondary_gpu_state->damage_rectangles[i].rectangles,
secondary_gpu_state->damage_rectangles[i].n_rectangles * sizeof(MtkRectangle));
offset += secondary_gpu_state->damage_rectangles[i].n_rectangles;
}
}
if (!meta_renderer_native_gles3_blit_shared_bo (egl,
gles3,
egl_display,
renderer_gpu_data->secondary.egl_context,
secondary_gpu_state->egl_surface,
bo,
blit_rectangles,
blit_n_rectangles,
error))
{
g_prefix_error (error, "Failed to blit shared framebuffer: ");
@ -1009,10 +1140,8 @@ copy_shared_framebuffer_primary_gpu (CoglOnscreen *onscre
error->message);
return NULL;
}
/* Limit the number of individual copies to 16 */
#define MAX_RECTS 16
if (n_rectangles == 0 || n_rectangles > MAX_RECTS)
if (n_rectangles == 0 || n_rectangles > MAX_DAMAGE_RECTANGLES)
{
if (!cogl_blit_framebuffer (framebuffer, COGL_FRAMEBUFFER (dmabuf_fb),
0, 0, 0, 0,
@ -1177,6 +1306,8 @@ static MetaDrmBuffer *
acquire_front_buffer (CoglOnscreen *onscreen,
MetaDrmBuffer *primary_gpu_fb,
MetaDrmBuffer *secondary_gpu_fb,
const int *rectangles,
int n_rectangles,
GError **error)
{
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
@ -1222,6 +1353,8 @@ acquire_front_buffer (CoglOnscreen *onscreen,
secondary_gpu_state,
renderer_gpu_data,
primary_gpu_fb,
rectangles,
n_rectangles,
error);
}
@ -1377,6 +1510,8 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
buffer = acquire_front_buffer (onscreen,
primary_gpu_fb,
secondary_gpu_fb,
rectangles,
n_rectangles,
&error);
if (buffer == NULL)
{

View file

@ -196,11 +196,13 @@ ensure_shader_program (ContextData *context_data,
"attribute vec2 position;\n"
"attribute vec2 texcoord;\n"
"varying vec2 v_texcoord;\n"
"uniform float framebuffer_width;\n"
"uniform float framebuffer_height;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = vec4(position, 0.0, 1.0);\n"
" v_texcoord = texcoord;\n"
" gl_Position = vec4(position.x / framebuffer_width * 2.0 - 1.0, position.y / framebuffer_height * 2.0 - 1.0, 0.0, 1.0);\n"
" v_texcoord = vec2(texcoord.x / framebuffer_width, texcoord.y / framebuffer_height);\n"
"}\n";
static const char fragment_shader_source[] =
@ -215,16 +217,8 @@ ensure_shader_program (ContextData *context_data,
" 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)
@ -254,23 +248,15 @@ ensure_shader_program (ContextData *context_data,
}
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)
blit_egl_image (MetaGles3 *gles3,
EGLImageKHR egl_image,
int width,
int height,
const MtkRectangle *rectangles,
int n_rectangles)
{
GLuint texture;
GLuint framebuffer;
@ -301,29 +287,152 @@ blit_egl_image (MetaGles3 *gles3,
GL_TEXTURE_2D, texture, 0));
GLBAS (gles3, glBindFramebuffer, (GL_READ_FRAMEBUFFER, framebuffer));
GLBAS (gles3, glBlitFramebuffer, (0, height, width, 0,
0, 0, width, height,
GL_COLOR_BUFFER_BIT,
GL_NEAREST));
if (n_rectangles > 0)
{
int i;
for (i = 0; i < n_rectangles; ++i)
{
GLint x1 = rectangles[i].x;
GLint y1 = height - rectangles[i].y - rectangles[i].height;
GLint src_y1 = rectangles[i].y;
GLint x2 = x1 + rectangles[i].width;
GLint y2 = y1 + rectangles[i].height;
GLint src_y2 = src_y1 + rectangles[i].height;
GLBAS (gles3, glBlitFramebuffer, (x1, src_y2, x2, src_y1,
x1, height - rectangles[i].y - rectangles[i].height, x2, y2,
GL_COLOR_BUFFER_BIT,
GL_NEAREST));
}
}
else
GLBAS (gles3, glBlitFramebuffer, (0, height, width, 0,
0, 0, width, height,
GL_COLOR_BUFFER_BIT,
GL_NEAREST));
GLBAS (gles3, glDeleteTextures, (1, &texture));
GLBAS (gles3, glDeleteFramebuffers, (1, &framebuffer));
}
static void
paint_egl_image (ContextData *context_data,
MetaGles3 *gles3,
EGLImageKHR egl_image,
int width,
int height)
paint_egl_image (ContextData *context_data,
MetaGles3 *gles3,
EGLImageKHR egl_image,
int width,
int height,
const MtkRectangle *rectangles,
int n_rectangles)
{
static const uint triangle_count_per_rectangle = 2;
static const uint vertex_per_triangle = 3;
static const uint values_per_vertex = 4;
const MtkRectangle full_damage_rectangle = {0, 0, width, height};
int i;
GLuint texture;
GLint *vertices = NULL;
GLuint vertex_buffer_object;
GLuint vertex_array_object;
GLint position_attrib, texcoord_attrib;
GLint framebuffer_width_uniform, framebuffer_height_uniform;
GLuint components_per_rectangle = triangle_count_per_rectangle * vertex_per_triangle * values_per_vertex;
GLuint size_per_rectangle = components_per_rectangle * sizeof(GLint);
GLuint vertices_size;
if (n_rectangles == 0)
{
n_rectangles = 1;
rectangles = &full_damage_rectangle;
}
vertices_size = n_rectangles * size_per_rectangle;
vertices = g_alloca (vertices_size);
for (i = 0; i < n_rectangles; ++i)
{
const MtkRectangle *rectangle = &rectangles[i];
GLint *rectangle_vertices = &vertices[i * components_per_rectangle];
int reversed_rect_y = height - rectangles[i].y - rectangles[i].height;
GLint x1 = rectangle->x;
GLint y1 = reversed_rect_y;
GLint x2 = rectangle->x + rectangle->width;
GLint y2 = reversed_rect_y + rectangle->height;
GLint u1 = rectangle->x;
GLint v1 = rectangle->y;
GLint u2 = rectangle->x + rectangle->width;
GLint v2 = rectangle->y + rectangle->height;
rectangle_vertices[0] = x1;
rectangle_vertices[1] = y2;
rectangle_vertices[2] = u1;
rectangle_vertices[3] = v1;
rectangle_vertices[4] = x2;
rectangle_vertices[5] = y2;
rectangle_vertices[6] = u2;
rectangle_vertices[7] = v1;
rectangle_vertices[8] = x1;
rectangle_vertices[9] = y1;
rectangle_vertices[10] = u1;
rectangle_vertices[11] = v2;
rectangle_vertices[12] = x1;
rectangle_vertices[13] = y1;
rectangle_vertices[14] = u1;
rectangle_vertices[15] = v2;
rectangle_vertices[16] = x2;
rectangle_vertices[17] = y2;
rectangle_vertices[18] = u2;
rectangle_vertices[19] = v1;
rectangle_vertices[20] = x2;
rectangle_vertices[21] = y1;
rectangle_vertices[22] = u2;
rectangle_vertices[23] = v2;
}
meta_gles3_clear_error (gles3);
ensure_shader_program (context_data, gles3);
g_return_if_fail (context_data->shader_program);
GLBAS (gles3, glViewport, (0, 0, width, height));
GLBAS (gles3, glGenVertexArrays, (1, &vertex_array_object));
GLBAS (gles3, glBindVertexArray, (vertex_array_object));
GLBAS (gles3, glGenBuffers, (1, &vertex_buffer_object));
GLBAS (gles3, glBindBuffer, (GL_ARRAY_BUFFER, vertex_buffer_object));
GLBAS (gles3, glBufferData,
(GL_ARRAY_BUFFER, vertices_size, vertices, GL_DYNAMIC_DRAW));
position_attrib = glGetAttribLocation (context_data->shader_program, "position");
GLBAS (gles3, glEnableVertexAttribArray, (position_attrib));
GLBAS (gles3, glVertexAttribPointer,
(position_attrib, 2, GL_INT, GL_FALSE, 4 * sizeof (GLint), NULL));
texcoord_attrib = glGetAttribLocation (context_data->shader_program, "texcoord");
GLBAS (gles3, glEnableVertexAttribArray, (texcoord_attrib));
GLBAS (gles3, glVertexAttribPointer,
(texcoord_attrib, 2, GL_INT, GL_FALSE, 4 * sizeof (GLint), (void*)(sizeof(GLint) * 2)));
framebuffer_width_uniform = glGetUniformLocation (context_data->shader_program, "framebuffer_width");
GLBAS (gles3, glUniform1f, (framebuffer_width_uniform, width));
framebuffer_height_uniform = glGetUniformLocation (context_data->shader_program, "framebuffer_height");
GLBAS (gles3, glUniform1f, (framebuffer_height_uniform, height));
GLBAS (gles3, glActiveTexture, (GL_TEXTURE0));
GLBAS (gles3, glGenTextures, (1, &texture));
GLBAS (gles3, glBindTexture, (GL_TEXTURE_EXTERNAL_OES, texture));
@ -342,19 +451,23 @@ paint_egl_image (ContextData *context_data,
GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE));
GLBAS (gles3, glDrawArrays, (GL_TRIANGLE_FAN, 0, 4));
GLBAS (gles3, glDrawArrays, (GL_TRIANGLES, 0, triangle_count_per_rectangle * vertex_per_triangle * n_rectangles));
GLBAS (gles3, glDeleteTextures, (1, &texture));
GLBAS (gles3, glDeleteBuffers, (1, &vertex_buffer_object));
GLBAS (gles3, glDeleteVertexArrays, (1, &vertex_array_object));
}
gboolean
meta_renderer_native_gles3_blit_shared_bo (MetaEgl *egl,
MetaGles3 *gles3,
EGLDisplay egl_display,
EGLContext egl_context,
EGLSurface egl_surface,
struct gbm_bo *shared_bo,
GError **error)
meta_renderer_native_gles3_blit_shared_bo (MetaEgl *egl,
MetaGles3 *gles3,
EGLDisplay egl_display,
EGLContext egl_context,
EGLSurface egl_surface,
struct gbm_bo *shared_bo,
const MtkRectangle *rectangles,
int n_rectangles,
GError **error)
{
int shared_bo_fd;
unsigned int width;
@ -435,9 +548,9 @@ meta_renderer_native_gles3_blit_shared_bo (MetaEgl *egl,
return FALSE;
if (can_blit)
blit_egl_image (gles3, egl_image, width, height);
blit_egl_image (gles3, egl_image, width, height, rectangles, n_rectangles);
else
paint_egl_image (context_data, gles3, egl_image, width, height);
paint_egl_image (context_data, gles3, egl_image, width, height, rectangles, n_rectangles);
meta_egl_destroy_image (egl, egl_display, egl_image, NULL);

View file

@ -25,14 +25,17 @@
#include "backends/meta-egl.h"
#include "backends/meta-gles3.h"
#include "mtk/mtk.h"
gboolean meta_renderer_native_gles3_blit_shared_bo (MetaEgl *egl,
MetaGles3 *gles3,
EGLDisplay egl_display,
EGLContext egl_context,
EGLSurface egl_surface,
struct gbm_bo *shared_bo,
GError **error);
gboolean meta_renderer_native_gles3_blit_shared_bo (MetaEgl *egl,
MetaGles3 *gles3,
EGLDisplay egl_display,
EGLContext egl_context,
EGLSurface egl_surface,
struct gbm_bo *shared_bo,
const MtkRectangle *rectangles,
int n_rectangles,
GError **error);
void meta_renderer_native_gles3_forget_context (MetaGles3 *gles3,
EGLContext egl_context);