Skip to content
Commits on Source (6)
......@@ -54,6 +54,7 @@
#include <libweston/libweston.h>
#include <libweston/backend-drm.h>
#include <libweston/weston-log.h>
#include "output-capture.h"
#include "shared/helpers.h"
#include "shared/weston-drm-fourcc.h"
#include "libinput-seat.h"
......@@ -196,6 +197,9 @@ enum wdrm_connector_property {
WDRM_CONNECTOR_EDID = 0,
WDRM_CONNECTOR_DPMS,
WDRM_CONNECTOR_CRTC_ID,
WDRM_CONNECTOR_WRITEBACK_PIXEL_FORMATS,
WDRM_CONNECTOR_WRITEBACK_FB_ID,
WDRM_CONNECTOR_WRITEBACK_OUT_FENCE_PTR,
WDRM_CONNECTOR_NON_DESKTOP,
WDRM_CONNECTOR_CONTENT_PROTECTION,
WDRM_CONNECTOR_HDCP_CONTENT_TYPE,
......@@ -552,12 +556,50 @@ struct drm_connector {
struct drm_property_info props[WDRM_CONNECTOR__COUNT];
};
enum writeback_screenshot_state {
/* No writeback connector screenshot ongoing. */
DRM_OUTPUT_WB_SCREENSHOT_OFF,
/* Screenshot client just triggered a writeback connector screenshot.
* Now we need to prepare an atomic commit that will make DRM perform
* the writeback operation. */
DRM_OUTPUT_WB_SCREENSHOT_PREPARE_COMMIT,
/* The atomic commit with writeback setup has been committed. After the
* commit is handled by DRM it will give us a sync fd that gets
* signalled when the writeback is done. */
DRM_OUTPUT_WB_SCREENSHOT_CHECK_FENCE,
/* The atomic commit completed and we received the sync fd from the
* kernel. We've polled to check if the writeback was over, but it
* wasn't. Now we must stop the repaint loop and wait until the
* writeback is complete, because we can't commit with KMS objects
* (CRTC, planes, etc) that are in used by the writeback job. */
DRM_OUTPUT_WB_SCREENSHOT_WAITING_SIGNAL,
};
struct drm_writeback_state {
struct drm_writeback *wb;
struct drm_output *output;
enum writeback_screenshot_state state;
struct weston_capture_task *ct;
struct drm_fb *fb;
int32_t out_fence_fd;
struct wl_event_source *wb_source;
/* Reference to fb's being used by the writeback job. These are all the
* framebuffers in every drm_plane_state of the output state that we've
* used to request the writeback job */
struct wl_array referenced_fbs;
};
struct drm_writeback {
/* drm_device::writeback_connector_list */
struct wl_list link;
struct drm_device *device;
struct drm_connector connector;
struct weston_drm_format_array formats;
};
struct drm_head {
......@@ -629,6 +671,9 @@ struct drm_output {
* yet acknowledged completion of state_cur. */
struct drm_output_state *state_last;
/* 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;
......@@ -657,6 +702,17 @@ to_drm_head(struct weston_head *base)
return container_of(base, struct drm_head, base);
}
void
drm_writeback_reference_planes(struct drm_writeback_state *state,
struct wl_list *plane_state_list);
bool
drm_writeback_should_wait_completion(struct drm_writeback_state *state);
void
drm_writeback_fail_screenshot(struct drm_writeback_state *state,
const char *err_msg);
enum writeback_screenshot_state
drm_output_get_writeback_state(struct drm_output *output);
void
drm_output_destroy(struct weston_output *output_base);
void
......
......@@ -41,6 +41,7 @@
#include <assert.h>
#include <sys/mman.h>
#include <time.h>
#include <poll.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
......@@ -444,6 +445,159 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
pixman_region32_fini(&scanout_damage);
}
static uint32_t
drm_connector_get_possible_crtcs_mask(struct drm_connector *connector)
{
struct drm_device *device = connector->device;
uint32_t possible_crtcs = 0;
drmModeConnector *conn = connector->conn;
drmModeEncoder *encoder;
int i;
for (i = 0; i < conn->count_encoders; i++) {
encoder = drmModeGetEncoder(device->drm.fd,
conn->encoders[i]);
if (!encoder)
continue;
possible_crtcs |= encoder->possible_crtcs;
drmModeFreeEncoder(encoder);
}
return possible_crtcs;
}
static struct drm_writeback *
drm_output_find_compatible_writeback(struct drm_output *output)
{
struct drm_crtc *crtc;
struct drm_writeback *wb;
bool in_use;
uint32_t possible_crtcs;
wl_list_for_each(wb, &output->device->writeback_connector_list, link) {
/* Another output may be using the writeback connector. */
in_use = false;
wl_list_for_each(crtc, &output->device->crtc_list, link) {
if (crtc->output && crtc->output->wb_state &&
crtc->output->wb_state->wb == wb) {
in_use = true;
break;
}
}
if (in_use)
continue;
/* Is the writeback connector compatible with the CRTC? */
possible_crtcs =
drm_connector_get_possible_crtcs_mask(&wb->connector);
if (!(possible_crtcs & (1 << output->crtc->pipe)))
continue;
/* Does the writeback connector support the output gbm format? */
if (!weston_drm_format_array_find_format(&wb->formats,
output->format->format))
continue;
return wb;
}
return NULL;
}
static struct drm_writeback_state *
drm_writeback_state_alloc(void)
{
struct drm_writeback_state *state;
state = zalloc(sizeof *state);
if (!state)
return NULL;
state->state = DRM_OUTPUT_WB_SCREENSHOT_OFF;
state->out_fence_fd = -1;
wl_array_init(&state->referenced_fbs);
return state;
}
static void
drm_writeback_state_free(struct drm_writeback_state *state)
{
struct drm_fb *fb;
if (state->out_fence_fd >= 0)
close(state->out_fence_fd);
/* Unref framebuffer that was given to save the content of the writeback */
if (state->fb)
drm_fb_unref(state->fb);
/* Unref framebuffers that were in use in the same commit of the one with
* the writeback setup */
wl_array_for_each(fb, &state->referenced_fbs)
drm_fb_unref(fb);
wl_array_release(&state->referenced_fbs);
free(state);
}
static void
drm_output_pick_writeback_capture_task(struct drm_output *output)
{
struct weston_capture_task *ct;
struct weston_buffer *buffer;
struct drm_writeback *wb;
const char *msg;
int32_t width = output->base.current_mode->width;
int32_t height = output->base.current_mode->height;
uint32_t format = output->format->format;
ct = weston_output_pull_capture_task(&output->base,
WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK,
width, height, pixel_format_get_info(format));
if (!ct)
return;
assert(output->device->atomic_modeset);
wb = drm_output_find_compatible_writeback(output);
if (!wb) {
msg = "drm: could not find writeback connector for output";
goto err;
}
buffer = weston_capture_task_get_buffer(ct);
assert(buffer->width == width);
assert(buffer->height == height);
assert(buffer->pixel_format->format == output->format->format);
output->wb_state = drm_writeback_state_alloc();
if (!output->wb_state) {
msg = "drm: failed to allocate memory for writeback state";
goto err;
}
output->wb_state->fb = drm_fb_create_dumb(output->device, width, height, format);
if (!output->wb_state->fb) {
msg = "drm: failed to create dumb buffer for writeback state";
goto err_fb;
}
output->wb_state->output = output;
output->wb_state->wb = wb;
output->wb_state->state = DRM_OUTPUT_WB_SCREENSHOT_PREPARE_COMMIT;
output->wb_state->ct = ct;
return;
err_fb:
drm_writeback_state_free(output->wb_state);
output->wb_state = NULL;
err:
weston_capture_task_retire_failed(ct, msg);
}
static int
drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage)
{
......@@ -482,6 +636,8 @@ drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage)
if (drm_output_ensure_hdr_output_metadata_blob(output) < 0)
goto err;
drm_output_pick_writeback_capture_task(output);
drm_output_render(state, damage);
scanout_state = drm_output_state_get_plane(state,
output->scanout_plane);
......@@ -792,6 +948,13 @@ drm_output_apply_mode(struct drm_output *output)
}
}
if (device->atomic_modeset && !output->base.disable_planes)
weston_output_update_capture_info(&output->base,
WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK,
output->base.current_mode->width,
output->base.current_mode->height,
pixel_format_get_info(output->format->format));
return 0;
}
......@@ -1521,26 +1684,13 @@ drm_output_init_gamma_size(struct drm_output *output)
return 0;
}
static uint32_t
drm_connector_get_possible_crtcs_mask(struct drm_connector *connector)
enum writeback_screenshot_state
drm_output_get_writeback_state(struct drm_output *output)
{
struct drm_device *device = connector->device;
uint32_t possible_crtcs = 0;
drmModeConnector *conn = connector->conn;
drmModeEncoder *encoder;
int i;
for (i = 0; i < conn->count_encoders; i++) {
encoder = drmModeGetEncoder(device->drm.fd,
conn->encoders[i]);
if (!encoder)
continue;
if (!output->wb_state)
return DRM_OUTPUT_WB_SCREENSHOT_OFF;
possible_crtcs |= encoder->possible_crtcs;
drmModeFreeEncoder(encoder);
}
return possible_crtcs;
return output->wb_state->state;
}
/** Pick a CRTC that might be able to drive all attached connectors
......@@ -1967,6 +2117,12 @@ drm_output_enable(struct weston_output *base)
output->base.switch_mode = drm_output_switch_mode;
output->base.set_gamma = drm_output_set_gamma;
if (device->atomic_modeset && !base->disable_planes)
weston_output_update_capture_info(base, WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK,
base->current_mode->width,
base->current_mode->height,
pixel_format_get_info(output->format->format));
weston_log("Output %s (crtc %d) video modes:\n",
output->base.name, output->crtc->crtc_id);
drm_output_print_modes(output);
......@@ -2441,6 +2597,196 @@ drm_output_create(struct weston_backend *backend, const char *name)
return &output->base;
}
static void
pixman_copy_screenshot(uint32_t *dst, uint32_t *src, int dst_stride,
int src_stride, int pixman_format, int width, int height)
{
pixman_image_t *pixman_dst;
pixman_image_t *pixman_src;
pixman_src = pixman_image_create_bits(pixman_format,
width, height,
src, src_stride);
pixman_dst = pixman_image_create_bits(pixman_format,
width, height,
dst, dst_stride);
assert(pixman_src);
assert(pixman_dst);
pixman_image_composite32(PIXMAN_OP_SRC,
pixman_src, /* src */
NULL, /* mask */
pixman_dst, /* dst */
0, 0, /* src_x, src_y */
0, 0, /* mask_x, mask_y */
0, 0, /* dst_x, dst_y */
width, height); /* width, height */
pixman_image_unref(pixman_src);
pixman_image_unref(pixman_dst);
}
static void
drm_writeback_success_screenshot(struct drm_writeback_state *state)
{
struct drm_output *output = state->output;
struct weston_buffer *buffer =
weston_capture_task_get_buffer(state->ct);
int width, height;
int dst_stride, src_stride;
uint32_t *src, *dst;
src = state->fb->map;
src_stride = state->fb->strides[0];
dst = wl_shm_buffer_get_data(buffer->shm_buffer);
dst_stride = wl_shm_buffer_get_stride(buffer->shm_buffer);
width = state->fb->width;
height = state->fb->height;
wl_shm_buffer_begin_access(buffer->shm_buffer);
pixman_copy_screenshot(dst, src, dst_stride, src_stride,
buffer->pixel_format->pixman_format,
width, height);
wl_shm_buffer_end_access(buffer->shm_buffer);
weston_capture_task_retire_complete(state->ct);
drm_writeback_state_free(state);
output->wb_state = NULL;
}
void
drm_writeback_fail_screenshot(struct drm_writeback_state *state,
const char *err_msg)
{
struct drm_output *output = state->output;
weston_capture_task_retire_failed(state->ct, err_msg);
drm_writeback_state_free(state);
output->wb_state = NULL;
}
static int
drm_writeback_save_callback(int fd, uint32_t mask, void *data)
{
struct drm_writeback_state *state = data;
wl_event_source_remove(state->wb_source);
close(fd);
drm_writeback_success_screenshot(state);
return 0;
}
static bool
drm_writeback_has_finished(struct drm_writeback_state *state)
{
struct pollfd pollfd;
int ret;
pollfd.fd = state->out_fence_fd;
pollfd.events = POLLIN;
while ((ret = poll(&pollfd, 1, 0)) == -1 && errno == EINTR)
continue;
if (ret < 0) {
drm_writeback_fail_screenshot(state, "drm: polling wb fence failed");
return true;
} else if (ret > 0) {
/* fence already signaled, simply save the screenshot */
drm_writeback_success_screenshot(state);
return true;
}
/* poll() returned 0, what means that out fence was not signalled yet */
return false;
}
bool
drm_writeback_should_wait_completion(struct drm_writeback_state *state)
{
struct weston_compositor *ec = state->output->base.compositor;
struct wl_event_loop *event_loop;
if (state->state == DRM_OUTPUT_WB_SCREENSHOT_WAITING_SIGNAL)
return true;
if (state->state == DRM_OUTPUT_WB_SCREENSHOT_CHECK_FENCE) {
if (drm_writeback_has_finished(state))
return false;
/* The writeback has not finished yet. So add callback that gets
* called when the sync fd of the writeback job gets signalled.
* We need to wait for that to resume the repaint loop. */
event_loop = wl_display_get_event_loop(ec->wl_display);
state->wb_source =
wl_event_loop_add_fd(event_loop, state->out_fence_fd,
WL_EVENT_READABLE,
drm_writeback_save_callback, state);
if (!state->wb_source) {
drm_writeback_fail_screenshot(state, "drm: out of memory");
return false;
}
state->state = DRM_OUTPUT_WB_SCREENSHOT_WAITING_SIGNAL;
return true;
}
return false;
}
void
drm_writeback_reference_planes(struct drm_writeback_state *state,
struct wl_list *plane_state_list)
{
struct drm_plane_state *plane_state;
struct drm_fb **fb;
wl_list_for_each(plane_state, plane_state_list, link) {
if (!plane_state->fb)
continue;
fb = wl_array_add(&state->referenced_fbs, sizeof(*fb));
*fb = drm_fb_ref(plane_state->fb);
}
}
static int
drm_writeback_populate_formats(struct drm_writeback *wb)
{
struct drm_property_info *info = wb->connector.props;
drmModeObjectProperties *props = wb->connector.props_drm;
uint64_t blob_id;
drmModePropertyBlobPtr blob;
uint32_t *blob_formats;
unsigned int i;
blob_id = drm_property_get_value(&info[WDRM_CONNECTOR_WRITEBACK_PIXEL_FORMATS],
props, 0);
if (blob_id == 0)
return -1;
blob = drmModeGetPropertyBlob(wb->device->drm.fd, blob_id);
if (!blob)
return -1;
blob_formats = blob->data;
for (i = 0; i < blob->length / sizeof(uint32_t); i++)
if (!weston_drm_format_array_add_format(&wb->formats,
blob_formats[i]))
goto err;
return 0;
err:
drmModeFreePropertyBlob(blob);
return -1;
}
/**
* Create a Weston writeback for a writeback connector
*
......@@ -2470,9 +2816,16 @@ drm_writeback_create(struct drm_device *device, drmModeConnector *conn)
if (ret < 0)
goto err;
weston_drm_format_array_init(&writeback->formats);
ret = drm_writeback_populate_formats(writeback);
if (ret < 0)
goto err_formats;
wl_list_insert(&device->writeback_connector_list, &writeback->link);
return 0;
err_formats:
weston_drm_format_array_fini(&writeback->formats);
err:
drm_connector_fini(&writeback->connector);
free(writeback);
......@@ -2483,6 +2836,7 @@ static void
drm_writeback_destroy(struct drm_writeback *writeback)
{
drm_connector_fini(&writeback->connector);
weston_drm_format_array_fini(&writeback->formats);
wl_list_remove(&writeback->link);
free(writeback);
......
......@@ -165,6 +165,9 @@ const struct drm_property_info connector_props[] = {
.num_enum_values = WDRM_DPMS_STATE__COUNT,
},
[WDRM_CONNECTOR_CRTC_ID] = { .name = "CRTC_ID", },
[WDRM_CONNECTOR_WRITEBACK_PIXEL_FORMATS] = { .name = "WRITEBACK_PIXEL_FORMATS", },
[WDRM_CONNECTOR_WRITEBACK_FB_ID] = { .name = "WRITEBACK_FB_ID", },
[WDRM_CONNECTOR_WRITEBACK_OUT_FENCE_PTR] = { .name = "WRITEBACK_OUT_FENCE_PTR", },
[WDRM_CONNECTOR_NON_DESKTOP] = { .name = "non-desktop", },
[WDRM_CONNECTOR_CONTENT_PROTECTION] = {
.name = "Content Protection",
......@@ -1159,6 +1162,9 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
struct drm_plane_state *plane_state;
struct drm_mode *current_mode = to_drm_mode(output->base.current_mode);
struct drm_head *head;
struct drm_writeback_state *wb_state = output->wb_state;
enum writeback_screenshot_state wb_screenshot_state =
drm_output_get_writeback_state(output);
int ret = 0;
drm_debug(b, "\t\t[atomic] %s output %lu (%s) state\n",
......@@ -1170,6 +1176,11 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
*flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
}
if (wb_screenshot_state == DRM_OUTPUT_WB_SCREENSHOT_PREPARE_COMMIT) {
drm_debug(b, "\t\t\t[atomic] Writeback connector screenshot requested, modeset OK\n");
*flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
}
if (state->dpms == WESTON_DPMS_ON) {
ret = drm_mode_ensure_blob(device, current_mode);
if (ret != 0)
......@@ -1195,10 +1206,29 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
WDRM_CONNECTOR_CRTC_ID,
crtc->crtc_id);
}
if (wb_screenshot_state == DRM_OUTPUT_WB_SCREENSHOT_PREPARE_COMMIT) {
ret |= connector_add_prop(req, &wb_state->wb->connector,
WDRM_CONNECTOR_CRTC_ID,
crtc->crtc_id);
ret |= connector_add_prop(req, &wb_state->wb->connector,
WDRM_CONNECTOR_WRITEBACK_FB_ID,
wb_state->fb->fb_id);
ret |= connector_add_prop(req, &wb_state->wb->connector,
WDRM_CONNECTOR_WRITEBACK_OUT_FENCE_PTR,
(uintptr_t)&wb_state->out_fence_fd);
if (!(*flags & DRM_MODE_ATOMIC_TEST_ONLY))
wb_state->state = DRM_OUTPUT_WB_SCREENSHOT_CHECK_FENCE;
}
} else {
ret |= crtc_add_prop(req, crtc, WDRM_CRTC_MODE_ID, 0);
ret |= crtc_add_prop(req, crtc, WDRM_CRTC_ACTIVE, 0);
if (wb_screenshot_state == DRM_OUTPUT_WB_SCREENSHOT_PREPARE_COMMIT) {
drm_debug(b, "\t\t\t[atomic] Writeback connector screenshot requested but CRTC is off\n");
drm_writeback_fail_screenshot(wb_state, "drm: CRTC is off");
}
/* No need for the DPMS property, since it is implicit in
* routing and CRTC activity. */
wl_list_for_each(head, &output->base.head_list, base.output_link)
......@@ -1456,6 +1486,10 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
}
if (ret != 0) {
wl_list_for_each(output_state, &pending_state->output_list, link)
if (drm_output_get_writeback_state(output_state->output) != DRM_OUTPUT_WB_SCREENSHOT_OFF)
drm_writeback_fail_screenshot(output_state->output->wb_state,
"drm: atomic commit failed");
weston_log("atomic: couldn't commit new state: %s\n",
strerror(errno));
goto out;
......@@ -1714,8 +1748,23 @@ int
on_drm_input(int fd, uint32_t mask, void *data)
{
struct drm_device *device = data;
struct drm_writeback_state *state;
struct drm_crtc *crtc;
bool wait_wb_completion = false;
drmEventContext evctx;
/* If we have a pending writeback job for this output, we can't continue
* with the repaint loop. The KMS UAPI docs says that we need to wait
* until the writeback is over before we send a new atomic commit that
* uses the KMS objects (CRTC, planes, etc) in use by the writeback. */
wl_list_for_each(crtc, &device->crtc_list, link) {
state = crtc->output ? crtc->output->wb_state : NULL;
if (state && drm_writeback_should_wait_completion(state))
wait_wb_completion = true;
}
if (wait_wb_completion)
return 1;
memset(&evctx, 0, sizeof evctx);
evctx.version = 3;
if (device->atomic_modeset)
......
......@@ -960,6 +960,7 @@ drm_assign_planes(struct weston_output *output_base)
struct drm_pending_state *pending_state = device->repaint_data;
struct drm_output_state *state = NULL;
struct drm_plane_state *plane_state;
struct drm_writeback_state *wb_state = output->wb_state;
struct weston_paint_node *pnode;
struct weston_plane *primary = &output_base->compositor->primary_plane;
enum drm_output_propose_state_mode mode = DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY;
......@@ -980,18 +981,29 @@ drm_assign_planes(struct weston_output *output_base)
pending_state,
mode);
}
if (!state) {
drm_debug(b, "\t[repaint] could not build mixed-mode "
"state, trying renderer-only\n");
}
} else {
drm_debug(b, "\t[state] no overlay plane support\n");
}
/* We can enter this block in two situations:
* 1. If we didn't enter the last block (for some reason we can't use planes)
* 2. If we entered but both the planes-only and the mixed modes didn't work */
if (!state) {
drm_debug(b, "\t[repaint] could not build state with planes, "
"trying renderer-only\n");
mode = DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY;
state = drm_output_propose_state(output_base, pending_state,
mode);
/* If renderer only mode failed and we are in a writeback
* screenshot, let's abort the writeback screenshot and try
* again. */
if (!state && drm_output_get_writeback_state(output) != DRM_OUTPUT_WB_SCREENSHOT_OFF) {
drm_debug(b, "\t[repaint] could not build renderer-only "
"state, trying without writeback setup\n");
drm_writeback_fail_screenshot(wb_state, "drm: failed to propose state");
state = drm_output_propose_state(output_base, pending_state,
mode);
}
}
assert(state);
......@@ -1079,6 +1091,9 @@ drm_assign_planes(struct weston_output *output_base)
if (!plane_state || !plane_state->fb)
drm_output_set_cursor_view(output, NULL);
}
if (drm_output_get_writeback_state(output) == DRM_OUTPUT_WB_SCREENSHOT_PREPARE_COMMIT)
drm_writeback_reference_planes(wb_state, &state->plane_list);
}
static void
......
/*
* Copyright © 2023 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include "weston-test-client-helper.h"
#include "weston-test-fixture-compositor.h"
#include "weston-output-capture-client-protocol.h"
static enum test_result_code
fixture_setup(struct weston_test_harness *harness)
{
struct compositor_setup setup;
compositor_setup_defaults(&setup);
setup.backend = WESTON_BACKEND_DRM;
setup.renderer = WESTON_RENDERER_PIXMAN;
setup.shell = SHELL_TEST_DESKTOP;
weston_ini_setup(&setup,
cfgln("[shell]"),
cfgln("startup-animation=%s", "none"));
return weston_test_harness_execute_as_client(harness, &setup);
}
DECLARE_FIXTURE_SETUP(fixture_setup);
static void
draw_stuff(pixman_image_t *image)
{
int w, h;
int stride; /* bytes */
int x, y;
uint32_t r, g, b;
uint32_t *pixels;
uint32_t *pixel;
pixman_format_code_t fmt;
fmt = pixman_image_get_format(image);
w = pixman_image_get_width(image);
h = pixman_image_get_height(image);
stride = pixman_image_get_stride(image);
pixels = pixman_image_get_data(image);
assert(PIXMAN_FORMAT_BPP(fmt) == 32);
for (x = 0; x < w; x++)
for (y = 0; y < h; y++) {
b = x;
g = x + y;
r = y;
pixel = pixels + (y * stride / 4) + x;
*pixel = (255U << 24) | (r << 16) | (g << 8) | b;
}
}
TEST(drm_writeback_screenshot) {
struct client *client;
struct buffer *buffer;
struct buffer *screenshot = NULL;
pixman_image_t *reference = NULL;
pixman_image_t *diffimg = NULL;
struct wl_surface *surface;
struct rectangle clip;
const char *fname;
bool match;
int frame;
/* create client */
testlog("Creating client for test\n");
client = create_client_and_test_surface(100, 100, 100, 100);
assert(client);
surface = client->surface->wl_surface;
/* move pointer away from image so it does not interfere with the
* comparison of the writeback screenshot with the reference image */
weston_test_move_pointer(client->test->weston_test, 0, 1, 0, 0, 0);
buffer = create_shm_buffer_a8r8g8b8(client, 100, 100);
draw_stuff(buffer->image);
wl_surface_attach(surface, buffer->proxy, 0, 0);
wl_surface_damage(surface, 0, 0, 100, 100);
frame_callback_set(surface, &frame);
wl_surface_commit(surface);
frame_callback_wait(client, &frame);
/* take screenshot */
testlog("Taking a screenshot\n");
screenshot = client_capture_output(client, client->output,
WESTON_CAPTURE_V1_SOURCE_WRITEBACK);
assert(screenshot);
/* take another screenshot; this is important to ensure the
* writeback state machine is working correctly */
testlog("Taking another screenshot\n");
screenshot = client_capture_output(client, client->output,
WESTON_CAPTURE_V1_SOURCE_WRITEBACK);
assert(screenshot);
/* load reference image */
fname = screenshot_reference_filename("drm-writeback-screenshot", 0);
testlog("Loading good reference image %s\n", fname);
reference = load_image_from_png(fname);
assert(reference);
/* check if they match - only the colored square matters, so the
* clip is used to ignore the background */
clip.x = 100;
clip.y = 100;
clip.width = 100;
clip.height = 100;
match = check_images_match(screenshot->image, reference, &clip, NULL);
testlog("Screenshot %s reference image\n", match? "equal to" : "different from");
if (!match) {
diffimg = visualize_image_difference(screenshot->image, reference, &clip, NULL);
fname = screenshot_output_filename("drm-writeback-screenshot-error", 0);
write_image_as_png(diffimg, fname);
pixman_image_unref(diffimg);
}
pixman_image_unref(reference);
buffer_destroy(screenshot);
client_destroy(client);
assert(match);
}
\ No newline at end of file
......@@ -156,6 +156,7 @@ tests = [
'dep_objs': dep_libdrm_headers,
},
{ 'name': 'drm-smoke', 'run_exclusive': true },
{ 'name': 'drm-writeback-screenshot', 'run_exclusive': true },
{ 'name': 'event', },
{ 'name': 'internal-screenshot', },
{
......