diff --git a/include/wpe/exported-image-egl.h b/include/wpe/exported-image-egl.h index 9e71150..7324644 100644 --- a/include/wpe/exported-image-egl.h +++ b/include/wpe/exported-image-egl.h @@ -30,6 +30,18 @@ #ifndef __exported_image_egl_h__ #define __exported_image_egl_h__ +/** + * SECTION:egl_exported_image + * @short_description: EGL exported images. + * @include wpe/fdo-egl.h + * + * Represents an EGL exported image with some associated attributes. + * + * An `wpe_fdo_egl_exported_image` represents an `EGLImageKHR` object, + * which may be retrieved using wpe_fdo_egl_exported_image_get_egl_image(), + * and provides additional information about it. + */ + #include #ifdef __cplusplus @@ -40,14 +52,98 @@ typedef void* EGLImageKHR; struct wpe_fdo_egl_exported_image; +/** + * wpe_fdo_rect: + * @x: Horizontal position. + * @y: Vertical position. + * @width: Rectange width. + * @height: Rectangle height. + * + * Describes the position and size of a rectangle. + * + * This is used to report damaged regions for exported images. + * The origin of coordinates is at the top-left corner of the image. + */ +struct wpe_fdo_rect { + uint32_t x, y, width, height; +}; + +/** + * wpe_fdo_egl_exported_image_get_width: + * @image: (transfer none): An exported EGL image. + * + * Gets the width of an exported @image. + * + * Returns: Image width. + */ uint32_t -wpe_fdo_egl_exported_image_get_width(struct wpe_fdo_egl_exported_image*); +wpe_fdo_egl_exported_image_get_width(struct wpe_fdo_egl_exported_image *image); +/** + * wpe_fdo_egl_exported_image_get_height: + * @image: (transfer none): An exported EGL image. + * + * Gets the height of an exported @image. + * + * Returns: Image height. + */ uint32_t -wpe_fdo_egl_exported_image_get_height(struct wpe_fdo_egl_exported_image*); +wpe_fdo_egl_exported_image_get_height(struct wpe_fdo_egl_exported_image *image); +/** + * wpe_fdo_egl_exported_image_get_egl_image: + * @image: (transfer none): An exported EGL image. + * + * Gets the `EGLImage` for en exported @image. + * + * Returns: (transfer none): An `EGLImage` handle. + */ EGLImageKHR -wpe_fdo_egl_exported_image_get_egl_image(struct wpe_fdo_egl_exported_image*); +wpe_fdo_egl_exported_image_get_egl_image(struct wpe_fdo_egl_exported_image *image); + +/** + * wpe_fdo_egl_exported_image_get_damage_regions: + * @image: (transfer none): An exported EGL image. + * @n_rectangles: (transfer none) (out) (not nullable): Location where to store the number of rectangles. + * + * Gets rectangles describing the damage regions for an @image. + * + * Images may indicate which parts have changed since the previous exported + * image. If the set of damaged regions cannot be determined, then `NULL` is + * returned and @n_regions set to zero. This may be the case if: + * + * - The image contains many changes compared to the previous exported image + * and it is considered that it is not worth it using damage regions. + * - Some component in the graphics stack is not able to handle damage + * regions. + * + * When the set of regions is empty, the whole image **must** be considered + * to be changed. + * + * **Important:** Damage regions are only meaningful when an image is used + * to be displayed right after the one exported immediately before it! In + * particular, if an exported image is kept around to be displayed later, + * and not at the moment when it has been exported, damage information *must + * not* be used. + * + * Note that the layout of the `wpe_fdo_rect` type is designed in such a way + * that the result from this function may be passed directly to EGL for + * swapping buffers: + * + * ```c + * struct wpe_fdo_egl_exported_image *image = get_image(); + * + * uint32_t n_regions; + * const struct wpe_fdo_rect *regions = + * wpe_fdo_egl_exported_image_get_damage_regions(image, &n_regions); + * + * eglSwapBuffersWithDamageEXT(eglDisplay, eglSurface, (EGLint*) regions, n_regions); + * ``` + * + * Returns: (transfer none) (nullable): Pointer to an array of regions. + */ +const struct wpe_fdo_rect* +wpe_fdo_egl_exported_image_get_damage_regions(struct wpe_fdo_egl_exported_image*, uint32_t *n_rectangles); #ifdef __cplusplus } diff --git a/meson.build b/meson.build index fb06475..3545a80 100644 --- a/meson.build +++ b/meson.build @@ -255,14 +255,22 @@ add_project_arguments( ) wpe_dep = dependency('wpe-1.0', version: '>=1.6.0', fallback: ['libwpe', 'libwpe_dep']) +wl_server_dep = dependency('wayland-server') + +assert(cxx.has_header_symbol('wayland-server.h', 'WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION', dependencies: wl_server_dep), + 'Including does not define WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION') +assert(cxx.has_member('struct wl_surface_interface', 'damage_buffer', + prefix: '#include ', + dependencies: wl_server_dep), + 'struct member wl_surface_interface::damage_buffer is not defined') deps = [ - dependency('epoxy'), - dependency('gio-2.0'), - dependency('gobject-2.0'), - dependency('wayland-client'), - dependency('wayland-server'), - wpe_dep, + dependency('epoxy'), + dependency('gio-2.0'), + dependency('gobject-2.0'), + dependency('wayland-client'), + wl_server_dep, + wpe_dep, ] # Will be set to the library dependency which provides the wl_egl_* functions. diff --git a/src/exported-image-egl.cpp b/src/exported-image-egl.cpp index 651dd5a..bdafab5 100644 --- a/src/exported-image-egl.cpp +++ b/src/exported-image-egl.cpp @@ -52,4 +52,20 @@ wpe_fdo_egl_exported_image_get_egl_image(struct wpe_fdo_egl_exported_image* imag return image->eglImage; } +__attribute__((visibility("default"))) +const struct wpe_fdo_rect* +wpe_fdo_egl_exported_image_get_damage_regions(struct wpe_fdo_egl_exported_image *image, uint32_t *n_rectangles) +{ + g_return_val_if_fail(n_rectangles, nullptr); + + const auto* regions = WS::Instance::singleton().getDamageRegions(image->bufferResource); + if (regions && regions->size()) { + *n_rectangles = regions->size(); + return regions->data(); + } else { + *n_rectangles = 0; + return nullptr; + } +} + } diff --git a/src/ws-client.cpp b/src/ws-client.cpp index ee1d875..8b888e5 100644 --- a/src/ws-client.cpp +++ b/src/ws-client.cpp @@ -260,7 +260,7 @@ const struct wl_registry_listener BaseTarget::s_registryListener = { auto& target = *reinterpret_cast(data); if (!std::strcmp(interface, "wl_compositor")) - target.m_wl.compositor = static_cast(wl_registry_bind(registry, name, &wl_compositor_interface, 1)); + target.m_wl.compositor = static_cast(wl_registry_bind(registry, name, &wl_compositor_interface, 4)); if (!std::strcmp(interface, "wpe_bridge")) target.m_wl.wpeBridge = static_cast(wl_registry_bind(registry, name, &wpe_bridge_interface, 1)); if (!std::strcmp(interface, "wpe_dmabuf_pool_manager")) diff --git a/src/ws.cpp b/src/ws.cpp index ef10e6a..74942c4 100644 --- a/src/ws.cpp +++ b/src/ws.cpp @@ -91,15 +91,21 @@ GSourceFuncs ServerSource::s_sourceFuncs = { static const struct wl_surface_interface s_surfaceInterface = { // destroy - [](struct wl_client*, struct wl_resource*) { }, + [](struct wl_client*, struct wl_resource *surfaceResource) { + auto& surface = *static_cast(wl_resource_get_user_data(surfaceResource)); + Instance::singleton().clearPendingBufferDamage(surface.bufferResource); + }, // attach [](struct wl_client*, struct wl_resource* surfaceResource, struct wl_resource* bufferResource, int32_t, int32_t) { auto& surface = *static_cast(wl_resource_get_user_data(surfaceResource)); + WS::Instance::singleton().clearPendingBufferDamage(surface.bufferResource); Instance::singleton().impl().surfaceAttach(surface, bufferResource); }, // damage - [](struct wl_client*, struct wl_resource*, int32_t, int32_t, int32_t, int32_t) { }, + [](struct wl_client*, struct wl_resource*, int32_t, int32_t, int32_t, int32_t) { + g_warning_once("%s:%u: wl_surface_interface::set_buffer_transform is a no-op", __FILE__, __LINE__); + }, // frame [](struct wl_client* client, struct wl_resource* surfaceResource, uint32_t callback) { @@ -120,26 +126,40 @@ static const struct wl_surface_interface s_surfaceInterface = { surface.addFrameCallback(callbackResource); }, // set_opaque_region - [](struct wl_client*, struct wl_resource*, struct wl_resource*) { }, + [](struct wl_client*, struct wl_resource*, struct wl_resource*) { + g_warning_once("%s:%u: wl_surface_interface::set_buffer_transform is a no-op", __FILE__, __LINE__); + }, // set_input_region - [](struct wl_client*, struct wl_resource*, struct wl_resource*) { }, + [](struct wl_client*, struct wl_resource*, struct wl_resource*) { + g_warning_once("%s:%u: wl_surface_interface::set_buffer_transform is a no-op", __FILE__, __LINE__); + }, // commit [](struct wl_client*, struct wl_resource* surfaceResource) { auto& surface = *static_cast(wl_resource_get_user_data(surfaceResource)); surface.commit(); + auto* bufferResource = surface.bufferResource; WS::Instance::singleton().impl().surfaceCommit(surface); + WS::Instance::singleton().clearPendingBufferDamage(bufferResource); }, // set_buffer_transform - [](struct wl_client*, struct wl_resource*, int32_t) { }, + [](struct wl_client*, struct wl_resource*, int32_t) { + g_warning_once("%s:%u: wl_surface_interface::set_buffer_transform is a no-op", __FILE__, __LINE__); + }, // set_buffer_scale - [](struct wl_client*, struct wl_resource*, int32_t) { }, -#if (WAYLAND_VERSION_MAJOR > 1) || (WAYLAND_VERSION_MAJOR == 1 && WAYLAND_VERSION_MINOR >= 10) + [](struct wl_client*, struct wl_resource*, int32_t) { + g_warning_once("%s:%u: wl_surface_interface::set_buffer_scale is a no-op", __FILE__, __LINE__); + }, // damage_buffer - [](struct wl_client*, struct wl_resource*, int32_t, int32_t, int32_t, int32_t) { }, -#endif + [](struct wl_client*, struct wl_resource* surfaceResource, int32_t x, int32_t y, int32_t width, int32_t height) { + auto& surface = *static_cast(wl_resource_get_user_data(surfaceResource)); + WS::Instance::singleton().addBufferDamageRegion(surface.bufferResource, x, y, width, height); + }, }; +G_STATIC_ASSERT(WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION >= WL_SURFACE_SET_BUFFER_TRANSFORM_SINCE_VERSION); +G_STATIC_ASSERT(WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION >= WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION); + static const struct wl_compositor_interface s_compositorInterface = { // create_surface [](struct wl_client* client, struct wl_resource* resource, uint32_t id) @@ -416,7 +436,7 @@ Instance::Instance(std::unique_ptr&& impl) { m_impl->setInstance(*this); - m_compositor = wl_global_create(m_display, &wl_compositor_interface, 3, this, + m_compositor = wl_global_create(m_display, &wl_compositor_interface, WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION, this, [](struct wl_client* client, void*, uint32_t version, uint32_t id) { struct wl_resource* resource = wl_resource_create(client, &wl_compositor_interface, version, id); @@ -673,4 +693,36 @@ bool Instance::dispatchFrameCallbacks(uint32_t bridgeId) return it->second->dispatchFrameCallbacks(); } +void Instance::addBufferDamageRegion(struct wl_resource *surfaceResource, uint32_t x, uint32_t y, uint32_t width, uint32_t height) +{ + if (!surfaceResource) + return; + + wpe_fdo_rect rect {x, y, width, height}; + auto it = m_damageRegions.find(surfaceResource); + if (it == m_damageRegions.end()) { + std::vector regions = { std::move(rect) }; + m_damageRegions.emplace(surfaceResource, std::move(regions)); + } else { + it->second.emplace_back(std::move(rect)); + } +} + +void Instance::clearPendingBufferDamage(struct wl_resource* bufferResource) +{ + m_damageRegions.erase(bufferResource); +} + +const std::vector* Instance::getDamageRegions(struct wl_resource* bufferResource) +{ + if (!bufferResource) + return nullptr; + + auto it = m_damageRegions.find(bufferResource); + if (it == m_damageRegions.cend()) + return nullptr; + + return &it->second; +} + } // namespace WS diff --git a/src/ws.h b/src/ws.h index 29fe645..f454955 100644 --- a/src/ws.h +++ b/src/ws.h @@ -25,11 +25,13 @@ #pragma once +#include "../include/wpe/exported-image-egl.h" #include "ws-types.h" #include #include #include #include +#include #include struct linux_dmabuf_buffer; @@ -152,6 +154,9 @@ class Instance { void registerSurface(uint32_t, Surface*); void unregisterSurface(Surface*); + void addBufferDamageRegion(struct wl_resource*, uint32_t x, uint32_t y, uint32_t width, uint32_t height); + void clearPendingBufferDamage(struct wl_resource*); + const std::vector* getDamageRegions(struct wl_resource* bufferResource); void registerViewBackend(uint32_t, APIClient&); void unregisterViewBackend(uint32_t); bool dispatchFrameCallbacks(uint32_t); @@ -188,6 +193,7 @@ class Instance { struct wl_global* m_wpeBridge { nullptr }; struct wl_global* m_wpeDmabufPoolManager { nullptr }; GSource* m_source { nullptr }; + std::unordered_map> m_damageRegions; // (bridgeId -> Surface) std::unordered_map m_viewBackendMap;