1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
|
// 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, ExperimentalAsh> 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, ExperimentalAsh> 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;
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
|