Add awareness of, rather than support for, universal planes. Activate
the client cap when we start if possible, and if this is activated,
studiously ignore non-overlay planes. For now.

v2: Add asserts to be careful. Bump libdrm dependency version.

Signed-off-by: Daniel Stone <dani...@collabora.com>
---
 configure.ac         |   2 +-
 src/compositor-drm.c | 411 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 411 insertions(+), 2 deletions(-)

diff --git a/configure.ac b/configure.ac
index e047fd5..30d1427 100644
--- a/configure.ac
+++ b/configure.ac
@@ -151,7 +151,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.30 gbm mtdev 
>= 1.1.0])
+  PKG_CHECK_MODULES(DRM_COMPOSITOR, [libudev >= 136 libdrm >= 2.4.47 gbm mtdev 
>= 1.1.0])
 fi
 
 
diff --git a/src/compositor-drm.c b/src/compositor-drm.c
index dbf1dbe..09fb3af 100644
--- a/src/compositor-drm.c
+++ b/src/compositor-drm.c
@@ -56,10 +56,18 @@
 #include "vaapi-recorder.h"
 #include "presentation_timing-server-protocol.h"
 
+#ifndef static_assert
+#define static_assert(cond, msg) assert((cond) && msg)
+#endif
+
 #ifndef DRM_CAP_TIMESTAMP_MONOTONIC
 #define DRM_CAP_TIMESTAMP_MONOTONIC 0x6
 #endif
 
+#ifndef DRM_CLIENT_CAP_UNIVERSAL_PLANES
+#define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2
+#endif
+
 #ifndef DRM_CAP_CURSOR_WIDTH
 #define DRM_CAP_CURSOR_WIDTH 0x8
 #endif
@@ -83,6 +91,42 @@ enum output_config {
        OUTPUT_CONFIG_MODELINE
 };
 
