Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ws: Save and expose damaged image regions (v2) #188

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 99 additions & 3 deletions include/wpe/exported-image-egl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <stdint.h>

#ifdef __cplusplus
Expand All @@ -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
}
Expand Down
20 changes: 14 additions & 6 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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 <wayland-server.h> does not define WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION')
assert(cxx.has_member('struct wl_surface_interface', 'damage_buffer',
prefix: '#include <wayland-server.h>',
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.
Expand Down
16 changes: 16 additions & 0 deletions src/exported-image-egl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

}
2 changes: 1 addition & 1 deletion src/ws-client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ const struct wl_registry_listener BaseTarget::s_registryListener = {
auto& target = *reinterpret_cast<BaseTarget*>(data);

if (!std::strcmp(interface, "wl_compositor"))
target.m_wl.compositor = static_cast<struct wl_compositor*>(wl_registry_bind(registry, name, &wl_compositor_interface, 1));
target.m_wl.compositor = static_cast<struct wl_compositor*>(wl_registry_bind(registry, name, &wl_compositor_interface, 4));
if (!std::strcmp(interface, "wpe_bridge"))
target.m_wl.wpeBridge = static_cast<struct wpe_bridge*>(wl_registry_bind(registry, name, &wpe_bridge_interface, 1));
if (!std::strcmp(interface, "wpe_dmabuf_pool_manager"))
Expand Down
72 changes: 62 additions & 10 deletions src/ws.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Surface*>(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<Surface*>(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)
{
Expand All @@ -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<Surface*>(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<Surface*>(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)
Expand Down Expand Up @@ -416,7 +436,7 @@ Instance::Instance(std::unique_ptr<Impl>&& 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);
Expand Down Expand Up @@ -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<struct wpe_fdo_rect> 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<struct wpe_fdo_rect>* 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
6 changes: 6 additions & 0 deletions src/ws.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@

#pragma once

#include "../include/wpe/exported-image-egl.h"
#include "ws-types.h"
#include <functional>
#include <glib.h>
#include <memory>
#include <unordered_map>
#include <vector>
#include <wayland-server.h>

struct linux_dmabuf_buffer;
Expand Down Expand Up @@ -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<struct wpe_fdo_rect>* getDamageRegions(struct wl_resource* bufferResource);
void registerViewBackend(uint32_t, APIClient&);
void unregisterViewBackend(uint32_t);
bool dispatchFrameCallbacks(uint32_t);
Expand Down Expand Up @@ -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<struct wl_resource*, std::vector<struct wpe_fdo_rect>> m_damageRegions;

// (bridgeId -> Surface)
std::unordered_map<uint32_t, Surface*> m_viewBackendMap;
Expand Down
Loading