[PATCH 3/3] compositor-drm: add sprite support v6
Benjamin Franzke
benjaminfranzke at googlemail.com
Tue Feb 14 01:52:11 PST 2012
2012/2/9 Jesse Barnes <jbarnes at virtuousgeek.org>:
> Add support for assigning surfaces to overlay sprites using the new
> assign_planes hook.
>
> v2: queue per-sprite vblank events to avoid de-queuing sprite updates
> for unrelated outputs (reported by krh)
> v3: handle output and surface transformation when calculating src & dest
> rects for the sprite (from pq & krh)
> v4: use new gbm function to get actual surface format and check against
> supported formats in sprite
> v5: track overlapped surfaces in compositor-drm better (krh)
> v6: update with comments from Pekka
> - remove is_cursor, it was really just checking for buffers
> - use pixman_region32_fini before damaging the old surface
> - add comments about damage requirements and transform update
> ---
> configure.ac | 2 +-
> src/compositor-drm.c | 447 +++++++++++++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 447 insertions(+), 2 deletions(-)
>
> diff --git a/configure.ac b/configure.ac
> index 62d36eb..94c5ab9 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -73,7 +73,7 @@ AC_ARG_ENABLE(drm-compositor, [ --enable-drm-compositor],,
> AM_CONDITIONAL(ENABLE_DRM_COMPOSITOR, test x$enable_drm_compositor == xyes)
> if test x$enable_drm_compositor == xyes; then
> AC_DEFINE([BUILD_DRM_COMPOSITOR], [1], [Build the DRM compositor])
> - PKG_CHECK_MODULES(DRM_COMPOSITOR, [libudev >= 136 libdrm >= 2.4.23 gbm])
> + PKG_CHECK_MODULES(DRM_COMPOSITOR, [libudev >= 136 libdrm >= 2.4.30 gbm])
> fi
>
>
> diff --git a/src/compositor-drm.c b/src/compositor-drm.c
> index adbd03c..97bdfd0 100644
> --- a/src/compositor-drm.c
> +++ b/src/compositor-drm.c
> @@ -23,6 +23,7 @@
>
> #define _GNU_SOURCE
>
> +#include <errno.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> @@ -31,6 +32,7 @@
>
> #include <xf86drm.h>
> #include <xf86drmMode.h>
> +#include <drm_fourcc.h>
>
> #include <gbm.h>
>
> @@ -50,10 +52,14 @@ struct drm_compositor {
> int fd;
> } drm;
> struct gbm_device *gbm;
> + uint32_t *crtcs;
> + int num_crtcs;
> uint32_t crtc_allocator;
> uint32_t connector_allocator;
> struct tty *tty;
>
> + struct wl_list sprite_list;
> +
> uint32_t prev_state;
> };
>
> @@ -82,6 +88,66 @@ struct drm_output {
> struct wl_listener pending_scanout_buffer_destroy_listener;
> };
>
> +/*
> + * An output has a primary display plane plus zero or more sprites for
> + * blending display contents.
> + */
> +struct drm_sprite {
> + struct wl_list link;
> +
> + uint32_t fb_id;
> + uint32_t pending_fb_id;
> + struct weston_surface *surface;
> + struct weston_surface *pending_surface;
> +
> + struct drm_compositor *compositor;
> +
> + struct wl_listener destroy_listener;
> + struct wl_listener pending_destroy_listener;
> +
> + uint32_t possible_crtcs;
> + uint32_t plane_id;
> + uint32_t count_formats;
> +
> + int32_t src_x, src_y;
> + uint32_t src_w, src_h;
> + uint32_t dest_x, dest_y;
> + uint32_t dest_w, dest_h;
> +
> + uint32_t formats[];
> +};
> +
> +static int
> +surface_is_primary(struct weston_compositor *ec, struct weston_surface *es)
> +{
> + struct weston_surface *primary;
> +
> + primary = container_of(ec->surface_list.next, struct weston_surface,
> + link);
> + if (es == primary)
> + return -1;
> + return 0;
> +}
> +
> +static int
> +drm_sprite_crtc_supported(struct weston_output *output_base, uint32_t supported)
> +{
> + struct weston_compositor *ec = output_base->compositor;
> + struct drm_compositor *c =(struct drm_compositor *) ec;
> + struct drm_output *output = (struct drm_output *) output_base;
> + int crtc;
> +
> + for (crtc = 0; crtc < c->num_crtcs; crtc++) {
> + if (c->crtcs[crtc] != output->crtc_id)
> + continue;
> +
> + if (supported & (1 << crtc))
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> static int
> drm_output_prepare_scanout_surface(struct drm_output *output)
> {
> @@ -149,7 +215,9 @@ drm_output_repaint(struct weston_output *output_base)
> struct drm_compositor *compositor =
> (struct drm_compositor *) output->base.compositor;
> struct weston_surface *surface;
> + struct drm_sprite *s;
> uint32_t fb_id = 0;
> + int ret = 0;
>
> glFramebufferRenderbuffer(GL_FRAMEBUFFER,
> GL_COLOR_ATTACHMENT0,
> @@ -181,10 +249,71 @@ drm_output_repaint(struct weston_output *output_base)
> return;
> }
>
> + /*
> + * Now, update all the sprite surfaces
> + */
> + wl_list_for_each(s, &compositor->sprite_list, link) {
> + uint32_t flags = 0;
> + drmVBlank vbl = {
> + .request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
> + .request.sequence = 1,
> + };
> +
> + if (!drm_sprite_crtc_supported(output_base, s->possible_crtcs))
> + continue;
> +
> + ret = drmModeSetPlane(compositor->drm.fd, s->plane_id,
> + output->crtc_id, s->pending_fb_id, flags,
> + s->dest_x, s->dest_y,
> + s->dest_w, s->dest_h,
> + s->src_x, s->src_y,
> + s->src_w, s->src_h);
> + if (ret)
> + fprintf(stderr, "setplane failed: %d: %s\n",
> + ret, strerror(errno));
> +
> + /*
> + * Queue a vblank signal so we know when the surface
> + * becomes active on the display or has been replaced.
> + */
> + vbl.request.signal = (unsigned long)s;
> + ret = drmWaitVBlank(compositor->drm.fd, &vbl);
> + if (ret) {
> + fprintf(stderr, "vblank event request failed: %d: %s\n",
> + ret, strerror(errno));
> + }
> + }
> +
> return;
> }
>
> static void
> +vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
> + void *data)
> +{
> + struct drm_sprite *s = (struct drm_sprite *)data;
> + struct drm_compositor *c = s->compositor;
> +
> + if (s->surface) {
> + weston_buffer_post_release(s->surface->buffer);
> + wl_list_remove(&s->destroy_listener.link);
> + s->surface = NULL;
> + drmModeRmFB(c->drm.fd, s->fb_id);
> + s->fb_id = 0;
> + }
> +
> + if (s->pending_surface) {
> + wl_list_remove(&s->pending_destroy_listener.link);
> + wl_list_insert(s->pending_surface->buffer->resource.destroy_listener_list.prev,
> + &s->destroy_listener.link);
> + s->surface = s->pending_surface;
> + s->pending_surface = NULL;
> + s->fb_id = s->pending_fb_id;
> + s->pending_fb_id = 0;
> + }
> +}
> +
> +static void
> page_flip_handler(int fd, unsigned int frame,
> unsigned int sec, unsigned int usec, void *data)
> {
> @@ -216,6 +345,235 @@ page_flip_handler(int fd, unsigned int frame,
> }
>
> static int
> +drm_surface_format_supported(struct drm_sprite *s, uint32_t format)
> +{
> + int i;
> +
> + for (i = 0; i < s->count_formats; i++)
> + if (s->formats[i] == format)
> + return 1;
> +
> + return 0;
> +}
> +
> +static int
> +drm_surface_transform_supported(struct weston_surface *es)
> +{
> + if (es->transform.enabled)
> + return 0;
> +
> + return 1;
> +}
> +
> +static int
> +drm_surface_overlap_supported(struct weston_output *output_base,
> + pixman_region32_t *overlap)
> +{
> + /* We could potentially use a color key here if the surface left
> + * to display has rectangular regions
> + */
> + if (pixman_region32_not_empty(overlap))
> + return 0;
> + return 1;
> +}
> +
> +static void
> +drm_disable_unused_sprites(struct weston_output *output_base)
> +{
> + struct weston_compositor *ec = output_base->compositor;
> + struct drm_compositor *c =(struct drm_compositor *) ec;
> + struct drm_output *output = (struct drm_output *) output_base;
> + struct drm_sprite *s;
> + int ret;
> +
> + wl_list_for_each(s, &c->sprite_list, link) {
> + if (s->pending_fb_id)
> + continue;
> +
> + ret = drmModeSetPlane(c->drm.fd, s->plane_id,
> + output->crtc_id, 0, 0,
> + 0, 0, 0, 0, 0, 0, 0, 0);
> + if (ret)
> + fprintf(stderr,
> + "failed to disable plane: %d: %s\n",
> + ret, strerror(errno));
> + drmModeRmFB(c->drm.fd, s->fb_id);
> + s->surface = NULL;
> + s->pending_surface = NULL;
> + s->fb_id = 0;
> + s->pending_fb_id = 0;
> + }
> +}
> +
> +/*
> + * This function must take care to damage any previously assigned surface
> + * if the sprite ends up binding to a different surface than in the
> + * previous frame.
> + */
> +static int
> +drm_output_prepare_overlay_surface(struct weston_output *output_base,
> + struct weston_surface *es,
> + pixman_region32_t *overlap)
> +{
> + struct weston_compositor *ec = output_base->compositor;
> + struct drm_compositor *c =(struct drm_compositor *) ec;
> + struct drm_sprite *s;
> + int found = 0;
> + EGLint handle, stride;
> + struct gbm_bo *bo;
> + uint32_t fb_id = 0;
> + uint32_t handles[4], pitches[4], offsets[4];
> + int ret = 0;
> + pixman_region32_t dest_rect, src_rect;
> + pixman_box32_t *box;
> + uint32_t format;
> +
> + if (surface_is_primary(ec, es))
> + return -1;
> +
> + if (!es->buffer)
> + return -1;
> +
> + if (!drm_surface_transform_supported(es))
> + return -1;
> +
> + if (!drm_surface_overlap_supported(output_base, overlap))
> + return -1;
> +
> + wl_list_for_each(s, &c->sprite_list, link) {
> + if (!drm_sprite_crtc_supported(output_base, s->possible_crtcs))
> + continue;
> +
> + if (!s->pending_fb_id) {
> + found = 1;
> + break;
> + }
> + }
> +
> + /* No sprites available */
> + if (!found)
> + return -1;
> +
> + bo = gbm_bo_create_from_egl_image(c->gbm, c->base.display, es->image,
> + es->geometry.width, es->geometry.height,
> + GBM_BO_USE_SCANOUT);
> + format = gbm_bo_get_format(bo);
Havent seen a patch for GBM, that introduces gbm_bo_get_format.
From how you use format, it looks like you expect it to return a DRM_FORMAT_*?
Since GBM is in general modesetting-api independent it should return a
GBM_BO_FORMAT_*.
The reason for that is, that I want to keep it possible to implement
OpenWF Display with GBM, without DRM.
So that we have practical example and API towards the
Wayland-needs-KMS misinformation.
> + handle = gbm_bo_get_handle(bo).s32;
> + stride = gbm_bo_get_pitch(bo);
> +
> + gbm_bo_destroy(bo);
> +
> + if (!drm_surface_format_supported(s, format))
> + return -1;
> +
> + if (!handle)
> + return -1;
> +
> + handles[0] = handle;
> + pitches[0] = stride;
> + offsets[0] = 0;
> +
> + ret = drmModeAddFB2(c->drm.fd, es->geometry.width, es->geometry.height,
> + format, handles, pitches, offsets,
> + &fb_id, 0);
> + if (ret) {
> + fprintf(stderr, "addfb2 failed: %d\n", ret);
> + return -1;
> + }
> +
> + if (s->surface && s->surface != es) {
> + struct weston_surface *old_surf = s->surface;
> + pixman_region32_fini(&old_surf->damage);
> + pixman_region32_init_rect(&old_surf->damage,
> + old_surf->geometry.x, old_surf->geometry.y,
> + old_surf->geometry.width, old_surf->geometry.height);
> + }
> +
> + s->pending_fb_id = fb_id;
> + s->pending_surface = es;
> + es->buffer->busy_count++;
> +
> + /*
> + * Calculate the source & dest rects properly based on actual
> + * postion (note the caller has called weston_surface_update_transform()
> + * for us already).
> + */
> + pixman_region32_init(&dest_rect);
> + pixman_region32_intersect(&dest_rect, &es->transform.boundingbox,
> + &output_base->region);
> + pixman_region32_translate(&dest_rect, -output_base->x, -output_base->y);
> + box = pixman_region32_extents(&dest_rect);
> + s->dest_x = box->x1;
> + s->dest_y = box->y1;
> + s->dest_w = box->x2 - box->x1;
> + s->dest_h = box->y2 - box->y1;
> + pixman_region32_fini(&dest_rect);
> +
> + pixman_region32_init(&src_rect);
> + pixman_region32_intersect(&src_rect, &es->transform.boundingbox,
> + &output_base->region);
> + pixman_region32_translate(&src_rect, -es->geometry.x, -es->geometry.y);
> + box = pixman_region32_extents(&src_rect);
> + s->src_x = box->x1;
> + s->src_y = box->y1;
> + s->src_w = box->x2 - box->x1;
> + s->src_h = box->y2 - box->y1;
> + pixman_region32_fini(&src_rect);
> +
> +
> + wl_list_insert(es->buffer->resource.destroy_listener_list.prev,
> + &s->pending_destroy_listener.link);
> + return 0;
> +}
> +
> +static void
> +drm_assign_planes(struct weston_output *output)
> +{
> + struct weston_compositor *ec = output->compositor;
> + struct weston_surface *es;
> + pixman_region32_t overlap, surface_overlap;
> +
> + /*
> + * Find a surface for each sprite in the output using some heuristics:
> + * 1) size
> + * 2) frequency of update
> + * 3) opacity (though some hw might support alpha blending)
> + * 4) clipping (this can be fixed with color keys)
> + *
> + * The idea is to save on blitting since this should save power.
> + * If we can get a large video surface on the sprite for example,
> + * the main display surface may not need to update at all, and
> + * the client buffer can be used directly for the sprite surface
> + * as we do for flipping full screen surfaces.
> + */
> + pixman_region32_init(&overlap);
> + wl_list_for_each(es, &ec->surface_list, link) {
> + weston_surface_update_transform(es);
> +
> + /*
> + * FIXME: try to assign hw cursors here too, they're just
> + * special overlays
> + */
> + pixman_region32_init(&surface_overlap);
> + pixman_region32_intersect(&surface_overlap, &overlap,
> + &es->transform.boundingbox);
> +
> + if (!drm_output_prepare_overlay_surface(output, es,
> + &surface_overlap)) {
> + pixman_region32_fini(&es->damage);
> + pixman_region32_init(&es->damage);
> + } else {
> + pixman_region32_union(&overlap, &overlap,
> + &es->transform.boundingbox);
> + }
> + pixman_region32_fini(&surface_overlap);
> + }
> + pixman_region32_fini(&overlap);
> +
> + drm_disable_unused_sprites(output);
> +}
> +
> +static int
> drm_output_set_cursor(struct weston_output *output_base,
> struct weston_input_device *eid)
> {
> @@ -341,6 +699,7 @@ on_drm_input(int fd, uint32_t mask, void *data)
> memset(&evctx, 0, sizeof evctx);
> evctx.version = DRM_EVENT_CONTEXT_VERSION;
> evctx.page_flip_handler = page_flip_handler;
> + evctx.vblank_handler = vblank_handler;
> drmHandleEvent(fd, &evctx);
>
> return 1;
> @@ -485,6 +844,30 @@ output_handle_pending_scanout_buffer_destroy(struct wl_listener *listener,
> weston_compositor_schedule_repaint(output->base.compositor);
> }
>
> +static void
> +sprite_handle_buffer_destroy(struct wl_listener *listener,
> + struct wl_resource *resource,
> + uint32_t time)
> +{
> + struct drm_sprite *sprite =
> + container_of(listener, struct drm_sprite,
> + destroy_listener);
> +
> + sprite->surface = NULL;
> +}
> +
> +static void
> +sprite_handle_pending_buffer_destroy(struct wl_listener *listener,
> + struct wl_resource *resource,
> + uint32_t time)
> +{
> + struct drm_sprite *sprite =
> + container_of(listener, struct drm_sprite,
> + pending_destroy_listener);
> +
> + sprite->pending_surface = NULL;
> +}
> +
> static int
> create_output_for_connector(struct drm_compositor *ec,
> drmModeRes *resources,
> @@ -618,7 +1001,7 @@ create_output_for_connector(struct drm_compositor *ec,
> output->base.repaint = drm_output_repaint;
> output->base.set_hardware_cursor = drm_output_set_cursor;
> output->base.destroy = drm_output_destroy;
> - output->base.assign_planes = NULL;
> + output->base.assign_planes = drm_assign_planes;
>
> return 0;
>
> @@ -654,6 +1037,58 @@ err_free:
> return -1;
> }
>
> +static void
> +create_sprites(struct drm_compositor *ec)
> +{
> + struct drm_sprite *sprite;
> + drmModePlaneRes *plane_res;
> + drmModePlane *plane;
> + int i;
> +
> + plane_res = drmModeGetPlaneResources(ec->drm.fd);
> + if (!plane_res) {
> + fprintf(stderr, "failed to get plane resources: %s\n",
> + strerror(errno));
> + return;
> + }
> +
> + for (i = 0; i < plane_res->count_planes; i++) {
> + plane = drmModeGetPlane(ec->drm.fd, plane_res->planes[i]);
> + if (!plane)
> + continue;
> + sprite = malloc(sizeof(*sprite) + ((sizeof(uint32_t)) *
> + plane->count_formats));
> + if (!sprite) {
> + fprintf(stderr, "%s: out of memory\n",
> + __func__);
> + free(plane);
> + continue;
> + }
> +
> + memset(sprite, 0, sizeof *sprite);
> +
> + sprite->possible_crtcs = plane->possible_crtcs;
> + sprite->plane_id = plane->plane_id;
> + sprite->surface = NULL;
> + sprite->pending_surface = NULL;
> + sprite->fb_id = 0;
> + sprite->pending_fb_id = 0;
> + sprite->destroy_listener.func = sprite_handle_buffer_destroy;
> + sprite->pending_destroy_listener.func =
> + sprite_handle_pending_buffer_destroy;
> + sprite->compositor = ec;
> + memcpy(sprite->formats, plane->formats,
> + plane->count_formats);
> + drmModeFreePlane(plane);
> +
> + wl_list_insert(&ec->sprite_list, &sprite->link);
> + }
> +
> + free(plane_res->planes);
> + free(plane_res);
> +}
> +
> +
> static int
> create_outputs(struct drm_compositor *ec, int option_connector)
> {
> @@ -668,6 +1103,13 @@ create_outputs(struct drm_compositor *ec, int option_connector)
> return -1;
> }
>
> + ec->crtcs = calloc(resources->count_crtcs, sizeof(uint32_t));
> + if (!ec->crtcs)
> + return -1;
> +
> + ec->num_crtcs = resources->count_crtcs;
> + memcpy(ec->crtcs, resources->crtcs, sizeof(uint32_t) * ec->num_crtcs);
> +
> for (i = 0; i < resources->count_connectors; i++) {
> connector = drmModeGetConnector(ec->drm.fd,
> resources->connectors[i]);
> @@ -999,6 +1441,9 @@ drm_compositor_create(struct wl_display *display,
> if (weston_compositor_init(&ec->base, display) < 0)
> return NULL;
>
> + wl_list_init(&ec->sprite_list);
> + create_sprites(ec);
> +
> if (create_outputs(ec, connector) < 0) {
> fprintf(stderr, "failed to create output for %s\n", path);
> return NULL;
> --
> 1.7.4.1
>
> _______________________________________________
> wayland-devel mailing list
> wayland-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/wayland-devel
More information about the wayland-devel
mailing list