diff --git a/include/common/mir/time/posix_clock.h b/include/common/mir/time/posix_clock.h new file mode 100644 index 00000000000..4b75b74fb31 --- /dev/null +++ b/include/common/mir/time/posix_clock.h @@ -0,0 +1,51 @@ + +#ifndef MIR_COMMON_TIME_POSIX_CLOCK_ +#define MIR_COMMON_TIME_POSIX_CLOCK_ + +#include + +namespace mir::time +{ +template +struct is_steady_specialisation +{ + static constexpr bool is_steady = false; +}; + +template<> +struct is_steady_specialisation +{ + static constexpr bool is_steady = true; +}; + +template<> +struct is_steady_specialisation +{ + static constexpr bool is_steady = true; +}; + +template<> +struct is_steady_specialisation +{ + static constexpr bool is_steady = true; +}; + +auto clock_gettime_checked(clockid_t clock_id) -> std::chrono::nanoseconds; + +template +class PosixClock : public is_steady_specialisation +{ +public: + typedef std::chrono::nanoseconds duration; + typedef duration::rep rep; + typedef duration::period period; + typedef std::chrono::time_point time_point; + + static auto now() -> time_point + { + return time_point{clock_gettime_checked(clock_id)}; + } +}; +} + +#endif // MIR_COMMON_TIME_POSIX_CLOCK_ diff --git a/include/platform/mir/graphics/drm_syncobj.h b/include/platform/mir/graphics/drm_syncobj.h new file mode 100644 index 00000000000..e3e9dd4306a --- /dev/null +++ b/include/platform/mir/graphics/drm_syncobj.h @@ -0,0 +1,39 @@ +/* + * Copyright © Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 2 or 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "mir/fd.h" +#include "mir/time/posix_clock.h" + +namespace mir::graphics::drm +{ + +class Syncobj +{ +public: + Syncobj(Fd drm_fd, uint32_t handle); + + ~Syncobj(); + + auto wait(uint64_t point, time::PosixClock::time_point until) const -> bool; + + void signal(uint64_t point); + + auto to_eventfd(uint64_t point) const -> Fd; +private: + Fd const drm_fd; + uint32_t const handle; +}; +} diff --git a/include/platform/mir/graphics/platform.h b/include/platform/mir/graphics/platform.h index 4f8373bbc58..d5c72a56741 100644 --- a/include/platform/mir/graphics/platform.h +++ b/include/platform/mir/graphics/platform.h @@ -26,6 +26,7 @@ #include "mir/module_properties.h" #include "mir/module_deleter.h" #include "mir/renderer/sw/pixel_source.h" +#include "mir/fd.h" #include @@ -167,6 +168,21 @@ class GLRenderingProvider : public RenderingProvider GLConfig const& config) -> std::unique_ptr = 0; }; +namespace drm +{ +class Syncobj; +} + +class DRMRenderingProvider : public GLRenderingProvider +{ +public: + class Tag : public RenderingProvider::Tag + { + }; + + virtual auto import_syncobj(mir::Fd const& syncobj_fd) -> std::unique_ptr = 0; +}; + /** * \defgroup platform_enablement Mir platform enablement * diff --git a/src/common/symbols.map b/src/common/symbols.map index 14496e7a6da..296575f60e1 100644 --- a/src/common/symbols.map +++ b/src/common/symbols.map @@ -701,6 +701,8 @@ global: MIR_COMMON_2.19 { global: extern "C++" { + mir::time::clock_gettime_checked*; + mir::time::is_steady_specialisation::is_steady*; mir::SharedLibrary::Handle::?Handle*; mir::SharedLibrary::Handle::operator*; mir::SharedLibrary::get_handle*; @@ -708,6 +710,7 @@ global: std::hash?mir::SharedLibrary::Handle?::operator*; typeinfo?for?mir::SharedLibrary::Handle; typeinfo?for?mir::SharedLibrary::Handle::HandleHash; + typeinfo?for?mir::time::is_steady_specialisation; }; } MIR_COMMON_2.18; diff --git a/src/common/time/CMakeLists.txt b/src/common/time/CMakeLists.txt index 4dd7f6272b0..50cbdaa6cdd 100644 --- a/src/common/time/CMakeLists.txt +++ b/src/common/time/CMakeLists.txt @@ -2,4 +2,6 @@ ADD_LIBRARY( mirtime OBJECT steady_clock.cpp + ${PROJECT_SOURCE_DIR}/include/common/mir/time/posix_clock.h + posix_clock.cpp ) diff --git a/src/common/time/posix_clock.cpp b/src/common/time/posix_clock.cpp new file mode 100644 index 00000000000..ca5ab2e9a8e --- /dev/null +++ b/src/common/time/posix_clock.cpp @@ -0,0 +1,33 @@ +#include "mir/time/posix_clock.h" +#include +#include +#include +#include + +namespace mt = mir::time; + +auto mt::clock_gettime_checked(clockid_t clock_id) -> std::chrono::nanoseconds +{ + struct timespec ts; + if (auto err = clock_gettime(clock_id, &ts)) + { + BOOST_THROW_EXCEPTION(( + std::system_error{ + err, + std::system_category(), + "clock_gettime failed"})); + } + return std::chrono::nanoseconds{ts.tv_nsec + ts.tv_sec * std::chrono::nanoseconds::period::den}; +} + +static_assert( + std::chrono::is_clock_v>, + "PosixClock is a Clock type"); + +static_assert( + mt::PosixClock::is_steady, + "PosixClock should be steady"); + +static_assert( + !mt::PosixClock::is_steady, + "PosixClock should not be steady"); diff --git a/src/platform/graphics/CMakeLists.txt b/src/platform/graphics/CMakeLists.txt index 5460468a51f..9ae8ddc6ded 100644 --- a/src/platform/graphics/CMakeLists.txt +++ b/src/platform/graphics/CMakeLists.txt @@ -34,6 +34,8 @@ add_library(mirplatformgraphicscommon OBJECT egl_context_executor.cpp egl_buffer_copy.h egl_buffer_copy.cpp + ${PROJECT_SOURCE_DIR}/include/platform/mir/graphics/drm_syncobj.h + drm_syncobj.cpp ) mir_generate_protocol_wrapper(mirplatformgraphicscommon "zwp_" linux-dmabuf-unstable-v1.xml) diff --git a/src/platform/graphics/drm_syncobj.cpp b/src/platform/graphics/drm_syncobj.cpp new file mode 100644 index 00000000000..bad7ecd4d90 --- /dev/null +++ b/src/platform/graphics/drm_syncobj.cpp @@ -0,0 +1,89 @@ +#include "mir/graphics/drm_syncobj.h" + +#include "mir/log.h" + +#include +#include +#include +#include +#include + +namespace mg = mir::graphics; +namespace mt = mir::time; + +mg::drm::Syncobj::Syncobj(Fd drm_fd, uint32_t handle) + : drm_fd{std::move(drm_fd)}, + handle{handle} +{ +} + +mg::drm::Syncobj::~Syncobj() +{ + drmSyncobjDestroy(drm_fd, handle); +} + +auto mg::drm::Syncobj::wait(uint64_t point, mt::PosixClock::time_point until) const -> bool +{ + if (auto err = drmSyncobjTimelineWait( + drm_fd, + const_cast(&handle), + &point, + 1, + duration_cast(until.time_since_epoch()).count(), + DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, nullptr)) + { + if (err == -ETIME) + { + // Timeout; this is not an error + return false; + } + BOOST_THROW_EXCEPTION(( + std::system_error{ + -err, + std::system_category(), + "Failed to wait for DRM sync timeline" + } + )); + } + return true; +} + +void mg::drm::Syncobj::signal(uint64_t point) +{ + if (auto err = drmSyncobjTimelineSignal( + drm_fd, + &handle, + &point, + 1)) + { + BOOST_THROW_EXCEPTION(( + std::system_error{ + -err, + std::system_category(), + "Failed to signal DRM timeline syncpoint" + } + )); + } +} + +auto mg::drm::Syncobj::to_eventfd(uint64_t point) const -> Fd +{ + mir::Fd event_fd{eventfd(0, EFD_CLOEXEC)}; + if (event_fd == mir::Fd::invalid) + { + BOOST_THROW_EXCEPTION(( + std::system_error{ + errno, + std::system_category(), + "Failed to create eventfd"})); + } + if (auto err = drmSyncobjEventfd(drm_fd, handle, point, event_fd, 0)) + { + BOOST_THROW_EXCEPTION(( + std::system_error{ + -err, + std::system_category(), + "Failed to associate eventfd with DRM sync point"})); + } + return event_fd; +} diff --git a/src/platform/graphics/drm_syncobj.h b/src/platform/graphics/drm_syncobj.h new file mode 100644 index 00000000000..ff9697ddf9a --- /dev/null +++ b/src/platform/graphics/drm_syncobj.h @@ -0,0 +1,24 @@ +/* + * Copyright © Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 2 or 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +namespace mir::graphics::drm +{ +class Syncobj +{ +public: + virtual ~Syncobj() = default; +}; +} diff --git a/src/platform/symbols.map b/src/platform/symbols.map index fee9508e5b7..39c98eb989c 100644 --- a/src/platform/symbols.map +++ b/src/platform/symbols.map @@ -88,6 +88,11 @@ MIR_PLATFORM_2.19 { mir::graphics::wayland::bind_display*; mir::graphics::wayland::buffer_from_resource*; mir::graphics::wayland::unbind_display*; + mir::graphics::drm::Syncobj::Syncobj*; + mir::graphics::drm::Syncobj::?Syncobj*; + mir::graphics::drm::Syncobj::signal*; + mir::graphics::drm::Syncobj::to_eventfd*; + mir::graphics::drm::Syncobj::wait*; mir::options::Configuration::?Configuration*; mir::options::Configuration::Configuration*; mir::options::Configuration::operator*; diff --git a/src/platforms/gbm-kms/server/buffer_allocator.cpp b/src/platforms/gbm-kms/server/buffer_allocator.cpp index be849f41205..e3c688a309b 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.cpp +++ b/src/platforms/gbm-kms/server/buffer_allocator.cpp @@ -38,6 +38,7 @@ #include "mir/graphics/egl_error.h" #include "cpu_copy_output_surface.h" #include "surfaceless_egl_context.h" +#include "mir/graphics/drm_syncobj.h" #include #include @@ -552,6 +553,21 @@ auto mgg::GLRenderingProvider::surface_for_sink( config); } +auto mgg::GLRenderingProvider::import_syncobj(Fd const& syncobj_fd) + -> std::unique_ptr +{ + uint32_t handle; + if (auto err = drmSyncobjFDToHandle(drm_fd, syncobj_fd, &handle)) + { + BOOST_THROW_EXCEPTION(( + std::system_error{ + -err, + std::system_category(), + "Failed to import DRM syncobj"})); + } + return std::make_unique(drm_fd, handle); +} + auto mgg::GLRenderingProvider::make_framebuffer_provider(DisplaySink& /*sink*/) -> std::unique_ptr { @@ -570,12 +586,14 @@ auto mgg::GLRenderingProvider::make_framebuffer_provider(DisplaySink& /*sink*/) } mgg::GLRenderingProvider::GLRenderingProvider( + Fd drm_fd, std::shared_ptr associated_display, std::shared_ptr egl_delegate, std::shared_ptr dmabuf_provider, EGLDisplay dpy, EGLContext ctx) - : bound_display{std::move(associated_display)}, + : drm_fd{std::move(drm_fd)}, + bound_display{std::move(associated_display)}, dpy{dpy}, ctx{ctx}, dmabuf_provider{std::move(dmabuf_provider)}, diff --git a/src/platforms/gbm-kms/server/buffer_allocator.h b/src/platforms/gbm-kms/server/buffer_allocator.h index bc5472cc356..54d9dbc26d2 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.h +++ b/src/platforms/gbm-kms/server/buffer_allocator.h @@ -86,10 +86,11 @@ class BufferAllocator: bool egl_display_bound{false}; }; -class GLRenderingProvider : public graphics::GLRenderingProvider +class GLRenderingProvider : public graphics::DRMRenderingProvider { public: GLRenderingProvider( + Fd drm_fd, std::shared_ptr associated_display, std::shared_ptr egl_delegate, std::shared_ptr dmabuf_provider, @@ -109,7 +110,10 @@ class GLRenderingProvider : public graphics::GLRenderingProvider DisplaySink& sink, GLConfig const& config) -> std::unique_ptr override; + auto import_syncobj(Fd const& syncobj_fd) -> std::unique_ptr override; + private: + Fd const drm_fd; std::shared_ptr const bound_display; ///< Associated Display provider (if any - null is valid) EGLDisplay const dpy; EGLContext const ctx; diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index 303818917be..5474dbaa6d8 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -19,6 +19,7 @@ #include "display.h" #include "mir/console_services.h" #include "mir/emergency_cleanup_registry.h" +#include "mir/fd.h" #include "mir/graphics/dmabuf_buffer.h" #include "mir/graphics/drm_formats.h" #include "mir/graphics/egl_context_executor.h" @@ -30,12 +31,14 @@ #include "one_shot_device_observer.h" #include "mir/graphics/linux_dmabuf.h" #include "mir/graphics/egl_context_executor.h" +#include "mir/graphics/drm_syncobj.h" #include "kms_cpu_addressable_display_provider.h" #include "surfaceless_egl_context.h" #include #include #include #include +#include #define MIR_LOG_COMPONENT "platform-graphics-gbm-kms" #include "mir/log.h" @@ -409,11 +412,13 @@ mir::UniqueModulePtr mgg::RenderingPlatform::create_ } auto mgg::RenderingPlatform::maybe_create_provider( - RenderingProvider::Tag const& type_tag) -> std::shared_ptr + RenderingProvider::Tag const& type_tag) -> std::shared_ptr { - if (dynamic_cast(&type_tag)) + if (dynamic_cast(&type_tag) || + dynamic_cast(&type_tag)) { return std::make_shared( + Fd{IntOwnedFd{gbm_device_get_fd(device.get())}}, bound_display, egl_delegate, dmabuf_provider, diff --git a/src/server/frontend_wayland/CMakeLists.txt b/src/server/frontend_wayland/CMakeLists.txt index 7997af9dab3..723e939c3e7 100644 --- a/src/server/frontend_wayland/CMakeLists.txt +++ b/src/server/frontend_wayland/CMakeLists.txt @@ -66,6 +66,7 @@ set( wp_viewporter.cpp wp_viewporter.h fractional_scale_v1.cpp fractional_scale_v1.h xdg_activation_v1.cpp xdg_activation_v1.h + linux_drm_syncobj.cpp linux_drm_syncobj.h ) add_custom_command( diff --git a/src/server/frontend_wayland/linux_drm_syncobj.cpp b/src/server/frontend_wayland/linux_drm_syncobj.cpp new file mode 100644 index 00000000000..33577bf37e3 --- /dev/null +++ b/src/server/frontend_wayland/linux_drm_syncobj.cpp @@ -0,0 +1,195 @@ +/* + * Copyright © Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 or 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "linux_drm_syncobj.h" +#include "linux-drm-syncobj-v1_wrapper.h" +#include "mir/graphics/platform.h" +#include "mir/graphics/drm_syncobj.h" +#include "mir/wayland/protocol_error.h" +#include "mir/wayland/weak.h" +#include "wl_surface.h" +#include +#include +#include +#include +#include +#include + +namespace mf = mir::frontend; +namespace mg = mir::graphics; + +using DRMProviderList = std::vector>; + +class mf::syncobj::Manager : public mir::wayland::LinuxDrmSyncobjManagerV1 +{ +public: + Manager( + struct wl_resource* resource, + std::shared_ptr providers) + : LinuxDrmSyncobjManagerV1(resource, Version<1>{}), + providers{std::move(providers)} + { + } + +private: + void get_surface(struct wl_resource* id, struct wl_resource* wl_surface) override + { + auto surface = WlSurface::from(wl_surface); + try + { + new SyncTimeline(id, surface); + } + catch (WlSurface::TimelineAlreadyAssociated const&) + { + throw wayland::ProtocolError{ + resource, + Error::surface_exists, + "Surface already has a linux_drm_syncobj_surface_v1 associated"}; + } + } + void import_timeline(struct wl_resource* id, mir::Fd fd) override + { + try + { + new Timeline(id, std::move(fd), *providers); + } + catch (std::runtime_error const&) + { + throw wayland::ProtocolError{ + resource, + Error::invalid_timeline, + "Failed to import DRM Syncobj timeline"}; + } + } + + std::shared_ptr const providers; +}; + + + +mf::LinuxDRMSyncobjManager::LinuxDRMSyncobjManager( + struct wl_display* display, + std::span> providers) + : Global(display, Version<1>{}), + providers{std::make_shared>>( + providers.begin(), + providers.end())} +{ +} + +void mf::LinuxDRMSyncobjManager::bind(struct wl_resource* new_resource) +{ + new syncobj::Manager{new_resource, providers}; +} + +mf::SyncPoint::SyncPoint(std::shared_ptr timeline, uint64_t point) + : timeline{std::move(timeline)}, + point{point} +{ +} + +void mf::SyncPoint::signal() +{ + timeline->signal(point); +} + +auto mf::SyncPoint::wait(mir::time::PosixClock::time_point until) const -> bool +{ + return timeline->wait(point, until); +} + +auto mf::SyncPoint::to_eventfd() const -> Fd +{ + return timeline->to_eventfd(point); +} + +mf::SyncTimeline::SyncTimeline(struct wl_resource* new_resource, WlSurface* surface) + : mir::wayland::LinuxDrmSyncobjSurfaceV1(new_resource, Version<1>{}) +{ + surface->associate_sync_timeline(wayland::make_weak(this)); +} + +auto mf::SyncTimeline::claim_timeline() -> Points +{ + if (!acquire_point) + { + BOOST_THROW_EXCEPTION(( + wayland::ProtocolError{ + resource, + Error::no_acquire_point, + "Buffer does not have an acquire point set"})); + } + if (!release_point) + { + BOOST_THROW_EXCEPTION(( + wayland::ProtocolError{ + resource, + Error::no_release_point, + "Buffer does not have a release point set"})); + } + + return Points { + .acquire = std::move(std::exchange(acquire_point, std::nullopt).value()), + .release = std::move(std::exchange(release_point, std::nullopt).value()) + }; +} + +auto mf::SyncTimeline::timeline_set() const -> bool +{ + return true; +} + +void mf::SyncTimeline::set_acquire_point(struct wl_resource* timeline, uint32_t point_hi, uint32_t point_lo) +{ + auto tl = syncobj::Timeline::from(timeline); + acquire_point.emplace(SyncPoint{ + tl->syncobj(), + static_cast(point_hi) << 32 | point_lo}); +} + +void mf::SyncTimeline::set_release_point(struct wl_resource* timeline, uint32_t point_hi, uint32_t point_lo) +{ + auto tl = syncobj::Timeline::from(timeline); + release_point.emplace(SyncPoint{ + tl->syncobj(), + static_cast(point_hi) << 32 | point_lo}); +} + +mf::syncobj::Timeline::Timeline( + struct wl_resource* new_timeline, + Fd fd, + DRMProviderList const& providers) + : LinuxDrmSyncobjTimelineV1(new_timeline, Version<1>{}) +{ + for (auto const& provider : providers) + { + if (auto obj = provider->import_syncobj(fd)) + { + imported_timeline = std::move(obj); + } + } +} + +auto mf::syncobj::Timeline::from(struct wl_resource* resource) -> Timeline* +{ + return dynamic_cast(LinuxDrmSyncobjTimelineV1::from(resource)); +} + +auto mf::syncobj::Timeline::syncobj() const -> std::shared_ptr +{ + return imported_timeline; +} diff --git a/src/server/frontend_wayland/linux_drm_syncobj.h b/src/server/frontend_wayland/linux_drm_syncobj.h new file mode 100644 index 00000000000..933924ec193 --- /dev/null +++ b/src/server/frontend_wayland/linux_drm_syncobj.h @@ -0,0 +1,113 @@ +/* + * Copyright © Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 or 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MIR_SERVER_FRONTEND_LINUX_DRM_SYNCOBJ_H_ +#define MIR_SERVER_FRONTEND_LINUX_DRM_SYNCOBJ_H_ + +#include "linux-drm-syncobj-v1_wrapper.h" +#include "mir/graphics/platform.h" +#include "mir/time/posix_clock.h" + +namespace mir::frontend +{ +class WlSurface; + +class LinuxDRMSyncobjManager : public wayland::LinuxDrmSyncobjManagerV1::Global +{ +public: + LinuxDRMSyncobjManager( + struct wl_display* display, + std::span> providers); + +private: + void bind(struct wl_resource* new_wp_linux_drm_syncobj_manager_v1) override; + + std::shared_ptr> const> const providers; +}; + +class SyncPoint +{ +public: + auto wait(time::PosixClock::time_point until) const -> bool; + + void signal(); + + auto to_eventfd() const -> Fd; +private: + friend class SyncTimeline; + SyncPoint(std::shared_ptr timeline, uint64_t point); + + std::shared_ptr timeline; + uint64_t point; +}; + +class SyncTimeline : public mir::wayland::LinuxDrmSyncobjSurfaceV1 +{ +public: + SyncTimeline(struct wl_resource* new_timeline_surface, WlSurface* surface); + + struct Points + { + SyncPoint acquire; + SyncPoint release; + }; + /** + * Acquire the current timeline, resetting to the empty state + * + * \throws The appropriate ProtocolError if either of acquire_point or release_point are missing + */ + auto claim_timeline() -> Points; + + /** + * Check if *any* timeline values are set + * + * This does not guarantee that both acquire and release are set, just that at least one + * is set. + */ + bool timeline_set() const; + +private: + void set_acquire_point(struct wl_resource* timeline, uint32_t point_hi, uint32_t point_lo) override; + void set_release_point(struct wl_resource* timeline, uint32_t point_hi, uint32_t point_lo) override; + + std::optional acquire_point; + std::optional release_point; +}; + +namespace syncobj +{ +class Manager; + +class Timeline : public wayland::LinuxDrmSyncobjTimelineV1 +{ +public: + auto syncobj() const -> std::shared_ptr; + + static auto from(struct wl_resource* resource) -> Timeline*; +private: + friend class Manager; + + Timeline( + struct wl_resource* new_timeline, + mir::Fd fd, + std::vector> const& providers); + + std::shared_ptr imported_timeline; +}; +} +} + +#endif // MIR_SERVER_LINUX_DRM_SYNCOBJ_H_ diff --git a/src/server/frontend_wayland/wayland_connector.cpp b/src/server/frontend_wayland/wayland_connector.cpp index 5c7a90e45d7..0123142425d 100644 --- a/src/server/frontend_wayland/wayland_connector.cpp +++ b/src/server/frontend_wayland/wayland_connector.cpp @@ -17,6 +17,7 @@ #include "wayland_connector.h" #include "mir/shell/token_authority.h" +#include "mir/graphics/platform.h" #include "wl_client.h" #include "wl_data_device_manager.h" #include "wayland_utils.h" @@ -30,6 +31,7 @@ #include "desktop_file_manager.h" #include "foreign_toplevel_manager_v1.h" #include "wp_viewporter.h" +#include "linux_drm_syncobj.h" #include "mir/main_loop.h" #include "mir/thread_name.h" @@ -245,7 +247,8 @@ mf::WaylandConnector::WaylandConnector( std::shared_ptr const& session_lock, std::shared_ptr const& decoration_strategy, std::shared_ptr const& session_coordinator, - std::shared_ptr const& token_authority) + std::shared_ptr const& token_authority, + std::vector> const& render_platforms) : extension_filter{extension_filter}, display{wl_display_create(), &cleanup_display}, pause_signal{eventfd(0, EFD_CLOEXEC | EFD_SEMAPHORE)}, @@ -341,6 +344,22 @@ mf::WaylandConnector::WaylandConnector( viewporter = std::make_unique(display.get()); + { + std::vector> providers; + for (auto platform : render_platforms) + { + if (auto provider = mg::RenderingPlatform::acquire_provider(platform)) + { + providers.push_back(std::move(provider)); + } + } + if (!providers.empty()) + { + mir::log_debug("Detected DRM Syncobj capable rendering platform. Enabling linux_drm_syncobj_v1 explicit sync support."); + drm_syncobj = std::make_unique(display.get(), providers); + } + } + char const* wayland_display = nullptr; if (auto const display_name = getenv("WAYLAND_DISPLAY")) diff --git a/src/server/frontend_wayland/wayland_connector.h b/src/server/frontend_wayland/wayland_connector.h index f2b3fc634bf..8d00c552fa3 100644 --- a/src/server/frontend_wayland/wayland_connector.h +++ b/src/server/frontend_wayland/wayland_connector.h @@ -56,6 +56,7 @@ namespace graphics { class GraphicBufferAllocator; class DisplayConfigurationObserver; +class RenderingPlatform; } namespace shell { @@ -88,6 +89,7 @@ class WlShm; class WlSubcompositor; class WlSurface; class WpViewporter; +class LinuxDRMSyncobjManager; class DesktopFileManager; class WaylandExtensions @@ -172,7 +174,8 @@ class WaylandConnector : public Connector std::shared_ptr const& session_lock, std::shared_ptr const& decoration_strategy, std::shared_ptr const& session_coordinator, - std::shared_ptr const& token_authority); + std::shared_ptr const& token_authority, + std::vector> const& render_platforms); ~WaylandConnector() override; @@ -219,6 +222,7 @@ class WaylandConnector : public Connector std::unique_ptr data_device_manager_global; std::unique_ptr shm_global; std::unique_ptr viewporter; + std::unique_ptr drm_syncobj; std::shared_ptr const executor; std::shared_ptr const allocator; std::shared_ptr const shell; diff --git a/src/server/frontend_wayland/wayland_default_configuration.cpp b/src/server/frontend_wayland/wayland_default_configuration.cpp index 52e89f4cc4c..9be3b66201e 100644 --- a/src/server/frontend_wayland/wayland_default_configuration.cpp +++ b/src/server/frontend_wayland/wayland_default_configuration.cpp @@ -399,7 +399,8 @@ std::shared_ptr the_session_lock(), the_decoration_strategy(), the_session_coordinator(), - the_token_authority()); + the_token_authority(), + the_rendering_platforms()); }); } diff --git a/src/server/frontend_wayland/wl_surface.cpp b/src/server/frontend_wayland/wl_surface.cpp index c1e7c76ba06..2e7f421331f 100644 --- a/src/server/frontend_wayland/wl_surface.cpp +++ b/src/server/frontend_wayland/wl_surface.cpp @@ -19,12 +19,14 @@ #include "fractional_scale_v1.h" #include "mir/wayland/weak.h" #include "viewporter_wrapper.h" +#include "wayland_connector.h" #include "wayland_utils.h" #include "wl_surface_role.h" #include "wl_subcompositor.h" #include "wl_region.h" #include "shm.h" #include "resource_lifetime_tracker.h" +#include "linux_drm_syncobj.h" #include "wayland_wrapper.h" @@ -48,6 +50,7 @@ #include #include #include +#include #include namespace mf = mir::frontend; @@ -55,6 +58,13 @@ namespace geom = mir::geometry; namespace mw = mir::wayland; namespace msh = mir::shell; +struct mf::WlSurface::PendingBufferState +{ + WlSurface* surf; + Fd eventfd; + mf::SyncPoint release; +}; + mf::WlSurfaceState::Callback::Callback(wl_resource* new_resource) : mw::Callback{new_resource, Version<1>()} { @@ -83,6 +93,9 @@ void mf::WlSurfaceState::update_from(WlSurfaceState const& source) if (source.surface_data_invalidated) surface_data_invalidated = true; + + if (source.release_fence) + release_fence = source.release_fence; } bool mf::WlSurfaceState::surface_data_needs_refresh() const @@ -367,7 +380,18 @@ void mf::WlSurface::commit(WlSurfaceState const& state) } else { - auto release_buffer = [executor = wayland_executor, weak_buffer]() + std::function release_buffer; + + if (state.release_fence) + { + release_buffer = [release = *state.release_fence]() mutable + { + release.signal(); + }; + } + else + { + release_buffer = [executor = wayland_executor, weak_buffer]() { executor->spawn([weak_buffer]() { @@ -377,6 +401,8 @@ void mf::WlSurface::commit(WlSurfaceState const& state) } }); }; + } + auto executor_send_frame_callbacks = [executor = wayland_executor, weak_self = mw::make_weak(this)]() { executor->spawn([weak_self]() @@ -419,6 +445,14 @@ void mf::WlSurface::commit(WlSurfaceState const& state) } else { + if (state.release_fence) + { + throw wayland::ProtocolError{ + sync_timeline.value().resource, + SyncTimeline::Error::no_buffer, + "Timeline release sync point set, but no buffer committed"}; + } + /* * Frame request committed with no associated buffer. * The Wayland protocol says that @@ -465,6 +499,7 @@ void mf::WlSurface::commit(WlSurfaceState const& state) } ); } + } if (needs_buffer_submission && current_buffer) @@ -509,20 +544,115 @@ void mf::WlSurface::commit() if (pending.input_shape && *pending.input_shape == input_shape) pending.input_shape = std::nullopt; + auto const [acquire_syncpoint, release_syncpoint] = [&]() -> std::pair, std::optional> + { + if (sync_timeline && sync_timeline.value().timeline_set()) + { + auto [acquire, release] = sync_timeline.value().claim_timeline(); + return std::make_pair(acquire, release); + } + return std::make_pair(std::nullopt, std::nullopt); + }(); + + /* Any previously committed, but not yet ready, buffer is now superceded + */ + if (buffer_ready_source) + { + // We don't need to handle the buffer becoming ready anymore... + wl_event_source_remove(buffer_ready_source); + // ...and the client is now free to reuse it + pending_context->release.signal(); + // Now clean up our context handling. + pending_context.reset(); + buffer_ready_source = nullptr; + } + + if (acquire_syncpoint && release_syncpoint) + { + /* If we have an acquire syncpoint then we need to wait for the buffer to be ready + * before we actually commit. + * + * In future we might want to push this further into the pipeline, but for now, + * waiting before doing any of the commit is sufficient. + */ + + if (!pending.buffer || !(*pending.buffer)) + { + throw wayland::ProtocolError{ + sync_timeline.value().resource, + SyncTimeline::Error::no_buffer, + "Timeline acquire sync point set, but no buffer committed"}; + } + + pending_context = std::make_unique(PendingBufferState{ + this, + acquire_syncpoint->to_eventfd(), + *release_syncpoint}); + + wl_event_loop_fd_func_t handle_buffer_ready = + [](int /*fd*/, uint32_t /*mask*/, void* data) + { + /* Because we are dispatched from the Wayland event loop we do not + * need locking here; either data is valid, or we should have been + * removed from the run queue. + */ + auto& state = *static_cast(data); + + state.surf->pending.release_fence = state.release; + + try + { + complete_commit(state.surf); + } + catch (wayland::ProtocolError const& err) + { + wl_resource_post_error(err.resource(), err.code(), "%s", err.message()); + } + catch (...) + { + wayland::internal_error_processing_request(state.surf->client->raw_client(), "Surface::commit()"); + } + + wl_event_source_remove(state.surf->buffer_ready_source); + state.surf->buffer_ready_source = nullptr; + state.surf->pending_context.reset(); + + return 0; + }; + + auto client = wl_resource_get_client(resource); + auto display = wl_client_get_display(client); + auto event_loop = wl_display_get_event_loop(display); + + buffer_ready_source = wl_event_loop_add_fd( + event_loop, + pending_context->eventfd, + WL_EVENT_READABLE, + handle_buffer_ready, + pending_context.get()); + } + else + { + complete_commit(this); + } +} + +void mf::WlSurface::complete_commit(WlSurface* surf) +{ // order is important - auto const state = std::move(pending); - pending = WlSurfaceState(); - role->commit(state); + auto const state = std::move(surf->pending); + surf->pending = WlSurfaceState(); + surf->role->commit(state); - if (scene_surface_created_callbacks.size()) + if (surf->scene_surface_created_callbacks.size()) { - if (auto const surface = scene_surface(); surface && surface.value()) + if (auto const surface = surf->scene_surface(); surface && surface.value()) { - for (auto const& callback : scene_surface_created_callbacks) + for (auto const& callback : surf->scene_surface_created_callbacks) { callback(surface.value()); } - scene_surface_created_callbacks.clear(); + surf->scene_surface_created_callbacks.clear(); } } } @@ -560,6 +690,15 @@ void mf::WlSurface::associate_viewport(wayland::Weak viewport) pending.viewport = viewport; } +void mf::WlSurface::associate_sync_timeline(wayland::Weak timeline) +{ + if (this->sync_timeline) + { + BOOST_THROW_EXCEPTION(TimelineAlreadyAssociated{}); + } + sync_timeline = timeline; +} + void mir::frontend::WlSurface::update_surface_spec(shell::SurfaceSpecification const& spec) { pending.surface_spec.update_from(spec); diff --git a/src/server/frontend_wayland/wl_surface.h b/src/server/frontend_wayland/wl_surface.h index 0fd1eade7f1..05e95e33cbd 100644 --- a/src/server/frontend_wayland/wl_surface.h +++ b/src/server/frontend_wayland/wl_surface.h @@ -29,7 +29,9 @@ #include "mir/geometry/point.h" #include "mir/geometry/rectangle.h" #include "mir/shell/surface_specification.h" +#include "linux_drm_syncobj.h" +#include #include #include @@ -60,6 +62,7 @@ class WlSurface; class WlSubsurface; class ResourceLifetimeTracker; class Viewport; +class SyncTimeline; struct WlSurfaceState { @@ -88,6 +91,8 @@ struct WlSurfaceState std::vector> frame_callbacks; wayland::Weak viewport; + std::optional release_fence; + private: // only set to true if invalidate_surface_data() is called // surface_data_needs_refresh() returns true if this is true, or if other things are changed which mandate a refresh @@ -156,6 +161,26 @@ class WlSurface : public wayland::Surface */ void associate_viewport(wayland::Weak viewport); + /** + * Associate a DRM Syncobj timeline with this surface + * + * This associates explicit synchronisation fences with the surface's + * buffer submissions. + * See linux-drm-syncobj-v1 protocol for more details + * + * \throws A TimelineAlreadyAssociated if the surface already has a timeline associated + */ + void associate_sync_timeline(wayland::Weak timeline); + + class TimelineAlreadyAssociated : public std::logic_error + { + public: + TimelineAlreadyAssociated() + : std::logic_error("Surface already has a sync timeline associated") + { + } + }; + std::shared_ptr const session; std::shared_ptr const stream; @@ -179,6 +204,14 @@ class WlSurface : public wayland::Surface */ std::shared_ptr current_buffer; + /* State for when a buffer update is waiting for a client fence to signal + */ + struct PendingBufferState; + std::unique_ptr pending_context; + struct wl_event_source* buffer_ready_source{nullptr}; + + static void complete_commit(WlSurface* surf); + WlSurfaceState pending; geometry::Displacement offset_; float scale{1}; @@ -191,6 +224,7 @@ class WlSurface : public wayland::Surface std::vector scene_surface_created_callbacks; wayland::Weak viewport; wayland::Weak fractional_scale; + wayland::Weak sync_timeline; void send_frame_callbacks(CallbackList& list); diff --git a/src/wayland/CMakeLists.txt b/src/wayland/CMakeLists.txt index e4820524fab..acc3e2cc62b 100644 --- a/src/wayland/CMakeLists.txt +++ b/src/wayland/CMakeLists.txt @@ -40,6 +40,7 @@ mir_generate_protocol_wrapper(mirwayland "z" xdg-decoration-unstable-v1.xml) mir_generate_protocol_wrapper(mirwayland "wp_" viewporter.xml) mir_generate_protocol_wrapper(mirwayland "wp_" fractional-scale-v1.xml) mir_generate_protocol_wrapper(mirwayland "z" xdg-activation-v1.xml) +mir_generate_protocol_wrapper(mirwayland "wp_" linux-drm-syncobj-v1.xml) target_link_libraries(mirwayland PUBLIC diff --git a/tests/include/mir/test/doubles/stub_buffer.h b/tests/include/mir/test/doubles/stub_buffer.h index e45325547b9..9215e7b8395 100644 --- a/tests/include/mir/test/doubles/stub_buffer.h +++ b/tests/include/mir/test/doubles/stub_buffer.h @@ -55,7 +55,7 @@ class StubBuffer : size, mir_pixel_format_abgr_8888, graphics::BufferUsage::hardware}, - geometry::Stride{}} + geometry::Stride{size.height.as_uint32_t() * MIR_BYTES_PER_PIXEL(mir_pixel_format_abgr_8888)}} { } @@ -66,8 +66,7 @@ class StubBuffer : size, pixel_format, graphics::BufferUsage::hardware}, - geometry::Stride{}} - + geometry::Stride{size.height.as_uint32_t() * MIR_BYTES_PER_PIXEL(pixel_format)}} { } diff --git a/tests/include/mir/test/doubles/stub_display_sink.h b/tests/include/mir/test/doubles/stub_display_sink.h index 0f16c39b490..d045373d2cd 100644 --- a/tests/include/mir/test/doubles/stub_display_sink.h +++ b/tests/include/mir/test/doubles/stub_display_sink.h @@ -17,8 +17,13 @@ #ifndef MIR_TEST_DOUBLES_STUB_DISPLAY_BUFFER_H_ #define MIR_TEST_DOUBLES_STUB_DISPLAY_BUFFER_H_ +#include "mir/graphics/drm_formats.h" +#include "mir/graphics/platform.h" #include "mir/test/doubles/null_display_sink.h" #include "mir/geometry/rectangle.h" +#include "mir/test/doubles/stub_buffer.h" +#include "mir_toolkit/common.h" +#include namespace mir { @@ -27,15 +32,90 @@ namespace test namespace doubles { +class DummyCPUAddressableDisplayAllocator : public graphics::CPUAddressableDisplayAllocator +{ + class MappableFB : public graphics::CPUAddressableDisplayAllocator::MappableFB + { + public: + MappableFB(geometry::Size size, graphics::DRMFormat format) + : buffer{std::make_unique(size, format.as_mir_format().value())} + { + } + + auto map_writeable() -> std::unique_ptr> override + { + return buffer->map_writeable(); + } + + auto format() const -> MirPixelFormat override + { + return buffer->format(); + } + + auto stride() const -> geometry::Stride override + { + return buffer->stride(); + } + auto size() const -> geometry::Size override + { + return buffer->size(); + } + private: + std::unique_ptr const buffer; + }; + +public: + DummyCPUAddressableDisplayAllocator(geometry::Size size) + : size{size} + { + } + + auto supported_formats() const -> std::vector override + { + return {graphics::DRMFormat{DRM_FORMAT_ARGB8888}}; + } + auto alloc_fb(graphics::DRMFormat format) -> std::unique_ptr override + { + return std::make_unique(size, format); + } + auto output_size() const -> geometry::Size override + { + return size; + } + +private: + geometry::Size const size; +}; + class StubDisplaySink : public NullDisplaySink { public: - StubDisplaySink(geometry::Rectangle const& view_area_) : view_area_(view_area_) {} - StubDisplaySink(StubDisplaySink const& s) : view_area_(s.view_area_) {} + StubDisplaySink(geometry::Rectangle const& view_area_) + : view_area_(view_area_), + allocator{view_area_.size} + { + } + + StubDisplaySink(StubDisplaySink const& s) + : StubDisplaySink(s.view_area_) + { + } + geometry::Rectangle view_area() const override { return view_area_; } private: + auto maybe_create_allocator(graphics::DisplayAllocator::Tag const& tag) + -> graphics::DisplayAllocator* override + { + if (dynamic_cast(&tag)) + { + return &allocator; + } + return nullptr; + } + geometry::Rectangle view_area_; + DummyCPUAddressableDisplayAllocator allocator; }; } diff --git a/tests/mir_test_framework/stubbed_graphics_platform.cpp b/tests/mir_test_framework/stubbed_graphics_platform.cpp index a82612e5fd2..d9bd4eeb316 100644 --- a/tests/mir_test_framework/stubbed_graphics_platform.cpp +++ b/tests/mir_test_framework/stubbed_graphics_platform.cpp @@ -100,9 +100,16 @@ auto mtf::StubGraphicPlatform::maybe_create_provider( return nullptr; } -auto mtf::StubGraphicPlatform::maybe_create_provider(mg::DisplayProvider::Tag const&) +auto mtf::StubGraphicPlatform::maybe_create_provider(mg::DisplayProvider::Tag const& tag) -> std::shared_ptr { + class NullCPUAddressableDisplayProvider : public mg::CPUAddressableDisplayProvider + { + }; + if (dynamic_cast(&tag)) + { + return std::make_shared(); + } return nullptr; } diff --git a/tests/mir_test_framework/test_display_server.cpp b/tests/mir_test_framework/test_display_server.cpp index 46034d8ff16..59d099e7f57 100644 --- a/tests/mir_test_framework/test_display_server.cpp +++ b/tests/mir_test_framework/test_display_server.cpp @@ -60,7 +60,6 @@ miral::TestDisplayServer::TestDisplayServer(int argc, char const** argv) : unsetenv("WAYLAND_DISPLAY"); // We don't want to conflict with any existing server add_to_environment("MIR_SERVER_PLATFORM_PATH", mtf::server_platform_path().c_str()); add_to_environment("MIR_SERVER_PLATFORM_DISPLAY_LIBS", "mir:stub-graphics"); - add_to_environment("MIR_SERVER_PLATFORM_RENDERING_LIBS", "mir:stub-graphics"); add_to_environment("MIR_SERVER_PLATFORM_INPUT_LIB", "mir:stub-input"); add_to_environment("MIR_SERVER_CONSOLE_PROVIDER", "none"); } @@ -104,24 +103,6 @@ void miral::TestDisplayServer::start_server() }); }); - - server.override_the_display_buffer_compositor_factory( - [&server]() -> std::shared_ptr - { - auto first_rendering_platform = server.the_rendering_platforms().front(); - auto gl_provider = - mg::RenderingPlatform::acquire_provider( - std::move(first_rendering_platform)); - if (gl_provider) - { - return std::make_shared( - std::move(gl_provider), - server.the_gl_config()); - } - BOOST_THROW_EXCEPTION((std::runtime_error{"Platform does not support GL interface"})); - }); - - server.override_the_logger([&]() { std::shared_ptr result{}; diff --git a/tests/unit-tests/test_udev_wrapper.cpp b/tests/unit-tests/test_udev_wrapper.cpp index 4c1e8b5645d..e08a207e21f 100644 --- a/tests/unit-tests/test_udev_wrapper.cpp +++ b/tests/unit-tests/test_udev_wrapper.cpp @@ -317,19 +317,6 @@ TEST_F(UdevWrapperTest, MemberDereferenceWorks) EXPECT_STREQ("drm", iter->subsystem()); } -TEST_F(UdevWrapperTest, UdevMonitorDoesNotTriggerBeforeEnabling) -{ - mir::udev::Monitor monitor{mir::udev::Context()}; - bool event_handler_called = false; - - udev_environment.add_device("drm", "control64D", NULL, {}, {}); - - monitor.process_events([&event_handler_called](mir::udev::Monitor::EventType, - mir::udev::Device const&) {event_handler_called = true;}); - - EXPECT_FALSE(event_handler_called); -} - TEST_F(UdevWrapperTest, UdevMonitorTriggersAfterEnabling) { mir::udev::Monitor monitor{mir::udev::Context()}; diff --git a/wayland-protocols/linux-drm-syncobj-v1.xml b/wayland-protocols/linux-drm-syncobj-v1.xml new file mode 100644 index 00000000000..2c491eaf43a --- /dev/null +++ b/wayland-protocols/linux-drm-syncobj-v1.xml @@ -0,0 +1,261 @@ + + + + Copyright 2016 The Chromium Authors. + Copyright 2017 Intel Corporation + Copyright 2018 Collabora, Ltd + Copyright 2021 Simon Ser + + 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. + + + + This protocol allows clients to request explicit synchronization for + buffers. It is tied to the Linux DRM synchronization object framework. + + Synchronization refers to co-ordination of pipelined operations performed + on buffers. Most GPU clients will schedule an asynchronous operation to + render to the buffer, then immediately send the buffer to the compositor + to be attached to a surface. + + With implicit synchronization, ensuring that the rendering operation is + complete before the compositor displays the buffer is an implementation + detail handled by either the kernel or userspace graphics driver. + + By contrast, with explicit synchronization, DRM synchronization object + timeline points mark when the asynchronous operations are complete. When + submitting a buffer, the client provides a timeline point which will be + waited on before the compositor accesses the buffer, and another timeline + point that the compositor will signal when it no longer needs to access the + buffer contents for the purposes of the surface commit. + + Linux DRM synchronization objects are documented at: + https://dri.freedesktop.org/docs/drm/gpu/drm-mm.html#drm-sync-objects + + Warning! The protocol described in this file is currently in the testing + phase. Backward compatible changes may be added together with the + corresponding interface version bump. Backward incompatible changes can + only be done by creating a new major version of the extension. + + + + + This global is a factory interface, allowing clients to request + explicit synchronization for buffers on a per-surface basis. + + See wp_linux_drm_syncobj_surface_v1 for more information. + + + + + Destroy this explicit synchronization factory object. Other objects + shall not be affected by this request. + + + + + + + + + + + Instantiate an interface extension for the given wl_surface to provide + explicit synchronization. + + If the given wl_surface already has an explicit synchronization object + associated, the surface_exists protocol error is raised. + + Graphics APIs, like EGL or Vulkan, that manage the buffer queue and + commits of a wl_surface themselves, are likely to be using this + extension internally. If a client is using such an API for a + wl_surface, it should not directly use this extension on that surface, + to avoid raising a surface_exists protocol error. + + + + + + + + Import a DRM synchronization object timeline. + + If the FD cannot be imported, the invalid_timeline error is raised. + + + + + + + + + This object represents an explicit synchronization object timeline + imported by the client to the compositor. + + + + + Destroy the synchronization object timeline. Other objects are not + affected by this request, in particular timeline points set by + set_acquire_point and set_release_point are not unset. + + + + + + + This object is an add-on interface for wl_surface to enable explicit + synchronization. + + Each surface can be associated with only one object of this interface at + any time. + + Explicit synchronization is guaranteed to be supported for buffers + created with any version of the linux-dmabuf protocol. Compositors are + free to support explicit synchronization for additional buffer types. + If at surface commit time the attached buffer does not support explicit + synchronization, an unsupported_buffer error is raised. + + As long as the wp_linux_drm_syncobj_surface_v1 object is alive, the + compositor may ignore implicit synchronization for buffers attached and + committed to the wl_surface. The delivery of wl_buffer.release events + for buffers attached to the surface becomes undefined. + + Clients must set both acquire and release points if and only if a + non-null buffer is attached in the same surface commit. See the + no_buffer, no_acquire_point and no_release_point protocol errors. + + If at surface commit time the acquire and release DRM syncobj timelines + are identical, the acquire point value must be strictly less than the + release point value, or else the conflicting_points protocol error is + raised. + + + + + Destroy this surface synchronization object. + + Any timeline point set by this object with set_acquire_point or + set_release_point since the last commit may be discarded by the + compositor. Any timeline point set by this object before the last + commit will not be affected. + + + + + + + + + + + + + + + Set the timeline point that must be signalled before the compositor may + sample from the buffer attached with wl_surface.attach. + + The 64-bit unsigned value combined from point_hi and point_lo is the + point value. + + The acquire point is double-buffered state, and will be applied on the + next wl_surface.commit request for the associated surface. Thus, it + applies only to the buffer that is attached to the surface at commit + time. + + If an acquire point has already been attached during the same commit + cycle, the new point replaces the old one. + + If the associated wl_surface was destroyed, a no_surface error is + raised. + + If at surface commit time there is a pending acquire timeline point set + but no pending buffer attached, a no_buffer error is raised. If at + surface commit time there is a pending buffer attached but no pending + acquire timeline point set, the no_acquire_point protocol error is + raised. + + + + + + + + + Set the timeline point that must be signalled by the compositor when it + has finished its usage of the buffer attached with wl_surface.attach + for the relevant commit. + + Once the timeline point is signaled, and assuming the associated buffer + is not pending release from other wl_surface.commit requests, no + additional explicit or implicit synchronization with the compositor is + required to safely re-use the buffer. + + Note that clients cannot rely on the release point being always + signaled after the acquire point: compositors may release buffers + without ever reading from them. In addition, the compositor may use + different presentation paths for different commits, which may have + different release behavior. As a result, the compositor may signal the + release points in a different order than the client committed them. + + Because signaling a timeline point also signals every previous point, + it is generally not safe to use the same timeline object for the + release points of multiple buffers. The out-of-order signaling + described above may lead to a release point being signaled before the + compositor has finished reading. To avoid this, it is strongly + recommended that each buffer should use a separate timeline for its + release points. + + The 64-bit unsigned value combined from point_hi and point_lo is the + point value. + + The release point is double-buffered state, and will be applied on the + next wl_surface.commit request for the associated surface. Thus, it + applies only to the buffer that is attached to the surface at commit + time. + + If a release point has already been attached during the same commit + cycle, the new point replaces the old one. + + If the associated wl_surface was destroyed, a no_surface error is + raised. + + If at surface commit time there is a pending release timeline point set + but no pending buffer attached, a no_buffer error is raised. If at + surface commit time there is a pending buffer attached but no pending + release timeline point set, the no_release_point protocol error is + raised. + + + + + + +