Skip to content
Commits on Source (7)
......@@ -1198,6 +1198,7 @@ struct weston_buffer {
int32_t width, height;
uint32_t busy_count;
int y_inverted;
void *backend_private;
};
struct weston_buffer_reference {
......
......@@ -276,6 +276,7 @@ struct drm_backend {
int min_height, max_height;
struct wl_list plane_list;
uint32_t next_plane_idx;
void *repaint_data;
......@@ -340,8 +341,8 @@ struct drm_fb {
uint64_t modifier;
int width, height;
int fd;
struct weston_buffer_reference buffer_ref;
struct weston_buffer_release_reference buffer_release_ref;
uint32_t plane_mask;
/* Used by gbm fbs */
struct gbm_bo *bo;
......@@ -351,6 +352,12 @@ struct drm_fb {
void *map;
};
struct drm_buffer_fb {
struct drm_fb *fb;
enum try_view_on_plane_failure_reasons failure_reasons;
struct wl_listener buffer_destroy_listener;
};
struct drm_edid {
char eisa_id[13];
char monitor_name[13];
......@@ -411,6 +418,10 @@ struct drm_plane_state {
struct drm_output_state *output_state;
struct drm_fb *fb;
struct {
struct weston_buffer_reference buffer;
struct weston_buffer_release_reference release;
} fb_ref;
struct weston_view *ev; /**< maintained for drm_assign_planes only */
......@@ -455,6 +466,7 @@ struct drm_plane {
uint32_t possible_crtcs;
uint32_t plane_id;
uint32_t plane_idx;
struct drm_property_info props[WDRM_PLANE__COUNT];
......
......@@ -764,6 +764,7 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane)
}
plane->backend = b;
plane->plane_idx = b->next_plane_idx++;
plane->state_cur = drm_plane_state_alloc(NULL, plane);
plane->state_cur->complete = true;
plane->possible_crtcs = kplane->possible_crtcs;
......@@ -1725,6 +1726,7 @@ get_scanout_formats(struct drm_backend *b)
err:
weston_drm_format_array_fini(&union_planes_formats);
weston_drm_format_array_fini(scanout_formats);
free(scanout_formats);
return NULL;
}
......@@ -3154,6 +3156,7 @@ drm_backend_create(struct weston_compositor *compositor,
ret = weston_dmabuf_feedback_format_table_set_scanout_indices(compositor->dmabuf_feedback_format_table,
scanout_formats);
weston_drm_format_array_fini(scanout_formats);
free(scanout_formats);
if (ret < 0)
goto err_udev_monitor;
}
......
......@@ -48,8 +48,6 @@ drm_fb_destroy(struct drm_fb *fb)
{
if (fb->fb_id != 0)
drmModeRmFB(fb->fd, fb->fb_id);
weston_buffer_reference(&fb->buffer_ref, NULL);
weston_buffer_release_reference(&fb->buffer_release_ref, NULL);
free(fb);
}
......@@ -410,17 +408,6 @@ err_free:
free(fb);
return NULL;
}
static void
drm_fb_set_buffer(struct drm_fb *fb, struct weston_buffer *buffer,
struct weston_buffer_release *buffer_release)
{
assert(fb->buffer_ref.buffer == NULL);
assert(fb->type == BUFFER_CLIENT || fb->type == BUFFER_DMABUF);
weston_buffer_reference(&fb->buffer_ref, buffer);
weston_buffer_release_reference(&fb->buffer_release_ref,
buffer_release);
}
#endif
void
......@@ -474,6 +461,58 @@ drm_can_scanout_dmabuf(struct weston_compositor *ec,
return ret;
}
static bool
drm_fb_compatible_with_plane(struct drm_fb *fb, struct drm_plane *plane)
{
struct drm_backend *b = plane->backend;
struct weston_drm_format *fmt;
/* Check whether the format is supported */
fmt = weston_drm_format_array_find_format(&plane->formats,
fb->format->format);
if (fmt) {
/* We never try to promote a dmabuf with DRM_FORMAT_MOD_INVALID
* to a KMS plane (see drm_fb_get_from_dmabuf() for more details).
* So if fb->modifier == DRM_FORMAT_MOD_INVALID, we are sure
* that this is for the legacy GBM import path, in which a
* wl_drm is being used for scanout. Mesa is the only user we
* care in this case (even though recent versions are also using
* dmabufs), and it should know better what works or not. */
if (fb->modifier == DRM_FORMAT_MOD_INVALID)
return true;
if (weston_drm_format_has_modifier(fmt, fb->modifier))
return true;
}
drm_debug(b, "\t\t\t\t[%s] not placing view on %s: "
"no free %s planes matching format %s (0x%lx) "
"modifier 0x%llx\n",
drm_output_get_plane_type_name(plane),
drm_output_get_plane_type_name(plane),
drm_output_get_plane_type_name(plane),
fb->format->drm_format_name,
(unsigned long) fb->format->format,
(unsigned long long) fb->modifier);
return false;
}
static void
drm_fb_handle_buffer_destroy(struct wl_listener *listener, void *data)
{
struct drm_buffer_fb *buf_fb =
container_of(listener, struct drm_buffer_fb, buffer_destroy_listener);
if (buf_fb->fb) {
assert(buf_fb->fb->type == BUFFER_CLIENT ||
buf_fb->fb->type == BUFFER_DMABUF);
drm_fb_unref(buf_fb->fb);
}
free(buf_fb);
}
struct drm_fb *
drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev,
uint32_t *try_view_on_plane_failure_reasons)
......@@ -481,9 +520,11 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev,
struct drm_output *output = state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
struct drm_buffer_fb *buf_fb;
bool is_opaque = weston_view_is_opaque(ev, &ev->transform.boundingbox);
struct linux_dmabuf_buffer *dmabuf;
struct drm_fb *fb;
struct drm_plane *plane;
if (ev->alpha != 1.0f)
return NULL;
......@@ -498,38 +539,67 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev,
if (!buffer)
return NULL;
if (buffer->backend_private) {
buf_fb = buffer->backend_private;
*try_view_on_plane_failure_reasons |= buf_fb->failure_reasons;
return buf_fb->fb ? drm_fb_ref(buf_fb->fb) : NULL;
}
buf_fb = zalloc(sizeof(*buf_fb));
buffer->backend_private = buf_fb;
buf_fb->buffer_destroy_listener.notify = drm_fb_handle_buffer_destroy;
wl_signal_add(&buffer->destroy_signal, &buf_fb->buffer_destroy_listener);
if (wl_shm_buffer_get(buffer->resource))
return NULL;
goto unsuitable;
/* GBM is used for dmabuf import as well as from client wl_buffer. */
if (!b->gbm)
return NULL;
goto unsuitable;
dmabuf = linux_dmabuf_buffer_get(buffer->resource);
if (dmabuf) {
fb = drm_fb_get_from_dmabuf(dmabuf, b, is_opaque,
try_view_on_plane_failure_reasons);
&buf_fb->failure_reasons);
if (!fb)
return NULL;
goto unsuitable;
} else {
struct gbm_bo *bo;
bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
buffer->resource, GBM_BO_USE_SCANOUT);
if (!bo)
return NULL;
goto unsuitable;
fb = drm_fb_get_from_bo(bo, b, is_opaque, BUFFER_CLIENT);
if (!fb) {
gbm_bo_destroy(bo);
return NULL;
goto unsuitable;
}
}
/* Check if this buffer can ever go on any planes. If it can't, we have
* no reason to ever have a drm_fb, so we fail it here. */
wl_list_for_each(plane, &b->plane_list, link) {
if (drm_fb_compatible_with_plane(fb, plane))
fb->plane_mask |= (1 << plane->plane_idx);
}
if (fb->plane_mask == 0) {
drm_fb_unref(fb);
buf_fb->failure_reasons |= FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
goto unsuitable;
}
/* The caller holds its own ref to the drm_fb, so when creating a new
* drm_fb we take an additional ref for the weston_buffer's cache. */
buf_fb->fb = drm_fb_ref(fb);
drm_debug(b, "\t\t\t[view] view %p format: %s\n",
ev, fb->format->drm_format_name);
drm_fb_set_buffer(fb, buffer,
ev->surface->buffer_release_ref.buffer_release);
return fb;
unsuitable:
*try_view_on_plane_failure_reasons |= buf_fb->failure_reasons;
return NULL;
}
#endif
......@@ -93,6 +93,8 @@ drm_plane_state_free(struct drm_plane_state *state, bool force)
if (force || state != state->plane->state_cur) {
drm_fb_unref(state->fb);
weston_buffer_reference(&state->fb_ref.buffer, NULL);
weston_buffer_release_reference(&state->fb_ref.release, NULL);
free(state);
}
}
......@@ -128,8 +130,21 @@ drm_plane_state_duplicate(struct drm_output_state *state_output,
}
wl_list_insert(&state_output->plane_list, &dst->link);
if (src->fb)
/* Take a reference on the src framebuffer; if it wraps a client
* buffer, then we must also transfer the reference on the client
* buffer. */
if (src->fb) {
dst->fb = drm_fb_ref(src->fb);
memset(&dst->fb_ref, 0, sizeof(dst->fb_ref));
weston_buffer_reference(&dst->fb_ref.buffer,
src->fb_ref.buffer.buffer);
weston_buffer_release_reference(&dst->fb_ref.release,
src->fb_ref.release.buffer_release);
} else {
assert(!src->fb_ref.buffer.buffer);
assert(!src->fb_ref.release.buffer_release);
}
dst->output_state = state_output;
dst->complete = false;
......
......@@ -124,64 +124,6 @@ drm_output_check_plane_has_view_assigned(struct drm_plane *plane,
return false;
}
static bool
drm_output_plane_has_valid_format(struct drm_plane *plane,
struct drm_output_state *state,
struct drm_fb *fb)
{
struct drm_backend *b = plane->backend;
struct weston_drm_format *fmt;
if (!fb)
return false;
/* Check whether the format is supported */
fmt = weston_drm_format_array_find_format(&plane->formats,
fb->format->format);
if (fmt) {
/* We never try to promote a dmabuf with DRM_FORMAT_MOD_INVALID
* to a KMS plane (see drm_fb_get_from_dmabuf() for more details).
* So if fb->modifier == DRM_FORMAT_MOD_INVALID, we are sure
* that this is for the legacy GBM import path, in which a
* wl_drm is being used for scanout. Mesa is the only user we
* care in this case (even though recent versions are also using
* dmabufs), and it should know better what works or not. */
if (fb->modifier == DRM_FORMAT_MOD_INVALID)
return true;
if (weston_drm_format_has_modifier(fmt, fb->modifier))
return true;
}
drm_debug(b, "\t\t\t\t[%s] not placing view on %s: "
"no free %s planes matching format %s (0x%lx) "
"modifier 0x%llx\n",
drm_output_get_plane_type_name(plane),
drm_output_get_plane_type_name(plane),
drm_output_get_plane_type_name(plane),
fb->format->drm_format_name,
(unsigned long) fb->format->format,
(unsigned long long) fb->modifier);
return false;
}
static bool
drm_output_plane_cursor_has_valid_format(struct weston_view *ev)
{
struct wl_shm_buffer *shmbuf =
wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource);
/* When we have cursor planes we've already checked for wl_shm buffer in
* the view before calling this function. */
assert(shmbuf);
if (wl_shm_buffer_get_format(shmbuf) == WL_SHM_FORMAT_ARGB8888)
return true;
return false;
}
static struct drm_plane_state *
drm_output_prepare_overlay_view(struct drm_plane *plane,
struct drm_output_state *output_state,
......@@ -497,28 +439,6 @@ err:
return NULL;
}
static bool
drm_output_plane_view_has_valid_format(struct drm_plane *plane,
struct drm_output_state *state,
struct weston_view *ev,
struct drm_fb *fb)
{
/* depending on the type of the plane we have different requirements */
switch (plane->type) {
case WDRM_PLANE_TYPE_CURSOR:
return drm_output_plane_cursor_has_valid_format(ev);
case WDRM_PLANE_TYPE_OVERLAY:
return drm_output_plane_has_valid_format(plane, state, fb);
case WDRM_PLANE_TYPE_PRIMARY:
return drm_output_plane_has_valid_format(plane, state, fb);
default:
assert(0);
return false;
}
return false;
}
static struct drm_plane_state *
drm_output_try_view_on_plane(struct drm_plane *plane,
struct drm_output_state *state,
......@@ -532,6 +452,7 @@ drm_output_try_view_on_plane(struct drm_plane *plane,
struct drm_plane *scanout_plane = state->output->scanout_plane;
struct drm_plane_state *ps = NULL;
const char *p_name = drm_output_get_plane_type_name(plane);
struct weston_surface *surface = ev->surface;
enum {
NO_PLANES, /* generic err-handle */
NO_PLANES_ACCEPTED,
......@@ -603,6 +524,20 @@ out:
plane->plane_id, ev, p_name);
break;
case PLACED_ON_PLANE:
/* Take a reference on the buffer so that we don't release it
* back to the client until we're done with it; cursor buffers
* don't require a reference since we copy them. */
assert(ps->fb_ref.buffer.buffer == NULL);
assert(ps->fb_ref.release.buffer_release == NULL);
if (ps->plane->type == WDRM_PLANE_TYPE_CURSOR) {
assert(ps->fb->type == BUFFER_CURSOR);
} else if (fb->type == BUFFER_CLIENT || fb->type == BUFFER_DMABUF) {
assert(ps->fb == fb);
weston_buffer_reference(&ps->fb_ref.buffer,
surface->buffer_ref.buffer);
weston_buffer_release_reference(&ps->fb_ref.release,
surface->buffer_release_ref.buffer_release);
}
break;
}
......@@ -773,6 +708,8 @@ drm_output_prepare_plane_view(struct drm_output_state *state,
buffer = ev->surface->buffer_ref.buffer;
shmbuf = wl_shm_buffer_get(buffer->resource);
fb = drm_fb_get_from_view(state, ev, try_view_on_plane_failure_reasons);
if (!shmbuf && !fb)
return NULL;
/* assemble a list with possible candidates */
wl_list_for_each(plane, &b->plane_list, link) {
......@@ -817,15 +754,17 @@ drm_output_prepare_plane_view(struct drm_output_state *state,
continue;
}
if (plane->type == WDRM_PLANE_TYPE_CURSOR && !shmbuf) {
if (plane->type == WDRM_PLANE_TYPE_CURSOR &&
(!shmbuf || wl_shm_buffer_get_format(shmbuf) != WL_SHM_FORMAT_ARGB8888)) {
drm_debug(b, "\t\t\t\t[plane] not adding plane %d, type cursor to "
"candidate list: cursor planes only support wl_shm "
"buffers and the view buffer is of another type\n",
"candidate list: cursor planes only support ARGB8888"
"wl_shm buffers and the view buffer is of another type\n",
plane->plane_id);
continue;
}
if (!drm_output_plane_view_has_valid_format(plane, state, ev, fb)) {
if (plane->type != WDRM_PLANE_TYPE_CURSOR &&
(!fb || !(fb->plane_mask & (1 << plane->plane_idx)))) {
*try_view_on_plane_failure_reasons |=
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
......
......@@ -189,6 +189,8 @@ cp_destroy_listener(struct wl_listener *listener, void *data)
wl_list_remove(&cp->protected_list);
weston_log_scope_destroy(cp->debug);
cp->debug = NULL;
if (cp->surface_protection_update)
wl_event_source_remove(cp->surface_protection_update);
cp->surface_protection_update = NULL;
cp->compositor->content_protection = NULL;
free(cp);
......