screencopy: perform DMA-BUF copy on output commit

This commit is contained in:
Simon Ser 2020-11-04 16:03:54 +01:00
parent f0945e112f
commit 51fc7ddb29
2 changed files with 106 additions and 49 deletions

View File

@ -53,6 +53,7 @@ struct wlr_screencopy_frame_v1 {
struct wlr_output *output;
struct wl_listener output_precommit;
struct wl_listener output_commit;
struct wl_listener output_destroy;
struct wl_listener output_enable;

View File

@ -143,6 +143,7 @@ static void frame_destroy(struct wlr_screencopy_frame_v1 *frame) {
}
wl_list_remove(&frame->link);
wl_list_remove(&frame->output_precommit.link);
wl_list_remove(&frame->output_commit.link);
wl_list_remove(&frame->output_destroy.link);
wl_list_remove(&frame->output_enable.link);
wl_list_remove(&frame->buffer_destroy.link);
@ -152,6 +153,41 @@ static void frame_destroy(struct wlr_screencopy_frame_v1 *frame) {
free(frame);
}
static void frame_send_damage(struct wlr_screencopy_frame_v1 *frame) {
if (!frame->with_damage) {
return;
}
struct screencopy_damage *damage =
screencopy_damage_get_or_create(frame->client, frame->output);
if (damage == NULL) {
return;
}
// TODO: send fine-grained damage events
struct pixman_box32 *damage_box =
pixman_region32_extents(&damage->damage);
int damage_x = damage_box->x1;
int damage_y = damage_box->y1;
int damage_width = damage_box->x2 - damage_box->x1;
int damage_height = damage_box->y2 - damage_box->y1;
zwlr_screencopy_frame_v1_send_damage(frame->resource,
damage_x, damage_y, damage_width, damage_height);
pixman_region32_clear(&damage->damage);
}
static void frame_send_ready(struct wlr_screencopy_frame_v1 *frame,
struct timespec *when) {
time_t tv_sec = when->tv_sec;
uint32_t tv_sec_hi = (sizeof(tv_sec) > 4) ? tv_sec >> 32 : 0;
uint32_t tv_sec_lo = tv_sec & 0xFFFFFFFF;
zwlr_screencopy_frame_v1_send_ready(frame->resource,
tv_sec_hi, tv_sec_lo, when->tv_nsec);
}
static void frame_handle_output_precommit(struct wl_listener *listener,
void *_data) {
struct wlr_screencopy_frame_v1 *frame =
@ -165,9 +201,14 @@ static void frame_handle_output_precommit(struct wl_listener *listener,
return;
}
struct screencopy_damage *damage = NULL;
struct wl_shm_buffer *shm_buffer = frame->shm_buffer;
if (shm_buffer == NULL) {
return;
}
if (frame->with_damage) {
damage = screencopy_damage_get_or_create(frame->client, output);
struct screencopy_damage *damage =
screencopy_damage_get_or_create(frame->client, output);
if (damage) {
screencopy_damage_accumulate(damage);
if (!pixman_region32_not_empty(&damage->damage)) {
@ -182,36 +223,19 @@ static void frame_handle_output_precommit(struct wl_listener *listener,
int x = frame->box.x;
int y = frame->box.y;
bool ok = false;
uint32_t flags = 0;
enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buffer);
int32_t width = wl_shm_buffer_get_width(shm_buffer);
int32_t height = wl_shm_buffer_get_height(shm_buffer);
int32_t stride = wl_shm_buffer_get_stride(shm_buffer);
struct wl_shm_buffer *shm_buffer = frame->shm_buffer;
struct wlr_dmabuf_v1_buffer *dma_buffer = frame->dma_buffer;
assert(shm_buffer || dma_buffer);
if (shm_buffer) {
enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buffer);
int32_t width = wl_shm_buffer_get_width(shm_buffer);
int32_t height = wl_shm_buffer_get_height(shm_buffer);
int32_t stride = wl_shm_buffer_get_stride(shm_buffer);
wl_shm_buffer_begin_access(shm_buffer);
void *data = wl_shm_buffer_get_data(shm_buffer);
uint32_t renderer_flags = 0;
ok = wlr_renderer_read_pixels(renderer, fmt, &renderer_flags,
stride, width, height, x, y, 0, 0, data);
flags |= renderer_flags & WLR_RENDERER_READ_PIXELS_Y_INVERT ?
ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT : 0;
wl_shm_buffer_end_access(shm_buffer);
} else if (dma_buffer) {
struct wlr_dmabuf_attributes attr = { 0 };
ok = wlr_output_export_dmabuf(frame->output, &attr);
ok = ok && wlr_renderer_blit_dmabuf(renderer,
&dma_buffer->attributes, &attr);
flags |= dma_buffer->attributes.flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT ?
ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT : 0;
wlr_dmabuf_attributes_finish(&attr);
}
wl_shm_buffer_begin_access(shm_buffer);
void *data = wl_shm_buffer_get_data(shm_buffer);
uint32_t renderer_flags = 0;
bool ok = wlr_renderer_read_pixels(renderer, fmt, &renderer_flags,
stride, width, height, x, y, 0, 0, data);
uint32_t flags = renderer_flags & WLR_RENDERER_READ_PIXELS_Y_INVERT ?
ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT : 0;
wl_shm_buffer_end_access(shm_buffer);
if (!ok) {
zwlr_screencopy_frame_v1_send_failed(frame->resource);
@ -220,29 +244,57 @@ static void frame_handle_output_precommit(struct wl_listener *listener,
}
zwlr_screencopy_frame_v1_send_flags(frame->resource, flags);
frame_send_damage(frame);
frame_send_ready(frame, event->when);
frame_destroy(frame);
}
// TODO: send fine-grained damage events
if (damage) {
struct pixman_box32 *damage_box =
pixman_region32_extents(&damage->damage);
static void frame_handle_output_commit(struct wl_listener *listener,
void *data) {
struct wlr_screencopy_frame_v1 *frame =
wl_container_of(listener, frame, output_commit);
struct wlr_output_event_commit *event = data;
struct wlr_output *output = frame->output;
struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend);
assert(renderer);
int damage_x = damage_box->x1;
int damage_y = damage_box->y1;
int damage_width = damage_box->x2 - damage_box->x1;
int damage_height = damage_box->y2 - damage_box->y1;
zwlr_screencopy_frame_v1_send_damage(frame->resource,
damage_x, damage_y, damage_width, damage_height);
pixman_region32_clear(&damage->damage);
if (!(event->committed & WLR_OUTPUT_STATE_BUFFER)) {
return;
}
time_t tv_sec = event->when->tv_sec;
uint32_t tv_sec_hi = (sizeof(tv_sec) > 4) ? tv_sec >> 32 : 0;
uint32_t tv_sec_lo = tv_sec & 0xFFFFFFFF;
zwlr_screencopy_frame_v1_send_ready(frame->resource,
tv_sec_hi, tv_sec_lo, event->when->tv_nsec);
struct wlr_dmabuf_v1_buffer *dma_buffer = frame->dma_buffer;
if (dma_buffer == NULL) {
return;
}
if (frame->with_damage) {
struct screencopy_damage *damage =
screencopy_damage_get_or_create(frame->client, output);
if (damage && !pixman_region32_not_empty(&damage->damage)) {
return;
}
}
wl_list_remove(&frame->output_commit.link);
wl_list_init(&frame->output_commit.link);
struct wlr_dmabuf_attributes attr = { 0 };
bool ok = wlr_output_export_dmabuf(output, &attr);
ok = ok && wlr_renderer_blit_dmabuf(renderer,
&dma_buffer->attributes, &attr);
uint32_t flags = dma_buffer->attributes.flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT ?
ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT : 0;
wlr_dmabuf_attributes_finish(&attr);
if (!ok) {
zwlr_screencopy_frame_v1_send_failed(frame->resource);
frame_destroy(frame);
return;
}
zwlr_screencopy_frame_v1_send_flags(frame->resource, flags);
frame_send_damage(frame);
frame_send_ready(frame, event->when);
frame_destroy(frame);
}
@ -364,6 +416,9 @@ static void frame_handle_copy(struct wl_client *wl_client,
wl_signal_add(&output->events.precommit, &frame->output_precommit);
frame->output_precommit.notify = frame_handle_output_precommit;
wl_signal_add(&output->events.commit, &frame->output_commit);
frame->output_commit.notify = frame_handle_output_commit;
wl_signal_add(&output->events.destroy, &frame->output_enable);
frame->output_enable.notify = frame_handle_output_enable;
@ -466,6 +521,7 @@ static void capture_output(struct wl_client *wl_client,
wl_list_insert(&client->manager->frames, &frame->link);
wl_list_init(&frame->output_precommit.link);
wl_list_init(&frame->output_commit.link);
wl_list_init(&frame->output_enable.link);
wl_list_init(&frame->output_destroy.link);
wl_list_init(&frame->buffer_destroy.link);