+/**
+ * List of properties attached to DRM planes
+ */
+enum wdrm_plane_property {
+       WDRM_PLANE_TYPE = 0,
+       WDRM_PLANE__COUNT
+};
+
+/**
+ * Possible values for the WDRM_PLANE_TYPE property.
+ */
+enum wdrm_plane_type {
+       WDRM_PLANE_TYPE_PRIMARY = 0,
+       WDRM_PLANE_TYPE_CURSOR,
+       WDRM_PLANE_TYPE_OVERLAY,
+       WDRM_PLANE_TYPE__COUNT
+};
+
+/**
+ * One property attached to a DRM mode object (plane, CRTC, connector).
+ */
+struct property_item {
+       drmModePropertyRes *drm_prop;
+       uint32_t id;
+       uint64_t value;
+};
+
+/**
+ * Holding structure for plane properties.
+ */
+struct plane_properties {
+       struct property_item item[WDRM_PLANE__COUNT];
+       uint64_t typemap[WDRM_PLANE_TYPE__COUNT]; /**< map for type enum */
+       uint32_t value_valid_mask;
+};
+
 struct drm_compositor {
        struct weston_compositor base;
 
@@ -119,6 +163,8 @@ struct drm_compositor {
 
        int cursors_are_broken;
 
+       bool universal_planes;
+
        int use_pixman;
 
        uint32_t prev_state;
@@ -177,10 +223,14 @@ struct drm_plane {
 
        struct weston_plane base;
 
+       enum wdrm_plane_type type;
+
        struct drm_fb *current, *next;
        struct drm_output *output;
        struct drm_compositor *compositor;
 
+       struct plane_properties props;
+
        uint32_t possible_crtcs;
        uint32_t plane_id;
        uint32_t count_formats;
@@ -239,6 +289,342 @@ static struct gl_renderer_interface *gl_renderer;
 
 static const char default_seat[] = "seat0";
 
+/**
+ * Return a string describing the type of a DRM object
+ */
+static const char *
+drm_object_type_str(uint32_t obj_type)
+{
+       switch (obj_type) {
+       case DRM_MODE_OBJECT_CRTC:      return "crtc";
+       case DRM_MODE_OBJECT_CONNECTOR: return "connector";
+       case DRM_MODE_OBJECT_ENCODER:   return "encoder";
+       case DRM_MODE_OBJECT_MODE:      return "mode";
+       case DRM_MODE_OBJECT_PROPERTY:  return "property";
+       case DRM_MODE_OBJECT_FB:        return "fb";
+       case DRM_MODE_OBJECT_BLOB:      return "blob";
+       case DRM_MODE_OBJECT_PLANE:     return "plane";
+       default: return "???";
+       }
+}
+
+/**
+ * Cache a mapping from static values to a DRM property enum
+ *
+ * DRM property enum values are dynamic at runtime; the user must query the
+ * property to find out the desired runtime value for a requested string
+ * name. Using  the 'type' field on planes as an example, there is no single
+ * hardcoded constant for primary plane types; instead, the property must be
+ * queried at runtime to find the value associated with the string "Primary".
+ *
+ * This helper queries and caches the enum values, to allow us to use a set
+ * of compile-time-constant enums portably across various implementations.
+ * The values given in enum_names are searched for, and stored in the
+ * same-indexed field of the map array.
+ *
+ * For example, if the DRM driver exposes 'Foo' as value 7 and 'Bar' as value
+ * 19, passing in enum_names containing 'Foo' and 'Bar' in that order, will
+ * populate map with 7 and 19, in that order.
+ *
+ * This should always be used with static C enums to represent each value, e.g.
+ * WDRM_PLANE_MISC_FOO, WDRM_PLANE_MISC_BAR.
+ *
+ * Property lookup is not mandatory and may fail; users should carefully
+ * check the return value, which is a bitmask populated with the indices
+ * of properties which were successfully looked up. Values not successfully
+ * looked up may return 0, which may represent a false positive.
+ *
+ * @param prop DRM enum property to cache map for
+ * @param map Array for enum values; each entry is written for the 
corresponding
+ *            entry in enum_names
+ * @param enum_names List of string values to look up enum values for
+ * @param nenums Length of enum_names and map arrays
+ *
+ * @returns Bitmask of populated entries in map
+ */
+static uint32_t
+drm_property_get_enum_map(const struct property_item *item, uint64_t *map,
+                         const char * const *enum_names, int nenums)
+{
+       drmModePropertyRes *prop = item->drm_prop;
+       uint32_t valid_mask = 0;
+       int seen = 0;
+       int i, j;
+
+       assert(nenums <= 32 && "update return type");
+       memset(map, 0, nenums * sizeof *map);
+
+       if (!prop) {
+               weston_log("DRM warning: attempting to init property enum, "
+                          "that was not found. (e.g. '%s')\n",
+                          enum_names[0]);
+               return 0;
+       }
+
+       if (!(prop->flags & DRM_MODE_PROP_ENUM) || prop->count_enums < 1) {
+               weston_log("DRM error: property %d '%s' is not an enum.\n",
+                          prop->prop_id, prop->name);
+               return 0;
+       }
+
+       for (i = 0; i < prop->count_enums; i++) {
+               struct drm_mode_property_enum *en = &prop->enums[i];
+
+               for (j = 0; j < nenums; j++) {
+                       if (!strcmp(en->name, enum_names[j]))
+                               break;
+               }
+
+               if (j == nenums) {
+                       weston_log("DRM debug: property %d '%s' "
+                                  "has unrecognized enum %#" PRIx64 " '%s'\n",
+                                  prop->prop_id, prop->name,
+                                  (uint64_t)en->value, en->name);
+                       break;
+               }
+
+               map[j] = en->value;
+               valid_mask |= (1U << j);
+               seen++;
+       }
+
+       if (seen != nenums)
+               weston_log("DRM debug: property %d '%s' has %u of %u "
+                          "expected enum values.\n",
+                          prop->prop_id, prop->name, seen, nenums);
+
+       return valid_mask;
+}
+
+/**
+ * Cache DRM property values
+ *
+ * Given a particular DRM object, find specified properties by name, and
+ * cache their internal property_item representation. Taking a list of
+ * names in prop_names, each corresponding entry in prop_items (matching
+ * index) will be populated with the corresponding DRM property.
+ *
+ * All users of DRM object properties in this file should use this
+ * mechanism.
+ *
+ * Property lookup is not mandatory and may fail; users should carefully
+ * check the return value, which is a bitmask populated with the indices
+ * of properties which were successfully looked up.
+ *
+ * @param ec Internal DRM compositor structure
+ * @param prop_items Array of internal property representations
+ * @param prop_names Array of property names to look up
+ * @param nprops Length of prop_items and prop_names arrays
+ * @param obj_id DRM object ID
+ * @param obj_type DRM object type (DRM_MODE_OBJECT_*)
+ *
+ * @returns Bitmask of populated entries in prop_items
+ */
+static uint32_t
+drm_properties_get_from_obj(struct drm_compositor *ec,
+                           struct property_item *prop_items,
+                           const char * const *prop_names,
+                           unsigned nprops,
+                           uint32_t obj_id, uint32_t obj_type)
+{
+       drmModeObjectProperties *props;
+       drmModePropertyRes *prop;
+       uint32_t valid_mask = 0;
+       unsigned i, j;
+
+       assert(nprops <= 32 && "update return type");
+
+       memset(prop_items, 0, nprops * sizeof *prop_items);
+
+       props = drmModeObjectGetProperties(ec->drm.fd, obj_id, obj_type);
+       if (!props) {
+               weston_log("DRM error : get properties for object %u "
+                          "of type %#x '%s' failed.\n", obj_id, obj_type,
+                          drm_object_type_str(obj_type));
+               return valid_mask;
+       }
+
+       for (i = 0; i < props->count_props; i++) {
+               prop = drmModeGetProperty(ec->drm.fd, props->props[i]);
+               if (!prop)
+                       continue;
+
+               for (j = 0; j < nprops; j++) {
+                       if (strcmp(prop->name, prop_names[j]) == 0)
+                               break;
+               }
+
+               if (j == nprops) {
+                       weston_log("DRM debug: unrecognized property %u '%s' on"
+                                  " object %u of type %#x '%s'\n",
+                                  prop->prop_id, prop->name,
+                                  obj_id, obj_type,
+                                  drm_object_type_str(obj_type));
+                       drmModeFreeProperty(prop);
+                       continue;
+               }
+
+               assert(prop_items[j].drm_prop == NULL);
+               prop_items[j].drm_prop = prop;
+               prop_items[j].id = prop->prop_id;
+               prop_items[j].value = props->prop_values[i];
+               valid_mask |= 1U << j;
+       }
+
+       for (i = 0; i < nprops; i++) {
+               if (prop_items[i].drm_prop == NULL)
+                       weston_log("DRM warning: property '%s' missing from obj"
+                                  " %u of type %#x '%s'\n", prop_names[i],
+                                  obj_id, obj_type,
+                                  drm_object_type_str(obj_type));
+       }
+
+       drmModeFreeObjectProperties(props);
+
+       return valid_mask;
+}
+
+/**
+ * Frees an array of property_items
+ *
+ * @param items Array to free
+ * @param len Number of items in array
+ */
+static void
+property_item_array_release(struct property_item *items, int len)
+{
+       int i;
+
+       for (i = 0; i < len; i++) {
+               if (items[i].drm_prop)
+                       drmModeFreeProperty(items[i].drm_prop);
+       }
+}
+
+/**
+ * Get one property from a DRM plane
+ *
+ * Retrieve the cached value of a property on a DRM plane. The property passed
+ * must be valid for the plane, i.e. must have been in the return value of
+ * drm_properties_get_from_obj.
+ *
+ * @param plane Plane to retrieve property for
+ * @param prop Property to retrieve
+ */
+static uint64_t
+drm_plane_property_get(struct drm_plane *plane, enum wdrm_plane_property prop)
+{
+       assert(plane->props.value_valid_mask & (1U << prop));
+       return plane->props.item[prop].value;
+}
+
+/**
+ * Initialise DRM properties for planes
+ *
+ * Set up the holding structures to track DRM object properties set on planes.
+ * Free the memory allocated here with plane_properties_release.
+ *
+ * @param plane Plane to configure
+ */
+static bool
+plane_properties_init(struct drm_plane *plane)
+{
+       static const char * const plane_property_names[] = {
+               [WDRM_PLANE_TYPE] = "type",
+       };
+       static const char * const plane_type_names[] = {
+               [WDRM_PLANE_TYPE_PRIMARY] = "Primary",
+               [WDRM_PLANE_TYPE_OVERLAY] = "Overlay",
+               [WDRM_PLANE_TYPE_CURSOR] = "Cursor",
+       };
+       uint32_t type_mask;
+       uint32_t required_mask;
+
+       static_assert(ARRAY_LENGTH(plane_property_names) == WDRM_PLANE__COUNT,
+                     "plane_property_names mismatch with the enum");
+       static_assert(ARRAY_LENGTH(plane_type_names) == WDRM_PLANE_TYPE__COUNT,
+                     "plane_type_names mismatch with the enum");
+       static_assert(WDRM_PLANE__COUNT <= 32,
+                     "need more bits for plane item_valid_mask");
+
+       plane->props.value_valid_mask =
+               drm_properties_get_from_obj(plane->compositor,
+                                           plane->props.item,
+                                           plane_property_names,
+                                           WDRM_PLANE__COUNT,
+                                           plane->plane_id,
+                                           DRM_MODE_OBJECT_PLANE);
+
+       required_mask = 0;
+       if (plane->compositor->universal_planes)
+               required_mask |= 1 << WDRM_PLANE_TYPE;
+
+       if (plane->props.value_valid_mask != required_mask) {
+               weston_log("DRM error: failed to look up all plane properties "
+                          "(wanted 0x%x got 0x%x) on ID %d\n",
+                          required_mask, plane->props.value_valid_mask,
+                          plane->plane_id);
+               return false;
+       }
+
+       /* Only the universal-plane enum below here. */
+       if (!plane->compositor->universal_planes)
+               return true;
+
+       type_mask =
+               drm_property_get_enum_map(&plane->props.item[WDRM_PLANE_TYPE],
+                                         plane->props.typemap,
+                                         plane_type_names,
+                                         WDRM_PLANE_TYPE__COUNT);
+
+       required_mask = 1 << WDRM_PLANE_TYPE_PRIMARY;
+       required_mask |= 1 << WDRM_PLANE_TYPE_CURSOR;
+       required_mask |= 1 << WDRM_PLANE_TYPE_OVERLAY;
+       if (type_mask != required_mask) {
+               weston_log("DRM error: failed to look up all plane type "
+                          "enum members (wanted 0x%x got 0x%x) on ID %d\n",
+                          required_mask, type_mask, plane->plane_id);
+               return false;
+       }
+
+       return true;
+}
+
+/**
+ * Free DRM plane properties
+ *
+ * The counterpart to plane_properties_init.
+ *
+ * @param plane Plane to release properties for
+ */
+static void plane_properties_release(struct drm_plane *plane)
+{
+       property_item_array_release(plane->props.item, WDRM_PLANE__COUNT);
+       plane->props.value_valid_mask = 0;
+}
+
+/**
+ * Get type for a DRM plane
+ *
+ * Given a drm_plane object, find its type as an internal enum.
+ *
+ * @param plane Plane to find type for
+ * @returns Internal enum for plane type, or OVERLAY if unknown
+ */
+static enum wdrm_plane_type
+drm_plane_get_type(struct drm_plane *plane)
+{
+       uint64_t drm_type = drm_plane_property_get(plane, WDRM_PLANE_TYPE);
+       int i;
+
+       for (i = 0; i < WDRM_PLANE_TYPE__COUNT; i++) {
+               if (plane->props.typemap[i] == drm_type)
+                       return i;
+       }
+
+       return WDRM_PLANE_TYPE_OVERLAY;
+}
+
 static void
 drm_output_set_cursor(struct drm_output *output);
 
@@ -1413,6 +1799,11 @@ init_drm(struct drm_compositor *ec, struct udev_device 
*device)
        else
                ec->cursor_height = 64;
 
+       ret = drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+       ec->universal_planes = (ret == 0);
+       weston_log("DRM: %s universal planes\n",
+                  ec->universal_planes ? "supports" : "does not support");
+
        return 0;
 }
 
@@ -2386,6 +2777,16 @@ drm_plane_create(struct drm_compositor *ec, const 
drmModePlane *kplane)
        memcpy(plane->formats, kplane->formats,
               kplane->count_formats * sizeof(kplane->formats[0]));
 
+       if (!plane_properties_init(plane)) {
+               free(plane);
+               return NULL;
+       }
+
+       if (ec->universal_planes)
+               plane->type = drm_plane_get_type(plane);
+       else
+               plane->type = WDRM_PLANE_TYPE_OVERLAY;
+
        weston_plane_init(&plane->base, &ec->base, 0, 0);
        wl_list_insert(&ec->sprite_list, &plane->link);
 
@@ -2412,6 +2813,7 @@ drm_plane_destroy(struct drm_plane *plane)
                drm_output_release_fb(plane->output, plane->next);
        weston_plane_release(&plane->base);
        wl_list_remove(&plane->link);
+       plane_properties_release(plane);
        free(plane);
 }
 
@@ -2449,7 +2851,14 @@ create_sprites(struct drm_compositor *ec)
                if (!drm_plane)
                        continue;
 
-               weston_compositor_stack_plane(&ec->base, &drm_plane->base,
+               /* Ignore non-overlay planes for now. */
+               if (drm_plane->type != WDRM_PLANE_TYPE_OVERLAY) {
+                       drm_plane_destroy(drm_plane);
+                       continue;
+               }
+
+               weston_compositor_stack_plane(&ec->base,
+                                             &drm_plane->base,
                                              &ec->base.primary_plane);
        }
 
-- 
2.4.3

_______________________________________________
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to