linuxOS_AP06/buildroot/package/weston/0070-backend-drm-Support-increasing-buffers.patch
2025-06-03 12:28:32 +08:00

600 lines
20 KiB
Diff

From 816816bc0c0fc17d8b9625c48ef1925dc90ca912 Mon Sep 17 00:00:00 2001
From: Jeffy Chen <jeffy.chen@rock-chips.com>
Date: Wed, 23 Aug 2023 18:18:58 +0800
Subject: [PATCH 70/95] backend-drm: Support increasing buffers
Default is 2 buffers, set env "WESTON_DRM_MIN_BUFFERS" to increase it.
This can help to reduce tearing when dumping low-level DRM buffers.
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
libweston/backend-drm/drm-gbm.c | 159 ++++++++++++++++++++-------
libweston/backend-drm/drm-internal.h | 16 ++-
libweston/backend-drm/drm.c | 29 +++--
libweston/renderer-gl/gl-renderer.c | 82 ++++++++++++--
libweston/renderer-gl/gl-renderer.h | 3 +
5 files changed, 228 insertions(+), 61 deletions(-)
diff --git a/libweston/backend-drm/drm-gbm.c b/libweston/backend-drm/drm-gbm.c
index 055ef0b..27c76d7 100644
--- a/libweston/backend-drm/drm-gbm.c
+++ b/libweston/backend-drm/drm-gbm.c
@@ -122,8 +122,11 @@ static void drm_output_fini_cursor_egl(struct drm_output *output)
unsigned int i;
for (i = 0; i < ARRAY_LENGTH(output->gbm_cursor_fb); i++) {
+ if (!output->gbm_cursor_fb[i])
+ continue;
+
/* This cursor does not have a GBM device */
- if (output->gbm_cursor_fb[i] && !output->gbm_cursor_fb[i]->bo)
+ if (!output->gbm_cursor_fb[i]->bo)
output->gbm_cursor_fb[i]->type = BUFFER_PIXMAN_DUMB;
drm_fb_unref(output->gbm_cursor_fb[i]);
output->gbm_cursor_fb[i] = NULL;
@@ -179,25 +182,16 @@ err:
return -1;
}
-static void
-create_gbm_surface(struct gbm_device *gbm, struct drm_output *output)
+static struct gbm_surface *
+create_gbm_surface(struct gbm_device *gbm, struct drm_output *output,
+ struct weston_drm_format *fmt)
{
struct weston_mode *mode = output->base.current_mode;
- struct drm_plane *plane = output->scanout_plane;
struct drm_device *device = output->device;
- struct weston_drm_format *fmt;
+ struct gbm_surface *gbm_surface = NULL;
const uint64_t *modifiers;
unsigned int num_modifiers;
- fmt = weston_drm_format_array_find_format(&plane->formats,
- output->format->format);
- if (!fmt) {
- weston_log("format %s not supported by output %s\n",
- output->format->drm_format_name,
- output->base.name);
- return;
- }
-
/* HACK: Prefer valid modifilers */
if (device->fb_modifiers) {
#define MAX_MODIFIERS 128
@@ -211,13 +205,44 @@ create_gbm_surface(struct gbm_device *gbm, struct drm_output *output)
modifiers = _modifiers;
num_modifiers = j;
- output->gbm_surface =
+ gbm_surface =
gbm_surface_create_with_modifiers(gbm,
mode->width, mode->height,
output->format->format,
modifiers, num_modifiers);
}
+ /* We may allocate with no modifiers in the following situations:
+ *
+ * 1. the KMS driver does not support modifiers;
+ * 2. if allocating with modifiers failed, what can happen when the KMS
+ * display device supports modifiers but the GBM driver does not,
+ * e.g. the old i915 Mesa driver.
+ */
+ if (!gbm_surface)
+ gbm_surface = gbm_surface_create(gbm, mode->width, mode->height,
+ output->format->format,
+ output->gbm_bo_flags);
+
+ return gbm_surface;
+}
+
+static bool
+create_gbm_surfaces(struct gbm_device *gbm, struct drm_output *output)
+{
+ struct drm_plane *plane = output->scanout_plane;
+ struct weston_drm_format *fmt;
+ unsigned int i;
+
+ fmt = weston_drm_format_array_find_format(&plane->formats,
+ output->format->format);
+ if (!fmt) {
+ weston_log("format %s not supported by output %s\n",
+ output->format->drm_format_name,
+ output->base.name);
+ return false;
+ }
+
/*
* If we cannot use modifiers to allocate the GBM surface and the GBM
* device differs from the KMS display device (because we are rendering
@@ -227,18 +252,22 @@ create_gbm_surface(struct gbm_device *gbm, struct drm_output *output)
if (gbm_device_get_fd(gbm) != output->device->drm.fd)
output->gbm_bo_flags |= GBM_BO_USE_LINEAR;
- /* We may allocate with no modifiers in the following situations:
- *
- * 1. the KMS driver does not support modifiers;
- * 2. if allocating with modifiers failed, what can happen when the KMS
- * display device supports modifiers but the GBM driver does not,
- * e.g. the old i915 Mesa driver.
- */
- if (!output->gbm_surface)
- output->gbm_surface = gbm_surface_create(gbm,
- mode->width, mode->height,
- output->format->format,
- output->gbm_bo_flags);
+ for (i = 0; i < output->num_surfaces; i++) {
+ output->gbm_surfaces[i] = create_gbm_surface(gbm, output, fmt);
+ if (!output->gbm_surfaces[i]) {
+ weston_log("failed to create gbm surface\n");
+ goto err;
+ }
+ }
+
+ return true;
+err:
+ for (i = 0; i < output->num_surfaces; i++) {
+ if (output->gbm_surfaces[i])
+ gbm_surface_destroy(output->gbm_surfaces[i]);
+ output->gbm_surfaces[i] = NULL;
+ }
+ return false;
}
/* Init output state that depends on gl or gbm */
@@ -247,6 +276,7 @@ drm_output_init_egl(struct drm_output *output, struct drm_backend *b)
{
const struct weston_renderer *renderer = b->compositor->renderer;
const struct weston_mode *mode = output->base.current_mode;
+ const struct gl_renderer_interface *gl = renderer->gl;
const struct pixel_format_info *format[2] = {
output->format,
fallback_format_for(output->format),
@@ -260,24 +290,52 @@ drm_output_init_egl(struct drm_output *output, struct drm_backend *b)
.area.height = mode->height,
.fb_size.width = mode->width,
.fb_size.height = mode->height,
+ .window_for_legacy = NULL,
+ .window_for_platform = NULL,
};
+ unsigned int i;
assert(output->gbm_surface == NULL);
- create_gbm_surface(b->gbm, output);
- if (!output->gbm_surface) {
- weston_log("failed to create gbm surface\n");
+
+ output->next_image = 0;
+
+ if (!create_gbm_surfaces(b->gbm, output))
return -1;
- }
+
+ output->gbm_surface = output->gbm_surfaces[output->next_image];
if (options.formats[1])
options.formats_count = 2;
- options.window_for_legacy = (EGLNativeWindowType) output->gbm_surface;
- options.window_for_platform = output->gbm_surface;
- if (renderer->gl->output_window_create(&output->base, &options) < 0) {
- weston_log("failed to create gl renderer output state\n");
- gbm_surface_destroy(output->gbm_surface);
- output->gbm_surface = NULL;
- return -1;
+
+ if (output->num_surfaces > 1) {
+ if (gl->output_window_create(&output->base, &options) < 0) {
+ weston_log("failed to create gl renderer output state\n");
+ drm_output_fini_egl(output);
+ return -1;
+ }
+
+ for (i = 0; i < output->num_surfaces; i++) {
+ options.window_for_legacy =
+ (EGLNativeWindowType) output->gbm_surfaces[i];
+ options.window_for_platform = output->gbm_surfaces[i];
+
+ output->renderbuffer[i] =
+ gl->create_buffer(&output->base, &options);
+ if (!output->renderbuffer[i]) {
+ weston_log("failed to create window surface\n");
+ drm_output_fini_egl(output);
+ return -1;
+ }
+ }
+ } else {
+ options.window_for_legacy =
+ (EGLNativeWindowType) output->gbm_surface;
+ options.window_for_platform = output->gbm_surface;
+ if (gl->output_window_create(&output->base, &options) < 0) {
+ weston_log("failed to create gl renderer output state\n");
+ drm_output_fini_egl(output);
+ return -1;
+ }
}
drm_output_init_cursor_egl(output, b);
@@ -290,6 +348,7 @@ drm_output_fini_egl(struct drm_output *output)
{
struct drm_backend *b = output->backend;
const struct weston_renderer *renderer = b->compositor->renderer;
+ unsigned int i;
/* Destroying the GBM surface will destroy all our GBM buffers,
* regardless of refcount. Ensure we destroy them here. */
@@ -300,8 +359,18 @@ drm_output_fini_egl(struct drm_output *output)
}
renderer->gl->output_destroy(&output->base);
- gbm_surface_destroy(output->gbm_surface);
+ for (i = 0; i < output->num_surfaces; i++) {
+ if (output->renderbuffer[i])
+ weston_renderbuffer_unref(output->renderbuffer[i]);
+ output->renderbuffer[i] = NULL;
+ }
+ for (i = 0; i < output->num_surfaces; i++) {
+ if (output->gbm_surfaces[i])
+ gbm_surface_destroy(output->gbm_surfaces[i]);
+ output->gbm_surfaces[i] = NULL;
+ }
output->gbm_surface = NULL;
+
drm_output_fini_cursor_egl(output);
}
@@ -310,11 +379,19 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage)
{
struct drm_output *output = state->output;
struct drm_device *device = output->device;
+ struct weston_renderbuffer *renderbuffer = NULL;
struct gbm_bo *bo;
struct drm_fb *ret;
+ output->gbm_surface = output->gbm_surfaces[output->next_image];
+
+ if (output->num_surfaces > 1)
+ renderbuffer = output->renderbuffer[output->next_image];
+
+ output->next_image = (output->next_image + 1) % output->num_surfaces;
+
output->base.compositor->renderer->repaint_output(&output->base,
- damage, NULL);
+ damage, renderbuffer);
bo = gbm_surface_lock_front_buffer(output->gbm_surface);
if (!bo) {
@@ -326,7 +403,7 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage)
/* Output transparent/opaque image according to the format required by
* the client. */
ret = drm_fb_get_from_bo(bo, device, !output->format->opaque_substitute,
- BUFFER_GBM_SURFACE);
+ BUFFER_GBM_SURFACE);
if (!ret) {
weston_log("failed to get drm_fb for bo\n");
gbm_surface_release_buffer(output->gbm_surface, bo);
diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h
index 8151d10..93a3ff6 100644
--- a/libweston/backend-drm/drm-internal.h
+++ b/libweston/backend-drm/drm-internal.h
@@ -122,6 +122,8 @@
#define WESTON_DRM_CONFIG_FILE "/tmp/.weston_drm.conf"
#define DRM_CONFIG_UPDATE_MS 100
+#define DRM_MAX_BUFFERS 16
+
/**
* Represents the values of an enum-type KMS property
*/
@@ -592,7 +594,6 @@ struct drm_output {
struct wl_listener cursor_view_destroy_listener;
int current_cursor;
- struct gbm_surface *gbm_surface;
const struct pixel_format_info *format;
uint32_t gbm_bo_flags;
@@ -617,12 +618,17 @@ struct drm_output {
/* only set when a writeback screenshot is ongoing */
struct drm_writeback_state *wb_state;
- struct drm_fb *dumb[2];
- struct weston_renderbuffer *renderbuffer[2];
- int current_image;
+ struct gbm_surface *gbm_surfaces[DRM_MAX_BUFFERS];
+ struct gbm_surface *gbm_surface;
+ unsigned int num_surfaces;
+
+ struct drm_fb *dumb[DRM_MAX_BUFFERS];
+ struct weston_renderbuffer *renderbuffer[DRM_MAX_BUFFERS];
+ int next_image;
+ unsigned int num_images;
/* Wrap fb for scale/rotate usage */
- struct drm_fb *wrap[2];
+ struct drm_fb *wrap[DRM_MAX_BUFFERS];
int next_wrap;
struct vaapi_recorder *recorder;
diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c
index 1fc8840..b9bc2f9 100644
--- a/libweston/backend-drm/drm.c
+++ b/libweston/backend-drm/drm.c
@@ -568,13 +568,14 @@ drm_output_render_pixman(struct drm_output_state *state,
{
struct drm_output *output = state->output;
struct weston_compositor *ec = output->base.compositor;
-
- output->current_image ^= 1;
+ struct drm_fb *fb;
ec->renderer->repaint_output(&output->base, damage,
- output->renderbuffer[output->current_image]);
+ output->renderbuffer[output->next_image]);
+ fb = drm_fb_ref(output->dumb[output->next_image]);
- return drm_fb_ref(output->dumb[output->current_image]);
+ output->next_image = (output->next_image + 1) % output->num_images;
+ return fb;
}
static struct drm_fb *
@@ -634,7 +635,7 @@ drm_output_get_wrap_fb(struct drm_backend *b, struct drm_output *output,
output->wrap[output->next_wrap] = fb;
out:
- output->next_wrap ^= 1;
+ output->next_wrap = (output->next_wrap + 1) % output->num_images;
return drm_fb_ref(fb);
}
@@ -2053,7 +2054,7 @@ drm_output_init_pixman(struct drm_output *output, struct drm_backend *b)
goto err;
/* FIXME error checking */
- for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
+ for (i = 0; i < output->num_images; i++) {
output->dumb[i] = drm_fb_create_dumb(device, w, h,
options.format->format);
if (!output->dumb[i])
@@ -2111,8 +2112,11 @@ drm_output_fini_pixman(struct drm_output *output)
}
for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
- weston_renderbuffer_unref(output->renderbuffer[i]);
- drm_fb_unref(output->dumb[i]);
+ if (output->dumb[i])
+ drm_fb_unref(output->dumb[i]);
+ if (output->renderbuffer[i])
+ weston_renderbuffer_unref(output->renderbuffer[i]);
+
output->dumb[i] = NULL;
output->renderbuffer[i] = NULL;
}
@@ -3292,6 +3296,7 @@ drm_output_create(struct weston_backend *backend, const char *name)
struct drm_backend *b = container_of(backend, struct drm_backend, base);
struct drm_device *device;
struct drm_output *output;
+ const char *env;
device = drm_device_find_by_output(b->compositor, name);
if (!device)
@@ -3311,6 +3316,14 @@ drm_output_create(struct weston_backend *backend, const char *name)
output->gbm_bo_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
#endif
+ env = getenv("WESTON_DRM_MIN_BUFFERS");
+ if (env)
+ output->num_images = atoi(env);
+
+ output->num_images = MIN(MAX(output->num_images, 2), DRM_MAX_BUFFERS);
+ output->num_surfaces = (output->num_images + 1) / 2;
+ weston_log("%s using at least %d buffers\n", name, output->num_images);
+
weston_output_init(&output->base, b->compositor, name);
output->base.enable = drm_output_enable;
diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c
index 539fa29..1216557 100644
--- a/libweston/renderer-gl/gl-renderer.c
+++ b/libweston/renderer-gl/gl-renderer.c
@@ -109,6 +109,9 @@ struct gl_renderbuffer {
uint32_t *pixels;
struct wl_list link;
int age;
+
+ EGLDisplay egl_display;
+ EGLSurface egl_surface;
};
struct gl_output_state {
@@ -118,6 +121,8 @@ struct gl_output_state {
float y_flip;
EGLSurface egl_surface;
+ EGLSurface default_egl_surface;
+
struct gl_border_image borders[4];
enum gl_border_status border_status;
@@ -629,6 +634,10 @@ gl_renderer_renderbuffer_destroy(struct weston_renderbuffer *renderbuffer)
{
struct gl_renderbuffer *rb = to_gl_renderbuffer(renderbuffer);
+ if (rb->egl_surface != EGL_NO_SURFACE)
+ weston_platform_destroy_egl_surface(rb->egl_display,
+ rb->egl_surface);
+
glDeleteFramebuffers(1, &rb->fbo);
glDeleteRenderbuffers(1, &rb->rb);
pixman_region32_fini(&rb->base.damage);
@@ -2035,7 +2044,8 @@ output_get_buffer_age(struct weston_output *output)
EGLint buffer_age = 0;
EGLBoolean ret;
- if (gr->has_egl_buffer_age && go->egl_surface != EGL_NO_SURFACE) {
+ if (gr->has_egl_buffer_age && go->egl_surface != EGL_NO_SURFACE &&
+ go->egl_surface == go->default_egl_surface) {
ret = eglQuerySurface(gr->egl_display, go->egl_surface,
EGL_BUFFER_AGE_EXT, &buffer_age);
if (ret == EGL_FALSE) {
@@ -2278,6 +2288,16 @@ gl_renderer_repaint_output(struct weston_output *output,
output->color_outcome->from_blend_to_output == NULL ||
shadow_exists(go));
+ go->egl_surface = go->default_egl_surface;
+ if (renderbuffer) {
+ rb = to_gl_renderbuffer(renderbuffer);
+ if (rb->egl_surface != EGL_NO_SURFACE) {
+ /* HACK: Renderbuffer only for passing egl_surface */
+ go->egl_surface = rb->egl_surface;
+ renderbuffer = NULL;
+ }
+ }
+
if (use_output(output) < 0)
return;
@@ -3978,7 +3998,8 @@ static int
gl_renderer_output_create(struct weston_output *output,
EGLSurface surface,
const struct weston_size *fb_size,
- const struct weston_geometry *area)
+ const struct weston_geometry *area,
+ const bool y_flip)
{
struct gl_output_state *go;
struct gl_renderer *gr = get_renderer(output->compositor);
@@ -3990,8 +4011,9 @@ gl_renderer_output_create(struct weston_output *output,
if (go == NULL)
return -1;
+ go->default_egl_surface = surface;
go->egl_surface = surface;
- go->y_flip = surface == EGL_NO_SURFACE ? 1.0f : -1.0f;
+ go->y_flip = y_flip ? -1.0f : 1.0f;
if (gr->has_disjoint_timer_query)
gr->gen_queries(1, &go->render_query);
@@ -4029,6 +4051,42 @@ gl_renderer_output_create(struct weston_output *output,
return 0;
}
+static struct weston_renderbuffer *
+gl_renderer_create_buffer(struct weston_output *output,
+ const struct gl_renderer_output_options *options)
+{
+ struct weston_compositor *ec = output->compositor;
+ struct gl_output_state *go = get_output_state(output);
+ struct gl_renderer *gr = get_renderer(ec);
+ struct gl_renderbuffer *renderbuffer;
+ EGLSurface egl_surface =
+ gl_renderer_create_window_surface(gr,
+ options->window_for_legacy,
+ options->window_for_platform,
+ options->formats,
+ options->formats_count);
+ if (egl_surface == EGL_NO_SURFACE) {
+ weston_log("failed to create egl surface\n");
+ return NULL;
+ }
+
+ renderbuffer = xzalloc(sizeof(*renderbuffer));
+
+ renderbuffer->egl_surface = egl_surface;
+ renderbuffer->egl_display = gr->egl_display;
+
+ pixman_region32_init(&renderbuffer->base.damage);
+ /*
+ * One reference is kept on the renderbuffer_list,
+ * the other is returned to the calling backend.
+ */
+ renderbuffer->base.refcount = 2;
+ renderbuffer->base.destroy = gl_renderer_renderbuffer_destroy;
+ wl_list_insert(&go->renderbuffer_list, &renderbuffer->link);
+
+ return &renderbuffer->base;
+}
+
static int
gl_renderer_output_window_create(struct weston_output *output,
const struct gl_renderer_output_options *options)
@@ -4038,6 +4096,9 @@ gl_renderer_output_window_create(struct weston_output *output,
EGLSurface egl_surface = EGL_NO_SURFACE;
int ret;
+ if (!options->window_for_legacy && !options->window_for_platform)
+ goto create_output;
+
egl_surface = gl_renderer_create_window_surface(gr,
options->window_for_legacy,
options->window_for_platform,
@@ -4048,9 +4109,10 @@ gl_renderer_output_window_create(struct weston_output *output,
return -1;
}
+create_output:
ret = gl_renderer_output_create(output, egl_surface,
- &options->fb_size, &options->area);
- if (ret < 0)
+ &options->fb_size, &options->area, true);
+ if (ret < 0 && egl_surface != EGL_NO_SURFACE)
weston_platform_destroy_egl_surface(gr->egl_display, egl_surface);
return ret;
@@ -4061,7 +4123,7 @@ gl_renderer_output_fbo_create(struct weston_output *output,
const struct gl_renderer_fbo_options *options)
{
return gl_renderer_output_create(output, EGL_NO_SURFACE,
- &options->fb_size, &options->area);
+ &options->fb_size, &options->area, false);
}
static void
@@ -4242,13 +4304,18 @@ gl_renderer_output_destroy(struct weston_output *output)
struct gl_output_state *go = get_output_state(output);
struct timeline_render_point *trp, *tmp;
+ if (!go)
+ return;
+
if (shadow_exists(go))
gl_fbo_texture_fini(&go->shadow);
eglMakeCurrent(gr->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
gr->egl_context);
- weston_platform_destroy_egl_surface(gr->egl_display, go->egl_surface);
+ if (go->default_egl_surface != EGL_NO_SURFACE)
+ weston_platform_destroy_egl_surface(gr->egl_display,
+ go->default_egl_surface);
if (!wl_list_empty(&go->timeline_render_point_list))
weston_log("warning: discarding pending timeline render"
@@ -4848,6 +4915,7 @@ gl_renderer_setup(struct weston_compositor *ec)
WL_EXPORT struct gl_renderer_interface gl_renderer_interface = {
.display_create = gl_renderer_display_create,
+ .create_buffer = gl_renderer_create_buffer,
.output_window_create = gl_renderer_output_window_create,
.output_fbo_create = gl_renderer_output_fbo_create,
.output_destroy = gl_renderer_output_destroy,
diff --git a/libweston/renderer-gl/gl-renderer.h b/libweston/renderer-gl/gl-renderer.h
index 6fc7f6f..7e7d1bc 100644
--- a/libweston/renderer-gl/gl-renderer.h
+++ b/libweston/renderer-gl/gl-renderer.h
@@ -135,6 +135,9 @@ struct gl_renderer_interface {
int (*display_create)(struct weston_compositor *ec,
const struct gl_renderer_display_options *options);
+ struct weston_renderbuffer *(*create_buffer)(struct weston_output *output,
+ const struct gl_renderer_output_options *options);
+
/**
* Attach GL-renderer to the output with a native window
*
--
2.20.1