From 9e26808c280cb32f32835231a76b5b105011fd1e Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 21 May 2018 18:50:51 +0100 Subject: [PATCH 01/40] output, backend/drm: add wlr_output_export_dmabuf --- backend/drm/drm.c | 21 +++++++++++++++++++++ backend/drm/renderer.c | 25 +++++++++++++++---------- include/backend/drm/renderer.h | 2 ++ include/wlr/interfaces/wlr_output.h | 2 ++ include/wlr/types/wlr_output.h | 3 +++ types/wlr_output.c | 8 ++++++++ 6 files changed, 51 insertions(+), 10 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index ef8efb9a..5b6054d3 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -254,6 +254,26 @@ static uint32_t drm_connector_get_gamma_size(struct wlr_output *output) { return 0; } +static bool drm_connector_export_dmabuf(struct wlr_output *output, + struct wlr_dmabuf_buffer_attribs *attribs) { + struct wlr_drm_connector *conn = (struct wlr_drm_connector *)output; + struct wlr_drm_backend *drm = (struct wlr_drm_backend *)output->backend; + + if (!drm->session->active) { + return false; + } + + struct wlr_drm_crtc *crtc = conn->crtc; + if (!crtc) { + return false; + } + struct wlr_drm_plane *plane = crtc->primary; + struct wlr_drm_surface *surf = &plane->surf; + + export_drm_bo(surf->back, attribs); + return true; +} + static void drm_connector_start_renderer(struct wlr_drm_connector *conn) { if (conn->state != WLR_DRM_CONN_CONNECTED) { return; @@ -742,6 +762,7 @@ static const struct wlr_output_impl output_impl = { .swap_buffers = drm_connector_swap_buffers, .set_gamma = drm_connector_set_gamma, .get_gamma_size = drm_connector_get_gamma_size, + .export_dmabuf = drm_connector_export_dmabuf, }; bool wlr_output_is_drm(struct wlr_output *output) { diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index d5bcef2b..2cb47a12 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -160,6 +160,19 @@ void post_drm_surface(struct wlr_drm_surface *surf) { } } +void export_drm_bo(struct gbm_bo *bo, + struct wlr_dmabuf_buffer_attribs *attribs) { + memset(attribs, 0, sizeof(struct wlr_dmabuf_buffer_attribs)); + attribs->n_planes = 1; + attribs->width = gbm_bo_get_width(bo); + attribs->height = gbm_bo_get_height(bo); + attribs->format = gbm_bo_get_format(bo); + attribs->offset[0] = 0; + attribs->stride[0] = gbm_bo_get_stride_for_plane(bo, 0); + attribs->modifier[0] = DRM_FORMAT_MOD_LINEAR; + attribs->fd[0] = gbm_bo_get_fd(bo); +} + struct tex { struct wlr_egl *egl; EGLImageKHR img; @@ -186,16 +199,8 @@ static struct wlr_texture *get_tex_for_bo(struct wlr_drm_renderer *renderer, return NULL; } - struct wlr_dmabuf_buffer_attribs attribs = { - .n_planes = 1, - .width = gbm_bo_get_width(bo), - .height = gbm_bo_get_height(bo), - .format = gbm_bo_get_format(bo), - }; - attribs.offset[0] = 0; - attribs.stride[0] = gbm_bo_get_stride_for_plane(bo, 0); - attribs.modifier[0] = DRM_FORMAT_MOD_LINEAR; - attribs.fd[0] = gbm_bo_get_fd(bo); + struct wlr_dmabuf_buffer_attribs attribs; + export_drm_bo(bo, &attribs); tex->tex = wlr_texture_from_dmabuf(renderer->wlr_rend, &attribs); if (tex->tex == NULL) { diff --git a/include/backend/drm/renderer.h b/include/backend/drm/renderer.h index 510abe43..f26ca3d6 100644 --- a/include/backend/drm/renderer.h +++ b/include/backend/drm/renderer.h @@ -52,5 +52,7 @@ struct gbm_bo *get_drm_surface_front(struct wlr_drm_surface *surf); void post_drm_surface(struct wlr_drm_surface *surf); struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest, struct gbm_bo *src); +void export_drm_bo(struct gbm_bo *bo, + struct wlr_dmabuf_buffer_attribs *attribs); #endif diff --git a/include/wlr/interfaces/wlr_output.h b/include/wlr/interfaces/wlr_output.h index 7ecc7551..d39e4edc 100644 --- a/include/wlr/interfaces/wlr_output.h +++ b/include/wlr/interfaces/wlr_output.h @@ -23,6 +23,8 @@ struct wlr_output_impl { void (*set_gamma)(struct wlr_output *output, uint32_t size, uint16_t *r, uint16_t *g, uint16_t *b); uint32_t (*get_gamma_size)(struct wlr_output *output); + bool (*export_dmabuf)(struct wlr_output *output, + struct wlr_dmabuf_buffer_attribs *attribs); }; void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index cef3fc5d..669b96ed 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -6,6 +6,7 @@ #include #include #include +#include struct wlr_output_mode { uint32_t flags; // enum wl_output_mode @@ -162,6 +163,8 @@ void wlr_output_schedule_frame(struct wlr_output *output); void wlr_output_set_gamma(struct wlr_output *output, uint32_t size, uint16_t *r, uint16_t *g, uint16_t *b); uint32_t wlr_output_get_gamma_size(struct wlr_output *output); +bool wlr_output_export_dmabuf(struct wlr_output *output, + struct wlr_dmabuf_buffer_attribs *attribs); void wlr_output_set_fullscreen_surface(struct wlr_output *output, struct wlr_surface *surface); struct wlr_output *wlr_output_from_resource(struct wl_resource *resource); diff --git a/types/wlr_output.c b/types/wlr_output.c index a5a6d0eb..f7001b16 100644 --- a/types/wlr_output.c +++ b/types/wlr_output.c @@ -560,6 +560,14 @@ uint32_t wlr_output_get_gamma_size(struct wlr_output *output) { return output->impl->get_gamma_size(output); } +bool wlr_output_export_dmabuf(struct wlr_output *output, + struct wlr_dmabuf_buffer_attribs *attribs) { + if (!output->impl->export_dmabuf) { + return false; + } + return output->impl->export_dmabuf(output, attribs); +} + void wlr_output_update_needs_swap(struct wlr_output *output) { output->needs_swap = true; wlr_signal_emit_safe(&output->events.needs_swap, output); From 36bd4795d4fe2282dfcc59f26863bac2896a4a3f Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 22 May 2018 17:38:05 +0100 Subject: [PATCH 02/40] export-dmabuf: add basic and incomplete implementation --- include/wlr/types/wlr_export_dmabuf_v1.h | 33 +++ protocol/meson.build | 3 +- protocol/wlr-export-dmabuf-unstable-v1.xml | 230 +++++++++++++++++++++ types/meson.build | 1 + types/wlr_export_dmabuf_v1.c | 185 +++++++++++++++++ 5 files changed, 451 insertions(+), 1 deletion(-) create mode 100644 include/wlr/types/wlr_export_dmabuf_v1.h create mode 100644 protocol/wlr-export-dmabuf-unstable-v1.xml create mode 100644 types/wlr_export_dmabuf_v1.c diff --git a/include/wlr/types/wlr_export_dmabuf_v1.h b/include/wlr/types/wlr_export_dmabuf_v1.h new file mode 100644 index 00000000..78db1e61 --- /dev/null +++ b/include/wlr/types/wlr_export_dmabuf_v1.h @@ -0,0 +1,33 @@ +#ifndef WLR_TYPES_WLR_EXPORT_DMABUF_V1_H +#define WLR_TYPES_WLR_EXPORT_DMABUF_V1_H + +#include + +struct wlr_export_dmabuf_manager_v1; + +struct wlr_export_dmabuf_frame_v1 { + struct wl_resource *resource; + struct wlr_export_dmabuf_manager_v1 *manager; + struct wl_list link; + + struct wlr_output *output; +}; + +struct wlr_export_dmabuf_manager_v1 { + struct wl_global *global; + struct wl_list resources; + struct wl_list frames; + + struct wl_listener display_destroy; + + struct { + struct wl_signal destroy; + } events; +}; + +struct wlr_export_dmabuf_manager_v1 *wlr_export_dmabuf_manager_v1_create( + struct wl_display *display); +void wlr_export_dmabuf_manager_v1_destroy( + struct wlr_export_dmabuf_manager_v1 *manager); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index 8fa64ca9..a14e9723 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -39,8 +39,9 @@ protocols = [ 'screenshooter.xml', 'server-decoration.xml', 'virtual-keyboard-unstable-v1.xml', - 'wlr-layer-shell-unstable-v1.xml', + 'wlr-export-dmabuf-unstable-v1.xml', 'wlr-input-inhibitor-unstable-v1.xml', + 'wlr-layer-shell-unstable-v1.xml', ] client_protocols = [ diff --git a/protocol/wlr-export-dmabuf-unstable-v1.xml b/protocol/wlr-export-dmabuf-unstable-v1.xml new file mode 100644 index 00000000..6332b146 --- /dev/null +++ b/protocol/wlr-export-dmabuf-unstable-v1.xml @@ -0,0 +1,230 @@ + + + + + Copyright © 2018 Rostislav Pehlivanov + + 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. + + + + An interface to capture surfaces in an efficient way. + Overall usage: + + 1.) client registers with zwlr_screencontent_manager_v1 + 2.) server sends client info about surfaces via "receive_surface_info" + 3.) client subscribes to capture a surface via the "capture" requests + 4.) server sends client events via the "zwlr_screencontent_frame" interface + 5.) client finishes and informs server via the "frame_destroy" event + 6.) client optionally resubscribes via repeating steps 3.) through 5.) + + + + + This object represents a frame which is ready to have its resources + fetched and used. + + The receive callback shall be called first, followed by either the + "dma_object" callback once per object or the "dma_layer" callback, + once per layer. The "dma_plane" callback shall only be called after + the "dma_layer" callback corresponding to the layer the plane belongs + to has been called. Finally, the "ready" event is called to indicate that + all the data has been made available for readout, as well as the time + at which presentation happened at. + The ownership of the frame is passed to the client, who's responsible for + destroying it via the "destroy" event once finished. + The data the API describes has been based off of what + VASurfaceAttribExternalBuffers contains. + All frames are read-only and may not be written into or altered. + + + + + Special flags that must be respected by the client. + Transient frames indicate short lifetime frames (such as swapchain + images from external clients). Clients are advised to copy them and do + all processing outside of the "ready" event. + + + + + + + Main callback supplying the client with information about the frame, + as well as an object to serve as context for destruction. Always called + first before any other events. + + The "transform" argument describes the orientation needed to be applied + to correctly orient the buffer. For example, a buffer rotated by 90 + degrees will have a value of "3" here, corresponding to the need to + apply a 270 degree transpose to correctly present the buffer. + + + + + + + + + + + + + + + Callback which serves to supply the client with the file descriptors + containing the data for each object. + + + + + + + + Callback which serves to supply the client with information on what's + contained in each file descriptor and how its laid out. + Will be called after the main receive event, once per layer. + + + + + + + + Callback which supplies the client with plane information for each + layer. + + + + + + + + + + Called as soon as the frame is presented, indicating it is available + for reading. + The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, + each component being an unsigned 32-bit value. Whole seconds are in + tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, + and the additional fractional part in tv_nsec as nanoseconds. Hence, + for valid timestamps tv_nsec must be in [0, 999999999]. + The seconds part may have an arbitrary offset at start. + + + + + + + + If the frame is no longer valid after the "frame" event has been called, + this callback will be used to inform the client to scrap the frame. + Source is still valid for as long as the subscription function does not + return NULL. + This may get called if for instance the surface is in the process of + resizing. + + + + + + Unreferences the frame, allowing it to be reused. Must be called as soon + as its no longer used. + + + + + + + This object is a manager which informs clients about capturable windows + and is able to create callbacks from which to begin to receive content + from. The "title" argument in the "surface_info" event shall be used + to provide a user-readable identifier such as a window title or + program name. + + + + + This will be called whenever a surface that's able to be captured + appears. + + + + + + + Called if a surface becomes unavailable to capture, for example if has + been closed. + + + + + + + Request to start capturing from a surface with a given id. + If an ID becomes unavailable, a NULL will be returned. + + + + + + + + Request to start capturing from an entire wl_output. + If an output becomes unavailable, a NULL will be returned. + + + + + + diff --git a/types/meson.build b/types/meson.build index f9f5b469..0842f98c 100644 --- a/types/meson.build +++ b/types/meson.build @@ -22,6 +22,7 @@ lib_wlr_types = static_library( 'wlr_box.c', 'wlr_compositor.c', 'wlr_cursor.c', + 'wlr_export_dmabuf_v1.c', 'wlr_gamma_control.c', 'wlr_idle_inhibit_v1.c', 'wlr_idle.c', diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c new file mode 100644 index 00000000..fa732397 --- /dev/null +++ b/types/wlr_export_dmabuf_v1.c @@ -0,0 +1,185 @@ +#include +#include +#include +#include +#include +#include +#include "wlr-export-dmabuf-unstable-v1-protocol.h" + +#define EXPORT_DMABUF_MANAGER_VERSION 1 + + +static const struct zwlr_export_dmabuf_frame_v1_interface frame_impl; + +static struct wlr_export_dmabuf_frame_v1 *frame_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zwlr_export_dmabuf_frame_v1_interface, &frame_impl)); + return wl_resource_get_user_data(resource); +} + +static void frame_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct zwlr_export_dmabuf_frame_v1_interface frame_impl = { + .destroy = frame_handle_destroy, +}; + +static void frame_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_export_dmabuf_frame_v1 *frame = frame_from_resource(resource); + wl_list_remove(&frame->link); + free(frame); +} + + +static const struct zwlr_export_dmabuf_manager_v1_interface manager_impl; + +static struct wlr_export_dmabuf_manager_v1 *manager_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zwlr_export_dmabuf_manager_v1_interface, &manager_impl)); + return wl_resource_get_user_data(resource); +} + +static void manager_handle_capture_client(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id, + uint32_t client_id, int32_t overlay_cursor) { + // TODO +} + +static void manager_handle_capture_output(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id, + struct wl_resource *output_resource) { + struct wlr_export_dmabuf_manager_v1 *manager = + manager_from_resource(manager_resource); + struct wlr_output *output = wlr_output_from_resource(output_resource); + + struct wlr_export_dmabuf_frame_v1 *frame = + calloc(1, sizeof(struct wlr_export_dmabuf_frame_v1)); + if (frame == NULL) { + wl_resource_post_no_memory(manager_resource); + return; + } + frame->manager = manager; + frame->output = output; + + uint32_t version = wl_resource_get_version(manager_resource); + frame->resource = wl_resource_create(client, + &zwlr_export_dmabuf_frame_v1_interface, version, id); + if (frame->resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(frame->resource, &frame_impl, frame, + frame_handle_resource_destroy); + + wl_list_insert(&manager->frames, &frame->link); + + struct wlr_dmabuf_buffer_attribs attribs; + if (!wlr_output_export_dmabuf(output, &attribs)) { + zwlr_export_dmabuf_frame_v1_send_abort(frame->resource); + return; + } + + // TODO: multiple layers support + + uint32_t frame_flags = 0; + uint32_t mod_high = attribs.modifier[0] >> 32; + uint32_t mod_low = attribs.modifier[0]; + + zwlr_export_dmabuf_frame_v1_send_frame(frame->resource, + output->width, output->height, output->scale, output->transform, + attribs.flags, frame_flags, mod_high, mod_low, attribs.n_planes, 1); + + zwlr_export_dmabuf_frame_v1_send_dma_layer(frame->resource, 0, + attribs.format, 1); + + for (int i = 0; i < attribs.n_planes; ++i) { + // TODO: what to do if the kernel doesn't support seek on buffer + off_t size = lseek(attribs.fd[i], 0, SEEK_END); + + zwlr_export_dmabuf_frame_v1_send_dma_object(frame->resource, i, + attribs.fd[i], size); + zwlr_export_dmabuf_frame_v1_send_dma_plane(frame->resource, i, 0, i, + attribs.offset[i], attribs.stride[i]); + } + + // TODO: wait for the frame to be ready + // TODO: timestamps + zwlr_export_dmabuf_frame_v1_send_ready(frame->resource, 0, 0, 0); +} + +static const struct zwlr_export_dmabuf_manager_v1_interface manager_impl = { + .capture_client = manager_handle_capture_client, + .capture_output = manager_handle_capture_output, +}; + +static void manager_handle_resource_destroy(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +static void manager_bind(struct wl_client *client, void *data, uint32_t version, + uint32_t id) { + struct wlr_export_dmabuf_manager_v1 *manager = data; + + struct wl_resource *resource = wl_resource_create(client, + &zwlr_export_dmabuf_manager_v1_interface, version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &manager_impl, manager, + manager_handle_resource_destroy); + + wl_list_insert(&manager->resources, wl_resource_get_link(resource)); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_export_dmabuf_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + wlr_export_dmabuf_manager_v1_destroy(manager); +} + +struct wlr_export_dmabuf_manager_v1 *wlr_export_dmabuf_manager_v1_create( + struct wl_display *display) { + struct wlr_export_dmabuf_manager_v1 *manager = + calloc(1, sizeof(struct wlr_export_dmabuf_manager_v1)); + if (manager == NULL) { + return NULL; + } + wl_list_init(&manager->resources); + wl_list_init(&manager->frames); + + manager->global = wl_global_create(display, + &zwlr_export_dmabuf_manager_v1_interface, EXPORT_DMABUF_MANAGER_VERSION, + manager, manager_bind); + if (manager->global == NULL) { + free(manager); + return NULL; + } + + manager->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; +} + +void wlr_export_dmabuf_manager_v1_destroy( + struct wlr_export_dmabuf_manager_v1 *manager) { + if (manager == NULL) { + return; + } + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + struct wl_resource *resource, *resource_tmp; + wl_resource_for_each_safe(resource, resource_tmp, &manager->resources) { + wl_resource_destroy(resource); + } + struct wlr_export_dmabuf_frame_v1 *frame, *frame_tmp; + wl_list_for_each_safe(frame, frame_tmp, &manager->frames, link) { + wl_resource_destroy(frame->resource); + } + free(manager); +} From bd430b8620f5dec485e49269d6add8dc3c20a8ab Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 22 May 2018 23:07:15 +0100 Subject: [PATCH 03/40] backend/drm: support multi-planar DMA-BUFs when exporting --- backend/drm/drm.c | 3 +-- backend/drm/renderer.c | 33 ++++++++++++++++++++++++++------- include/backend/drm/renderer.h | 2 +- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 5b6054d3..d54c93f8 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -270,8 +270,7 @@ static bool drm_connector_export_dmabuf(struct wlr_output *output, struct wlr_drm_plane *plane = crtc->primary; struct wlr_drm_surface *surf = &plane->surf; - export_drm_bo(surf->back, attribs); - return true; + return export_drm_bo(surf->back, attribs); } static void drm_connector_start_renderer(struct wlr_drm_connector *conn) { diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 2cb47a12..15cf8cce 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -160,17 +160,33 @@ void post_drm_surface(struct wlr_drm_surface *surf) { } } -void export_drm_bo(struct gbm_bo *bo, +bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_buffer_attribs *attribs) { memset(attribs, 0, sizeof(struct wlr_dmabuf_buffer_attribs)); - attribs->n_planes = 1; + + attribs->n_planes = gbm_bo_get_plane_count(bo); + if (attribs->n_planes > WLR_LINUX_DMABUF_MAX_PLANES) { + return false; + } + attribs->width = gbm_bo_get_width(bo); attribs->height = gbm_bo_get_height(bo); attribs->format = gbm_bo_get_format(bo); - attribs->offset[0] = 0; - attribs->stride[0] = gbm_bo_get_stride_for_plane(bo, 0); - attribs->modifier[0] = DRM_FORMAT_MOD_LINEAR; - attribs->fd[0] = gbm_bo_get_fd(bo); + + for (int i = 0; i < attribs->n_planes; ++i) { + attribs->offset[i] = gbm_bo_get_offset(bo, i); + attribs->stride[i] = gbm_bo_get_stride_for_plane(bo, i); + attribs->modifier[i] = gbm_bo_get_modifier(bo); + attribs->fd[i] = gbm_bo_get_fd(bo); + if (attribs->fd[i] < 0) { + for (int j = 0; j < i; ++j) { + close(attribs->fd[j]); + } + return false; + } + } + + return true; } struct tex { @@ -200,7 +216,10 @@ static struct wlr_texture *get_tex_for_bo(struct wlr_drm_renderer *renderer, } struct wlr_dmabuf_buffer_attribs attribs; - export_drm_bo(bo, &attribs); + if (!export_drm_bo(bo, &attribs)) { + free(tex); + return NULL; + } tex->tex = wlr_texture_from_dmabuf(renderer->wlr_rend, &attribs); if (tex->tex == NULL) { diff --git a/include/backend/drm/renderer.h b/include/backend/drm/renderer.h index f26ca3d6..00e26976 100644 --- a/include/backend/drm/renderer.h +++ b/include/backend/drm/renderer.h @@ -52,7 +52,7 @@ struct gbm_bo *get_drm_surface_front(struct wlr_drm_surface *surf); void post_drm_surface(struct wlr_drm_surface *surf); struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest, struct gbm_bo *src); -void export_drm_bo(struct gbm_bo *bo, +bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_buffer_attribs *attribs); #endif From 5ba1a9af5652aa2407738ec6290e7a9a59a86ea6 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 23 May 2018 00:03:26 +0100 Subject: [PATCH 04/40] render: add wlr_texture_to_dmabuf --- include/wlr/render/egl.h | 5 ++++ include/wlr/render/interface.h | 2 ++ include/wlr/render/wlr_texture.h | 3 +++ render/egl.c | 46 +++++++++++++++++++++++++++++--- render/glapi.txt | 2 ++ render/gles2/texture.c | 29 ++++++++++++++++++++ render/wlr_texture.c | 8 ++++++ 7 files changed, 92 insertions(+), 3 deletions(-) diff --git a/include/wlr/render/egl.h b/include/wlr/render/egl.h index 4d837138..17fef7ed 100644 --- a/include/wlr/render/egl.h +++ b/include/wlr/render/egl.h @@ -20,6 +20,7 @@ struct wlr_egl { bool swap_buffers_with_damage; bool dmabuf_import; bool dmabuf_import_modifiers; + bool dmabuf_export; bool bind_wayland_display; } egl_exts; @@ -85,6 +86,10 @@ int wlr_egl_get_dmabuf_formats(struct wlr_egl *egl, int **formats); int wlr_egl_get_dmabuf_modifiers(struct wlr_egl *egl, int format, uint64_t **modifiers); +bool wlr_egl_export_image_to_dmabuf(struct wlr_egl *egl, EGLImageKHR image, + int32_t width, int32_t height, uint32_t flags, + struct wlr_dmabuf_buffer_attribs *attribs); + /** * Destroys an EGL image created with the given wlr_egl. */ diff --git a/include/wlr/render/interface.h b/include/wlr/render/interface.h index 2267d376..1f075e81 100644 --- a/include/wlr/render/interface.h +++ b/include/wlr/render/interface.h @@ -62,6 +62,8 @@ struct wlr_texture_impl { enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, const void *data); + bool (*to_dmabuf)(struct wlr_texture *texture, + struct wlr_dmabuf_buffer_attribs *attribs); void (*destroy)(struct wlr_texture *texture); }; diff --git a/include/wlr/render/wlr_texture.h b/include/wlr/render/wlr_texture.h index 239fc51b..481b2a37 100644 --- a/include/wlr/render/wlr_texture.h +++ b/include/wlr/render/wlr_texture.h @@ -48,6 +48,9 @@ bool wlr_texture_write_pixels(struct wlr_texture *texture, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, const void *data); +bool wlr_texture_to_dmabuf(struct wlr_texture *texture, + struct wlr_dmabuf_buffer_attribs *attribs); + /** * Destroys this wlr_texture. */ diff --git a/render/egl.c b/render/egl.c index 579bb5fe..1182a72f 100644 --- a/render/egl.c +++ b/render/egl.c @@ -178,8 +178,12 @@ bool wlr_egl_init(struct wlr_egl *egl, EGLenum platform, void *remote_display, check_egl_ext(egl->exts_str, "EGL_EXT_image_dma_buf_import_modifiers") && eglQueryDmaBufFormatsEXT && eglQueryDmaBufModifiersEXT; + egl->egl_exts.dmabuf_export = + check_egl_ext(egl->exts_str, "EGL_MESA_image_dma_buf_export"); + egl->egl_exts.bind_wayland_display = check_egl_ext(egl->exts_str, "EGL_WL_bind_wayland_display"); + print_dmabuf_formats(egl); return true; @@ -209,7 +213,7 @@ void wlr_egl_finish(struct wlr_egl *egl) { } bool wlr_egl_bind_display(struct wlr_egl *egl, struct wl_display *local_display) { - if (!egl->egl_exts.bind_wayland_display) { + if (!egl->egl_exts.bind_wayland_display || !eglBindWaylandDisplayWL) { return false; } @@ -414,7 +418,7 @@ EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, } #ifndef DRM_FORMAT_BIG_ENDIAN -# define DRM_FORMAT_BIG_ENDIAN 0x80000000 +#define DRM_FORMAT_BIG_ENDIAN 0x80000000 #endif bool wlr_egl_check_import_dmabuf(struct wlr_egl *egl, struct wlr_dmabuf_buffer *dmabuf) { @@ -446,7 +450,7 @@ bool wlr_egl_check_import_dmabuf(struct wlr_egl *egl, int wlr_egl_get_dmabuf_formats(struct wlr_egl *egl, int **formats) { if (!egl->egl_exts.dmabuf_import || - !egl->egl_exts.dmabuf_import_modifiers) { + !egl->egl_exts.dmabuf_import_modifiers) { wlr_log(L_DEBUG, "dmabuf extension not present"); return -1; } @@ -501,6 +505,42 @@ int wlr_egl_get_dmabuf_modifiers(struct wlr_egl *egl, return num; } +bool wlr_egl_export_image_to_dmabuf(struct wlr_egl *egl, EGLImageKHR image, + int32_t width, int32_t height, uint32_t flags, + struct wlr_dmabuf_buffer_attribs *attribs) { + memset(attribs, 0, sizeof(struct wlr_dmabuf_buffer_attribs)); + + if (!egl->egl_exts.dmabuf_export || !eglExportDMABUFImageQueryMESA || + !eglExportDMABUFImageMESA) { + return false; + } + + // Only one set of modifiers is returned for all planes + EGLuint64KHR modifiers; + if (!eglExportDMABUFImageQueryMESA(egl->display, image, + (int *)&attribs->format, &attribs->n_planes, &modifiers)) { + return false; + } + if (attribs->n_planes > WLR_LINUX_DMABUF_MAX_PLANES) { + wlr_log(L_ERROR, "EGL returned %d planes, but only %d are supported", + attribs->n_planes, WLR_LINUX_DMABUF_MAX_PLANES); + return false; + } + for (int i = 0; i < attribs->n_planes; ++i) { + attribs->modifier[i] = modifiers; + } + + if (!eglExportDMABUFImageMESA(egl->display, image, attribs->fd, + (EGLint *)attribs->stride, (EGLint *)attribs->offset)) { + return false; + } + + attribs->width = width; + attribs->height = height; + attribs->flags = flags; + return true; +} + bool wlr_egl_destroy_surface(struct wlr_egl *egl, EGLSurface surface) { if (!surface) { return true; diff --git a/render/glapi.txt b/render/glapi.txt index a8e4aaba..b1166f27 100644 --- a/render/glapi.txt +++ b/render/glapi.txt @@ -10,6 +10,8 @@ eglCreatePlatformWindowSurfaceEXT -eglSwapBuffersWithDamageKHR -eglQueryDmaBufFormatsEXT -eglQueryDmaBufModifiersEXT +-eglExportDMABUFImageQueryMESA +-eglExportDMABUFImageMESA -eglDebugMessageControlKHR -glDebugMessageCallbackKHR -glDebugMessageControlKHR diff --git a/render/gles2/texture.c b/render/gles2/texture.c index 37424802..742f9da0 100644 --- a/render/gles2/texture.c +++ b/render/gles2/texture.c @@ -74,6 +74,34 @@ static bool gles2_texture_write_pixels(struct wlr_texture *wlr_texture, return true; } +static bool gles2_texture_to_dmabuf(struct wlr_texture *wlr_texture, + struct wlr_dmabuf_buffer_attribs *attribs) { + struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); + + if (!texture->image) { + assert(texture->type == WLR_GLES2_TEXTURE_GLTEX); + + if (!eglCreateImageKHR) { + return false; + } + + texture->image = eglCreateImageKHR(texture->egl->display, + texture->egl->context, EGL_GL_TEXTURE_2D_KHR, + (EGLClientBuffer)(uintptr_t)texture->gl_tex, NULL); + if (texture->image == EGL_NO_IMAGE_KHR) { + return false; + } + } + + uint32_t flags = 0; + if (texture->inverted_y) { + flags |= WLR_DMABUF_BUFFER_ATTRIBS_FLAGS_Y_INVERT; + } + + return wlr_egl_export_image_to_dmabuf(texture->egl, texture->image, + texture->width, texture->height, flags, attribs); +} + static void gles2_texture_destroy(struct wlr_texture *wlr_texture) { if (wlr_texture == NULL) { return; @@ -102,6 +130,7 @@ static void gles2_texture_destroy(struct wlr_texture *wlr_texture) { static const struct wlr_texture_impl texture_impl = { .get_size = gles2_texture_get_size, .write_pixels = gles2_texture_write_pixels, + .to_dmabuf = gles2_texture_to_dmabuf, .destroy = gles2_texture_destroy, }; diff --git a/render/wlr_texture.c b/render/wlr_texture.c index 9aecfd98..f7ce0b44 100644 --- a/render/wlr_texture.c +++ b/render/wlr_texture.c @@ -54,3 +54,11 @@ bool wlr_texture_write_pixels(struct wlr_texture *texture, return texture->impl->write_pixels(texture, wl_fmt, stride, width, height, src_x, src_y, dst_x, dst_y, data); } + +bool wlr_texture_to_dmabuf(struct wlr_texture *texture, + struct wlr_dmabuf_buffer_attribs *attribs) { + if (!texture->impl->to_dmabuf) { + return false; + } + return texture->impl->to_dmabuf(texture, attribs); +} From e26f4dff9843c51762852642719aa0eee1449aca Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 23 May 2018 09:02:19 +0100 Subject: [PATCH 05/40] export-dmabuf: wait for the frame to be ready, send timestamp --- include/wlr/types/wlr_export_dmabuf_v1.h | 2 ++ include/wlr/types/wlr_output.h | 8 +++++++- types/wlr_export_dmabuf_v1.c | 23 +++++++++++++++++++---- types/wlr_output.c | 7 ++++++- 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/include/wlr/types/wlr_export_dmabuf_v1.h b/include/wlr/types/wlr_export_dmabuf_v1.h index 78db1e61..4cb3393f 100644 --- a/include/wlr/types/wlr_export_dmabuf_v1.h +++ b/include/wlr/types/wlr_export_dmabuf_v1.h @@ -11,6 +11,8 @@ struct wlr_export_dmabuf_frame_v1 { struct wl_list link; struct wlr_output *output; + + struct wl_listener output_swap_buffers; }; struct wlr_export_dmabuf_manager_v1 { diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 669b96ed..96c98dc3 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -82,7 +82,7 @@ struct wlr_output { struct { struct wl_signal frame; struct wl_signal needs_swap; - struct wl_signal swap_buffers; + struct wl_signal swap_buffers; // wlr_output_event_swap_buffers struct wl_signal enable; struct wl_signal mode; struct wl_signal scale; @@ -108,6 +108,12 @@ struct wlr_output { void *data; }; +struct wlr_output_event_swap_buffers { + struct wlr_output *output; + struct timespec *when; + pixman_region32_t *damage; +}; + struct wlr_surface; void wlr_output_enable(struct wlr_output *output, bool enable); diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index fa732397..2f1c88f2 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -30,9 +30,25 @@ static const struct zwlr_export_dmabuf_frame_v1_interface frame_impl = { static void frame_handle_resource_destroy(struct wl_resource *resource) { struct wlr_export_dmabuf_frame_v1 *frame = frame_from_resource(resource); wl_list_remove(&frame->link); + wl_list_remove(&frame->output_swap_buffers.link); free(frame); } +static void frame_output_handle_swap_buffers(struct wl_listener *listener, + void *data) { + struct wlr_export_dmabuf_frame_v1 *frame = + wl_container_of(listener, frame, output_swap_buffers); + struct wlr_output_event_swap_buffers *event = data; + + wl_list_remove(&frame->output_swap_buffers.link); + wl_list_init(&frame->output_swap_buffers.link); + + uint32_t tv_sec_hi = event->when->tv_sec << 32; + uint32_t tv_sec_lo = event->when->tv_sec & 0xFFFFFFFF; + zwlr_export_dmabuf_frame_v1_send_ready(frame->resource, + tv_sec_hi, tv_sec_lo, event->when->tv_nsec); +} + static const struct zwlr_export_dmabuf_manager_v1_interface manager_impl; @@ -87,7 +103,7 @@ static void manager_handle_capture_output(struct wl_client *client, uint32_t frame_flags = 0; uint32_t mod_high = attribs.modifier[0] >> 32; - uint32_t mod_low = attribs.modifier[0]; + uint32_t mod_low = attribs.modifier[0] & 0xFFFFFFFF; zwlr_export_dmabuf_frame_v1_send_frame(frame->resource, output->width, output->height, output->scale, output->transform, @@ -106,9 +122,8 @@ static void manager_handle_capture_output(struct wl_client *client, attribs.offset[i], attribs.stride[i]); } - // TODO: wait for the frame to be ready - // TODO: timestamps - zwlr_export_dmabuf_frame_v1_send_ready(frame->resource, 0, 0, 0); + frame->output_swap_buffers.notify = frame_output_handle_swap_buffers; + wl_signal_add(&output->events.swap_buffers, &frame->output_swap_buffers); } static const struct zwlr_export_dmabuf_manager_v1_interface manager_impl = { diff --git a/types/wlr_output.c b/types/wlr_output.c index f7001b16..b0321d7f 100644 --- a/types/wlr_output.c +++ b/types/wlr_output.c @@ -467,7 +467,12 @@ bool wlr_output_swap_buffers(struct wlr_output *output, struct timespec *when, output->idle_frame = NULL; } - wlr_signal_emit_safe(&output->events.swap_buffers, damage); + struct wlr_output_event_swap_buffers event = { + .output = output, + .when = when, + .damage = damage, + }; + wlr_signal_emit_safe(&output->events.swap_buffers, &event); int width, height; wlr_output_transformed_resolution(output, &width, &height); From 7901740f940dbaf5f674d533808898157790863a Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 23 May 2018 09:07:40 +0100 Subject: [PATCH 06/40] rootston: enable export-dmabuf --- include/rootston/desktop.h | 1 + rootston/desktop.c | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/rootston/desktop.h b/include/rootston/desktop.h index bb7d2084..dfe070ca 100644 --- a/include/rootston/desktop.h +++ b/include/rootston/desktop.h @@ -45,6 +45,7 @@ struct roots_desktop { struct wlr_xdg_shell *xdg_shell; struct wlr_gamma_control_manager *gamma_control_manager; struct wlr_screenshooter *screenshooter; + struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager_v1; struct wlr_server_decoration_manager *server_decoration_manager; struct wlr_primary_selection_device_manager *primary_selection_device_manager; struct wlr_idle *idle; diff --git a/rootston/desktop.c b/rootston/desktop.c index a6f9e9a0..2bba06e2 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -7,9 +7,10 @@ #include #include #include +#include #include -#include #include +#include #include #include #include @@ -18,9 +19,9 @@ #include #include #include +#include #include #include -#include #include #include "rootston/layers.h" #include "rootston/seat.h" @@ -845,6 +846,8 @@ struct roots_desktop *desktop_create(struct roots_server *server, desktop->gamma_control_manager = wlr_gamma_control_manager_create( server->wl_display); desktop->screenshooter = wlr_screenshooter_create(server->wl_display); + desktop->export_dmabuf_manager_v1 = + wlr_export_dmabuf_manager_v1_create(server->wl_display); desktop->server_decoration_manager = wlr_server_decoration_manager_create(server->wl_display); wlr_server_decoration_manager_set_default_mode( From 1377e551ef583976142bfb98a1ba5b61f1cc1196 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 23 May 2018 22:11:45 +0100 Subject: [PATCH 07/40] Update wlr-export-dmabuf protocol --- protocol/wlr-export-dmabuf-unstable-v1.xml | 66 ++++++++++++++-------- types/wlr_export_dmabuf_v1.c | 14 +++-- 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/protocol/wlr-export-dmabuf-unstable-v1.xml b/protocol/wlr-export-dmabuf-unstable-v1.xml index 6332b146..ab9694a6 100644 --- a/protocol/wlr-export-dmabuf-unstable-v1.xml +++ b/protocol/wlr-export-dmabuf-unstable-v1.xml @@ -24,7 +24,7 @@ DEALINGS IN THE SOFTWARE. - + An interface to capture surfaces in an efficient way. Overall usage: @@ -41,28 +41,27 @@ This object represents a frame which is ready to have its resources fetched and used. - The receive callback shall be called first, followed by either the - "dma_object" callback once per object or the "dma_layer" callback, - once per layer. The "dma_plane" callback shall only be called after - the "dma_layer" callback corresponding to the layer the plane belongs - to has been called. Finally, the "ready" event is called to indicate that - all the data has been made available for readout, as well as the time - at which presentation happened at. - The ownership of the frame is passed to the client, who's responsible for - destroying it via the "destroy" event once finished. + The receive callback shall be called first, followed by the "object" + callback once per dmabuf object or the "layer" callback, once per dmabuf + layer. The "plane" callback shall only be called after the "layer" + callback corresponding to the layer the plane belongs to has been called + Finally, the "ready" event is called to indicate that all the data has + been made available for readout, as well as the time at which presentation + happened at. The ownership of the frame is passed to the client, who's + responsible for destroying it via the "destroy" event once finished. The data the API describes has been based off of what VASurfaceAttribExternalBuffers contains. All frames are read-only and may not be written into or altered. - + Special flags that must be respected by the client. Transient frames indicate short lifetime frames (such as swapchain images from external clients). Clients are advised to copy them and do all processing outside of the "ready" event. - + @@ -85,10 +84,10 @@ - - @@ -99,7 +98,7 @@ - + Callback which serves to supply the client with the file descriptors containing the data for each object. @@ -111,7 +110,7 @@ - + Callback which serves to supply the client with information on what's contained in each file descriptor and how its laid out. @@ -124,7 +123,7 @@ - + Callback which supplies the client with plane information for each layer. @@ -158,6 +157,21 @@ + + + + Indicates reason for aborting the frame. + + + + + + If the frame is no longer valid after the "frame" event has been called, @@ -167,6 +181,8 @@ This may get called if for instance the surface is in the process of resizing. + @@ -208,23 +224,29 @@ Request to start capturing from a surface with a given id. - If an ID becomes unavailable, a NULL will be returned. + - Request to start capturing from an entire wl_output. - If an output becomes unavailable, a NULL will be returned. - + + + + + + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index 2f1c88f2..4a7382a3 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -61,13 +61,13 @@ static struct wlr_export_dmabuf_manager_v1 *manager_from_resource( static void manager_handle_capture_client(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, - uint32_t client_id, int32_t overlay_cursor) { + int32_t overlay_cursor, uint32_t client_id) { // TODO } static void manager_handle_capture_output(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, - struct wl_resource *output_resource) { + int32_t overlay_cursor, struct wl_resource *output_resource) { struct wlr_export_dmabuf_manager_v1 *manager = manager_from_resource(manager_resource); struct wlr_output *output = wlr_output_from_resource(output_resource); @@ -95,7 +95,9 @@ static void manager_handle_capture_output(struct wl_client *client, struct wlr_dmabuf_buffer_attribs attribs; if (!wlr_output_export_dmabuf(output, &attribs)) { - zwlr_export_dmabuf_frame_v1_send_abort(frame->resource); + wl_list_init(&frame->output_swap_buffers.link); + // TODO: abort reason + zwlr_export_dmabuf_frame_v1_send_abort(frame->resource, 0); return; } @@ -109,16 +111,16 @@ static void manager_handle_capture_output(struct wl_client *client, output->width, output->height, output->scale, output->transform, attribs.flags, frame_flags, mod_high, mod_low, attribs.n_planes, 1); - zwlr_export_dmabuf_frame_v1_send_dma_layer(frame->resource, 0, + zwlr_export_dmabuf_frame_v1_send_layer(frame->resource, 0, attribs.format, 1); for (int i = 0; i < attribs.n_planes; ++i) { // TODO: what to do if the kernel doesn't support seek on buffer off_t size = lseek(attribs.fd[i], 0, SEEK_END); - zwlr_export_dmabuf_frame_v1_send_dma_object(frame->resource, i, + zwlr_export_dmabuf_frame_v1_send_object(frame->resource, i, attribs.fd[i], size); - zwlr_export_dmabuf_frame_v1_send_dma_plane(frame->resource, i, 0, i, + zwlr_export_dmabuf_frame_v1_send_plane(frame->resource, i, 0, i, attribs.offset[i], attribs.stride[i]); } From 00e108f2fcd0c41d361a45149ad6a310a0a4b85d Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 23 May 2018 23:27:15 +0100 Subject: [PATCH 08/40] export-dmabuf: correctly finish wlr_linux_dmabuf_attribs --- include/wlr/types/wlr_export_dmabuf_v1.h | 2 ++ include/wlr/types/wlr_linux_dmabuf.h | 6 ++++++ types/wlr_export_dmabuf_v1.c | 27 +++++++++++++----------- types/wlr_linux_dmabuf.c | 15 ++++++++----- 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/include/wlr/types/wlr_export_dmabuf_v1.h b/include/wlr/types/wlr_export_dmabuf_v1.h index 4cb3393f..218b1635 100644 --- a/include/wlr/types/wlr_export_dmabuf_v1.h +++ b/include/wlr/types/wlr_export_dmabuf_v1.h @@ -2,6 +2,7 @@ #define WLR_TYPES_WLR_EXPORT_DMABUF_V1_H #include +#include struct wlr_export_dmabuf_manager_v1; @@ -10,6 +11,7 @@ struct wlr_export_dmabuf_frame_v1 { struct wlr_export_dmabuf_manager_v1 *manager; struct wl_list link; + struct wlr_dmabuf_buffer_attribs attribs; struct wlr_output *output; struct wl_listener output_swap_buffers; diff --git a/include/wlr/types/wlr_linux_dmabuf.h b/include/wlr/types/wlr_linux_dmabuf.h index 531e68ab..1677398b 100644 --- a/include/wlr/types/wlr_linux_dmabuf.h +++ b/include/wlr/types/wlr_linux_dmabuf.h @@ -37,6 +37,12 @@ struct wlr_dmabuf_buffer { struct wlr_dmabuf_buffer_attribs attributes; }; +/** + * Closes all file descriptors in the DMA-BUF attributes. + */ +void wlr_dmabuf_buffer_attribs_finish( + struct wlr_dmabuf_buffer_attribs *attribs); + /** * Returns true if the given resource was created via the linux-dmabuf * buffer protocol, false otherwise diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index 4a7382a3..1b284c44 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -5,6 +5,7 @@ #include #include #include "wlr-export-dmabuf-unstable-v1-protocol.h" +#include #define EXPORT_DMABUF_MANAGER_VERSION 1 @@ -31,6 +32,7 @@ static void frame_handle_resource_destroy(struct wl_resource *resource) { struct wlr_export_dmabuf_frame_v1 *frame = frame_from_resource(resource); wl_list_remove(&frame->link); wl_list_remove(&frame->output_swap_buffers.link); + wlr_dmabuf_buffer_attribs_finish(&frame->attribs); free(frame); } @@ -80,6 +82,7 @@ static void manager_handle_capture_output(struct wl_client *client, } frame->manager = manager; frame->output = output; + wl_list_init(&frame->output_swap_buffers.link); uint32_t version = wl_resource_get_version(manager_resource); frame->resource = wl_resource_create(client, @@ -93,9 +96,8 @@ static void manager_handle_capture_output(struct wl_client *client, wl_list_insert(&manager->frames, &frame->link); - struct wlr_dmabuf_buffer_attribs attribs; - if (!wlr_output_export_dmabuf(output, &attribs)) { - wl_list_init(&frame->output_swap_buffers.link); + struct wlr_dmabuf_buffer_attribs *attribs = &frame->attribs; + if (!wlr_output_export_dmabuf(output, attribs)) { // TODO: abort reason zwlr_export_dmabuf_frame_v1_send_abort(frame->resource, 0); return; @@ -104,28 +106,29 @@ static void manager_handle_capture_output(struct wl_client *client, // TODO: multiple layers support uint32_t frame_flags = 0; - uint32_t mod_high = attribs.modifier[0] >> 32; - uint32_t mod_low = attribs.modifier[0] & 0xFFFFFFFF; + uint32_t mod_high = attribs->modifier[0] >> 32; + uint32_t mod_low = attribs->modifier[0] & 0xFFFFFFFF; zwlr_export_dmabuf_frame_v1_send_frame(frame->resource, output->width, output->height, output->scale, output->transform, - attribs.flags, frame_flags, mod_high, mod_low, attribs.n_planes, 1); + attribs->flags, frame_flags, mod_high, mod_low, attribs->n_planes, 1); zwlr_export_dmabuf_frame_v1_send_layer(frame->resource, 0, - attribs.format, 1); + attribs->format, 1); - for (int i = 0; i < attribs.n_planes; ++i) { + for (int i = 0; i < attribs->n_planes; ++i) { // TODO: what to do if the kernel doesn't support seek on buffer - off_t size = lseek(attribs.fd[i], 0, SEEK_END); + off_t size = lseek(attribs->fd[i], 0, SEEK_END); zwlr_export_dmabuf_frame_v1_send_object(frame->resource, i, - attribs.fd[i], size); + attribs->fd[i], size); zwlr_export_dmabuf_frame_v1_send_plane(frame->resource, i, 0, i, - attribs.offset[i], attribs.stride[i]); + attribs->offset[i], attribs->stride[i]); } - frame->output_swap_buffers.notify = frame_output_handle_swap_buffers; + wl_list_remove(&frame->output_swap_buffers.link); wl_signal_add(&output->events.swap_buffers, &frame->output_swap_buffers); + frame->output_swap_buffers.notify = frame_output_handle_swap_buffers; } static const struct zwlr_export_dmabuf_manager_v1_interface manager_impl = { diff --git a/types/wlr_linux_dmabuf.c b/types/wlr_linux_dmabuf.c index 9bcd473f..8f46539f 100644 --- a/types/wlr_linux_dmabuf.c +++ b/types/wlr_linux_dmabuf.c @@ -51,12 +51,17 @@ struct wlr_dmabuf_buffer *wlr_dmabuf_buffer_from_buffer_resource( return buffer; } -static void linux_dmabuf_buffer_destroy(struct wlr_dmabuf_buffer *buffer) { - for (int i = 0; i < buffer->attributes.n_planes; i++) { - close(buffer->attributes.fd[i]); - buffer->attributes.fd[i] = -1; +void wlr_dmabuf_buffer_attribs_finish( + struct wlr_dmabuf_buffer_attribs *attribs) { + for (int i = 0; i < attribs->n_planes; ++i) { + close(attribs->fd[i]); + attribs->fd[i] = -1; } - buffer->attributes.n_planes = 0; + attribs->n_planes = 0; +} + +static void linux_dmabuf_buffer_destroy(struct wlr_dmabuf_buffer *buffer) { + wlr_dmabuf_buffer_attribs_finish(&buffer->attributes); free(buffer); } From 3f9796112ee2474bade668dede226b5e6f6144db Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 23 May 2018 23:56:05 +0100 Subject: [PATCH 09/40] export-dmabuf: fix tv_sec_hi --- types/wlr_export_dmabuf_v1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index 1b284c44..cbd54749 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -45,7 +45,7 @@ static void frame_output_handle_swap_buffers(struct wl_listener *listener, wl_list_remove(&frame->output_swap_buffers.link); wl_list_init(&frame->output_swap_buffers.link); - uint32_t tv_sec_hi = event->when->tv_sec << 32; + uint32_t tv_sec_hi = event->when->tv_sec >> 32; uint32_t tv_sec_lo = event->when->tv_sec & 0xFFFFFFFF; zwlr_export_dmabuf_frame_v1_send_ready(frame->resource, tv_sec_hi, tv_sec_lo, event->when->tv_nsec); From 2432c41a49277351f9159d2c7b0520b83f6dbbf0 Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 25 May 2018 11:11:29 +0100 Subject: [PATCH 10/40] export-dmabuf: correctly send the number of planes per layer --- types/wlr_export_dmabuf_v1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index cbd54749..590d5ffb 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -114,7 +114,7 @@ static void manager_handle_capture_output(struct wl_client *client, attribs->flags, frame_flags, mod_high, mod_low, attribs->n_planes, 1); zwlr_export_dmabuf_frame_v1_send_layer(frame->resource, 0, - attribs->format, 1); + attribs->format, attribs->n_planes); for (int i = 0; i < attribs->n_planes; ++i) { // TODO: what to do if the kernel doesn't support seek on buffer From 65198b0aab2d77b8ea91696d94b045b440abefea Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 25 May 2018 13:48:46 +0100 Subject: [PATCH 11/40] export-dmabuf: assert number of planes --- types/wlr_export_dmabuf_v1.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index 590d5ffb..572da262 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -102,8 +102,7 @@ static void manager_handle_capture_output(struct wl_client *client, zwlr_export_dmabuf_frame_v1_send_abort(frame->resource, 0); return; } - - // TODO: multiple layers support + assert(attribs->n_planes > 0); uint32_t frame_flags = 0; uint32_t mod_high = attribs->modifier[0] >> 32; From a16ad4327a07157ef2477e036456e8533c47a16e Mon Sep 17 00:00:00 2001 From: emersion Date: Sat, 26 May 2018 08:15:49 +0100 Subject: [PATCH 12/40] Update protocol --- protocol/wlr-export-dmabuf-unstable-v1.xml | 117 ++++++--------------- types/wlr_export_dmabuf_v1.c | 24 ++--- 2 files changed, 40 insertions(+), 101 deletions(-) diff --git a/protocol/wlr-export-dmabuf-unstable-v1.xml b/protocol/wlr-export-dmabuf-unstable-v1.xml index ab9694a6..760345a7 100644 --- a/protocol/wlr-export-dmabuf-unstable-v1.xml +++ b/protocol/wlr-export-dmabuf-unstable-v1.xml @@ -42,26 +42,21 @@ fetched and used. The receive callback shall be called first, followed by the "object" - callback once per dmabuf object or the "layer" callback, once per dmabuf - layer. The "plane" callback shall only be called after the "layer" - callback corresponding to the layer the plane belongs to has been called - Finally, the "ready" event is called to indicate that all the data has + callback once per dmabuf object or the "plane" callback, once per dmabuf + plane. The "ready" event is called last to indicate that all the data has been made available for readout, as well as the time at which presentation happened at. The ownership of the frame is passed to the client, who's - responsible for destroying it via the "destroy" event once finished. - The data the API describes has been based off of what - VASurfaceAttribExternalBuffers contains. + responsible for destroying it via the "destroy" event once finished and + by calling close() on the file descriptors received. All frames are read-only and may not be written into or altered. - Special flags that must be respected by the client. - Transient frames indicate short lifetime frames (such as swapchain - images from external clients). Clients are advised to copy them and do - all processing outside of the "ready" event. + Special flags that should be respected by the client. - + @@ -79,24 +74,25 @@ summary="frame width, scaling factor included"/> - - + + + - + @@ -110,28 +106,13 @@ - - - Callback which serves to supply the client with information on what's - contained in each file descriptor and how its laid out. - Will be called after the main receive event, once per layer. - - - - - Callback which supplies the client with plane information for each - layer. + plane. - - - - Indicates reason for aborting the frame. + + + Indicates reason for cancelling the frame. - - - + + + - + If the frame is no longer valid after the "frame" event has been called, this callback will be used to inform the client to scrap the frame. @@ -181,57 +160,27 @@ This may get called if for instance the surface is in the process of resizing. - + Unreferences the frame, allowing it to be reused. Must be called as soon as its no longer used. + Can be called at any time by the client after the "frame" event, after + which the compositor will not call any other events unless the client + resubscribes to capture more. The client will still have to close any + FDs it has been given. - This object is a manager which informs clients about capturable windows - and is able to create callbacks from which to begin to receive content - from. The "title" argument in the "surface_info" event shall be used - to provide a user-readable identifier such as a window title or - program name. + This object is a manager with which to start capturing from sources. - - - This will be called whenever a surface that's able to be captured - appears. - - - - - - - Called if a surface becomes unavailable to capture, for example if has - been closed. - - - - - - - Request to start capturing from a surface with a given id. - - - - - Request to start capturing from an entire wl_output. diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index 572da262..a2faf2ff 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -61,12 +61,6 @@ static struct wlr_export_dmabuf_manager_v1 *manager_from_resource( return wl_resource_get_user_data(resource); } -static void manager_handle_capture_client(struct wl_client *client, - struct wl_resource *manager_resource, uint32_t id, - int32_t overlay_cursor, uint32_t client_id) { - // TODO -} - static void manager_handle_capture_output(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, int32_t overlay_cursor, struct wl_resource *output_resource) { @@ -98,30 +92,27 @@ static void manager_handle_capture_output(struct wl_client *client, struct wlr_dmabuf_buffer_attribs *attribs = &frame->attribs; if (!wlr_output_export_dmabuf(output, attribs)) { - // TODO: abort reason - zwlr_export_dmabuf_frame_v1_send_abort(frame->resource, 0); + zwlr_export_dmabuf_frame_v1_send_cancel(frame->resource, + ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_TEMPORARY); return; } assert(attribs->n_planes > 0); - uint32_t frame_flags = 0; + uint32_t frame_flags = ZWLR_EXPORT_DMABUF_FRAME_V1_FLAGS_TRANSIENT; uint32_t mod_high = attribs->modifier[0] >> 32; uint32_t mod_low = attribs->modifier[0] & 0xFFFFFFFF; zwlr_export_dmabuf_frame_v1_send_frame(frame->resource, - output->width, output->height, output->scale, output->transform, - attribs->flags, frame_flags, mod_high, mod_low, attribs->n_planes, 1); - - zwlr_export_dmabuf_frame_v1_send_layer(frame->resource, 0, - attribs->format, attribs->n_planes); + output->width, output->height, 0, 0, attribs->flags, frame_flags, + attribs->format, mod_high, mod_low, attribs->n_planes, + attribs->n_planes); for (int i = 0; i < attribs->n_planes; ++i) { - // TODO: what to do if the kernel doesn't support seek on buffer off_t size = lseek(attribs->fd[i], 0, SEEK_END); zwlr_export_dmabuf_frame_v1_send_object(frame->resource, i, attribs->fd[i], size); - zwlr_export_dmabuf_frame_v1_send_plane(frame->resource, i, 0, i, + zwlr_export_dmabuf_frame_v1_send_plane(frame->resource, i, i, attribs->offset[i], attribs->stride[i]); } @@ -131,7 +122,6 @@ static void manager_handle_capture_output(struct wl_client *client, } static const struct zwlr_export_dmabuf_manager_v1_interface manager_impl = { - .capture_client = manager_handle_capture_client, .capture_output = manager_handle_capture_output, }; From b9b397ef8094b221bc1042aedf0dbbbb5d9a5f1e Mon Sep 17 00:00:00 2001 From: Rostislav Pehlivanov Date: Sun, 27 May 2018 04:03:29 +0100 Subject: [PATCH 13/40] Add a demo client for dmabuf export --- examples/dmabuf-capture.c | 767 ++++++++++++++++++++++++++++++++++++++ examples/meson.build | 10 + protocol/meson.build | 1 + 3 files changed, 778 insertions(+) create mode 100644 examples/dmabuf-capture.c diff --git a/examples/dmabuf-capture.c b/examples/dmabuf-capture.c new file mode 100644 index 00000000..f249d437 --- /dev/null +++ b/examples/dmabuf-capture.c @@ -0,0 +1,767 @@ +#define _XOPEN_SOURCE 700 +#define _POSIX_C_SOURCE 199309L +#include +#include +#include +#include +#include +#include + +#include "wlr-export-dmabuf-unstable-v1-client-protocol.h" + +#include +#include +#include +#include + +struct wayland_output { + struct wl_list link; + uint32_t id; + struct wl_output *output; + char *make; + char *model; + int width; + int height; + AVRational framerate; +}; + +struct capture_context { + AVClass *class; /* For pretty logging */ + struct wl_display *display; + struct wl_registry *registry; + struct zwlr_export_dmabuf_manager_v1 *export_manager; + + struct wl_list output_list; + + /* Target */ + struct wl_output *target_output; + uint32_t target_client; + + /* Main frame callback */ + struct zwlr_export_dmabuf_frame_v1 *frame_callback; + + /* If something happens during capture */ + int err; + int quit; + + /* FFmpeg specific parts */ + AVFrame *current_frame; + AVBufferRef *drm_device_ref; + AVBufferRef *drm_frames_ref; + + AVBufferRef *mapped_device_ref; + AVBufferRef *mapped_frames_ref; + + AVFormatContext *avf; + AVCodecContext *avctx; + + int64_t start_pts; + + /* Config */ + enum AVPixelFormat software_format; + enum AVHWDeviceType hw_device_type; + AVDictionary *encoder_opts; + int is_software_encoder; + char *hardware_device; + char *out_filename; + char *encoder_name; + float out_bitrate; +}; + +static void output_handle_geometry(void *data, struct wl_output *wl_output, + int32_t x, int32_t y, int32_t phys_width, int32_t phys_height, + int32_t subpixel, const char *make, const char *model, + int32_t transform) { + struct wayland_output *output = data; + output->make = av_strdup(make); + output->model = av_strdup(model); +} + +static void output_handle_mode(void *data, struct wl_output *wl_output, + uint32_t flags, int32_t width, int32_t height, int32_t refresh) { + if (flags & WL_OUTPUT_MODE_CURRENT) { + struct wayland_output *output = data; + output->width = width; + output->height = height; + output->framerate = (AVRational){ refresh, 1000 }; + } +} + +static void output_handle_done(void* data, struct wl_output *wl_output) { + /* Nothing to do */ +} + +static void output_handle_scale(void* data, struct wl_output *wl_output, + int32_t factor) { + /* Nothing to do */ +} + +static const struct wl_output_listener output_listener = { + output_handle_geometry, + output_handle_mode, + output_handle_done, + output_handle_scale, +}; + +static void registry_handle_add(void *data, struct wl_registry *reg, + uint32_t id, const char *interface, uint32_t ver) { + struct capture_context *ctx = data; + + if (!strcmp(interface, wl_output_interface.name)) { + struct wayland_output *output = av_mallocz(sizeof(*output)); + + output->id = id; + output->output = wl_registry_bind(reg, id, &wl_output_interface, 1); + + wl_output_add_listener(output->output, &output_listener, output); + wl_list_insert(&ctx->output_list, &output->link); + } + + if (!strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name)) { + ctx->export_manager = wl_registry_bind(reg, id, + &zwlr_export_dmabuf_manager_v1_interface, 1); + } +} + +static void remove_output(struct wayland_output *out) { + wl_list_remove(&out->link); + av_free(out->make); + av_free(out->model); + av_free(out); + return; +} + +static struct wayland_output *find_output(struct capture_context *ctx, + struct wl_output *out, uint32_t id) { + struct wayland_output *output, *tmp; + wl_list_for_each_safe(output, tmp, &ctx->output_list, link) + if ((output->output == out) || (output->id == id)) + return output; + return NULL; +} + +static void registry_handle_remove(void *data, struct wl_registry *reg, + uint32_t id) { + remove_output(find_output((struct capture_context *)data, NULL, id)); +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_add, + registry_handle_remove, +}; + +static void frame_free(void *opaque, uint8_t *data) { + AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)data; + + for (int i = 0; i < desc->nb_objects; ++i) { + close(desc->objects[i].fd); + } + + zwlr_export_dmabuf_frame_v1_destroy(opaque); + + av_free(data); +} + +static void frame_start(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, + uint32_t width, uint32_t height, uint32_t offset_x, uint32_t offset_y, + uint32_t buffer_flags, uint32_t flags, uint32_t format, + uint32_t mod_high, uint32_t mod_low, uint32_t num_objects, + uint32_t num_planes) { + struct capture_context *ctx = data; + int err = 0; + + /* Allocate DRM specific struct */ + AVDRMFrameDescriptor *desc = av_mallocz(sizeof(*desc)); + if (!desc) { + err = AVERROR(ENOMEM); + goto fail; + } + + desc->nb_objects = num_objects; + desc->objects[0].format_modifier = ((uint64_t)mod_high << 32) | mod_low; + + desc->nb_layers = 1; + desc->layers[0].format = format; + desc->layers[0].nb_planes = num_planes; + + /* Allocate a frame */ + AVFrame *f = av_frame_alloc(); + if (!f) { + err = AVERROR(ENOMEM); + goto fail; + } + + /* Set base frame properties */ + ctx->current_frame = f; + f->width = width; + f->height = height; + f->format = AV_PIX_FMT_DRM_PRIME; + + /* Set the frame data to the DRM specific struct */ + f->buf[0] = av_buffer_create((uint8_t*)desc, sizeof(*desc), + &frame_free, frame, 0); + if (!f->buf[0]) { + err = AVERROR(ENOMEM); + goto fail; + } + + f->data[0] = (uint8_t*)desc; + + return; + +fail: + ctx->err = err; + frame_free(frame, (uint8_t *)desc); +} + +static void frame_object(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, + uint32_t index, int32_t fd, uint32_t size) { + struct capture_context *ctx = data; + AVFrame *f = ctx->current_frame; + AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; + + desc->objects[index].fd = fd; + desc->objects[index].size = size; +} + +static void frame_plane(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, + uint32_t index, uint32_t object_index, + uint32_t offset, uint32_t stride) { + struct capture_context *ctx = data; + AVFrame *f = ctx->current_frame; + AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; + + desc->layers[0].planes[index].object_index = object_index; + desc->layers[0].planes[index].offset = offset; + desc->layers[0].planes[index].pitch = stride; +} + +static const uint32_t pixfmt_to_drm_map[] = { + [AV_PIX_FMT_NV12] = WL_SHM_FORMAT_NV12, + [AV_PIX_FMT_BGRA] = WL_SHM_FORMAT_ARGB8888, + [AV_PIX_FMT_BGR0] = WL_SHM_FORMAT_XRGB8888, + [AV_PIX_FMT_RGBA] = WL_SHM_FORMAT_ABGR8888, + [AV_PIX_FMT_RGB0] = WL_SHM_FORMAT_XBGR8888, + [AV_PIX_FMT_ABGR] = WL_SHM_FORMAT_RGBA8888, + [AV_PIX_FMT_0BGR] = WL_SHM_FORMAT_RGBX8888, + [AV_PIX_FMT_ARGB] = WL_SHM_FORMAT_BGRA8888, + [AV_PIX_FMT_0RGB] = WL_SHM_FORMAT_BGRX8888, +}; + +static enum AVPixelFormat drm_fmt_to_pixfmt(uint32_t fmt) { + for (enum AVPixelFormat i = 0; i < AV_PIX_FMT_NB; i++) { + if (pixfmt_to_drm_map[i] == fmt) { + return i; + } + } + return AV_PIX_FMT_NONE; +} + +static int attach_drm_frames_ref(struct capture_context *ctx, AVFrame *f, + enum AVPixelFormat sw_format) { + int err = 0; + AVHWFramesContext *hwfc; + + if (ctx->drm_frames_ref) { + hwfc = (AVHWFramesContext*)ctx->drm_frames_ref->data; + if (hwfc->width == f->width && hwfc->height == f->height && + hwfc->sw_format == sw_format) { + goto attach; + } + av_buffer_unref(&ctx->drm_frames_ref); + } + + ctx->drm_frames_ref = av_hwframe_ctx_alloc(ctx->drm_device_ref); + if (!ctx->drm_frames_ref) { + err = AVERROR(ENOMEM); + goto fail; + } + + hwfc = (AVHWFramesContext*)ctx->drm_frames_ref->data; + + hwfc->format = f->format; + hwfc->sw_format = sw_format; + hwfc->width = f->width; + hwfc->height = f->height; + + err = av_hwframe_ctx_init(ctx->drm_frames_ref); + if (err) { + av_log(ctx, AV_LOG_ERROR, "AVHWFramesContext init failed: %s!\n", + av_err2str(err)); + goto fail; + } + +attach: + /* Set frame hardware context referencce */ + f->hw_frames_ctx = av_buffer_ref(ctx->drm_frames_ref); + if (!f->hw_frames_ctx) { + err = AVERROR(ENOMEM); + goto fail; + } + + return 0; + +fail: + av_buffer_unref(&ctx->drm_frames_ref); + return err; +} + +static void register_cb(struct capture_context *ctx); + +static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, + uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { + struct capture_context *ctx = data; + AVFrame *f = ctx->current_frame; + AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; + int err = 0; + + /* Attach the hardware frame context to the frame */ + err = attach_drm_frames_ref(ctx, f, drm_fmt_to_pixfmt(desc->layers[0].format)); + if (err) { + goto end; + } + + AVFrame *mapped_frame = av_frame_alloc(); + if (!mapped_frame) { + err = AVERROR(ENOMEM); + goto end; + } + + AVHWFramesContext *mapped_hwfc; + mapped_hwfc = (AVHWFramesContext *)ctx->mapped_frames_ref->data; + mapped_frame->format = mapped_hwfc->format; + + /* Set frame hardware context referencce */ + mapped_frame->hw_frames_ctx = av_buffer_ref(ctx->mapped_frames_ref); + if (!mapped_frame->hw_frames_ctx) { + err = AVERROR(ENOMEM); + goto end; + } + + err = av_hwframe_map(mapped_frame, f, 0); + if (err) { + av_log(ctx, AV_LOG_ERROR, "Error mapping: %s!\n", av_err2str(err)); + goto end; + } + + AVFrame *enc_input = mapped_frame; + + if (ctx->is_software_encoder) { + AVFrame *soft_frame = av_frame_alloc(); + av_hwframe_transfer_data(soft_frame, mapped_frame, 0); + av_frame_free(&mapped_frame); + enc_input = soft_frame; + } + + /* Nanoseconds */ + enc_input->pts = (((uint64_t)tv_sec_hi) << 32) | tv_sec_lo; + enc_input->pts *= 1000000000; + enc_input->pts += tv_nsec; + + if (!ctx->start_pts) { + ctx->start_pts = enc_input->pts; + } + + enc_input->pts -= ctx->start_pts; + + enc_input->pts = av_rescale_q(enc_input->pts, (AVRational){ 1, 1000000000 }, + ctx->avctx->time_base); + + do { + err = avcodec_send_frame(ctx->avctx, enc_input); + + av_frame_free(&enc_input); + + if (err) { + av_log(ctx, AV_LOG_ERROR, "Error encoding: %s!\n", av_err2str(err)); + goto end; + } + + while (1) { + AVPacket pkt; + av_init_packet(&pkt); + + int ret = avcodec_receive_packet(ctx->avctx, &pkt); + if (ret == AVERROR(EAGAIN)) { + break; + } else if (ret == AVERROR_EOF) { + av_log(ctx, AV_LOG_INFO, "Encoder flushed!\n"); + ctx->quit = 2; + goto end; + } else if (ret) { + av_log(ctx, AV_LOG_ERROR, "Error encoding: %s!\n", + av_err2str(ret)); + err = ret; + goto end; + } + + pkt.stream_index = 0; + err = av_interleaved_write_frame(ctx->avf, &pkt); + + av_packet_unref(&pkt); + + if (err) { + av_log(ctx, AV_LOG_ERROR, "Writing packet fail: %s!\n", + av_err2str(err)); + goto end; + } + }; + } while (ctx->quit); + + av_log(NULL, AV_LOG_INFO, "Encoded frame %i!\n", ctx->avctx->frame_number); + + register_cb(ctx); + +end: + ctx->err = err; + av_frame_free(&ctx->current_frame); +} + +static void frame_cancel(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, + uint32_t reason) { + struct capture_context *ctx = data; + av_log(ctx, AV_LOG_WARNING, "Frame cancelled!\n"); + av_frame_free(&ctx->current_frame); + if (reason != ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERNAMENT) + register_cb(ctx); +} + +static const struct zwlr_export_dmabuf_frame_v1_listener frame_listener = { + frame_start, + frame_object, + frame_plane, + frame_ready, + frame_cancel, +}; + +static void register_cb(struct capture_context *ctx) +{ + ctx->frame_callback = + zwlr_export_dmabuf_manager_v1_capture_output(ctx->export_manager, 0, + ctx->target_output); + + zwlr_export_dmabuf_frame_v1_add_listener(ctx->frame_callback, + &frame_listener, ctx); +} + +static int init_lavu_hwcontext(struct capture_context *ctx) { + + /* DRM hwcontext */ + ctx->drm_device_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_DRM); + if (!ctx->drm_device_ref) + return AVERROR(ENOMEM); + + AVHWDeviceContext *ref_data = (AVHWDeviceContext*)ctx->drm_device_ref->data; + AVDRMDeviceContext *hwctx = ref_data->hwctx; + + /* We don't need a device (we don't even know it and can't open it) */ + hwctx->fd = -1; + + av_hwdevice_ctx_init(ctx->drm_device_ref); + + /* Mapped hwcontext */ + int err = av_hwdevice_ctx_create(&ctx->mapped_device_ref, + ctx->hw_device_type, ctx->hardware_device, NULL, 0); + if (err < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to create a hardware device: %s\n", + av_err2str(err)); + return err; + } + + return 0; +} + +static int set_hwframe_ctx(struct capture_context *ctx, + AVBufferRef *hw_device_ctx) +{ + AVHWFramesContext *frames_ctx = NULL; + int err = 0; + + if (!(ctx->mapped_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx))) { + return AVERROR(ENOMEM); + } + + AVHWFramesConstraints *cst = + av_hwdevice_get_hwframe_constraints(ctx->mapped_device_ref, NULL); + if (!cst) { + av_log(ctx, AV_LOG_ERROR, "Failed to get hw device constraints!\n"); + av_buffer_unref(&ctx->mapped_frames_ref); + return AVERROR(ENOMEM); + } + + frames_ctx = (AVHWFramesContext *)(ctx->mapped_frames_ref->data); + frames_ctx->format = cst->valid_hw_formats[0]; + frames_ctx->sw_format = ctx->avctx->pix_fmt; + frames_ctx->width = ctx->avctx->width; + frames_ctx->height = ctx->avctx->height; + frames_ctx->initial_pool_size = 16; + + av_hwframe_constraints_free(&cst); + + if ((err = av_hwframe_ctx_init(ctx->mapped_frames_ref)) < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to initialize hw frame context: %s!\n", + av_err2str(err)); + av_buffer_unref(&ctx->mapped_frames_ref); + return err; + } + + if (!ctx->is_software_encoder) { + ctx->avctx->pix_fmt = frames_ctx->format; + ctx->avctx->hw_frames_ctx = av_buffer_ref(ctx->mapped_frames_ref); + if (!ctx->avctx->hw_frames_ctx) { + av_buffer_unref(&ctx->mapped_frames_ref); + err = AVERROR(ENOMEM); + } + } + + return err; +} + +static int init_encoding(struct capture_context *ctx) { + int err; + + /* lavf init */ + err = avformat_alloc_output_context2(&ctx->avf, NULL, + NULL, ctx->out_filename); + if (err) { + av_log(ctx, AV_LOG_ERROR, "Unable to init lavf context!\n"); + return err; + } + + AVStream *st = avformat_new_stream(ctx->avf, NULL); + if (!st) { + av_log(ctx, AV_LOG_ERROR, "Unable to alloc stream!\n"); + return 1; + } + + /* Find encoder */ + AVCodec *out_codec = avcodec_find_encoder_by_name(ctx->encoder_name); + if (!out_codec) { + av_log(ctx, AV_LOG_ERROR, "Codec not found (not compiled in lavc?)!\n"); + return AVERROR(EINVAL); + } + ctx->avf->oformat->video_codec = out_codec->id; + ctx->is_software_encoder = !(out_codec->capabilities & AV_CODEC_CAP_HARDWARE); + + ctx->avctx = avcodec_alloc_context3(out_codec); + if (!ctx->avctx) + return 1; + + ctx->avctx->opaque = ctx; + ctx->avctx->bit_rate = (int)ctx->out_bitrate*1000000.0f; + ctx->avctx->pix_fmt = ctx->software_format; + ctx->avctx->time_base = (AVRational){ 1, 1000 }; + ctx->avctx->compression_level = 7; + ctx->avctx->width = find_output(ctx, ctx->target_output, 0)->width; + ctx->avctx->height = find_output(ctx, ctx->target_output, 0)->height; + + if (ctx->avf->oformat->flags & AVFMT_GLOBALHEADER) + ctx->avctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + + st->id = 0; + st->time_base = ctx->avctx->time_base; + st->avg_frame_rate = find_output(ctx, ctx->target_output, 0)->framerate; + + /* Init hw frames context */ + err = set_hwframe_ctx(ctx, ctx->mapped_device_ref); + if (err) + return err; + + err = avcodec_open2(ctx->avctx, out_codec, &ctx->encoder_opts); + if (err) { + av_log(ctx, AV_LOG_ERROR, "Cannot open encoder: %s!\n", + av_err2str(err)); + return err; + } + + if (avcodec_parameters_from_context(st->codecpar, ctx->avctx) < 0) { + av_log(ctx, AV_LOG_ERROR, "Couldn't copy codec params: %s!\n", + av_err2str(err)); + return err; + } + + /* Debug print */ + av_dump_format(ctx->avf, 0, ctx->out_filename, 1); + + /* Open for writing */ + err = avio_open(&ctx->avf->pb, ctx->out_filename, AVIO_FLAG_WRITE); + if (err) { + av_log(ctx, AV_LOG_ERROR, "Couldn't open %s: %s!\n", ctx->out_filename, + av_err2str(err)); + return err; + } + + err = avformat_write_header(ctx->avf, NULL); + if (err) { + av_log(ctx, AV_LOG_ERROR, "Couldn't write header: %s!\n", av_err2str(err)); + return err; + } + + return err; +} + +struct capture_context *q_ctx = NULL; + +void on_quit_signal(int signo) { + printf("\r"); + q_ctx->quit = 1; +} + +static int main_loop(struct capture_context *ctx) { + int err; + + q_ctx = ctx; + + if (signal(SIGINT, on_quit_signal) == SIG_ERR) { + av_log(ctx, AV_LOG_ERROR, "Unable to install signal handler!\n"); + return AVERROR(EINVAL); + } + + err = init_lavu_hwcontext(ctx); + if (err) + return err; + + err = init_encoding(ctx); + if (err) + return err; + + /* Start the frame callback */ + register_cb(ctx); + + while (!ctx->err && ctx->quit < 2) { + while (wl_display_prepare_read(ctx->display) != 0) { + wl_display_dispatch_pending(ctx->display); + } + + wl_display_flush(ctx->display); + + struct pollfd fds[1] = { + { .fd = wl_display_get_fd(ctx->display), .events = POLLIN }, + }; + + poll(fds, 1, -1); + + if (!(fds[0].revents & POLLIN)) { + wl_display_cancel_read(ctx->display); + } + + if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { + av_log(ctx, AV_LOG_ERROR, "Error occurred on the display fd!\n"); + break; + } + + if (fds[0].revents & POLLIN) { + if (wl_display_read_events(ctx->display) < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to read Wayland events!\n"); + break; + } + wl_display_dispatch_pending(ctx->display); + } + } + + err = av_write_trailer(ctx->avf); + if (err) { + av_log(ctx, AV_LOG_ERROR, "Error writing trailer: %s!\n", + av_err2str(err)); + return err; + } + + av_log(ctx, AV_LOG_INFO, "Wrote trailer!\n"); + + return ctx->err; +} + +static int init(struct capture_context *ctx) { + ctx->display = wl_display_connect(NULL); + if (!ctx->display) { + av_log(ctx, AV_LOG_ERROR, "Failed to connect to display!\n"); + return AVERROR(EINVAL); + } + + wl_list_init(&ctx->output_list); + + ctx->registry = wl_display_get_registry(ctx->display); + wl_registry_add_listener(ctx->registry, ®istry_listener, ctx); + + wl_display_roundtrip(ctx->display); + wl_display_dispatch(ctx->display); + + if (!ctx->export_manager) { + av_log(ctx, AV_LOG_ERROR, "Compositor doesn't support %s!\n", + zwlr_export_dmabuf_manager_v1_interface.name); + return -1; + } + + return 0; +} + +static void print_capturable_surfaces(struct capture_context *ctx) { + + struct wayland_output *o, *tmp_o; + wl_list_for_each_reverse_safe(o, tmp_o, &ctx->output_list, link) { + ctx->target_output = o->output; /* Default is first, whatever */ + av_log(ctx, AV_LOG_INFO, "Capturable output: %s Model: %s:\n", + o->make, o->model); + } + + av_log(ctx, AV_LOG_INFO, "Capturing from output: %s!\n", + find_output(ctx, ctx->target_output, 0)->model); +} + +static void uninit(struct capture_context *ctx); + +int main(int argc, char *argv[]) { + int err; + struct capture_context ctx = { 0 }; + ctx.class = &((AVClass) { + .class_name = "dmabuf-capture", + .item_name = av_default_item_name, + .version = LIBAVUTIL_VERSION_INT, + }); + + err = init(&ctx); + if (err) + goto end; + + print_capturable_surfaces(&ctx); + + ctx.hw_device_type = av_hwdevice_find_type_by_name("vaapi"); + ctx.hardware_device = "/dev/dri/renderD128"; + + ctx.encoder_name = "libx264"; + ctx.software_format = av_get_pix_fmt("nv12"); + av_dict_set(&ctx.encoder_opts, "preset", "veryfast", 0); + + ctx.out_filename = "dmabuf_recording_01.mkv"; + ctx.out_bitrate = 29.2f; /* Mbps */ + + err = main_loop(&ctx); + if (err) + goto end; + +end: + uninit(&ctx); + return err; +} + +static void uninit(struct capture_context *ctx) { + struct wayland_output *output, *tmp_o; + wl_list_for_each_safe(output, tmp_o, &ctx->output_list, link) + remove_output(output); + + if (ctx->export_manager) + zwlr_export_dmabuf_manager_v1_destroy(ctx->export_manager); + + av_buffer_unref(&ctx->drm_frames_ref); + av_buffer_unref(&ctx->drm_device_ref); + av_buffer_unref(&ctx->mapped_frames_ref); + av_buffer_unref(&ctx->mapped_device_ref); + + av_dict_free(&ctx->encoder_opts); + + avcodec_close(ctx->avctx); + if (ctx->avf) { + avio_closep(&ctx->avf->pb); + } + avformat_free_context(ctx->avf); +} diff --git a/examples/meson.build b/examples/meson.build index 4725b989..6a0bc46c 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -1,6 +1,10 @@ threads = dependency('threads') wayland_cursor = dependency('wayland-cursor') +libavutil = dependency('libavutil') +libavcodec = dependency('libavcodec') +libavformat = dependency('libavformat') + executable('simple', 'simple.c', dependencies: wlroots) executable('pointer', 'pointer.c', dependencies: wlroots) executable('touch', 'touch.c', 'cat.c', dependencies: wlroots) @@ -38,3 +42,9 @@ executable( 'input-inhibitor.c', dependencies: [wayland_cursor, wayland_client, wlr_protos, wlroots] ) + +executable( + 'dmabuf-capture', + 'dmabuf-capture.c', + dependencies: [wayland_client, wlr_protos, libavutil, libavcodec, libavformat] +) diff --git a/protocol/meson.build b/protocol/meson.build index a14e9723..ca0d82b5 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -50,6 +50,7 @@ client_protocols = [ [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], 'idle.xml', 'screenshooter.xml', + 'wlr-export-dmabuf-unstable-v1.xml', 'wlr-layer-shell-unstable-v1.xml', 'wlr-input-inhibitor-unstable-v1.xml', ] From f204a9127c69cd896d3667a38e3bfb34377715c9 Mon Sep 17 00:00:00 2001 From: Rostislav Pehlivanov Date: Mon, 28 May 2018 00:55:13 +0100 Subject: [PATCH 14/40] Command line parsing --- examples/dmabuf-capture.c | 62 +++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/examples/dmabuf-capture.c b/examples/dmabuf-capture.c index f249d437..a1d7d965 100644 --- a/examples/dmabuf-capture.c +++ b/examples/dmabuf-capture.c @@ -35,7 +35,6 @@ struct capture_context { /* Target */ struct wl_output *target_output; - uint32_t target_client; /* Main frame callback */ struct zwlr_export_dmabuf_frame_v1 *frame_callback; @@ -80,10 +79,10 @@ static void output_handle_geometry(void *data, struct wl_output *wl_output, static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { if (flags & WL_OUTPUT_MODE_CURRENT) { - struct wayland_output *output = data; - output->width = width; - output->height = height; - output->framerate = (AVRational){ refresh, 1000 }; + struct wayland_output *output = data; + output->width = width; + output->height = height; + output->framerate = (AVRational){ refresh, 1000 }; } } @@ -494,11 +493,10 @@ static int set_hwframe_ctx(struct capture_context *ctx, frames_ctx->sw_format = ctx->avctx->pix_fmt; frames_ctx->width = ctx->avctx->width; frames_ctx->height = ctx->avctx->height; - frames_ctx->initial_pool_size = 16; av_hwframe_constraints_free(&cst); - if ((err = av_hwframe_ctx_init(ctx->mapped_frames_ref)) < 0) { + if ((err = av_hwframe_ctx_init(ctx->mapped_frames_ref))) { av_log(ctx, AV_LOG_ERROR, "Failed to initialize hw frame context: %s!\n", av_err2str(err)); av_buffer_unref(&ctx->mapped_frames_ref); @@ -695,19 +693,6 @@ static int init(struct capture_context *ctx) { return 0; } -static void print_capturable_surfaces(struct capture_context *ctx) { - - struct wayland_output *o, *tmp_o; - wl_list_for_each_reverse_safe(o, tmp_o, &ctx->output_list, link) { - ctx->target_output = o->output; /* Default is first, whatever */ - av_log(ctx, AV_LOG_INFO, "Capturable output: %s Model: %s:\n", - o->make, o->model); - } - - av_log(ctx, AV_LOG_INFO, "Capturing from output: %s!\n", - find_output(ctx, ctx->target_output, 0)->model); -} - static void uninit(struct capture_context *ctx); int main(int argc, char *argv[]) { @@ -723,18 +708,39 @@ int main(int argc, char *argv[]) { if (err) goto end; - print_capturable_surfaces(&ctx); + struct wayland_output *o, *tmp_o; + wl_list_for_each_reverse_safe(o, tmp_o, &ctx.output_list, link) { + printf("Capturable output: %s Model: %s: ID: %i\n", + o->make, o->model, o->id); + } - ctx.hw_device_type = av_hwdevice_find_type_by_name("vaapi"); - ctx.hardware_device = "/dev/dri/renderD128"; + if (argc != 8) { + printf("Invalid number of arguments! Usage and example:\n" + "./dmabuf-capture " + " \n" + "./dmabuf-capture 0 vaapi /dev/dri/renderD129 libx264 nv12 12 " + "dmabuf_recording_01.mkv\n"); + return 1; + } + + const int o_id = strtol(argv[1], NULL, 10); + o = find_output(&ctx, NULL, o_id); + if (!o) { + printf("Unable to find output with ID %i!\n", o_id); + return 1; + } + + ctx.target_output = o->output; + ctx.hw_device_type = av_hwdevice_find_type_by_name(argv[2]); + ctx.hardware_device = argv[3]; + + ctx.encoder_name = argv[4]; + ctx.software_format = av_get_pix_fmt(argv[5]); + ctx.out_bitrate = strtof(argv[6], NULL); + ctx.out_filename = argv[7]; - ctx.encoder_name = "libx264"; - ctx.software_format = av_get_pix_fmt("nv12"); av_dict_set(&ctx.encoder_opts, "preset", "veryfast", 0); - ctx.out_filename = "dmabuf_recording_01.mkv"; - ctx.out_bitrate = 29.2f; /* Mbps */ - err = main_loop(&ctx); if (err) goto end; From 2681352e0422221c047b79dfa5c19425cf59d403 Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 28 May 2018 08:20:01 +0100 Subject: [PATCH 15/40] export-dmabuf: permanently fail if backend doesn't implement export_dmabuf --- types/wlr_export_dmabuf_v1.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index a2faf2ff..a86fe78c 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -90,6 +91,12 @@ static void manager_handle_capture_output(struct wl_client *client, wl_list_insert(&manager->frames, &frame->link); + if (!output->impl->export_dmabuf) { + zwlr_export_dmabuf_frame_v1_send_cancel(frame->resource, + ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERNAMENT); + return; + } + struct wlr_dmabuf_buffer_attribs *attribs = &frame->attribs; if (!wlr_output_export_dmabuf(output, attribs)) { zwlr_export_dmabuf_frame_v1_send_cancel(frame->resource, From 2198fd5eeda18fee7aecae5b3aaf8e6a207d372e Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 28 May 2018 08:20:22 +0100 Subject: [PATCH 16/40] examples/dmabuf-capture: fix indentation --- examples/dmabuf-capture.c | 172 +++++++++++++++++++------------------- 1 file changed, 86 insertions(+), 86 deletions(-) diff --git a/examples/dmabuf-capture.c b/examples/dmabuf-capture.c index a1d7d965..6f7f9d70 100644 --- a/examples/dmabuf-capture.c +++ b/examples/dmabuf-capture.c @@ -48,13 +48,13 @@ struct capture_context { AVBufferRef *drm_device_ref; AVBufferRef *drm_frames_ref; - AVBufferRef *mapped_device_ref; - AVBufferRef *mapped_frames_ref; + AVBufferRef *mapped_device_ref; + AVBufferRef *mapped_frames_ref; - AVFormatContext *avf; - AVCodecContext *avctx; + AVFormatContext *avf; + AVCodecContext *avctx; - int64_t start_pts; + int64_t start_pts; /* Config */ enum AVPixelFormat software_format; @@ -136,7 +136,7 @@ static struct wayland_output *find_output(struct capture_context *ctx, wl_list_for_each_safe(output, tmp, &ctx->output_list, link) if ((output->output == out) || (output->id == id)) return output; - return NULL; + return NULL; } static void registry_handle_remove(void *data, struct wl_registry *reg, @@ -328,7 +328,7 @@ static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, AVHWFramesContext *mapped_hwfc; mapped_hwfc = (AVHWFramesContext *)ctx->mapped_frames_ref->data; - mapped_frame->format = mapped_hwfc->format; + mapped_frame->format = mapped_hwfc->format; /* Set frame hardware context referencce */ mapped_frame->hw_frames_ctx = av_buffer_ref(ctx->mapped_frames_ref); @@ -339,8 +339,8 @@ static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, err = av_hwframe_map(mapped_frame, f, 0); if (err) { - av_log(ctx, AV_LOG_ERROR, "Error mapping: %s!\n", av_err2str(err)); - goto end; + av_log(ctx, AV_LOG_ERROR, "Error mapping: %s!\n", av_err2str(err)); + goto end; } AVFrame *enc_input = mapped_frame; @@ -369,7 +369,7 @@ static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, do { err = avcodec_send_frame(ctx->avctx, enc_input); - av_frame_free(&enc_input); + av_frame_free(&enc_input); if (err) { av_log(ctx, AV_LOG_ERROR, "Error encoding: %s!\n", av_err2str(err)); @@ -473,12 +473,12 @@ static int init_lavu_hwcontext(struct capture_context *ctx) { static int set_hwframe_ctx(struct capture_context *ctx, AVBufferRef *hw_device_ctx) { - AVHWFramesContext *frames_ctx = NULL; - int err = 0; + AVHWFramesContext *frames_ctx = NULL; + int err = 0; - if (!(ctx->mapped_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx))) { - return AVERROR(ENOMEM); - } + if (!(ctx->mapped_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx))) { + return AVERROR(ENOMEM); + } AVHWFramesConstraints *cst = av_hwdevice_get_hwframe_constraints(ctx->mapped_device_ref, NULL); @@ -512,7 +512,7 @@ static int set_hwframe_ctx(struct capture_context *ctx, } } - return err; + return err; } static int init_encoding(struct capture_context *ctx) { @@ -521,79 +521,79 @@ static int init_encoding(struct capture_context *ctx) { /* lavf init */ err = avformat_alloc_output_context2(&ctx->avf, NULL, NULL, ctx->out_filename); - if (err) { - av_log(ctx, AV_LOG_ERROR, "Unable to init lavf context!\n"); - return err; - } + if (err) { + av_log(ctx, AV_LOG_ERROR, "Unable to init lavf context!\n"); + return err; + } - AVStream *st = avformat_new_stream(ctx->avf, NULL); - if (!st) { - av_log(ctx, AV_LOG_ERROR, "Unable to alloc stream!\n"); - return 1; - } + AVStream *st = avformat_new_stream(ctx->avf, NULL); + if (!st) { + av_log(ctx, AV_LOG_ERROR, "Unable to alloc stream!\n"); + return 1; + } /* Find encoder */ - AVCodec *out_codec = avcodec_find_encoder_by_name(ctx->encoder_name); - if (!out_codec) { - av_log(ctx, AV_LOG_ERROR, "Codec not found (not compiled in lavc?)!\n"); - return AVERROR(EINVAL); - } - ctx->avf->oformat->video_codec = out_codec->id; - ctx->is_software_encoder = !(out_codec->capabilities & AV_CODEC_CAP_HARDWARE); + AVCodec *out_codec = avcodec_find_encoder_by_name(ctx->encoder_name); + if (!out_codec) { + av_log(ctx, AV_LOG_ERROR, "Codec not found (not compiled in lavc?)!\n"); + return AVERROR(EINVAL); + } + ctx->avf->oformat->video_codec = out_codec->id; + ctx->is_software_encoder = !(out_codec->capabilities & AV_CODEC_CAP_HARDWARE); ctx->avctx = avcodec_alloc_context3(out_codec); - if (!ctx->avctx) - return 1; + if (!ctx->avctx) + return 1; - ctx->avctx->opaque = ctx; - ctx->avctx->bit_rate = (int)ctx->out_bitrate*1000000.0f; - ctx->avctx->pix_fmt = ctx->software_format; - ctx->avctx->time_base = (AVRational){ 1, 1000 }; - ctx->avctx->compression_level = 7; - ctx->avctx->width = find_output(ctx, ctx->target_output, 0)->width; - ctx->avctx->height = find_output(ctx, ctx->target_output, 0)->height; + ctx->avctx->opaque = ctx; + ctx->avctx->bit_rate = (int)ctx->out_bitrate*1000000.0f; + ctx->avctx->pix_fmt = ctx->software_format; + ctx->avctx->time_base = (AVRational){ 1, 1000 }; + ctx->avctx->compression_level = 7; + ctx->avctx->width = find_output(ctx, ctx->target_output, 0)->width; + ctx->avctx->height = find_output(ctx, ctx->target_output, 0)->height; if (ctx->avf->oformat->flags & AVFMT_GLOBALHEADER) ctx->avctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; st->id = 0; - st->time_base = ctx->avctx->time_base; - st->avg_frame_rate = find_output(ctx, ctx->target_output, 0)->framerate; + st->time_base = ctx->avctx->time_base; + st->avg_frame_rate = find_output(ctx, ctx->target_output, 0)->framerate; - /* Init hw frames context */ - err = set_hwframe_ctx(ctx, ctx->mapped_device_ref); - if (err) - return err; + /* Init hw frames context */ + err = set_hwframe_ctx(ctx, ctx->mapped_device_ref); + if (err) + return err; err = avcodec_open2(ctx->avctx, out_codec, &ctx->encoder_opts); if (err) { - av_log(ctx, AV_LOG_ERROR, "Cannot open encoder: %s!\n", - av_err2str(err)); - return err; - } + av_log(ctx, AV_LOG_ERROR, "Cannot open encoder: %s!\n", + av_err2str(err)); + return err; + } if (avcodec_parameters_from_context(st->codecpar, ctx->avctx) < 0) { - av_log(ctx, AV_LOG_ERROR, "Couldn't copy codec params: %s!\n", - av_err2str(err)); - return err; - } + av_log(ctx, AV_LOG_ERROR, "Couldn't copy codec params: %s!\n", + av_err2str(err)); + return err; + } - /* Debug print */ - av_dump_format(ctx->avf, 0, ctx->out_filename, 1); + /* Debug print */ + av_dump_format(ctx->avf, 0, ctx->out_filename, 1); - /* Open for writing */ - err = avio_open(&ctx->avf->pb, ctx->out_filename, AVIO_FLAG_WRITE); - if (err) { - av_log(ctx, AV_LOG_ERROR, "Couldn't open %s: %s!\n", ctx->out_filename, - av_err2str(err)); - return err; - } + /* Open for writing */ + err = avio_open(&ctx->avf->pb, ctx->out_filename, AVIO_FLAG_WRITE); + if (err) { + av_log(ctx, AV_LOG_ERROR, "Couldn't open %s: %s!\n", ctx->out_filename, + av_err2str(err)); + return err; + } err = avformat_write_header(ctx->avf, NULL); - if (err) { - av_log(ctx, AV_LOG_ERROR, "Couldn't write header: %s!\n", av_err2str(err)); - return err; - } + if (err) { + av_log(ctx, AV_LOG_ERROR, "Couldn't write header: %s!\n", av_err2str(err)); + return err; + } return err; } @@ -608,12 +608,12 @@ void on_quit_signal(int signo) { static int main_loop(struct capture_context *ctx) { int err; - q_ctx = ctx; + q_ctx = ctx; - if (signal(SIGINT, on_quit_signal) == SIG_ERR) { + if (signal(SIGINT, on_quit_signal) == SIG_ERR) { av_log(ctx, AV_LOG_ERROR, "Unable to install signal handler!\n"); - return AVERROR(EINVAL); - } + return AVERROR(EINVAL); + } err = init_lavu_hwcontext(ctx); if (err) @@ -627,11 +627,11 @@ static int main_loop(struct capture_context *ctx) { register_cb(ctx); while (!ctx->err && ctx->quit < 2) { - while (wl_display_prepare_read(ctx->display) != 0) { + while (wl_display_prepare_read(ctx->display) != 0) { wl_display_dispatch_pending(ctx->display); } - wl_display_flush(ctx->display); + wl_display_flush(ctx->display); struct pollfd fds[1] = { { .fd = wl_display_get_fd(ctx->display), .events = POLLIN }, @@ -649,7 +649,7 @@ static int main_loop(struct capture_context *ctx) { } if (fds[0].revents & POLLIN) { - if (wl_display_read_events(ctx->display) < 0) { + if (wl_display_read_events(ctx->display) < 0) { av_log(ctx, AV_LOG_ERROR, "Failed to read Wayland events!\n"); break; } @@ -658,13 +658,13 @@ static int main_loop(struct capture_context *ctx) { } err = av_write_trailer(ctx->avf); - if (err) { - av_log(ctx, AV_LOG_ERROR, "Error writing trailer: %s!\n", - av_err2str(err)); - return err; - } + if (err) { + av_log(ctx, AV_LOG_ERROR, "Error writing trailer: %s!\n", + av_err2str(err)); + return err; + } - av_log(ctx, AV_LOG_INFO, "Wrote trailer!\n"); + av_log(ctx, AV_LOG_INFO, "Wrote trailer!\n"); return ctx->err; } @@ -679,7 +679,7 @@ static int init(struct capture_context *ctx) { wl_list_init(&ctx->output_list); ctx->registry = wl_display_get_registry(ctx->display); - wl_registry_add_listener(ctx->registry, ®istry_listener, ctx); + wl_registry_add_listener(ctx->registry, ®istry_listener, ctx); wl_display_roundtrip(ctx->display); wl_display_dispatch(ctx->display); @@ -721,7 +721,7 @@ int main(int argc, char *argv[]) { "./dmabuf-capture 0 vaapi /dev/dri/renderD129 libx264 nv12 12 " "dmabuf_recording_01.mkv\n"); return 1; - } + } const int o_id = strtol(argv[1], NULL, 10); o = find_output(&ctx, NULL, o_id); @@ -759,9 +759,9 @@ static void uninit(struct capture_context *ctx) { zwlr_export_dmabuf_manager_v1_destroy(ctx->export_manager); av_buffer_unref(&ctx->drm_frames_ref); - av_buffer_unref(&ctx->drm_device_ref); - av_buffer_unref(&ctx->mapped_frames_ref); - av_buffer_unref(&ctx->mapped_device_ref); + av_buffer_unref(&ctx->drm_device_ref); + av_buffer_unref(&ctx->mapped_frames_ref); + av_buffer_unref(&ctx->mapped_device_ref); av_dict_free(&ctx->encoder_opts); From 85b6b4b0c888deba26b2311a9a509d60160e6644 Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 28 May 2018 08:24:25 +0100 Subject: [PATCH 17/40] examples/dmabuf-capture: make building this example optional --- examples/meson.build | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/examples/meson.build b/examples/meson.build index 6a0bc46c..d82bd256 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -1,9 +1,9 @@ threads = dependency('threads') wayland_cursor = dependency('wayland-cursor') -libavutil = dependency('libavutil') -libavcodec = dependency('libavcodec') -libavformat = dependency('libavformat') +libavutil = dependency('libavutil', required: false) +libavcodec = dependency('libavcodec', required: false) +libavformat = dependency('libavformat', required: false) executable('simple', 'simple.c', dependencies: wlroots) executable('pointer', 'pointer.c', dependencies: wlroots) @@ -43,8 +43,10 @@ executable( dependencies: [wayland_cursor, wayland_client, wlr_protos, wlroots] ) -executable( - 'dmabuf-capture', - 'dmabuf-capture.c', - dependencies: [wayland_client, wlr_protos, libavutil, libavcodec, libavformat] -) +if libavutil.found() and libavcodec.found() and libavformat.found() + executable( + 'dmabuf-capture', + 'dmabuf-capture.c', + dependencies: [wayland_client, wlr_protos, libavutil, libavcodec, libavformat] + ) +endif From 70d324a0f9b94952b7e4fbbbcce76f8c570ced03 Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 28 May 2018 08:37:18 +0100 Subject: [PATCH 18/40] examples/dmabuf-capture: simplify event loop and fix style --- examples/dmabuf-capture.c | 163 +++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 91 deletions(-) diff --git a/examples/dmabuf-capture.c b/examples/dmabuf-capture.c index 6f7f9d70..cd8a9267 100644 --- a/examples/dmabuf-capture.c +++ b/examples/dmabuf-capture.c @@ -1,19 +1,17 @@ #define _XOPEN_SOURCE 700 #define _POSIX_C_SOURCE 199309L +#include +#include +#include +#include #include +#include #include #include #include #include -#include - #include "wlr-export-dmabuf-unstable-v1-client-protocol.h" -#include -#include -#include -#include - struct wayland_output { struct wl_list link; uint32_t id; @@ -72,7 +70,7 @@ static void output_handle_geometry(void *data, struct wl_output *wl_output, int32_t subpixel, const char *make, const char *model, int32_t transform) { struct wayland_output *output = data; - output->make = av_strdup(make); + output->make = av_strdup(make); output->model = av_strdup(model); } @@ -80,8 +78,8 @@ static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { if (flags & WL_OUTPUT_MODE_CURRENT) { struct wayland_output *output = data; - output->width = width; - output->height = height; + output->width = width; + output->height = height; output->framerate = (AVRational){ refresh, 1000 }; } } @@ -96,10 +94,10 @@ static void output_handle_scale(void* data, struct wl_output *wl_output, } static const struct wl_output_listener output_listener = { - output_handle_geometry, - output_handle_mode, - output_handle_done, - output_handle_scale, + .geometry = output_handle_geometry, + .mode = output_handle_mode, + .done = output_handle_done, + .scale = output_handle_scale, }; static void registry_handle_add(void *data, struct wl_registry *reg, @@ -109,7 +107,7 @@ static void registry_handle_add(void *data, struct wl_registry *reg, if (!strcmp(interface, wl_output_interface.name)) { struct wayland_output *output = av_mallocz(sizeof(*output)); - output->id = id; + output->id = id; output->output = wl_registry_bind(reg, id, &wl_output_interface, 1); wl_output_add_listener(output->output, &output_listener, output); @@ -117,8 +115,8 @@ static void registry_handle_add(void *data, struct wl_registry *reg, } if (!strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name)) { - ctx->export_manager = wl_registry_bind(reg, id, - &zwlr_export_dmabuf_manager_v1_interface, 1); + ctx->export_manager = wl_registry_bind(reg, id, + &zwlr_export_dmabuf_manager_v1_interface, 1); } } @@ -127,15 +125,16 @@ static void remove_output(struct wayland_output *out) { av_free(out->make); av_free(out->model); av_free(out); - return; } static struct wayland_output *find_output(struct capture_context *ctx, struct wl_output *out, uint32_t id) { struct wayland_output *output, *tmp; - wl_list_for_each_safe(output, tmp, &ctx->output_list, link) - if ((output->output == out) || (output->id == id)) + wl_list_for_each_safe(output, tmp, &ctx->output_list, link) { + if ((output->output == out) || (output->id == id)) { return output; + } + } return NULL; } @@ -145,8 +144,8 @@ static void registry_handle_remove(void *data, struct wl_registry *reg, } static const struct wl_registry_listener registry_listener = { - registry_handle_add, - registry_handle_remove, + .global = registry_handle_add, + .global_remove = registry_handle_remove, }; static void frame_free(void *opaque, uint8_t *data) { @@ -192,7 +191,7 @@ static void frame_start(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, /* Set base frame properties */ ctx->current_frame = f; - f->width = width; + f->width = width; f->height = height; f->format = AV_PIX_FMT_DRM_PRIME; @@ -219,7 +218,7 @@ static void frame_object(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, AVFrame *f = ctx->current_frame; AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; - desc->objects[index].fd = fd; + desc->objects[index].fd = fd; desc->objects[index].size = size; } @@ -231,8 +230,8 @@ static void frame_plane(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; desc->layers[0].planes[index].object_index = object_index; - desc->layers[0].planes[index].offset = offset; - desc->layers[0].planes[index].pitch = stride; + desc->layers[0].planes[index].offset = offset; + desc->layers[0].planes[index].pitch = stride; } static const uint32_t pixfmt_to_drm_map[] = { @@ -278,10 +277,10 @@ static int attach_drm_frames_ref(struct capture_context *ctx, AVFrame *f, hwfc = (AVHWFramesContext*)ctx->drm_frames_ref->data; - hwfc->format = f->format; + hwfc->format = f->format; hwfc->sw_format = sw_format; - hwfc->width = f->width; - hwfc->height = f->height; + hwfc->width = f->width; + hwfc->height = f->height; err = av_hwframe_ctx_init(ctx->drm_frames_ref); if (err) { @@ -421,30 +420,31 @@ static void frame_cancel(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, struct capture_context *ctx = data; av_log(ctx, AV_LOG_WARNING, "Frame cancelled!\n"); av_frame_free(&ctx->current_frame); - if (reason != ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERNAMENT) + if (reason == ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERNAMENT) { + av_log(ctx, AV_LOG_ERROR, "Permanent failure, exiting\n"); + ctx->err = 1; + } else { register_cb(ctx); + } } static const struct zwlr_export_dmabuf_frame_v1_listener frame_listener = { - frame_start, - frame_object, - frame_plane, - frame_ready, - frame_cancel, + .frame = frame_start, + .object = frame_object, + .plane = frame_plane, + .ready = frame_ready, + .cancel = frame_cancel, }; -static void register_cb(struct capture_context *ctx) -{ - ctx->frame_callback = - zwlr_export_dmabuf_manager_v1_capture_output(ctx->export_manager, 0, - ctx->target_output); +static void register_cb(struct capture_context *ctx) { + ctx->frame_callback = zwlr_export_dmabuf_manager_v1_capture_output( + ctx->export_manager, 0, ctx->target_output); zwlr_export_dmabuf_frame_v1_add_listener(ctx->frame_callback, &frame_listener, ctx); } static int init_lavu_hwcontext(struct capture_context *ctx) { - /* DRM hwcontext */ ctx->drm_device_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_DRM); if (!ctx->drm_device_ref) @@ -471,8 +471,7 @@ static int init_lavu_hwcontext(struct capture_context *ctx) { } static int set_hwframe_ctx(struct capture_context *ctx, - AVBufferRef *hw_device_ctx) -{ + AVBufferRef *hw_device_ctx) { AVHWFramesContext *frames_ctx = NULL; int err = 0; @@ -489,10 +488,10 @@ static int set_hwframe_ctx(struct capture_context *ctx, } frames_ctx = (AVHWFramesContext *)(ctx->mapped_frames_ref->data); - frames_ctx->format = cst->valid_hw_formats[0]; + frames_ctx->format = cst->valid_hw_formats[0]; frames_ctx->sw_format = ctx->avctx->pix_fmt; - frames_ctx->width = ctx->avctx->width; - frames_ctx->height = ctx->avctx->height; + frames_ctx->width = ctx->avctx->width; + frames_ctx->height = ctx->avctx->height; av_hwframe_constraints_free(&cst); @@ -545,25 +544,27 @@ static int init_encoding(struct capture_context *ctx) { if (!ctx->avctx) return 1; - ctx->avctx->opaque = ctx; - ctx->avctx->bit_rate = (int)ctx->out_bitrate*1000000.0f; - ctx->avctx->pix_fmt = ctx->software_format; - ctx->avctx->time_base = (AVRational){ 1, 1000 }; + ctx->avctx->opaque = ctx; + ctx->avctx->bit_rate = (int)ctx->out_bitrate*1000000.0f; + ctx->avctx->pix_fmt = ctx->software_format; + ctx->avctx->time_base = (AVRational){ 1, 1000 }; ctx->avctx->compression_level = 7; - ctx->avctx->width = find_output(ctx, ctx->target_output, 0)->width; - ctx->avctx->height = find_output(ctx, ctx->target_output, 0)->height; + ctx->avctx->width = find_output(ctx, ctx->target_output, 0)->width; + ctx->avctx->height = find_output(ctx, ctx->target_output, 0)->height; - if (ctx->avf->oformat->flags & AVFMT_GLOBALHEADER) + if (ctx->avf->oformat->flags & AVFMT_GLOBALHEADER) { ctx->avctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + } - st->id = 0; - st->time_base = ctx->avctx->time_base; + st->id = 0; + st->time_base = ctx->avctx->time_base; st->avg_frame_rate = find_output(ctx, ctx->target_output, 0)->framerate; /* Init hw frames context */ err = set_hwframe_ctx(ctx, ctx->mapped_device_ref); - if (err) + if (err) { return err; + } err = avcodec_open2(ctx->avctx, out_codec, &ctx->encoder_opts); if (err) { @@ -616,45 +617,21 @@ static int main_loop(struct capture_context *ctx) { } err = init_lavu_hwcontext(ctx); - if (err) + if (err) { return err; + } err = init_encoding(ctx); - if (err) + if (err) { return err; + } /* Start the frame callback */ register_cb(ctx); - while (!ctx->err && ctx->quit < 2) { - while (wl_display_prepare_read(ctx->display) != 0) { - wl_display_dispatch_pending(ctx->display); - } - - wl_display_flush(ctx->display); - - struct pollfd fds[1] = { - { .fd = wl_display_get_fd(ctx->display), .events = POLLIN }, - }; - - poll(fds, 1, -1); - - if (!(fds[0].revents & POLLIN)) { - wl_display_cancel_read(ctx->display); - } - - if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { - av_log(ctx, AV_LOG_ERROR, "Error occurred on the display fd!\n"); - break; - } - - if (fds[0].revents & POLLIN) { - if (wl_display_read_events(ctx->display) < 0) { - av_log(ctx, AV_LOG_ERROR, "Failed to read Wayland events!\n"); - break; - } - wl_display_dispatch_pending(ctx->display); - } + while (wl_display_dispatch(ctx->display) != -1 && !ctx->err && + ctx->quit < 2) { + // This space intentionally left blank } err = av_write_trailer(ctx->avf); @@ -705,8 +682,9 @@ int main(int argc, char *argv[]) { }); err = init(&ctx); - if (err) + if (err) { goto end; + } struct wayland_output *o, *tmp_o; wl_list_for_each_reverse_safe(o, tmp_o, &ctx.output_list, link) { @@ -742,8 +720,9 @@ int main(int argc, char *argv[]) { av_dict_set(&ctx.encoder_opts, "preset", "veryfast", 0); err = main_loop(&ctx); - if (err) + if (err) { goto end; + } end: uninit(&ctx); @@ -752,11 +731,13 @@ end: static void uninit(struct capture_context *ctx) { struct wayland_output *output, *tmp_o; - wl_list_for_each_safe(output, tmp_o, &ctx->output_list, link) + wl_list_for_each_safe(output, tmp_o, &ctx->output_list, link) { remove_output(output); + } - if (ctx->export_manager) + if (ctx->export_manager) { zwlr_export_dmabuf_manager_v1_destroy(ctx->export_manager); + } av_buffer_unref(&ctx->drm_frames_ref); av_buffer_unref(&ctx->drm_device_ref); From c844eaa1b32894417263ce2b5030033bdd7c7851 Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 28 May 2018 08:39:38 +0100 Subject: [PATCH 19/40] build: add dependencies for examples/dmabuf-capture --- .build.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.build.yml b/.build.yml index 9fd4f116..8b2f9c1f 100644 --- a/.build.yml +++ b/.build.yml @@ -1,15 +1,16 @@ image: archlinux packages: - - meson - - wayland - - wayland-protocols - - mesa + - clang + - ffmpeg + - libcap - libinput - libxkbcommon - - xcb-util-image - - libcap + - mesa + - meson - pixman - - clang + - wayland + - wayland-protocols + - xcb-util-image sources: - https://github.com/swaywm/wlroots tasks: From de91c55ea9da1b4c22bc377a7013efdcc71fff06 Mon Sep 17 00:00:00 2001 From: NeKit Date: Sun, 3 Jun 2018 01:20:09 +0300 Subject: [PATCH 20/40] Fix GLES2 renderer to use glGetUniformLocations locations This is needed as uniform locations are driver implementation-specific. --- include/render/gles2.h | 36 +++++++++++-- render/gles2/renderer.c | 109 ++++++++++++++++++++++++++++------------ 2 files changed, 108 insertions(+), 37 deletions(-) diff --git a/include/render/gles2.h b/include/render/gles2.h index 99beff29..c47751d1 100644 --- a/include/render/gles2.h +++ b/include/render/gles2.h @@ -32,11 +32,37 @@ struct wlr_gles2_renderer { const char *exts_str; struct { - GLuint quad; - GLuint ellipse; - GLuint tex_rgba; - GLuint tex_rgbx; - GLuint tex_ext; + struct { + GLuint program; + GLint proj; + GLint color; + } quad; + struct { + GLuint program; + GLint proj; + GLint color; + } ellipse; + struct { + GLuint program; + GLint proj; + GLint invert_y; + GLint tex; + GLint alpha; + } tex_rgba; + struct { + GLuint program; + GLint proj; + GLint invert_y; + GLint tex; + GLint alpha; + } tex_rgbx; + struct { + GLuint program; + GLint proj; + GLint invert_y; + GLint tex; + GLint alpha; + } tex_ext; } shaders; uint32_t viewport_width, viewport_height; diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index 1eeb915e..dce59d88 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -120,16 +120,36 @@ static bool gles2_render_texture_with_matrix(struct wlr_renderer *wlr_renderer, GLuint prog = 0; GLenum target = 0; + GLint proj_loc = 0; + GLint invert_y_loc = 0; + GLint tex_loc = 0; + GLint alpha_loc = 0; + switch (texture->type) { case WLR_GLES2_TEXTURE_GLTEX: case WLR_GLES2_TEXTURE_WL_DRM_GL: - prog = texture->has_alpha ? renderer->shaders.tex_rgba : - renderer->shaders.tex_rgbx; + if (texture->has_alpha) { + prog = renderer->shaders.tex_rgba.program; + proj_loc = renderer->shaders.tex_rgba.proj; + invert_y_loc = renderer->shaders.tex_rgba.invert_y; + tex_loc = renderer->shaders.tex_rgba.tex; + alpha_loc = renderer->shaders.tex_rgba.alpha; + } else { + prog = renderer->shaders.tex_rgbx.program; + proj_loc = renderer->shaders.tex_rgbx.proj; + invert_y_loc = renderer->shaders.tex_rgbx.invert_y; + tex_loc = renderer->shaders.tex_rgbx.tex; + alpha_loc = renderer->shaders.tex_rgbx.alpha; + } target = GL_TEXTURE_2D; break; case WLR_GLES2_TEXTURE_WL_DRM_EXT: case WLR_GLES2_TEXTURE_DMABUF: - prog = renderer->shaders.tex_ext; + prog = renderer->shaders.tex_ext.program; + proj_loc = renderer->shaders.tex_ext.proj; + invert_y_loc = renderer->shaders.tex_ext.invert_y; + tex_loc = renderer->shaders.tex_ext.tex; + alpha_loc = renderer->shaders.tex_ext.alpha; target = GL_TEXTURE_EXTERNAL_OES; break; } @@ -151,9 +171,10 @@ static bool gles2_render_texture_with_matrix(struct wlr_renderer *wlr_renderer, glUseProgram(prog); - glUniformMatrix3fv(0, 1, GL_FALSE, transposition); - glUniform1i(1, texture->inverted_y); - glUniform1f(3, alpha); + glUniformMatrix3fv(proj_loc, 1, GL_FALSE, transposition); + glUniform1i(invert_y_loc, texture->inverted_y); + glUniform1f(alpha_loc, alpha); + glUniform1i(tex_loc, 0); draw_quad(); @@ -173,9 +194,10 @@ static void gles2_render_quad_with_matrix(struct wlr_renderer *wlr_renderer, wlr_matrix_transpose(transposition, matrix); PUSH_GLES2_DEBUG; - glUseProgram(renderer->shaders.quad); - glUniformMatrix3fv(0, 1, GL_FALSE, transposition); - glUniform4f(1, color[0], color[1], color[2], color[3]); + glUseProgram(renderer->shaders.quad.program); + + glUniformMatrix3fv(renderer->shaders.quad.proj, 1, GL_FALSE, transposition); + glUniform4f(renderer->shaders.quad.color, color[0], color[1], color[2], color[3]); draw_quad(); POP_GLES2_DEBUG; } @@ -191,9 +213,10 @@ static void gles2_render_ellipse_with_matrix(struct wlr_renderer *wlr_renderer, wlr_matrix_transpose(transposition, matrix); PUSH_GLES2_DEBUG; - glUseProgram(renderer->shaders.ellipse); - glUniformMatrix3fv(0, 1, GL_FALSE, transposition); - glUniform4f(1, color[0], color[1], color[2], color[3]); + glUseProgram(renderer->shaders.ellipse.program); + + glUniformMatrix3fv(renderer->shaders.ellipse.proj, 1, GL_FALSE, transposition); + glUniform4f(renderer->shaders.ellipse.color, color[0], color[1], color[2], color[3]); draw_quad(); POP_GLES2_DEBUG; } @@ -313,11 +336,11 @@ static void gles2_destroy(struct wlr_renderer *wlr_renderer) { wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL); PUSH_GLES2_DEBUG; - glDeleteProgram(renderer->shaders.quad); - glDeleteProgram(renderer->shaders.ellipse); - glDeleteProgram(renderer->shaders.tex_rgba); - glDeleteProgram(renderer->shaders.tex_rgbx); - glDeleteProgram(renderer->shaders.tex_ext); + glDeleteProgram(renderer->shaders.quad.program); + glDeleteProgram(renderer->shaders.ellipse.program); + glDeleteProgram(renderer->shaders.tex_rgba.program); + glDeleteProgram(renderer->shaders.tex_rgbx.program); + glDeleteProgram(renderer->shaders.tex_ext.program); POP_GLES2_DEBUG; if (glDebugMessageCallbackKHR) { @@ -486,31 +509,53 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { PUSH_GLES2_DEBUG; - renderer->shaders.quad = link_program(quad_vertex_src, quad_fragment_src); - if (!renderer->shaders.quad) { + GLuint prog; + renderer->shaders.quad.program = prog = + link_program(quad_vertex_src, quad_fragment_src); + if (!renderer->shaders.quad.program) { goto error; } - renderer->shaders.ellipse = + renderer->shaders.quad.proj = glGetUniformLocation(prog, "proj"); + renderer->shaders.quad.color = glGetUniformLocation(prog, "color"); + + renderer->shaders.ellipse.program = prog = link_program(quad_vertex_src, ellipse_fragment_src); - if (!renderer->shaders.ellipse) { + if (!renderer->shaders.ellipse.program) { goto error; } - renderer->shaders.tex_rgba = + renderer->shaders.ellipse.proj = glGetUniformLocation(prog, "proj"); + renderer->shaders.ellipse.color = glGetUniformLocation(prog, "color"); + + renderer->shaders.tex_rgba.program = prog = link_program(tex_vertex_src, tex_fragment_src_rgba); - if (!renderer->shaders.tex_rgba) { + if (!renderer->shaders.tex_rgba.program) { goto error; } - renderer->shaders.tex_rgbx = + renderer->shaders.tex_rgba.proj = glGetUniformLocation(prog, "proj"); + renderer->shaders.tex_rgba.invert_y = glGetUniformLocation(prog, "invert_y"); + renderer->shaders.tex_rgba.tex = glGetUniformLocation(prog, "tex"); + renderer->shaders.tex_rgba.alpha = glGetUniformLocation(prog, "alpha"); + + renderer->shaders.tex_rgbx.program = prog = link_program(tex_vertex_src, tex_fragment_src_rgbx); - if (!renderer->shaders.tex_rgbx) { + if (!renderer->shaders.tex_rgbx.program) { goto error; } + renderer->shaders.tex_rgbx.proj = glGetUniformLocation(prog, "proj"); + renderer->shaders.tex_rgbx.invert_y = glGetUniformLocation(prog, "invert_y"); + renderer->shaders.tex_rgbx.tex = glGetUniformLocation(prog, "tex"); + renderer->shaders.tex_rgbx.alpha = glGetUniformLocation(prog, "alpha"); + if (glEGLImageTargetTexture2DOES) { - renderer->shaders.tex_ext = + renderer->shaders.tex_ext.program = prog = link_program(tex_vertex_src, tex_fragment_src_external); - if (!renderer->shaders.tex_ext) { + if (!renderer->shaders.tex_ext.program) { goto error; } + renderer->shaders.tex_ext.proj = glGetUniformLocation(prog, "proj"); + renderer->shaders.tex_ext.invert_y = glGetUniformLocation(prog, "invert_y"); + renderer->shaders.tex_ext.tex = glGetUniformLocation(prog, "tex"); + renderer->shaders.tex_ext.alpha = glGetUniformLocation(prog, "alpha"); } POP_GLES2_DEBUG; @@ -518,11 +563,11 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { return &renderer->wlr_renderer; error: - glDeleteProgram(renderer->shaders.quad); - glDeleteProgram(renderer->shaders.ellipse); - glDeleteProgram(renderer->shaders.tex_rgba); - glDeleteProgram(renderer->shaders.tex_rgbx); - glDeleteProgram(renderer->shaders.tex_ext); + glDeleteProgram(renderer->shaders.quad.program); + glDeleteProgram(renderer->shaders.ellipse.program); + glDeleteProgram(renderer->shaders.tex_rgba.program); + glDeleteProgram(renderer->shaders.tex_rgbx.program); + glDeleteProgram(renderer->shaders.tex_ext.program); POP_GLES2_DEBUG; From e4933ab445c70b9ac3c0026718b29a18be171ed5 Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 8 Jun 2018 20:06:13 +0100 Subject: [PATCH 21/40] Introduce wlr_buffer --- include/wlr/types/wlr_buffer.h | 36 +++++++ include/wlr/types/wlr_surface.h | 1 + types/meson.build | 1 + types/wlr_buffer.c | 186 ++++++++++++++++++++++++++++++++ types/wlr_surface.c | 130 +++++++--------------- 5 files changed, 263 insertions(+), 91 deletions(-) create mode 100644 include/wlr/types/wlr_buffer.h create mode 100644 types/wlr_buffer.c diff --git a/include/wlr/types/wlr_buffer.h b/include/wlr/types/wlr_buffer.h new file mode 100644 index 00000000..28567f26 --- /dev/null +++ b/include/wlr/types/wlr_buffer.h @@ -0,0 +1,36 @@ +#ifndef WLR_TYPES_WLR_BUFFER_H +#define WLR_TYPES_WLR_BUFFER_H + +#include +#include + +struct wlr_buffer { + struct wl_resource *resource; + struct wlr_texture *texture; + bool released; + size_t n_refs; + + struct wl_listener resource_destroy; +}; + +struct wlr_renderer; + +// Checks if a resource is a wl_buffer. +bool wlr_resource_is_buffer(struct wl_resource *resource); +// Returns the buffer size. +bool wlr_buffer_get_resource_size(struct wl_resource *resource, + struct wlr_renderer *renderer, int *width, int *height); + +// Uploads the texture to the GPU and references it. +struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, + struct wl_resource *resource); +// References and unreferences the buffer. +void wlr_buffer_ref(struct wlr_buffer *buffer); +void wlr_buffer_unref(struct wlr_buffer *buffer); +// Tries to update the texture in the provided buffer. This destroys `buffer` +// and returns a new buffer. +// Fails if `buffer->n_refs` > 1 or if the texture isn't mutable. +struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer, + struct wl_resource *resource, pixman_region32_t *damage); + +#endif diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index 526e4e2c..64503e78 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -69,6 +69,7 @@ struct wlr_subsurface { struct wlr_surface { struct wl_resource *resource; struct wlr_renderer *renderer; + struct wlr_buffer *buffer; struct wlr_texture *texture; struct wlr_surface_state *current, *pending; const char *role; // the lifetime-bound role or null diff --git a/types/meson.build b/types/meson.build index f9f5b469..87f21c55 100644 --- a/types/meson.build +++ b/types/meson.build @@ -20,6 +20,7 @@ lib_wlr_types = static_library( 'xdg_shell_v6/wlr_xdg_surface_v6.c', 'xdg_shell_v6/wlr_xdg_toplevel_v6.c', 'wlr_box.c', + 'wlr_buffer.c', 'wlr_compositor.c', 'wlr_cursor.c', 'wlr_gamma_control.c', diff --git a/types/wlr_buffer.c b/types/wlr_buffer.c new file mode 100644 index 00000000..e062df4c --- /dev/null +++ b/types/wlr_buffer.c @@ -0,0 +1,186 @@ +#include +#include +#include +#include +#include +#include + +bool wlr_resource_is_buffer(struct wl_resource *resource) { + return strcmp(wl_resource_get_class(resource), wl_buffer_interface.name) == 0; +} + +bool wlr_buffer_get_resource_size(struct wl_resource *resource, + struct wlr_renderer *renderer, int *width, int *height) { + assert(wlr_resource_is_buffer(resource)); + + struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource); + if (shm_buf != NULL) { + *width = wl_shm_buffer_get_width(shm_buf); + *height = wl_shm_buffer_get_height(shm_buf); + } else if (wlr_renderer_resource_is_wl_drm_buffer(renderer, + resource)) { + wlr_renderer_wl_drm_buffer_get_size(renderer, resource, + width, height); + } else if (wlr_dmabuf_resource_is_buffer(resource)) { + struct wlr_dmabuf_buffer *dmabuf = + wlr_dmabuf_buffer_from_buffer_resource(resource); + *width = dmabuf->attributes.width; + *height = dmabuf->attributes.height; + } else { + *width = *height = 0; + return false; + } + + return true; +} + + +static void buffer_resource_handle_destroy(struct wl_listener *listener, + void *data) { + struct wlr_buffer *buffer = + wl_container_of(listener, buffer, resource_destroy); + wl_list_remove(&buffer->resource_destroy.link); + wl_list_init(&buffer->resource_destroy.link); + buffer->resource = NULL; + + if (!buffer->released) { + // The texture becomes invalid + wlr_texture_destroy(buffer->texture); + buffer->texture = NULL; + } +} + +struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, + struct wl_resource *resource) { + assert(wlr_resource_is_buffer(resource)); + + struct wlr_texture *texture = NULL; + bool released = false; + + struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource); + if (shm_buf != NULL) { + enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buf); + int32_t stride = wl_shm_buffer_get_stride(shm_buf); + int32_t width = wl_shm_buffer_get_width(shm_buf); + int32_t height = wl_shm_buffer_get_height(shm_buf); + + wl_shm_buffer_begin_access(shm_buf); + void *data = wl_shm_buffer_get_data(shm_buf); + texture = wlr_texture_from_pixels(renderer, fmt, stride, + width, height, data); + wl_shm_buffer_end_access(shm_buf); + + // We have uploaded the data, we don't need to access the wl_buffer + // anymore + wl_buffer_send_release(resource); + released = true; + } else if (wlr_renderer_resource_is_wl_drm_buffer(renderer, resource)) { + texture = wlr_texture_from_wl_drm(renderer, resource); + } else if (wlr_dmabuf_resource_is_buffer(resource)) { + struct wlr_dmabuf_buffer *dmabuf = + wlr_dmabuf_buffer_from_buffer_resource(resource); + texture = wlr_texture_from_dmabuf(renderer, &dmabuf->attributes); + } else { + wlr_log(L_ERROR, "Cannot upload texture: unknown buffer type"); + return NULL; + } + + if (texture == NULL) { + wlr_log(L_ERROR, "Failed to upload texture"); + return NULL; + } + + struct wlr_buffer *buffer = calloc(1, sizeof(struct wlr_buffer)); + if (buffer == NULL) { + return NULL; + } + buffer->resource = resource; + buffer->texture = texture; + buffer->released = released; + buffer->n_refs = 1; + + wl_resource_add_destroy_listener(resource, &buffer->resource_destroy); + buffer->resource_destroy.notify = buffer_resource_handle_destroy; + + return buffer; +} + +void wlr_buffer_ref(struct wlr_buffer *buffer) { + buffer->n_refs++; +} + +void wlr_buffer_unref(struct wlr_buffer *buffer) { + if (buffer == NULL) { + return; + } + + assert(buffer->n_refs > 0); + buffer->n_refs--; + if (buffer->n_refs > 0) { + return; + } + + if (!buffer->released && buffer->resource != NULL) { + wl_buffer_send_release(buffer->resource); + } + + wl_list_remove(&buffer->resource_destroy.link); + wlr_texture_destroy(buffer->texture); + free(buffer); +} + +struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer, + struct wl_resource *resource, pixman_region32_t *damage) { + assert(wlr_resource_is_buffer(resource)); + + if (buffer->n_refs > 1) { + // Someone else still has a reference to the buffer + return NULL; + } + + struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource); + if (shm_buf == NULL) { + // Uploading only damaged regions only works for wl_shm buffers + return NULL; + } + + enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buf); + int32_t stride = wl_shm_buffer_get_stride(shm_buf); + int32_t width = wl_shm_buffer_get_width(shm_buf); + int32_t height = wl_shm_buffer_get_height(shm_buf); + + int32_t texture_width, texture_height; + wlr_texture_get_size(buffer->texture, &texture_width, &texture_height); + if (width != texture_width || height != texture_height) { + return NULL; + } + + wl_shm_buffer_begin_access(shm_buf); + void *data = wl_shm_buffer_get_data(shm_buf); + + int n; + pixman_box32_t *rects = pixman_region32_rectangles(damage, &n); + for (int i = 0; i < n; ++i) { + pixman_box32_t *r = &rects[i]; + if (!wlr_texture_write_pixels(buffer->texture, fmt, stride, + r->x2 - r->x1, r->y2 - r->y1, r->x1, r->y1, + r->x1, r->y1, data)) { + wl_shm_buffer_end_access(shm_buf); + return NULL; + } + } + + wl_shm_buffer_end_access(shm_buf); + + // We have uploaded the data, we don't need to access the wl_buffer + // anymore + wl_buffer_send_release(resource); + + wl_list_remove(&buffer->resource_destroy.link); + wl_resource_add_destroy_listener(resource, &buffer->resource_destroy); + buffer->resource_destroy.notify = buffer_resource_handle_destroy; + + buffer->resource = resource; + buffer->released = true; + return buffer; +} diff --git a/types/wlr_surface.c b/types/wlr_surface.c index 9c3eb86d..7d8da02f 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -1,10 +1,9 @@ #include #include #include -#include #include +#include #include -#include #include #include #include @@ -46,13 +45,6 @@ static void surface_handle_buffer_destroy(struct wl_listener *listener, surface_state_reset_buffer(state); } -static void surface_state_release_buffer(struct wlr_surface_state *state) { - if (state->buffer) { - wl_buffer_send_release(state->buffer); - surface_state_reset_buffer(state); - } -} - static void surface_state_set_buffer(struct wlr_surface_state *state, struct wl_resource *buffer) { surface_state_reset_buffer(state); @@ -175,24 +167,8 @@ static bool surface_update_size(struct wlr_surface *surface, int scale = state->scale; enum wl_output_transform transform = state->transform; - struct wl_shm_buffer *buf = wl_shm_buffer_get(state->buffer); - if (buf != NULL) { - state->buffer_width = wl_shm_buffer_get_width(buf); - state->buffer_height = wl_shm_buffer_get_height(buf); - } else if (wlr_renderer_resource_is_wl_drm_buffer(surface->renderer, - state->buffer)) { - wlr_renderer_wl_drm_buffer_get_size(surface->renderer, state->buffer, - &state->buffer_width, &state->buffer_height); - } else if (wlr_dmabuf_resource_is_buffer(state->buffer)) { - struct wlr_dmabuf_buffer *dmabuf = - wlr_dmabuf_buffer_from_buffer_resource(state->buffer); - state->buffer_width = dmabuf->attributes.width; - state->buffer_height = dmabuf->attributes.height; - } else { - wlr_log(L_ERROR, "Unknown buffer handle attached"); - state->buffer_width = 0; - state->buffer_height = 0; - } + wlr_buffer_get_resource_size(state->buffer, surface->renderer, + &state->buffer_width, &state->buffer_height); int width = state->buffer_width / scale; int height = state->buffer_height / scale; @@ -243,7 +219,6 @@ static void surface_move_state(struct wlr_surface *surface, update_size = true; } if ((next->invalid & WLR_SURFACE_INVALID_BUFFER)) { - surface_state_release_buffer(state); surface_state_set_buffer(state, next->buffer); surface_state_reset_buffer(next); state->sx = next->sx; @@ -351,86 +326,60 @@ static void surface_damage_subsurfaces(struct wlr_subsurface *subsurface) { } static void surface_apply_damage(struct wlr_surface *surface, - bool invalid_buffer, bool reupload_buffer) { + bool invalid_buffer) { struct wl_resource *resource = surface->current->buffer; if (resource == NULL) { + // NULL commit + wlr_buffer_unref(surface->buffer); + surface->buffer = NULL; + surface->texture = NULL; return; } - struct wl_shm_buffer *buf = wl_shm_buffer_get(resource); - if (buf != NULL) { - wl_shm_buffer_begin_access(buf); + if (surface->buffer != NULL && !surface->buffer->released && + !invalid_buffer) { + // The buffer is still the same, no need to re-upload + return; + } - enum wl_shm_format fmt = wl_shm_buffer_get_format(buf); - int32_t stride = wl_shm_buffer_get_stride(buf); - int32_t width = wl_shm_buffer_get_width(buf); - int32_t height = wl_shm_buffer_get_height(buf); - void *data = wl_shm_buffer_get_data(buf); + if (surface->buffer != NULL && surface->buffer->released) { + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_copy(&damage, &surface->current->buffer_damage); + pixman_region32_intersect_rect(&damage, &damage, 0, 0, + surface->current->buffer_width, surface->current->buffer_height); - if (surface->texture == NULL || reupload_buffer) { - wlr_texture_destroy(surface->texture); - surface->texture = wlr_texture_from_pixels(surface->renderer, fmt, - stride, width, height, data); - } else { - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_copy(&damage, &surface->current->buffer_damage); - pixman_region32_intersect_rect(&damage, &damage, 0, 0, - surface->current->buffer_width, - surface->current->buffer_height); + struct wlr_buffer *updated_buffer = + wlr_buffer_apply_damage(surface->buffer, resource, &damage); - int n; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &n); - for (int i = 0; i < n; ++i) { - pixman_box32_t *r = &rects[i]; - if (!wlr_texture_write_pixels(surface->texture, fmt, stride, - r->x2 - r->x1, r->y2 - r->y1, r->x1, r->y1, - r->x1, r->y1, data)) { - break; - } - } + pixman_region32_fini(&damage); - pixman_region32_fini(&damage); - } - - wl_shm_buffer_end_access(buf); - } else if (invalid_buffer || reupload_buffer) { - wlr_texture_destroy(surface->texture); - - if (wlr_renderer_resource_is_wl_drm_buffer(surface->renderer, resource)) { - surface->texture = - wlr_texture_from_wl_drm(surface->renderer, resource); - } else if (wlr_dmabuf_resource_is_buffer(resource)) { - struct wlr_dmabuf_buffer *dmabuf = - wlr_dmabuf_buffer_from_buffer_resource(resource); - surface->texture = - wlr_texture_from_dmabuf(surface->renderer, &dmabuf->attributes); - } else { - surface->texture = NULL; - wlr_log(L_ERROR, "Unknown buffer handle attached"); + if (updated_buffer != NULL) { + surface->buffer = updated_buffer; + return; } } - surface_state_release_buffer(surface->current); + wlr_buffer_unref(surface->buffer); + surface->buffer = NULL; + surface->texture = NULL; + + struct wlr_buffer *buffer = wlr_buffer_create(surface->renderer, resource); + if (buffer == NULL) { + wlr_log(L_ERROR, "Failed to upload buffer"); + return; + } + + surface->buffer = buffer; + surface->texture = buffer->texture; } static void surface_commit_pending(struct wlr_surface *surface) { - int32_t oldw = surface->current->buffer_width; - int32_t oldh = surface->current->buffer_height; - bool invalid_buffer = surface->pending->invalid & WLR_SURFACE_INVALID_BUFFER; - bool null_buffer_commit = invalid_buffer && surface->pending->buffer == NULL; surface_move_state(surface, surface->pending, surface->current); - if (null_buffer_commit) { - wlr_texture_destroy(surface->texture); - surface->texture = NULL; - } - - bool reupload_buffer = oldw != surface->current->buffer_width || - oldh != surface->current->buffer_height; - surface_apply_damage(surface, invalid_buffer, reupload_buffer); + surface_apply_damage(surface, invalid_buffer); // commit subsurface order struct wlr_subsurface *subsurface; @@ -652,10 +601,9 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(surface->resource)); wl_list_remove(&surface->renderer_destroy.link); - wlr_texture_destroy(surface->texture); surface_state_destroy(surface->pending); surface_state_destroy(surface->current); - + wlr_buffer_unref(surface->buffer); free(surface); } From 7d24af43e5f2fc04b41c5697101a44d67a384a12 Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 8 Jun 2018 20:28:57 +0100 Subject: [PATCH 22/40] buffer: improve docs --- include/wlr/types/wlr_buffer.h | 38 ++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/include/wlr/types/wlr_buffer.h b/include/wlr/types/wlr_buffer.h index 28567f26..c57d79ed 100644 --- a/include/wlr/types/wlr_buffer.h +++ b/include/wlr/types/wlr_buffer.h @@ -4,9 +4,12 @@ #include #include +/** + * A client buffer. + */ struct wlr_buffer { - struct wl_resource *resource; - struct wlr_texture *texture; + struct wl_resource *resource; // can be NULL + struct wlr_texture *texture; // can be NULL bool released; size_t n_refs; @@ -15,21 +18,38 @@ struct wlr_buffer { struct wlr_renderer; -// Checks if a resource is a wl_buffer. +/** + * Check if a resource is a wl_buffer resource. + */ bool wlr_resource_is_buffer(struct wl_resource *resource); -// Returns the buffer size. +/** + * Get the size of a wl_buffer resource. + */ bool wlr_buffer_get_resource_size(struct wl_resource *resource, struct wlr_renderer *renderer, int *width, int *height); -// Uploads the texture to the GPU and references it. +/** + * Upload a buffer to the GPU and reference it. + */ struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, struct wl_resource *resource); -// References and unreferences the buffer. +/** + * Reference the buffer. + */ void wlr_buffer_ref(struct wlr_buffer *buffer); +/** + * Unreference the buffer. After this call, `buffer` may not be accessed + * anymore. + */ void wlr_buffer_unref(struct wlr_buffer *buffer); -// Tries to update the texture in the provided buffer. This destroys `buffer` -// and returns a new buffer. -// Fails if `buffer->n_refs` > 1 or if the texture isn't mutable. +/** + * Try to update the buffer's content. On success, returns the updated buffer + * and destroys the provided `buffer`. On error, `buffer` is intact and NULL is + * returned. + * + * Fails if there's more than one reference to the buffer or if the texture + * isn't mutable. + */ struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer, struct wl_resource *resource, pixman_region32_t *damage); From 38d415dd2047114badd6a9f65dab6fe0b5b8e5d0 Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 11 Jun 2018 08:13:35 +0100 Subject: [PATCH 23/40] buffer: make wlr_buffer_ref return the buffer --- include/wlr/types/wlr_buffer.h | 2 +- types/wlr_buffer.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/wlr/types/wlr_buffer.h b/include/wlr/types/wlr_buffer.h index c57d79ed..fc348a1c 100644 --- a/include/wlr/types/wlr_buffer.h +++ b/include/wlr/types/wlr_buffer.h @@ -36,7 +36,7 @@ struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, /** * Reference the buffer. */ -void wlr_buffer_ref(struct wlr_buffer *buffer); +struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer); /** * Unreference the buffer. After this call, `buffer` may not be accessed * anymore. diff --git a/types/wlr_buffer.c b/types/wlr_buffer.c index e062df4c..82a359f0 100644 --- a/types/wlr_buffer.c +++ b/types/wlr_buffer.c @@ -105,8 +105,9 @@ struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, return buffer; } -void wlr_buffer_ref(struct wlr_buffer *buffer) { +struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer) { buffer->n_refs++; + return buffer; } void wlr_buffer_unref(struct wlr_buffer *buffer) { From 6f29db10446662ac6741e4a63bac74380f01199e Mon Sep 17 00:00:00 2001 From: NeKit Date: Wed, 13 Jun 2018 13:43:01 +0300 Subject: [PATCH 24/40] gles2 renderer: introduce struct wlr_gles2_tex_shader --- include/render/gles2.h | 32 +++++++++++--------------------- render/gles2/renderer.c | 34 +++++++++------------------------- 2 files changed, 20 insertions(+), 46 deletions(-) diff --git a/include/render/gles2.h b/include/render/gles2.h index c47751d1..67d4e9f5 100644 --- a/include/render/gles2.h +++ b/include/render/gles2.h @@ -25,6 +25,14 @@ struct wlr_gles2_pixel_format { bool has_alpha; }; +struct wlr_gles2_tex_shader { + GLuint program; + GLint proj; + GLint invert_y; + GLint tex; + GLint alpha; +}; + struct wlr_gles2_renderer { struct wlr_renderer wlr_renderer; @@ -42,27 +50,9 @@ struct wlr_gles2_renderer { GLint proj; GLint color; } ellipse; - struct { - GLuint program; - GLint proj; - GLint invert_y; - GLint tex; - GLint alpha; - } tex_rgba; - struct { - GLuint program; - GLint proj; - GLint invert_y; - GLint tex; - GLint alpha; - } tex_rgbx; - struct { - GLuint program; - GLint proj; - GLint invert_y; - GLint tex; - GLint alpha; - } tex_ext; + struct wlr_gles2_tex_shader tex_rgba; + struct wlr_gles2_tex_shader tex_rgbx; + struct wlr_gles2_tex_shader tex_ext; } shaders; uint32_t viewport_width, viewport_height; diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index dce59d88..05426fa9 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -118,38 +118,22 @@ static bool gles2_render_texture_with_matrix(struct wlr_renderer *wlr_renderer, struct wlr_gles2_texture *texture = get_gles2_texture_in_context(wlr_texture); - GLuint prog = 0; + struct wlr_gles2_tex_shader *shader = NULL; GLenum target = 0; - GLint proj_loc = 0; - GLint invert_y_loc = 0; - GLint tex_loc = 0; - GLint alpha_loc = 0; switch (texture->type) { case WLR_GLES2_TEXTURE_GLTEX: case WLR_GLES2_TEXTURE_WL_DRM_GL: if (texture->has_alpha) { - prog = renderer->shaders.tex_rgba.program; - proj_loc = renderer->shaders.tex_rgba.proj; - invert_y_loc = renderer->shaders.tex_rgba.invert_y; - tex_loc = renderer->shaders.tex_rgba.tex; - alpha_loc = renderer->shaders.tex_rgba.alpha; + shader = &renderer->shaders.tex_rgba; } else { - prog = renderer->shaders.tex_rgbx.program; - proj_loc = renderer->shaders.tex_rgbx.proj; - invert_y_loc = renderer->shaders.tex_rgbx.invert_y; - tex_loc = renderer->shaders.tex_rgbx.tex; - alpha_loc = renderer->shaders.tex_rgbx.alpha; + shader = &renderer->shaders.tex_rgbx; } target = GL_TEXTURE_2D; break; case WLR_GLES2_TEXTURE_WL_DRM_EXT: case WLR_GLES2_TEXTURE_DMABUF: - prog = renderer->shaders.tex_ext.program; - proj_loc = renderer->shaders.tex_ext.proj; - invert_y_loc = renderer->shaders.tex_ext.invert_y; - tex_loc = renderer->shaders.tex_ext.tex; - alpha_loc = renderer->shaders.tex_ext.alpha; + shader = &renderer->shaders.tex_ext; target = GL_TEXTURE_EXTERNAL_OES; break; } @@ -169,12 +153,12 @@ static bool gles2_render_texture_with_matrix(struct wlr_renderer *wlr_renderer, glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glUseProgram(prog); + glUseProgram(shader->program); - glUniformMatrix3fv(proj_loc, 1, GL_FALSE, transposition); - glUniform1i(invert_y_loc, texture->inverted_y); - glUniform1f(alpha_loc, alpha); - glUniform1i(tex_loc, 0); + glUniformMatrix3fv(shader->proj, 1, GL_FALSE, transposition); + glUniform1i(shader->invert_y, texture->inverted_y); + glUniform1i(shader->tex, 0); + glUniform1f(shader->alpha, alpha); draw_quad(); From 0378d143d9517c7508b64b81e9267a29ab1951aa Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 13 Jun 2018 19:38:10 +0100 Subject: [PATCH 25/40] surface: remove wlr_surface.texture The texture is managed by the surface's wlr_buffer now. In particular, the buffer can destroy the texture early if it becomes invalid. --- include/wlr/types/wlr_buffer.h | 11 +++++++++-- include/wlr/types/wlr_surface.h | 14 +++++++++++++- rootston/output.c | 6 +++--- types/wlr_output.c | 10 +++++----- types/wlr_surface.c | 12 ++++++++---- 5 files changed, 38 insertions(+), 15 deletions(-) diff --git a/include/wlr/types/wlr_buffer.h b/include/wlr/types/wlr_buffer.h index fc348a1c..eabc8b51 100644 --- a/include/wlr/types/wlr_buffer.h +++ b/include/wlr/types/wlr_buffer.h @@ -8,8 +8,15 @@ * A client buffer. */ struct wlr_buffer { - struct wl_resource *resource; // can be NULL - struct wlr_texture *texture; // can be NULL + /** + * The buffer resource, if any. Will be NULL if the client destroys it. + */ + struct wl_resource *resource; + /** + * The buffer's texture, if any. A buffer will not have a texture if the + * client destroys the buffer before it has been released. + */ + struct wlr_texture *texture; bool released; size_t n_refs; diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index 64503e78..8517934a 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -69,8 +69,13 @@ struct wlr_subsurface { struct wlr_surface { struct wl_resource *resource; struct wlr_renderer *renderer; + /** + * The surface's buffer, if any. A surface has an attached buffer when it + * commits with a non-null buffer in its pending state. A surface will not + * have a buffer if it has never committed one, has committed a null buffer, + * or something went wrong with uploading the buffer. + */ struct wlr_buffer *buffer; - struct wlr_texture *texture; struct wlr_surface_state *current, *pending; const char *role; // the lifetime-bound role or null @@ -125,6 +130,13 @@ int wlr_surface_set_role(struct wlr_surface *surface, const char *role, */ bool wlr_surface_has_buffer(struct wlr_surface *surface); +/** + * Get the texture of the buffer currently attached to this surface. Returns + * NULL if no buffer is currently attached or if something went wrong with + * uploading the buffer. + */ +struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface); + /** * Create a new subsurface resource with the provided new ID. If `resource_list` * is non-NULL, adds the subsurface's resource to the list. diff --git a/rootston/output.c b/rootston/output.c index faa808d1..353d431f 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -189,7 +189,8 @@ static void render_surface(struct wlr_surface *surface, int sx, int sy, struct roots_output *output = data->output; float rotation = data->layout.rotation; - if (!wlr_surface_has_buffer(surface)) { + struct wlr_texture *texture = wlr_surface_get_texture(surface); + if (texture == NULL) { return; } @@ -230,8 +231,7 @@ static void render_surface(struct wlr_surface *surface, int sx, int sy, pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); for (int i = 0; i < nrects; ++i) { scissor_output(output, &rects[i]); - wlr_render_texture_with_matrix(renderer, surface->texture, matrix, - data->alpha); + wlr_render_texture_with_matrix(renderer, texture, matrix, data->alpha); } damage_finish: diff --git a/types/wlr_output.c b/types/wlr_output.c index a5a6d0eb..7befb651 100644 --- a/types/wlr_output.c +++ b/types/wlr_output.c @@ -367,7 +367,8 @@ static void output_fullscreen_surface_render(struct wlr_output *output, struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); assert(renderer); - if (!wlr_surface_has_buffer(surface)) { + struct wlr_texture *texture = wlr_surface_get_texture(surface); + if (texture == NULL) { wlr_renderer_clear(renderer, (float[]){0, 0, 0, 1}); return; } @@ -386,8 +387,7 @@ static void output_fullscreen_surface_render(struct wlr_output *output, for (int i = 0; i < nrects; ++i) { output_scissor(output, &rects[i]); wlr_renderer_clear(renderer, (float[]){0, 0, 0, 1}); - wlr_render_texture_with_matrix(surface->renderer, surface->texture, - matrix, 1.0f); + wlr_render_texture_with_matrix(surface->renderer, texture, matrix, 1.0f); } wlr_renderer_scissor(renderer, NULL); @@ -418,7 +418,7 @@ static void output_cursor_render(struct wlr_output_cursor *cursor, struct wlr_texture *texture = cursor->texture; if (cursor->surface != NULL) { - texture = cursor->surface->texture; + texture = wlr_surface_get_texture(cursor->surface); } if (texture == NULL) { return; @@ -700,7 +700,7 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { enum wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; struct wlr_texture *texture = cursor->texture; if (cursor->surface != NULL) { - texture = cursor->surface->texture; + texture = wlr_surface_get_texture(cursor->surface); scale = cursor->surface->current->scale; transform = cursor->surface->current->transform; } diff --git a/types/wlr_surface.c b/types/wlr_surface.c index 7d8da02f..af1e9446 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -332,7 +332,6 @@ static void surface_apply_damage(struct wlr_surface *surface, // NULL commit wlr_buffer_unref(surface->buffer); surface->buffer = NULL; - surface->texture = NULL; return; } @@ -362,7 +361,6 @@ static void surface_apply_damage(struct wlr_surface *surface, wlr_buffer_unref(surface->buffer); surface->buffer = NULL; - surface->texture = NULL; struct wlr_buffer *buffer = wlr_buffer_create(surface->renderer, resource); if (buffer == NULL) { @@ -371,7 +369,6 @@ static void surface_apply_damage(struct wlr_surface *surface, } surface->buffer = buffer; - surface->texture = buffer->texture; } static void surface_commit_pending(struct wlr_surface *surface) { @@ -660,8 +657,15 @@ struct wlr_surface *wlr_surface_create(struct wl_client *client, return surface; } +struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface) { + if (surface->buffer == NULL) { + return NULL; + } + return surface->buffer->texture; +} + bool wlr_surface_has_buffer(struct wlr_surface *surface) { - return surface->texture != NULL; + return wlr_surface_get_texture(surface) != NULL; } int wlr_surface_set_role(struct wlr_surface *surface, const char *role, From d27eeaa14c9a35c709f09de862aa6d4f0ef9ff83 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Wed, 13 Jun 2018 19:57:42 -0400 Subject: [PATCH 26/40] Revert "Merge pull request #1050 from emersion/wlr-buffer" This reverts commit 5e4af4862e7247528eda0891c11daa1d86786c86, reversing changes made to 9a1f0e2d5fe56870f3bd7d12113742766e89f4e6. --- include/wlr/types/wlr_buffer.h | 56 ---------- include/wlr/types/wlr_surface.h | 1 - types/meson.build | 1 - types/wlr_buffer.c | 187 -------------------------------- types/wlr_surface.c | 137 ++++++++++++++++------- 5 files changed, 97 insertions(+), 285 deletions(-) delete mode 100644 include/wlr/types/wlr_buffer.h delete mode 100644 types/wlr_buffer.c diff --git a/include/wlr/types/wlr_buffer.h b/include/wlr/types/wlr_buffer.h deleted file mode 100644 index fc348a1c..00000000 --- a/include/wlr/types/wlr_buffer.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef WLR_TYPES_WLR_BUFFER_H -#define WLR_TYPES_WLR_BUFFER_H - -#include -#include - -/** - * A client buffer. - */ -struct wlr_buffer { - struct wl_resource *resource; // can be NULL - struct wlr_texture *texture; // can be NULL - bool released; - size_t n_refs; - - struct wl_listener resource_destroy; -}; - -struct wlr_renderer; - -/** - * Check if a resource is a wl_buffer resource. - */ -bool wlr_resource_is_buffer(struct wl_resource *resource); -/** - * Get the size of a wl_buffer resource. - */ -bool wlr_buffer_get_resource_size(struct wl_resource *resource, - struct wlr_renderer *renderer, int *width, int *height); - -/** - * Upload a buffer to the GPU and reference it. - */ -struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, - struct wl_resource *resource); -/** - * Reference the buffer. - */ -struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer); -/** - * Unreference the buffer. After this call, `buffer` may not be accessed - * anymore. - */ -void wlr_buffer_unref(struct wlr_buffer *buffer); -/** - * Try to update the buffer's content. On success, returns the updated buffer - * and destroys the provided `buffer`. On error, `buffer` is intact and NULL is - * returned. - * - * Fails if there's more than one reference to the buffer or if the texture - * isn't mutable. - */ -struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer, - struct wl_resource *resource, pixman_region32_t *damage); - -#endif diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index 64503e78..526e4e2c 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -69,7 +69,6 @@ struct wlr_subsurface { struct wlr_surface { struct wl_resource *resource; struct wlr_renderer *renderer; - struct wlr_buffer *buffer; struct wlr_texture *texture; struct wlr_surface_state *current, *pending; const char *role; // the lifetime-bound role or null diff --git a/types/meson.build b/types/meson.build index 87f21c55..f9f5b469 100644 --- a/types/meson.build +++ b/types/meson.build @@ -20,7 +20,6 @@ lib_wlr_types = static_library( 'xdg_shell_v6/wlr_xdg_surface_v6.c', 'xdg_shell_v6/wlr_xdg_toplevel_v6.c', 'wlr_box.c', - 'wlr_buffer.c', 'wlr_compositor.c', 'wlr_cursor.c', 'wlr_gamma_control.c', diff --git a/types/wlr_buffer.c b/types/wlr_buffer.c deleted file mode 100644 index 82a359f0..00000000 --- a/types/wlr_buffer.c +++ /dev/null @@ -1,187 +0,0 @@ -#include -#include -#include -#include -#include -#include - -bool wlr_resource_is_buffer(struct wl_resource *resource) { - return strcmp(wl_resource_get_class(resource), wl_buffer_interface.name) == 0; -} - -bool wlr_buffer_get_resource_size(struct wl_resource *resource, - struct wlr_renderer *renderer, int *width, int *height) { - assert(wlr_resource_is_buffer(resource)); - - struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource); - if (shm_buf != NULL) { - *width = wl_shm_buffer_get_width(shm_buf); - *height = wl_shm_buffer_get_height(shm_buf); - } else if (wlr_renderer_resource_is_wl_drm_buffer(renderer, - resource)) { - wlr_renderer_wl_drm_buffer_get_size(renderer, resource, - width, height); - } else if (wlr_dmabuf_resource_is_buffer(resource)) { - struct wlr_dmabuf_buffer *dmabuf = - wlr_dmabuf_buffer_from_buffer_resource(resource); - *width = dmabuf->attributes.width; - *height = dmabuf->attributes.height; - } else { - *width = *height = 0; - return false; - } - - return true; -} - - -static void buffer_resource_handle_destroy(struct wl_listener *listener, - void *data) { - struct wlr_buffer *buffer = - wl_container_of(listener, buffer, resource_destroy); - wl_list_remove(&buffer->resource_destroy.link); - wl_list_init(&buffer->resource_destroy.link); - buffer->resource = NULL; - - if (!buffer->released) { - // The texture becomes invalid - wlr_texture_destroy(buffer->texture); - buffer->texture = NULL; - } -} - -struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, - struct wl_resource *resource) { - assert(wlr_resource_is_buffer(resource)); - - struct wlr_texture *texture = NULL; - bool released = false; - - struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource); - if (shm_buf != NULL) { - enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buf); - int32_t stride = wl_shm_buffer_get_stride(shm_buf); - int32_t width = wl_shm_buffer_get_width(shm_buf); - int32_t height = wl_shm_buffer_get_height(shm_buf); - - wl_shm_buffer_begin_access(shm_buf); - void *data = wl_shm_buffer_get_data(shm_buf); - texture = wlr_texture_from_pixels(renderer, fmt, stride, - width, height, data); - wl_shm_buffer_end_access(shm_buf); - - // We have uploaded the data, we don't need to access the wl_buffer - // anymore - wl_buffer_send_release(resource); - released = true; - } else if (wlr_renderer_resource_is_wl_drm_buffer(renderer, resource)) { - texture = wlr_texture_from_wl_drm(renderer, resource); - } else if (wlr_dmabuf_resource_is_buffer(resource)) { - struct wlr_dmabuf_buffer *dmabuf = - wlr_dmabuf_buffer_from_buffer_resource(resource); - texture = wlr_texture_from_dmabuf(renderer, &dmabuf->attributes); - } else { - wlr_log(L_ERROR, "Cannot upload texture: unknown buffer type"); - return NULL; - } - - if (texture == NULL) { - wlr_log(L_ERROR, "Failed to upload texture"); - return NULL; - } - - struct wlr_buffer *buffer = calloc(1, sizeof(struct wlr_buffer)); - if (buffer == NULL) { - return NULL; - } - buffer->resource = resource; - buffer->texture = texture; - buffer->released = released; - buffer->n_refs = 1; - - wl_resource_add_destroy_listener(resource, &buffer->resource_destroy); - buffer->resource_destroy.notify = buffer_resource_handle_destroy; - - return buffer; -} - -struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer) { - buffer->n_refs++; - return buffer; -} - -void wlr_buffer_unref(struct wlr_buffer *buffer) { - if (buffer == NULL) { - return; - } - - assert(buffer->n_refs > 0); - buffer->n_refs--; - if (buffer->n_refs > 0) { - return; - } - - if (!buffer->released && buffer->resource != NULL) { - wl_buffer_send_release(buffer->resource); - } - - wl_list_remove(&buffer->resource_destroy.link); - wlr_texture_destroy(buffer->texture); - free(buffer); -} - -struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer, - struct wl_resource *resource, pixman_region32_t *damage) { - assert(wlr_resource_is_buffer(resource)); - - if (buffer->n_refs > 1) { - // Someone else still has a reference to the buffer - return NULL; - } - - struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource); - if (shm_buf == NULL) { - // Uploading only damaged regions only works for wl_shm buffers - return NULL; - } - - enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buf); - int32_t stride = wl_shm_buffer_get_stride(shm_buf); - int32_t width = wl_shm_buffer_get_width(shm_buf); - int32_t height = wl_shm_buffer_get_height(shm_buf); - - int32_t texture_width, texture_height; - wlr_texture_get_size(buffer->texture, &texture_width, &texture_height); - if (width != texture_width || height != texture_height) { - return NULL; - } - - wl_shm_buffer_begin_access(shm_buf); - void *data = wl_shm_buffer_get_data(shm_buf); - - int n; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &n); - for (int i = 0; i < n; ++i) { - pixman_box32_t *r = &rects[i]; - if (!wlr_texture_write_pixels(buffer->texture, fmt, stride, - r->x2 - r->x1, r->y2 - r->y1, r->x1, r->y1, - r->x1, r->y1, data)) { - wl_shm_buffer_end_access(shm_buf); - return NULL; - } - } - - wl_shm_buffer_end_access(shm_buf); - - // We have uploaded the data, we don't need to access the wl_buffer - // anymore - wl_buffer_send_release(resource); - - wl_list_remove(&buffer->resource_destroy.link); - wl_resource_add_destroy_listener(resource, &buffer->resource_destroy); - buffer->resource_destroy.notify = buffer_resource_handle_destroy; - - buffer->resource = resource; - buffer->released = true; - return buffer; -} diff --git a/types/wlr_surface.c b/types/wlr_surface.c index 7d8da02f..fca4e847 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -1,9 +1,10 @@ #include #include #include +#include #include -#include #include +#include #include #include #include @@ -45,6 +46,13 @@ static void surface_handle_buffer_destroy(struct wl_listener *listener, surface_state_reset_buffer(state); } +static void surface_state_release_buffer(struct wlr_surface_state *state) { + if (state->buffer) { + wl_buffer_send_release(state->buffer); + surface_state_reset_buffer(state); + } +} + static void surface_state_set_buffer(struct wlr_surface_state *state, struct wl_resource *buffer) { surface_state_reset_buffer(state); @@ -167,8 +175,24 @@ static bool surface_update_size(struct wlr_surface *surface, int scale = state->scale; enum wl_output_transform transform = state->transform; - wlr_buffer_get_resource_size(state->buffer, surface->renderer, - &state->buffer_width, &state->buffer_height); + struct wl_shm_buffer *buf = wl_shm_buffer_get(state->buffer); + if (buf != NULL) { + state->buffer_width = wl_shm_buffer_get_width(buf); + state->buffer_height = wl_shm_buffer_get_height(buf); + } else if (wlr_renderer_resource_is_wl_drm_buffer(surface->renderer, + state->buffer)) { + wlr_renderer_wl_drm_buffer_get_size(surface->renderer, state->buffer, + &state->buffer_width, &state->buffer_height); + } else if (wlr_dmabuf_resource_is_buffer(state->buffer)) { + struct wlr_dmabuf_buffer *dmabuf = + wlr_dmabuf_buffer_from_buffer_resource(state->buffer); + state->buffer_width = dmabuf->attributes.width; + state->buffer_height = dmabuf->attributes.height; + } else { + wlr_log(L_ERROR, "Unknown buffer handle attached"); + state->buffer_width = 0; + state->buffer_height = 0; + } int width = state->buffer_width / scale; int height = state->buffer_height / scale; @@ -219,6 +243,7 @@ static void surface_move_state(struct wlr_surface *surface, update_size = true; } if ((next->invalid & WLR_SURFACE_INVALID_BUFFER)) { + surface_state_release_buffer(state); surface_state_set_buffer(state, next->buffer); surface_state_reset_buffer(next); state->sx = next->sx; @@ -326,60 +351,91 @@ static void surface_damage_subsurfaces(struct wlr_subsurface *subsurface) { } static void surface_apply_damage(struct wlr_surface *surface, - bool invalid_buffer) { + bool invalid_buffer, bool reupload_buffer) { struct wl_resource *resource = surface->current->buffer; if (resource == NULL) { - // NULL commit - wlr_buffer_unref(surface->buffer); - surface->buffer = NULL; - surface->texture = NULL; return; } - if (surface->buffer != NULL && !surface->buffer->released && - !invalid_buffer) { - // The buffer is still the same, no need to re-upload - return; - } + struct wl_shm_buffer *buf = wl_shm_buffer_get(resource); + if (buf != NULL) { + wl_shm_buffer_begin_access(buf); - if (surface->buffer != NULL && surface->buffer->released) { - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_copy(&damage, &surface->current->buffer_damage); - pixman_region32_intersect_rect(&damage, &damage, 0, 0, - surface->current->buffer_width, surface->current->buffer_height); + enum wl_shm_format fmt = wl_shm_buffer_get_format(buf); + int32_t stride = wl_shm_buffer_get_stride(buf); + int32_t width = wl_shm_buffer_get_width(buf); + int32_t height = wl_shm_buffer_get_height(buf); + void *data = wl_shm_buffer_get_data(buf); - struct wlr_buffer *updated_buffer = - wlr_buffer_apply_damage(surface->buffer, resource, &damage); + if (surface->texture == NULL || reupload_buffer) { + wlr_texture_destroy(surface->texture); + surface->texture = wlr_texture_from_pixels(surface->renderer, fmt, + stride, width, height, data); + } else { + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_copy(&damage, &surface->current->buffer_damage); + pixman_region32_intersect_rect(&damage, &damage, 0, 0, + surface->current->buffer_width, + surface->current->buffer_height); - pixman_region32_fini(&damage); + int n; + pixman_box32_t *rects = pixman_region32_rectangles(&damage, &n); + for (int i = 0; i < n; ++i) { + pixman_box32_t *r = &rects[i]; + if (!wlr_texture_write_pixels(surface->texture, fmt, stride, + r->x2 - r->x1, r->y2 - r->y1, r->x1, r->y1, + r->x1, r->y1, data)) { + break; + } + } - if (updated_buffer != NULL) { - surface->buffer = updated_buffer; - return; + pixman_region32_fini(&damage); } + + wl_shm_buffer_end_access(buf); + + // We've uploaded the wl_shm_buffer data to the GPU, we won't access the + // wl_buffer anymore + surface_state_release_buffer(surface->current); + } else if (invalid_buffer || reupload_buffer) { + wlr_texture_destroy(surface->texture); + + if (wlr_renderer_resource_is_wl_drm_buffer(surface->renderer, resource)) { + surface->texture = + wlr_texture_from_wl_drm(surface->renderer, resource); + } else if (wlr_dmabuf_resource_is_buffer(resource)) { + struct wlr_dmabuf_buffer *dmabuf = + wlr_dmabuf_buffer_from_buffer_resource(resource); + surface->texture = + wlr_texture_from_dmabuf(surface->renderer, &dmabuf->attributes); + } else { + surface->texture = NULL; + wlr_log(L_ERROR, "Unknown buffer handle attached"); + } + + // Don't release the wl_buffer yet: since the texture is shared with the + // client, we'll access the wl_buffer when rendering } - - wlr_buffer_unref(surface->buffer); - surface->buffer = NULL; - surface->texture = NULL; - - struct wlr_buffer *buffer = wlr_buffer_create(surface->renderer, resource); - if (buffer == NULL) { - wlr_log(L_ERROR, "Failed to upload buffer"); - return; - } - - surface->buffer = buffer; - surface->texture = buffer->texture; } static void surface_commit_pending(struct wlr_surface *surface) { + int32_t oldw = surface->current->buffer_width; + int32_t oldh = surface->current->buffer_height; + bool invalid_buffer = surface->pending->invalid & WLR_SURFACE_INVALID_BUFFER; + bool null_buffer_commit = invalid_buffer && surface->pending->buffer == NULL; surface_move_state(surface, surface->pending, surface->current); - surface_apply_damage(surface, invalid_buffer); + if (null_buffer_commit) { + wlr_texture_destroy(surface->texture); + surface->texture = NULL; + } + + bool reupload_buffer = oldw != surface->current->buffer_width || + oldh != surface->current->buffer_height; + surface_apply_damage(surface, invalid_buffer, reupload_buffer); // commit subsurface order struct wlr_subsurface *subsurface; @@ -601,9 +657,10 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(surface->resource)); wl_list_remove(&surface->renderer_destroy.link); + wlr_texture_destroy(surface->texture); surface_state_destroy(surface->pending); surface_state_destroy(surface->current); - wlr_buffer_unref(surface->buffer); + free(surface); } From 3a2ef75d3af98e2a6e87c12b3236ff63769f8bde Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 14 Jun 2018 08:51:38 +0100 Subject: [PATCH 27/40] Add back wlr_buffer This reverts commit d27eeaa14c9a35c709f09de862aa6d4f0ef9ff83. --- include/wlr/types/wlr_buffer.h | 56 ++++++++++ include/wlr/types/wlr_surface.h | 1 + types/meson.build | 1 + types/wlr_buffer.c | 187 ++++++++++++++++++++++++++++++++ types/wlr_surface.c | 147 ++++++++----------------- 5 files changed, 290 insertions(+), 102 deletions(-) create mode 100644 include/wlr/types/wlr_buffer.h create mode 100644 types/wlr_buffer.c diff --git a/include/wlr/types/wlr_buffer.h b/include/wlr/types/wlr_buffer.h new file mode 100644 index 00000000..fc348a1c --- /dev/null +++ b/include/wlr/types/wlr_buffer.h @@ -0,0 +1,56 @@ +#ifndef WLR_TYPES_WLR_BUFFER_H +#define WLR_TYPES_WLR_BUFFER_H + +#include +#include + +/** + * A client buffer. + */ +struct wlr_buffer { + struct wl_resource *resource; // can be NULL + struct wlr_texture *texture; // can be NULL + bool released; + size_t n_refs; + + struct wl_listener resource_destroy; +}; + +struct wlr_renderer; + +/** + * Check if a resource is a wl_buffer resource. + */ +bool wlr_resource_is_buffer(struct wl_resource *resource); +/** + * Get the size of a wl_buffer resource. + */ +bool wlr_buffer_get_resource_size(struct wl_resource *resource, + struct wlr_renderer *renderer, int *width, int *height); + +/** + * Upload a buffer to the GPU and reference it. + */ +struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, + struct wl_resource *resource); +/** + * Reference the buffer. + */ +struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer); +/** + * Unreference the buffer. After this call, `buffer` may not be accessed + * anymore. + */ +void wlr_buffer_unref(struct wlr_buffer *buffer); +/** + * Try to update the buffer's content. On success, returns the updated buffer + * and destroys the provided `buffer`. On error, `buffer` is intact and NULL is + * returned. + * + * Fails if there's more than one reference to the buffer or if the texture + * isn't mutable. + */ +struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer, + struct wl_resource *resource, pixman_region32_t *damage); + +#endif diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index 526e4e2c..64503e78 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -69,6 +69,7 @@ struct wlr_subsurface { struct wlr_surface { struct wl_resource *resource; struct wlr_renderer *renderer; + struct wlr_buffer *buffer; struct wlr_texture *texture; struct wlr_surface_state *current, *pending; const char *role; // the lifetime-bound role or null diff --git a/types/meson.build b/types/meson.build index f9f5b469..87f21c55 100644 --- a/types/meson.build +++ b/types/meson.build @@ -20,6 +20,7 @@ lib_wlr_types = static_library( 'xdg_shell_v6/wlr_xdg_surface_v6.c', 'xdg_shell_v6/wlr_xdg_toplevel_v6.c', 'wlr_box.c', + 'wlr_buffer.c', 'wlr_compositor.c', 'wlr_cursor.c', 'wlr_gamma_control.c', diff --git a/types/wlr_buffer.c b/types/wlr_buffer.c new file mode 100644 index 00000000..82a359f0 --- /dev/null +++ b/types/wlr_buffer.c @@ -0,0 +1,187 @@ +#include +#include +#include +#include +#include +#include + +bool wlr_resource_is_buffer(struct wl_resource *resource) { + return strcmp(wl_resource_get_class(resource), wl_buffer_interface.name) == 0; +} + +bool wlr_buffer_get_resource_size(struct wl_resource *resource, + struct wlr_renderer *renderer, int *width, int *height) { + assert(wlr_resource_is_buffer(resource)); + + struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource); + if (shm_buf != NULL) { + *width = wl_shm_buffer_get_width(shm_buf); + *height = wl_shm_buffer_get_height(shm_buf); + } else if (wlr_renderer_resource_is_wl_drm_buffer(renderer, + resource)) { + wlr_renderer_wl_drm_buffer_get_size(renderer, resource, + width, height); + } else if (wlr_dmabuf_resource_is_buffer(resource)) { + struct wlr_dmabuf_buffer *dmabuf = + wlr_dmabuf_buffer_from_buffer_resource(resource); + *width = dmabuf->attributes.width; + *height = dmabuf->attributes.height; + } else { + *width = *height = 0; + return false; + } + + return true; +} + + +static void buffer_resource_handle_destroy(struct wl_listener *listener, + void *data) { + struct wlr_buffer *buffer = + wl_container_of(listener, buffer, resource_destroy); + wl_list_remove(&buffer->resource_destroy.link); + wl_list_init(&buffer->resource_destroy.link); + buffer->resource = NULL; + + if (!buffer->released) { + // The texture becomes invalid + wlr_texture_destroy(buffer->texture); + buffer->texture = NULL; + } +} + +struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, + struct wl_resource *resource) { + assert(wlr_resource_is_buffer(resource)); + + struct wlr_texture *texture = NULL; + bool released = false; + + struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource); + if (shm_buf != NULL) { + enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buf); + int32_t stride = wl_shm_buffer_get_stride(shm_buf); + int32_t width = wl_shm_buffer_get_width(shm_buf); + int32_t height = wl_shm_buffer_get_height(shm_buf); + + wl_shm_buffer_begin_access(shm_buf); + void *data = wl_shm_buffer_get_data(shm_buf); + texture = wlr_texture_from_pixels(renderer, fmt, stride, + width, height, data); + wl_shm_buffer_end_access(shm_buf); + + // We have uploaded the data, we don't need to access the wl_buffer + // anymore + wl_buffer_send_release(resource); + released = true; + } else if (wlr_renderer_resource_is_wl_drm_buffer(renderer, resource)) { + texture = wlr_texture_from_wl_drm(renderer, resource); + } else if (wlr_dmabuf_resource_is_buffer(resource)) { + struct wlr_dmabuf_buffer *dmabuf = + wlr_dmabuf_buffer_from_buffer_resource(resource); + texture = wlr_texture_from_dmabuf(renderer, &dmabuf->attributes); + } else { + wlr_log(L_ERROR, "Cannot upload texture: unknown buffer type"); + return NULL; + } + + if (texture == NULL) { + wlr_log(L_ERROR, "Failed to upload texture"); + return NULL; + } + + struct wlr_buffer *buffer = calloc(1, sizeof(struct wlr_buffer)); + if (buffer == NULL) { + return NULL; + } + buffer->resource = resource; + buffer->texture = texture; + buffer->released = released; + buffer->n_refs = 1; + + wl_resource_add_destroy_listener(resource, &buffer->resource_destroy); + buffer->resource_destroy.notify = buffer_resource_handle_destroy; + + return buffer; +} + +struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer) { + buffer->n_refs++; + return buffer; +} + +void wlr_buffer_unref(struct wlr_buffer *buffer) { + if (buffer == NULL) { + return; + } + + assert(buffer->n_refs > 0); + buffer->n_refs--; + if (buffer->n_refs > 0) { + return; + } + + if (!buffer->released && buffer->resource != NULL) { + wl_buffer_send_release(buffer->resource); + } + + wl_list_remove(&buffer->resource_destroy.link); + wlr_texture_destroy(buffer->texture); + free(buffer); +} + +struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer, + struct wl_resource *resource, pixman_region32_t *damage) { + assert(wlr_resource_is_buffer(resource)); + + if (buffer->n_refs > 1) { + // Someone else still has a reference to the buffer + return NULL; + } + + struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource); + if (shm_buf == NULL) { + // Uploading only damaged regions only works for wl_shm buffers + return NULL; + } + + enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buf); + int32_t stride = wl_shm_buffer_get_stride(shm_buf); + int32_t width = wl_shm_buffer_get_width(shm_buf); + int32_t height = wl_shm_buffer_get_height(shm_buf); + + int32_t texture_width, texture_height; + wlr_texture_get_size(buffer->texture, &texture_width, &texture_height); + if (width != texture_width || height != texture_height) { + return NULL; + } + + wl_shm_buffer_begin_access(shm_buf); + void *data = wl_shm_buffer_get_data(shm_buf); + + int n; + pixman_box32_t *rects = pixman_region32_rectangles(damage, &n); + for (int i = 0; i < n; ++i) { + pixman_box32_t *r = &rects[i]; + if (!wlr_texture_write_pixels(buffer->texture, fmt, stride, + r->x2 - r->x1, r->y2 - r->y1, r->x1, r->y1, + r->x1, r->y1, data)) { + wl_shm_buffer_end_access(shm_buf); + return NULL; + } + } + + wl_shm_buffer_end_access(shm_buf); + + // We have uploaded the data, we don't need to access the wl_buffer + // anymore + wl_buffer_send_release(resource); + + wl_list_remove(&buffer->resource_destroy.link); + wl_resource_add_destroy_listener(resource, &buffer->resource_destroy); + buffer->resource_destroy.notify = buffer_resource_handle_destroy; + + buffer->resource = resource; + buffer->released = true; + return buffer; +} diff --git a/types/wlr_surface.c b/types/wlr_surface.c index fca4e847..7d8da02f 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -1,10 +1,9 @@ #include #include #include -#include #include +#include #include -#include #include #include #include @@ -46,13 +45,6 @@ static void surface_handle_buffer_destroy(struct wl_listener *listener, surface_state_reset_buffer(state); } -static void surface_state_release_buffer(struct wlr_surface_state *state) { - if (state->buffer) { - wl_buffer_send_release(state->buffer); - surface_state_reset_buffer(state); - } -} - static void surface_state_set_buffer(struct wlr_surface_state *state, struct wl_resource *buffer) { surface_state_reset_buffer(state); @@ -175,24 +167,8 @@ static bool surface_update_size(struct wlr_surface *surface, int scale = state->scale; enum wl_output_transform transform = state->transform; - struct wl_shm_buffer *buf = wl_shm_buffer_get(state->buffer); - if (buf != NULL) { - state->buffer_width = wl_shm_buffer_get_width(buf); - state->buffer_height = wl_shm_buffer_get_height(buf); - } else if (wlr_renderer_resource_is_wl_drm_buffer(surface->renderer, - state->buffer)) { - wlr_renderer_wl_drm_buffer_get_size(surface->renderer, state->buffer, - &state->buffer_width, &state->buffer_height); - } else if (wlr_dmabuf_resource_is_buffer(state->buffer)) { - struct wlr_dmabuf_buffer *dmabuf = - wlr_dmabuf_buffer_from_buffer_resource(state->buffer); - state->buffer_width = dmabuf->attributes.width; - state->buffer_height = dmabuf->attributes.height; - } else { - wlr_log(L_ERROR, "Unknown buffer handle attached"); - state->buffer_width = 0; - state->buffer_height = 0; - } + wlr_buffer_get_resource_size(state->buffer, surface->renderer, + &state->buffer_width, &state->buffer_height); int width = state->buffer_width / scale; int height = state->buffer_height / scale; @@ -243,7 +219,6 @@ static void surface_move_state(struct wlr_surface *surface, update_size = true; } if ((next->invalid & WLR_SURFACE_INVALID_BUFFER)) { - surface_state_release_buffer(state); surface_state_set_buffer(state, next->buffer); surface_state_reset_buffer(next); state->sx = next->sx; @@ -351,91 +326,60 @@ static void surface_damage_subsurfaces(struct wlr_subsurface *subsurface) { } static void surface_apply_damage(struct wlr_surface *surface, - bool invalid_buffer, bool reupload_buffer) { + bool invalid_buffer) { struct wl_resource *resource = surface->current->buffer; if (resource == NULL) { + // NULL commit + wlr_buffer_unref(surface->buffer); + surface->buffer = NULL; + surface->texture = NULL; return; } - struct wl_shm_buffer *buf = wl_shm_buffer_get(resource); - if (buf != NULL) { - wl_shm_buffer_begin_access(buf); - - enum wl_shm_format fmt = wl_shm_buffer_get_format(buf); - int32_t stride = wl_shm_buffer_get_stride(buf); - int32_t width = wl_shm_buffer_get_width(buf); - int32_t height = wl_shm_buffer_get_height(buf); - void *data = wl_shm_buffer_get_data(buf); - - if (surface->texture == NULL || reupload_buffer) { - wlr_texture_destroy(surface->texture); - surface->texture = wlr_texture_from_pixels(surface->renderer, fmt, - stride, width, height, data); - } else { - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_copy(&damage, &surface->current->buffer_damage); - pixman_region32_intersect_rect(&damage, &damage, 0, 0, - surface->current->buffer_width, - surface->current->buffer_height); - - int n; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &n); - for (int i = 0; i < n; ++i) { - pixman_box32_t *r = &rects[i]; - if (!wlr_texture_write_pixels(surface->texture, fmt, stride, - r->x2 - r->x1, r->y2 - r->y1, r->x1, r->y1, - r->x1, r->y1, data)) { - break; - } - } - - pixman_region32_fini(&damage); - } - - wl_shm_buffer_end_access(buf); - - // We've uploaded the wl_shm_buffer data to the GPU, we won't access the - // wl_buffer anymore - surface_state_release_buffer(surface->current); - } else if (invalid_buffer || reupload_buffer) { - wlr_texture_destroy(surface->texture); - - if (wlr_renderer_resource_is_wl_drm_buffer(surface->renderer, resource)) { - surface->texture = - wlr_texture_from_wl_drm(surface->renderer, resource); - } else if (wlr_dmabuf_resource_is_buffer(resource)) { - struct wlr_dmabuf_buffer *dmabuf = - wlr_dmabuf_buffer_from_buffer_resource(resource); - surface->texture = - wlr_texture_from_dmabuf(surface->renderer, &dmabuf->attributes); - } else { - surface->texture = NULL; - wlr_log(L_ERROR, "Unknown buffer handle attached"); - } - - // Don't release the wl_buffer yet: since the texture is shared with the - // client, we'll access the wl_buffer when rendering + if (surface->buffer != NULL && !surface->buffer->released && + !invalid_buffer) { + // The buffer is still the same, no need to re-upload + return; } + + if (surface->buffer != NULL && surface->buffer->released) { + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_copy(&damage, &surface->current->buffer_damage); + pixman_region32_intersect_rect(&damage, &damage, 0, 0, + surface->current->buffer_width, surface->current->buffer_height); + + struct wlr_buffer *updated_buffer = + wlr_buffer_apply_damage(surface->buffer, resource, &damage); + + pixman_region32_fini(&damage); + + if (updated_buffer != NULL) { + surface->buffer = updated_buffer; + return; + } + } + + wlr_buffer_unref(surface->buffer); + surface->buffer = NULL; + surface->texture = NULL; + + struct wlr_buffer *buffer = wlr_buffer_create(surface->renderer, resource); + if (buffer == NULL) { + wlr_log(L_ERROR, "Failed to upload buffer"); + return; + } + + surface->buffer = buffer; + surface->texture = buffer->texture; } static void surface_commit_pending(struct wlr_surface *surface) { - int32_t oldw = surface->current->buffer_width; - int32_t oldh = surface->current->buffer_height; - bool invalid_buffer = surface->pending->invalid & WLR_SURFACE_INVALID_BUFFER; - bool null_buffer_commit = invalid_buffer && surface->pending->buffer == NULL; surface_move_state(surface, surface->pending, surface->current); - if (null_buffer_commit) { - wlr_texture_destroy(surface->texture); - surface->texture = NULL; - } - - bool reupload_buffer = oldw != surface->current->buffer_width || - oldh != surface->current->buffer_height; - surface_apply_damage(surface, invalid_buffer, reupload_buffer); + surface_apply_damage(surface, invalid_buffer); // commit subsurface order struct wlr_subsurface *subsurface; @@ -657,10 +601,9 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(surface->resource)); wl_list_remove(&surface->renderer_destroy.link); - wlr_texture_destroy(surface->texture); surface_state_destroy(surface->pending); surface_state_destroy(surface->current); - + wlr_buffer_unref(surface->buffer); free(surface); } From 47985d2dc56a6af469ac9375e7548136765aff16 Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Thu, 14 Jun 2018 20:46:16 +1200 Subject: [PATCH 28/40] Multiseat fixes --- backend/libinput/backend.c | 4 +-- backend/session/direct.c | 60 +++++++++++++++++++++-------------- backend/session/logind.c | 5 +++ backend/session/session.c | 4 +-- include/wlr/backend/session.h | 6 +++- 5 files changed, 50 insertions(+), 29 deletions(-) diff --git a/backend/libinput/backend.c b/backend/libinput/backend.c index f4d54c97..1dde5854 100644 --- a/backend/libinput/backend.c +++ b/backend/libinput/backend.c @@ -55,8 +55,8 @@ static bool backend_start(struct wlr_backend *_backend) { return false; } - // TODO: Let user customize seat used - if (libinput_udev_assign_seat(backend->libinput_context, "seat0") != 0) { + if (libinput_udev_assign_seat(backend->libinput_context, + backend->session->seat) != 0) { wlr_log(L_ERROR, "Failed to assign libinput seat"); return false; } diff --git a/backend/session/direct.c b/backend/session/direct.c index 13a26d99..c1a59647 100644 --- a/backend/session/direct.c +++ b/backend/session/direct.c @@ -1,5 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include @@ -76,30 +77,39 @@ static void direct_session_close(struct wlr_session *base, int fd) { static bool direct_change_vt(struct wlr_session *base, unsigned vt) { struct direct_session *session = wl_container_of(base, session, base); + + // Only seat0 has VTs associated with it + if (strcmp(session->base.seat, "seat0") == 0) { + return true; + } + return ioctl(session->tty_fd, VT_ACTIVATE, (int)vt) == 0; } static void direct_session_destroy(struct wlr_session *base) { struct direct_session *session = wl_container_of(base, session, base); - struct vt_mode mode = { - .mode = VT_AUTO, - }; - errno = 0; + if (strcmp(session->base.seat, "seat0") == 0) { + struct vt_mode mode = { + .mode = VT_AUTO, + }; + errno = 0; - ioctl(session->tty_fd, KDSKBMODE, session->old_kbmode); - ioctl(session->tty_fd, KDSETMODE, KD_TEXT); - ioctl(session->tty_fd, VT_SETMODE, &mode); + ioctl(session->tty_fd, KDSKBMODE, session->old_kbmode); + ioctl(session->tty_fd, KDSETMODE, KD_TEXT); + ioctl(session->tty_fd, VT_SETMODE, &mode); - if (errno) { - wlr_log(L_ERROR, "Failed to restore tty"); + if (errno) { + wlr_log(L_ERROR, "Failed to restore tty"); + } + + wl_event_source_remove(session->vt_source); + close(session->tty_fd); } direct_ipc_finish(session->sock, session->child); close(session->sock); - wl_event_source_remove(session->vt_source); - close(session->tty_fd); free(session); } @@ -138,19 +148,19 @@ static int vt_handler(int signo, void *data) { } static bool setup_tty(struct direct_session *session, struct wl_display *display) { - int fd = dup(STDIN_FILENO); + int fd = open("/dev/tty", O_RDWR); if (fd == -1) { - wlr_log_errno(L_ERROR, "Cannot open tty"); + wlr_log_errno(L_ERROR, "Cannot open /dev/tty"); return false; } - struct stat st; - if (fstat(fd, &st) == -1 || major(st.st_rdev) != TTY_MAJOR || minor(st.st_rdev) == 0) { - wlr_log(L_ERROR, "Not running from a virtual terminal"); + struct vt_stat vt_stat; + if (ioctl(fd, VT_GETSTATE, &vt_stat)) { + wlr_log_errno(L_ERROR, "Could not get current tty number"); goto error; } - int tty = minor(st.st_rdev); + int tty = vt_stat.v_active; int ret, kd_mode, old_kbmode; ret = ioctl(fd, KDGETMODE, &kd_mode); @@ -224,20 +234,24 @@ static struct wlr_session *direct_session_create(struct wl_display *disp) { goto error_session; } - if (!setup_tty(session, disp)) { - goto error_ipc; - } - - // XXX: Is it okay to trust the environment like this? const char *seat = getenv("XDG_SEAT"); if (!seat) { seat = "seat0"; } - wlr_log(L_INFO, "Successfully loaded direct session"); + if (strcmp(seat, "seat0") == 0) { + if (!setup_tty(session, disp)) { + goto error_ipc; + } + } else { + session->base.vtnr = 0; + session->tty_fd = -1; + } snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat); session->base.impl = &session_direct; + + wlr_log(L_INFO, "Successfully loaded direct session"); return &session->base; error_ipc: diff --git a/backend/session/logind.c b/backend/session/logind.c index f0ac1e93..3a3cc57d 100644 --- a/backend/session/logind.c +++ b/backend/session/logind.c @@ -109,6 +109,11 @@ static void logind_release_device(struct wlr_session *base, int fd) { static bool logind_change_vt(struct wlr_session *base, unsigned vt) { struct logind_session *session = wl_container_of(base, session, base); + // Only seat0 has VTs associated with it + if (strcmp(session->base.seat, "seat0") != 0) { + return true; + } + int ret; sd_bus_message *msg = NULL; sd_bus_error error = SD_BUS_ERROR_NULL; diff --git a/backend/session/session.c b/backend/session/session.c index 2d5d9776..026040fd 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -19,9 +19,7 @@ extern const struct session_impl session_logind; extern const struct session_impl session_direct; static const struct session_impl *impls[] = { -#ifdef WLR_HAS_SYSTEMD - &session_logind, -#elif defined(WLR_HAS_ELOGIND) +#if defined(WLR_HAS_SYSTEMD) || defined(WLR_HAS_ELOGIND) &session_logind, #endif &session_direct, diff --git a/include/wlr/backend/session.h b/include/wlr/backend/session.h index 4d04b363..1cf41939 100644 --- a/include/wlr/backend/session.h +++ b/include/wlr/backend/session.h @@ -25,8 +25,12 @@ struct wlr_session { struct wl_signal session_signal; bool active; + /* + * 0 if virtual terminals are not supported + * i.e. seat != "seat0" + */ unsigned vtnr; - char seat[8]; + char seat[256]; struct udev *udev; struct udev_monitor *mon; From 964e0a50beabe03d84f042209fc019909b631e05 Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Thu, 14 Jun 2018 21:02:32 +1200 Subject: [PATCH 29/40] Check for seat0 properly --- backend/session/direct.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/session/direct.c b/backend/session/direct.c index c1a59647..7fa7d05b 100644 --- a/backend/session/direct.c +++ b/backend/session/direct.c @@ -79,7 +79,7 @@ static bool direct_change_vt(struct wlr_session *base, unsigned vt) { struct direct_session *session = wl_container_of(base, session, base); // Only seat0 has VTs associated with it - if (strcmp(session->base.seat, "seat0") == 0) { + if (strcmp(session->base.seat, "seat0") != 0) { return true; } From da114d501322fe2f1698d19a11d6dc298000dfe4 Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 14 Jun 2018 10:15:14 +0100 Subject: [PATCH 30/40] buffer: don't destroy DMA-BUF textures with wl_buffer After some discussions on #wayland, it seems that as soon as you hold a reference to a DMA-BUF (via EGLImage for instance), the underlying memory won't get free'd. The client is allowed to re-use the DMA-BUF and upload something else to it though. --- types/wlr_buffer.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/types/wlr_buffer.c b/types/wlr_buffer.c index 82a359f0..ccff7b46 100644 --- a/types/wlr_buffer.c +++ b/types/wlr_buffer.c @@ -43,11 +43,12 @@ static void buffer_resource_handle_destroy(struct wl_listener *listener, wl_list_init(&buffer->resource_destroy.link); buffer->resource = NULL; - if (!buffer->released) { - // The texture becomes invalid - wlr_texture_destroy(buffer->texture); - buffer->texture = NULL; - } + // At this point, if the wl_buffer comes from linux-dmabuf or wl_drm, we + // still haven't released it (ie. we'll read it in the future) but the + // client destroyed it. Reading the texture itself should be fine because + // we still hold a reference to the DMA-BUF via the texture. However the + // client could decide to re-use the same DMA-BUF for something else, in + // which case we'll read garbage. We decide to accept this risk. } struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, @@ -80,6 +81,10 @@ struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, struct wlr_dmabuf_buffer *dmabuf = wlr_dmabuf_buffer_from_buffer_resource(resource); texture = wlr_texture_from_dmabuf(renderer, &dmabuf->attributes); + + // We have imported the DMA-BUF, but we need to prevent the client from + // re-using the same DMA-BUF for the next frames, so we don't release + // the buffer yet. } else { wlr_log(L_ERROR, "Cannot upload texture: unknown buffer type"); return NULL; From 23707f65040546d4594e458ab5068c0bccc31bd0 Mon Sep 17 00:00:00 2001 From: Ilia Bozhinov Date: Sat, 16 Jun 2018 17:29:26 +0300 Subject: [PATCH 31/40] layer-shell: check whether the surface is mapped in layer_surface_destroy() If the layer surface has been closed by the compositor, using layer_surface_close(), then the unmap event is emitted. However, when the layer surface is later destroyed by the client, the compositor used to get a second unmap, which is fixed with this commit. --- types/wlr_layer_shell.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/types/wlr_layer_shell.c b/types/wlr_layer_shell.c index e64d7937..76d6d721 100644 --- a/types/wlr_layer_shell.c +++ b/types/wlr_layer_shell.c @@ -176,7 +176,9 @@ static void layer_surface_unmap(struct wlr_layer_surface *surface) { } static void layer_surface_destroy(struct wlr_layer_surface *surface) { - layer_surface_unmap(surface); + if (surface->configured && surface->mapped) { + layer_surface_unmap(surface); + } wlr_signal_emit_safe(&surface->events.destroy, surface); wl_resource_set_user_data(surface->resource, NULL); wl_list_remove(&surface->surface_destroy_listener.link); From 225aa815b00502c2d91897c1ac2c4c5c65f82ca5 Mon Sep 17 00:00:00 2001 From: emersion Date: Sat, 16 Jun 2018 19:01:13 +0100 Subject: [PATCH 32/40] buffer: fix wlr_texture leak on failed alloc --- types/wlr_buffer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/types/wlr_buffer.c b/types/wlr_buffer.c index ccff7b46..673d7088 100644 --- a/types/wlr_buffer.c +++ b/types/wlr_buffer.c @@ -97,6 +97,7 @@ struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, struct wlr_buffer *buffer = calloc(1, sizeof(struct wlr_buffer)); if (buffer == NULL) { + wlr_texture_destroy(texture); return NULL; } buffer->resource = resource; From 843621714fff0dc4c1ce63e105a03b5d4028b833 Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 17 Jun 2018 12:49:34 +0100 Subject: [PATCH 33/40] surface: fix double wl_buffer.release events Prior to this commit, we re-uploaded the buffer even if a new one wasn't attached. After uploading, we send wl_buffer.release. So, this sequence of requests resulted in a double release: surface.attach(buffer, 0, 0) surface.commit() <- buffer.release() surface.commit() <- buffer.release() --- types/wlr_surface.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/types/wlr_surface.c b/types/wlr_surface.c index af1e9446..0f84312e 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -325,8 +325,7 @@ static void surface_damage_subsurfaces(struct wlr_subsurface *subsurface) { } } -static void surface_apply_damage(struct wlr_surface *surface, - bool invalid_buffer) { +static void surface_apply_damage(struct wlr_surface *surface) { struct wl_resource *resource = surface->current->buffer; if (resource == NULL) { // NULL commit @@ -335,12 +334,6 @@ static void surface_apply_damage(struct wlr_surface *surface, return; } - if (surface->buffer != NULL && !surface->buffer->released && - !invalid_buffer) { - // The buffer is still the same, no need to re-upload - return; - } - if (surface->buffer != NULL && surface->buffer->released) { pixman_region32_t damage; pixman_region32_init(&damage); @@ -376,7 +369,9 @@ static void surface_commit_pending(struct wlr_surface *surface) { surface_move_state(surface, surface->pending, surface->current); - surface_apply_damage(surface, invalid_buffer); + if (invalid_buffer) { + surface_apply_damage(surface); + } // commit subsurface order struct wlr_subsurface *subsurface; From 9eddcbc376ff92a3a03002b910c31bf96bdba2da Mon Sep 17 00:00:00 2001 From: Rostislav Pehlivanov Date: Sun, 17 Jun 2018 14:06:52 +0100 Subject: [PATCH 34/40] Update example and protocol --- examples/dmabuf-capture.c | 28 ++-- protocol/wlr-export-dmabuf-unstable-v1.xml | 176 +++++++++++---------- 2 files changed, 100 insertions(+), 104 deletions(-) diff --git a/examples/dmabuf-capture.c b/examples/dmabuf-capture.c index cd8a9267..2f7db2f1 100644 --- a/examples/dmabuf-capture.c +++ b/examples/dmabuf-capture.c @@ -163,8 +163,7 @@ static void frame_free(void *opaque, uint8_t *data) { static void frame_start(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, uint32_t width, uint32_t height, uint32_t offset_x, uint32_t offset_y, uint32_t buffer_flags, uint32_t flags, uint32_t format, - uint32_t mod_high, uint32_t mod_low, uint32_t num_objects, - uint32_t num_planes) { + uint32_t mod_high, uint32_t mod_low, uint32_t num_objects) { struct capture_context *ctx = data; int err = 0; @@ -180,7 +179,6 @@ static void frame_start(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, desc->nb_layers = 1; desc->layers[0].format = format; - desc->layers[0].nb_planes = num_planes; /* Allocate a frame */ AVFrame *f = av_frame_alloc(); @@ -213,25 +211,18 @@ fail: } static void frame_object(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, - uint32_t index, int32_t fd, uint32_t size) { + uint32_t index, int32_t fd, uint32_t size, uint32_t offset, + uint32_t stride, uint32_t plane_index) { struct capture_context *ctx = data; AVFrame *f = ctx->current_frame; AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; desc->objects[index].fd = fd; desc->objects[index].size = size; -} -static void frame_plane(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, - uint32_t index, uint32_t object_index, - uint32_t offset, uint32_t stride) { - struct capture_context *ctx = data; - AVFrame *f = ctx->current_frame; - AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; - - desc->layers[0].planes[index].object_index = object_index; - desc->layers[0].planes[index].offset = offset; - desc->layers[0].planes[index].pitch = stride; + desc->layers[0].planes[plane_index].object_index = index; + desc->layers[0].planes[plane_index].offset = offset; + desc->layers[0].planes[plane_index].pitch = stride; } static const uint32_t pixfmt_to_drm_map[] = { @@ -311,14 +302,18 @@ static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, struct capture_context *ctx = data; AVFrame *f = ctx->current_frame; AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; + enum AVPixelFormat pix_fmt = drm_fmt_to_pixfmt(desc->layers[0].format); int err = 0; /* Attach the hardware frame context to the frame */ - err = attach_drm_frames_ref(ctx, f, drm_fmt_to_pixfmt(desc->layers[0].format)); + err = attach_drm_frames_ref(ctx, f, pix_fmt); if (err) { goto end; } + /* TODO: support multiplane stuff */ + desc->layers[0].nb_planes = av_pix_fmt_count_planes(pix_fmt); + AVFrame *mapped_frame = av_frame_alloc(); if (!mapped_frame) { err = AVERROR(ENOMEM); @@ -431,7 +426,6 @@ static void frame_cancel(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, static const struct zwlr_export_dmabuf_frame_v1_listener frame_listener = { .frame = frame_start, .object = frame_object, - .plane = frame_plane, .ready = frame_ready, .cancel = frame_cancel, }; diff --git a/protocol/wlr-export-dmabuf-unstable-v1.xml b/protocol/wlr-export-dmabuf-unstable-v1.xml index 760345a7..751f7efb 100644 --- a/protocol/wlr-export-dmabuf-unstable-v1.xml +++ b/protocol/wlr-export-dmabuf-unstable-v1.xml @@ -1,6 +1,5 @@ - Copyright © 2018 Rostislav Pehlivanov @@ -25,29 +24,56 @@ - An interface to capture surfaces in an efficient way. - Overall usage: + An interface to capture surfaces in an efficient way by exporting DMA-BUFs. - 1.) client registers with zwlr_screencontent_manager_v1 - 2.) server sends client info about surfaces via "receive_surface_info" - 3.) client subscribes to capture a surface via the "capture" requests - 4.) server sends client events via the "zwlr_screencontent_frame" interface - 5.) client finishes and informs server via the "frame_destroy" event - 6.) client optionally resubscribes via repeating steps 3.) through 5.) + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. - - - This object represents a frame which is ready to have its resources - fetched and used. + + + This object is a manager with which to start capturing from sources. + + + + + Capture the next frame of a an entire output. + + + + + + + + + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + + + + + + + This object represents a single DMA-BUF frame. + + If the capture is successful, the compositor will first send a "frame" + event, followed by one or several "object". When the frame is available + for readout, the "ready" event is sent. + + If the capture failed, the "cancel" event is sent. This can happen anytime + before the "ready" event. + + Once either a "ready" or a "cancel" event is received, the client should + destroy the frame. Once an "object" event is received, the client is + responsible for closing the associated file descriptor. - The receive callback shall be called first, followed by the "object" - callback once per dmabuf object or the "plane" callback, once per dmabuf - plane. The "ready" event is called last to indicate that all the data has - been made available for readout, as well as the time at which presentation - happened at. The ownership of the frame is passed to the client, who's - responsible for destroying it via the "destroy" event once finished and - by calling close() on the file descriptors received. All frames are read-only and may not be written into or altered. @@ -55,25 +81,23 @@ Special flags that should be respected by the client. - - - Main callback supplying the client with information about the frame, - as well as an object to serve as context for destruction. Always called - first before any other events. + + Main event supplying the client with information about the frame. If the + capture didn't fail, this event is always emitted first before any other + events. - The "transform" argument describes the orientation needed to be applied - to correctly orient the buffer. For example, a buffer rotated by 90 - degrees will have a value of "3" here, corresponding to the need to - apply a 270 degree transpose to correctly present the buffer. + This event is followed by a number of "object" as specified by the + "num_objects" argument. + summary="frame width in pixels"/> + summary="frame height in pixels"/> - + - - Callback which serves to supply the client with the file descriptors + + Event which serves to supply the client with the file descriptors containing the data for each object. + + After receiving this event, the client must always close the file + descriptor as soon as they're done with it and even if the frame fails. @@ -105,31 +131,28 @@ summary="fd of the current object"/> - - - - Callback which supplies the client with plane information for each - plane. - - - + + - Called as soon as the frame is presented, indicating it is available - for reading. + This event is sent as soon as the frame is presented, indicating it is + available for reading. This event includes the time at which + presentation happened at. + The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, each component being an unsigned 32-bit value. Whole seconds are in tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, and the additional fractional part in tv_nsec as nanoseconds. Hence, - for valid timestamps tv_nsec must be in [0, 999999999]. - The seconds part may have an arbitrary offset at start. + for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part + may have an arbitrary offset at start. + + After receiving this event, the client should destroy this object. @@ -143,22 +166,25 @@ Indicates reason for cancelling the frame. - - - + - If the frame is no longer valid after the "frame" event has been called, - this callback will be used to inform the client to scrap the frame. - Source is still valid for as long as the subscription function does not - return NULL. - This may get called if for instance the surface is in the process of - resizing. + If the capture failed or if the frame is no longer valid after the + "frame" event has been emitted, this event will be used to inform the + client to scrap the frame. + + If the failure is temporary, the client may capture again the same + source. If the failure is permanent, any further attempts to capture the + same source will fail again. + + After receiving this event, the client should destroy this object. @@ -166,35 +192,11 @@ - Unreferences the frame, allowing it to be reused. Must be called as soon - as its no longer used. - Can be called at any time by the client after the "frame" event, after - which the compositor will not call any other events unless the client - resubscribes to capture more. The client will still have to close any - FDs it has been given. - - - + Unreferences the frame. This request must be called as soon as its no + longer used. - - - This object is a manager with which to start capturing from sources. - - - - - Request to start capturing from an entire wl_output. - - - - - - - - All objects created by the manager will still remain valid, until their - appropriate destroy request has been called. + It can be called at any time by the client. The client will still have + to close any FDs it has been given. From bd0c1b794992bf94560bd429f4057c9d09989f06 Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 17 Jun 2018 14:19:45 +0100 Subject: [PATCH 35/40] export-dmabuf: update protocol --- examples/dmabuf-capture.c | 2 +- types/wlr_export_dmabuf_v1.c | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/dmabuf-capture.c b/examples/dmabuf-capture.c index 2f7db2f1..1aeaf9c5 100644 --- a/examples/dmabuf-capture.c +++ b/examples/dmabuf-capture.c @@ -415,7 +415,7 @@ static void frame_cancel(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, struct capture_context *ctx = data; av_log(ctx, AV_LOG_WARNING, "Frame cancelled!\n"); av_frame_free(&ctx->current_frame); - if (reason == ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERNAMENT) { + if (reason == ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERMANENT) { av_log(ctx, AV_LOG_ERROR, "Permanent failure, exiting\n"); ctx->err = 1; } else { diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index 40a0e289..68adda02 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -93,7 +93,7 @@ static void manager_handle_capture_output(struct wl_client *client, if (!output->impl->export_dmabuf) { zwlr_export_dmabuf_frame_v1_send_cancel(frame->resource, - ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERNAMENT); + ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERMANENT); return; } @@ -110,16 +110,13 @@ static void manager_handle_capture_output(struct wl_client *client, zwlr_export_dmabuf_frame_v1_send_frame(frame->resource, output->width, output->height, 0, 0, attribs->flags, frame_flags, - attribs->format, mod_high, mod_low, attribs->n_planes, - attribs->n_planes); + attribs->format, mod_high, mod_low, attribs->n_planes); for (int i = 0; i < attribs->n_planes; ++i) { off_t size = lseek(attribs->fd[i], 0, SEEK_END); zwlr_export_dmabuf_frame_v1_send_object(frame->resource, i, - attribs->fd[i], size); - zwlr_export_dmabuf_frame_v1_send_plane(frame->resource, i, i, - attribs->offset[i], attribs->stride[i]); + attribs->fd[i], size, attribs->offset[i], attribs->stride[i], i); } wl_list_remove(&frame->output_swap_buffers.link); From ed7d5b0f53167b6191d408e5c7f20a3672fac3c2 Mon Sep 17 00:00:00 2001 From: Rostislav Pehlivanov Date: Sun, 17 Jun 2018 15:19:17 +0100 Subject: [PATCH 36/40] Fix example --- examples/dmabuf-capture.c | 31 +++++++++++++------------------ examples/meson.build | 2 +- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/examples/dmabuf-capture.c b/examples/dmabuf-capture.c index 1aeaf9c5..0d4aa3ee 100644 --- a/examples/dmabuf-capture.c +++ b/examples/dmabuf-capture.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "wlr-export-dmabuf-unstable-v1-client-protocol.h" struct wayland_output { @@ -225,25 +226,19 @@ static void frame_object(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, desc->layers[0].planes[plane_index].pitch = stride; } -static const uint32_t pixfmt_to_drm_map[] = { - [AV_PIX_FMT_NV12] = WL_SHM_FORMAT_NV12, - [AV_PIX_FMT_BGRA] = WL_SHM_FORMAT_ARGB8888, - [AV_PIX_FMT_BGR0] = WL_SHM_FORMAT_XRGB8888, - [AV_PIX_FMT_RGBA] = WL_SHM_FORMAT_ABGR8888, - [AV_PIX_FMT_RGB0] = WL_SHM_FORMAT_XBGR8888, - [AV_PIX_FMT_ABGR] = WL_SHM_FORMAT_RGBA8888, - [AV_PIX_FMT_0BGR] = WL_SHM_FORMAT_RGBX8888, - [AV_PIX_FMT_ARGB] = WL_SHM_FORMAT_BGRA8888, - [AV_PIX_FMT_0RGB] = WL_SHM_FORMAT_BGRX8888, -}; - static enum AVPixelFormat drm_fmt_to_pixfmt(uint32_t fmt) { - for (enum AVPixelFormat i = 0; i < AV_PIX_FMT_NB; i++) { - if (pixfmt_to_drm_map[i] == fmt) { - return i; - } - } - return AV_PIX_FMT_NONE; + switch (fmt) { + case DRM_FORMAT_NV12: return AV_PIX_FMT_NV12; + case DRM_FORMAT_ARGB8888: return AV_PIX_FMT_BGRA; + case DRM_FORMAT_XRGB8888: return AV_PIX_FMT_BGR0; + case DRM_FORMAT_ABGR8888: return AV_PIX_FMT_RGBA; + case DRM_FORMAT_XBGR8888: return AV_PIX_FMT_RGB0; + case DRM_FORMAT_RGBA8888: return AV_PIX_FMT_ABGR; + case DRM_FORMAT_RGBX8888: return AV_PIX_FMT_0BGR; + case DRM_FORMAT_BGRA8888: return AV_PIX_FMT_ARGB; + case DRM_FORMAT_BGRX8888: return AV_PIX_FMT_0RGB; + default: return AV_PIX_FMT_NONE; + }; } static int attach_drm_frames_ref(struct capture_context *ctx, AVFrame *f, diff --git a/examples/meson.build b/examples/meson.build index d82bd256..939a4890 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -47,6 +47,6 @@ if libavutil.found() and libavcodec.found() and libavformat.found() executable( 'dmabuf-capture', 'dmabuf-capture.c', - dependencies: [wayland_client, wlr_protos, libavutil, libavcodec, libavformat] + dependencies: [wayland_client, wlr_protos, libavutil, libavcodec, libavformat, wlroots] ) endif From a6c0e25d363e850eb5693c081022925b2a31a8a2 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 20 Jun 2018 20:00:23 +0100 Subject: [PATCH 37/40] surface: remove wlr_frame_callback This removes the need to allocate a structure for frame callbacks. wl_resource_get_link is used instead. --- include/wlr/types/wlr_surface.h | 5 ---- types/wlr_surface.c | 45 +++++++++++---------------------- 2 files changed, 15 insertions(+), 35 deletions(-) diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index 8517934a..4f4f9ff4 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -8,11 +8,6 @@ #include #include -struct wlr_frame_callback { - struct wl_resource *resource; - struct wl_list link; -}; - #define WLR_SURFACE_INVALID_BUFFER 1 #define WLR_SURFACE_INVALID_SURFACE_DAMAGE 2 #define WLR_SURFACE_INVALID_BUFFER_DAMAGE 4 diff --git a/types/wlr_surface.c b/types/wlr_surface.c index 0f84312e..59a6fc2c 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -86,40 +86,25 @@ static void surface_damage(struct wl_client *client, x, y, width, height); } -static struct wlr_frame_callback *frame_callback_from_resource( - struct wl_resource *resource) { - assert(wl_resource_instance_of(resource, &wl_callback_interface, NULL)); - return wl_resource_get_user_data(resource); -} - static void callback_handle_resource_destroy(struct wl_resource *resource) { - struct wlr_frame_callback *cb = frame_callback_from_resource(resource); - wl_list_remove(&cb->link); - free(cb); + wl_list_remove(wl_resource_get_link(resource)); } static void surface_frame(struct wl_client *client, struct wl_resource *resource, uint32_t callback) { struct wlr_surface *surface = wlr_surface_from_resource(resource); - struct wlr_frame_callback *cb = - calloc(1, sizeof(struct wlr_frame_callback)); - if (cb == NULL) { + struct wl_resource *callback_resource = wl_resource_create(client, + &wl_callback_interface, CALLBACK_VERSION, callback); + if (callback_resource == NULL) { wl_resource_post_no_memory(resource); return; } - - cb->resource = wl_resource_create(client, &wl_callback_interface, - CALLBACK_VERSION, callback); - if (cb->resource == NULL) { - free(cb); - wl_resource_post_no_memory(resource); - return; - } - wl_resource_set_implementation(cb->resource, NULL, cb, + wl_resource_set_implementation(callback_resource, NULL, NULL, callback_handle_resource_destroy); - wl_list_insert(surface->pending->frame_callback_list.prev, &cb->link); + wl_list_insert(surface->pending->frame_callback_list.prev, + wl_resource_get_link(callback_resource)); surface->pending->invalid |= WLR_SURFACE_INVALID_FRAME_CALLBACK_LIST; } @@ -549,9 +534,9 @@ static struct wlr_surface_state *surface_state_create(void) { static void surface_state_destroy(struct wlr_surface_state *state) { surface_state_reset_buffer(state); - struct wlr_frame_callback *cb, *tmp; - wl_list_for_each_safe(cb, tmp, &state->frame_callback_list, link) { - wl_resource_destroy(cb->resource); + struct wl_resource *resource, *tmp; + wl_resource_for_each_safe(resource, tmp, &state->frame_callback_list) { + wl_resource_destroy(resource); } pixman_region32_fini(&state->surface_damage); @@ -965,11 +950,11 @@ static inline int64_t timespec_to_msec(const struct timespec *a) { void wlr_surface_send_frame_done(struct wlr_surface *surface, const struct timespec *when) { - struct wlr_frame_callback *cb, *cnext; - wl_list_for_each_safe(cb, cnext, &surface->current->frame_callback_list, - link) { - wl_callback_send_done(cb->resource, timespec_to_msec(when)); - wl_resource_destroy(cb->resource); + struct wl_resource *resource, *tmp; + wl_resource_for_each_safe(resource, tmp, + &surface->current->frame_callback_list) { + wl_callback_send_done(resource, timespec_to_msec(when)); + wl_resource_destroy(resource); } } From 482fc48c74361dd8b1adf0f5c14abd8e82f35bf6 Mon Sep 17 00:00:00 2001 From: Tobias Blass Date: Wed, 20 Jun 2018 21:35:15 +0200 Subject: [PATCH 38/40] FIX: Suprocess loops endlessly when the control socket closes. recvmsg(3) returns 0 if the connection partner has shut down its socket. The communicate function considered 0 a successful message, though, and keeps calling recvmsg(3) again and again. --- backend/session/direct-ipc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/session/direct-ipc.c b/backend/session/direct-ipc.c index 6c69b70a..f8ba07f7 100644 --- a/backend/session/direct-ipc.c +++ b/backend/session/direct-ipc.c @@ -130,7 +130,7 @@ static void communicate(int sock) { int drm_fd = -1; bool running = true; - while (running && recv_msg(sock, &drm_fd, &msg, sizeof(msg)) >= 0) { + while (running && recv_msg(sock, &drm_fd, &msg, sizeof(msg)) > 0) { switch (msg.type) { case MSG_OPEN: errno = 0; From 831b7297a4e0efde437ec19e2b4d5a929d0d07ff Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 20 Jun 2018 21:01:35 +0100 Subject: [PATCH 39/40] surface: remove matrices These were unused. --- include/wlr/types/wlr_surface.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index 8517934a..f1ab8701 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -79,9 +79,6 @@ struct wlr_surface { struct wlr_surface_state *current, *pending; const char *role; // the lifetime-bound role or null - float buffer_to_surface_matrix[9]; - float surface_to_buffer_matrix[9]; - struct { struct wl_signal commit; struct wl_signal new_subsurface; From a59774f364306f2535832e630d9577039d7beac8 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 20 Jun 2018 21:25:01 +0100 Subject: [PATCH 40/40] xdg-shell{,-v6}: fix compare_xdg_surface_toplevel_state --- types/xdg_shell/wlr_xdg_toplevel.c | 2 +- types/xdg_shell_v6/wlr_xdg_toplevel_v6.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/types/xdg_shell/wlr_xdg_toplevel.c b/types/xdg_shell/wlr_xdg_toplevel.c index fa0ec929..c1350931 100644 --- a/types/xdg_shell/wlr_xdg_toplevel.c +++ b/types/xdg_shell/wlr_xdg_toplevel.c @@ -40,7 +40,7 @@ bool compare_xdg_surface_toplevel_state(struct wlr_xdg_toplevel *state) { // last configure is actually the current state, just use it configured.state = state->current; configured.width = state->base->surface->current->width; - configured.height = state->base->surface->current->width; + configured.height = state->base->surface->current->height; } else { struct wlr_xdg_surface_configure *configure = wl_container_of(state->base->configure_list.prev, configure, link); diff --git a/types/xdg_shell_v6/wlr_xdg_toplevel_v6.c b/types/xdg_shell_v6/wlr_xdg_toplevel_v6.c index 0f9a26d3..e2e1e480 100644 --- a/types/xdg_shell_v6/wlr_xdg_toplevel_v6.c +++ b/types/xdg_shell_v6/wlr_xdg_toplevel_v6.c @@ -288,7 +288,7 @@ bool compare_xdg_surface_v6_toplevel_state(struct wlr_xdg_toplevel_v6 *state) { // last configure is actually the current state, just use it configured.state = state->current; configured.width = state->base->surface->current->width; - configured.height = state->base->surface->current->width; + configured.height = state->base->surface->current->height; } else { struct wlr_xdg_surface_v6_configure *configure = wl_container_of(state->base->configure_list.prev, configure, link);