| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/exo/wayland/zwp_linux_explicit_synchronization.h" |
| #include "base/memory/raw_ptr.h" |
| |
| #include <linux-explicit-synchronization-unstable-v1-server-protocol.h> |
| #include <sync/sync.h> |
| |
| #include "components/exo/surface.h" |
| #include "components/exo/surface_observer.h" |
| #include "components/exo/wayland/server_util.h" |
| #include "ui/gfx/gpu_fence.h" |
| #include "ui/gfx/gpu_fence_handle.h" |
| |
| namespace exo { |
| namespace wayland { |
| |
| namespace { |
| |
| // A property key containing a pointer to the surface_synchronization resource |
| // associated with the surface object. |
| DEFINE_UI_CLASS_PROPERTY_KEY(wl_resource*, |
| kSurfaceSynchronizationResource, |
| nullptr) |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // linux_buffer_release_v1 interface: |
| |
| // Implements the buffer release interface. |
| class LinuxBufferRelease { |
| public: |
| LinuxBufferRelease(wl_resource* resource, Surface* surface) |
| : resource_(resource), |
| release_callback_( |
| base::BindOnce(&LinuxBufferRelease::HandleExplicitRelease, |
| base::Unretained(this))) { |
| surface->SetPerCommitBufferReleaseCallback(release_callback_.callback()); |
| } |
| |
| LinuxBufferRelease(const LinuxBufferRelease&) = delete; |
| LinuxBufferRelease& operator=(const LinuxBufferRelease&) = delete; |
| |
| private: |
| void HandleExplicitRelease(gfx::GpuFenceHandle release_fence) { |
| if (!release_fence.is_null()) { |
| // Fd will be dup'd for us. |
| zwp_linux_buffer_release_v1_send_fenced_release(resource_, |
| release_fence.Peek()); |
| } else { |
| zwp_linux_buffer_release_v1_send_immediate_release(resource_); |
| } |
| // Protocol specifies that either of these events result in the buffer |
| // release object's destruction. |
| wl_client_flush(wl_resource_get_client(resource_)); |
| wl_resource_destroy(resource_); |
| } |
| |
| raw_ptr<wl_resource> resource_; |
| // Use a cancelable callback in case this object is destroyed while a commit |
| // is still in flight. |
| base::CancelableOnceCallback<void(gfx::GpuFenceHandle)> release_callback_; |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // linux_surface_synchronization_v1 interface: |
| |
| // Implements the surface synchronization interface, providing explicit |
| // synchronization for surface buffers using dma-fences. |
| class LinuxSurfaceSynchronization : public SurfaceObserver { |
| public: |
| explicit LinuxSurfaceSynchronization(wl_resource* resource, Surface* surface) |
| : surface_(surface) { |
| surface_->AddSurfaceObserver(this); |
| surface_->SetProperty(kSurfaceSynchronizationResource, resource); |
| } |
| |
| LinuxSurfaceSynchronization(const LinuxSurfaceSynchronization&) = delete; |
| LinuxSurfaceSynchronization& operator=(const LinuxSurfaceSynchronization&) = |
| delete; |
| |
| ~LinuxSurfaceSynchronization() override { |
| if (surface_) { |
| surface_->RemoveSurfaceObserver(this); |
| surface_->SetAcquireFence(nullptr); |
| surface_->SetProperty(kSurfaceSynchronizationResource, |
| static_cast<wl_resource*>(nullptr)); |
| } |
| } |
| |
| Surface* surface() { return surface_; } |
| |
| // Overridden from SurfaceObserver: |
| void OnSurfaceDestroying(Surface* surface) override { |
| surface->RemoveSurfaceObserver(this); |
| surface_ = nullptr; |
| } |
| |
| private: |
| raw_ptr<Surface> surface_; |
| }; |
| |
| void linux_surface_synchronization_destroy(struct wl_client* client, |
| struct wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| void linux_surface_synchronization_set_acquire_fence(wl_client* client, |
| wl_resource* resource, |
| int32_t fd) { |
| auto fence_fd = base::ScopedFD(fd); |
| auto* surface = |
| GetUserDataAs<LinuxSurfaceSynchronization>(resource)->surface(); |
| |
| if (!surface) { |
| wl_resource_post_error( |
| resource, ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_NO_SURFACE, |
| "Associated surface has been destroyed"); |
| return; |
| } |
| |
| if (surface->HasPendingAcquireFence()) { |
| wl_resource_post_error( |
| resource, ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_DUPLICATE_FENCE, |
| "surface already has an acquire fence"); |
| return; |
| } |
| |
| auto fence_info = |
| std::unique_ptr<sync_fence_info_data, void (*)(sync_fence_info_data*)>{ |
| sync_fence_info(fence_fd.get()), sync_fence_info_free}; |
| if (!fence_info) { |
| wl_resource_post_error( |
| resource, ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_INVALID_FENCE, |
| "the provided acquire fence is invalid"); |
| return; |
| } |
| |
| gfx::GpuFenceHandle handle; |
| if (fence_info->status != 1) { |
| // Not signalled yet. |
| handle.Adopt(std::move(fence_fd)); |
| } |
| surface->SetAcquireFence(std::make_unique<gfx::GpuFence>(std::move(handle))); |
| } |
| |
| void linux_surface_synchronization_get_release(wl_client* client, |
| wl_resource* resource, |
| uint32_t id) { |
| auto* surface = |
| GetUserDataAs<LinuxSurfaceSynchronization>(resource)->surface(); |
| |
| if (!surface) { |
| wl_resource_post_error( |
| resource, ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_NO_SURFACE, |
| "surface no longer exists"); |
| return; |
| } |
| |
| if (surface->HasPendingPerCommitBufferReleaseCallback()) { |
| wl_resource_post_error( |
| resource, ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_DUPLICATE_RELEASE, |
| "already has a buffer release"); |
| return; |
| } |
| |
| auto* linux_buffer_release_resource = |
| wl_resource_create(client, &zwp_linux_buffer_release_v1_interface, |
| wl_resource_get_version(resource), id); |
| |
| SetImplementation(linux_buffer_release_resource, nullptr, |
| std::make_unique<LinuxBufferRelease>( |
| linux_buffer_release_resource, surface)); |
| } |
| |
| const struct zwp_linux_surface_synchronization_v1_interface |
| linux_surface_synchronization_implementation = { |
| linux_surface_synchronization_destroy, |
| linux_surface_synchronization_set_acquire_fence, |
| linux_surface_synchronization_get_release, |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // linux_explicit_synchronization_v1 interface: |
| |
| void linux_explicit_synchronization_destroy(struct wl_client* client, |
| struct wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| void linux_explicit_synchronization_get_synchronization( |
| wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* surface_resource) { |
| Surface* surface = GetUserDataAs<Surface>(surface_resource); |
| if (surface->GetProperty(kSurfaceSynchronizationResource) != nullptr) { |
| wl_resource_post_error( |
| resource, |
| ZWP_LINUX_EXPLICIT_SYNCHRONIZATION_V1_ERROR_SYNCHRONIZATION_EXISTS, |
| "a synchronization object for the surface already exists"); |
| return; |
| } |
| |
| wl_resource* linux_surface_synchronization_resource = wl_resource_create( |
| client, &zwp_linux_surface_synchronization_v1_interface, |
| wl_resource_get_version(resource), id); |
| |
| SetImplementation(linux_surface_synchronization_resource, |
| &linux_surface_synchronization_implementation, |
| std::make_unique<LinuxSurfaceSynchronization>( |
| linux_surface_synchronization_resource, surface)); |
| } |
| |
| const struct zwp_linux_explicit_synchronization_v1_interface |
| linux_explicit_synchronization_implementation = { |
| linux_explicit_synchronization_destroy, |
| linux_explicit_synchronization_get_synchronization}; |
| |
| } // namespace |
| |
| void bind_linux_explicit_synchronization(wl_client* client, |
| void* data, |
| uint32_t version, |
| uint32_t id) { |
| wl_resource* resource = wl_resource_create( |
| client, &zwp_linux_explicit_synchronization_v1_interface, version, id); |
| |
| wl_resource_set_implementation(resource, |
| &linux_explicit_synchronization_implementation, |
| nullptr, nullptr); |
| } |
| |
| bool linux_surface_synchronization_validate_commit(Surface* surface) { |
| if (surface->HasPendingAttachedBuffer()) |
| return true; |
| |
| if (surface->HasPendingAcquireFence()) { |
| wl_resource* linux_surface_synchronization_resource = |
| surface->GetProperty(kSurfaceSynchronizationResource); |
| DCHECK(linux_surface_synchronization_resource); |
| |
| wl_resource_post_error( |
| linux_surface_synchronization_resource, |
| ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_NO_BUFFER, |
| "surface has acquire fence but no buffer for synchronization"); |
| |
| return false; |
| } |
| |
| if (surface->HasPendingPerCommitBufferReleaseCallback()) { |
| wl_resource* linux_surface_synchronization_resource = |
| surface->GetProperty(kSurfaceSynchronizationResource); |
| DCHECK(linux_surface_synchronization_resource); |
| |
| wl_resource_post_error( |
| linux_surface_synchronization_resource, |
| ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_NO_BUFFER, |
| "surface has buffer_release but no buffer for synchronization"); |
| |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace wayland |
| } // namespace exo |