Commit 4dc2dd06 authored by Maksim Sisov's avatar Maksim Sisov Committed by Commit Bot

[ozone/wayland] Add presentation feedback support.

Instead of blindly using a fake presentation feedback result,
use the wayland provided one if available. If not, pretend there is
a feedback, but provide a faked one.

In order to provide feedback, the callback, which comes from
the GPU, is stored in the Buffer instance. Although,
both frame and presentation feedback are setup, the frame callback
always comes first. Thus, we store the result and once the
feedback comes, we use the previous swap result and the presentation
feedback to let the GPU know the status of the request.

Bug: 859012
Change-Id: I4f4628be6101149daa30b6c74f3490254ace0fad
Reviewed-on: https://chromium-review.googlesource.com/1193883
Commit-Queue: Maksim Sisov <msisov@igalia.com>
Reviewed-by: default avatarRobert Kroeger <rjkroege@chromium.org>
Reviewed-by: default avatarTom Sepez <tsepez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#589467}
parent 2d854227
......@@ -85,6 +85,7 @@ source_set("wayland") {
"//third_party/minigbm",
"//third_party/wayland:wayland_client",
"//third_party/wayland-protocols:linux_dmabuf_protocol",
"//third_party/wayland-protocols:presentation_time_protocol",
"//third_party/wayland-protocols:xdg_shell_protocol",
"//ui/base",
"//ui/base:ui_features",
......
......@@ -220,12 +220,11 @@ void GbmSurfacelessWayland::FenceRetired(PendingFrame* frame) {
SubmitFrame();
}
void GbmSurfacelessWayland::OnScheduleBufferSwapDone(gfx::SwapResult result) {
void GbmSurfacelessWayland::OnScheduleBufferSwapDone(
gfx::SwapResult result,
const gfx::PresentationFeedback& feedback) {
OnSubmission(result, nullptr);
// TODO: presentation.
OnPresentation(
gfx::PresentationFeedback(base::TimeTicks::Now(), base::TimeDelta(),
gfx::PresentationFeedback::kZeroCopy));
OnPresentation(feedback);
planes_.clear();
}
......
......@@ -78,7 +78,8 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL {
EGLSyncKHR InsertFence(bool implicit);
void FenceRetired(PendingFrame* frame);
void OnScheduleBufferSwapDone(gfx::SwapResult result);
void OnScheduleBufferSwapDone(gfx::SwapResult result,
const gfx::PresentationFeedback& feedback);
void OnSubmission(gfx::SwapResult result,
std::unique_ptr<gfx::GpuFence> out_fence);
void OnPresentation(const gfx::PresentationFeedback& feedback);
......
......@@ -5,8 +5,8 @@
#include "ui/ozone/platform/wayland/wayland_buffer_manager.h"
#include <drm_fourcc.h>
#include <linux-dmabuf-unstable-v1-client-protocol.h>
#include <presentation-time-client-protocol.h>
#include "base/trace_event/trace_event.h"
#include "ui/ozone/common/linux/drm_util_linux.h"
......@@ -15,6 +15,33 @@
namespace ui {
namespace {
uint32_t GetPresentationKindFlags(uint32_t flags) {
uint32_t presentation_flags = 0;
if (flags & WP_PRESENTATION_FEEDBACK_KIND_VSYNC)
presentation_flags |= gfx::PresentationFeedback::kVSync;
if (flags & WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK)
presentation_flags |= gfx::PresentationFeedback::kHWClock;
if (flags & WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION)
presentation_flags |= gfx::PresentationFeedback::kHWCompletion;
if (flags & WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY)
presentation_flags |= gfx::PresentationFeedback::kZeroCopy;
return presentation_flags;
}
base::TimeTicks GetPresentationFeedbackTimeStamp(uint32_t tv_sec_hi,
uint32_t tv_sec_lo,
uint32_t tv_nsec) {
const int64_t seconds = (static_cast<int64_t>(tv_sec_hi) << 32) + tv_sec_lo;
const int64_t microseconds = seconds * base::Time::kMicrosecondsPerSecond +
tv_nsec / base::Time::kNanosecondsPerMicrosecond;
return base::TimeTicks() + base::TimeDelta::FromMicroseconds(microseconds);
}
} // namespace
WaylandBufferManager::Buffer::Buffer() = default;
WaylandBufferManager::Buffer::Buffer(uint32_t id,
zwp_linux_buffer_params_v1* zwp_params)
......@@ -132,8 +159,12 @@ bool WaylandBufferManager::DestroyBuffer(uint32_t buffer_id) {
// It can happen that a buffer is destroyed before a frame callback comes.
// Thus, just mark this as a successful swap, which is ok to do.
Buffer* buffer = it->second.get();
if (!buffer->buffer_swap_callback.is_null())
std::move(buffer->buffer_swap_callback).Run(gfx::SwapResult::SWAP_ACK);
if (!buffer->buffer_swap_callback.is_null()) {
std::move(buffer->buffer_swap_callback)
.Run(gfx::SwapResult::SWAP_ACK,
gfx::PresentationFeedback(base::TimeTicks::Now(),
base::TimeDelta(), 0));
}
buffers_.erase(it);
connection_->ScheduleFlush();
......@@ -168,6 +199,19 @@ bool WaylandBufferManager::SwapBuffer(Buffer* buffer) {
wl_callback_add_listener(buffer->wl_frame_callback.get(), &frame_listener,
this);
// Set up presentation feedback.
static const wp_presentation_feedback_listener feedback_listener = {
WaylandBufferManager::FeedbackSyncOutput,
WaylandBufferManager::FeedbackPresented,
WaylandBufferManager::FeedbackDiscarded};
if (connection_->presentation()) {
DCHECK(!buffer->wp_presentation_feedback);
buffer->wp_presentation_feedback.reset(wp_presentation_feedback(
connection_->presentation(), window->surface()));
wp_presentation_feedback_add_listener(
buffer->wp_presentation_feedback.get(), &feedback_listener, this);
}
wl_surface_commit(window->surface());
connection_->ScheduleFlush();
......@@ -268,6 +312,12 @@ void WaylandBufferManager::CreateSucceededInternal(
SwapBuffer(buffer);
}
void WaylandBufferManager::OnBufferSwapped(Buffer* buffer) {
DCHECK(!buffer->buffer_swap_callback.is_null());
std::move(buffer->buffer_swap_callback)
.Run(buffer->swap_result, std::move(buffer->feedback));
}
// static
void WaylandBufferManager::Modifiers(
void* data,
......@@ -318,8 +368,81 @@ void WaylandBufferManager::FrameCallbackDone(void* data,
for (auto& item : self->buffers_) {
Buffer* buffer = item.second.get();
if (buffer->wl_frame_callback.get() == callback) {
std::move(buffer->buffer_swap_callback).Run(gfx::SwapResult::SWAP_ACK);
buffer->swap_result = gfx::SwapResult::SWAP_ACK;
buffer->wl_frame_callback.reset();
// If presentation feedback is not supported, use fake feedback and
// trigger the callback.
if (!self->connection_->presentation()) {
buffer->feedback = gfx::PresentationFeedback(base::TimeTicks::Now(),
base::TimeDelta(), 0);
self->OnBufferSwapped(buffer);
}
return;
}
}
NOTREACHED();
}
// static
void WaylandBufferManager::FeedbackSyncOutput(
void* data,
struct wp_presentation_feedback* wp_presentation_feedback,
struct wl_output* output) {
NOTIMPLEMENTED_LOG_ONCE();
}
// static
void WaylandBufferManager::FeedbackPresented(
void* data,
struct wp_presentation_feedback* wp_presentation_feedback,
uint32_t tv_sec_hi,
uint32_t tv_sec_lo,
uint32_t tv_nsec,
uint32_t refresh,
uint32_t seq_hi,
uint32_t seq_lo,
uint32_t flags) {
WaylandBufferManager* self = static_cast<WaylandBufferManager*>(data);
DCHECK(self);
for (auto& item : self->buffers_) {
Buffer* buffer = item.second.get();
if (buffer->wp_presentation_feedback.get() == wp_presentation_feedback) {
// Frame callback must come before a feedback is presented.
DCHECK(!buffer->wl_frame_callback);
buffer->feedback = gfx::PresentationFeedback(
GetPresentationFeedbackTimeStamp(tv_sec_hi, tv_sec_lo, tv_nsec),
base::TimeDelta::FromNanoseconds(refresh),
GetPresentationKindFlags(flags));
buffer->wp_presentation_feedback.reset();
self->OnBufferSwapped(buffer);
return;
}
}
NOTREACHED();
}
// static
void WaylandBufferManager::FeedbackDiscarded(
void* data,
struct wp_presentation_feedback* wp_presentation_feedback) {
WaylandBufferManager* self = static_cast<WaylandBufferManager*>(data);
DCHECK(self);
for (auto& item : self->buffers_) {
Buffer* buffer = item.second.get();
if (buffer->wp_presentation_feedback.get() == wp_presentation_feedback) {
// Frame callback must come before a feedback is presented.
DCHECK(!buffer->wl_frame_callback);
buffer->feedback = gfx::PresentationFeedback::Failure();
buffer->wp_presentation_feedback.reset();
self->OnBufferSwapped(buffer);
return;
}
}
......
......@@ -12,11 +12,14 @@
#include "base/files/file.h"
#include "base/macros.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/presentation_feedback.h"
#include "ui/gfx/swap_result.h"
#include "ui/ozone/platform/wayland/wayland_object.h"
#include "ui/ozone/platform/wayland/wayland_util.h"
struct zwp_linux_dmabuf_v1;
struct zwp_linux_buffer_params_v1;
struct wp_presentation_feedback;
namespace gfx {
enum class BufferFormat;
......@@ -84,6 +87,13 @@ class WaylandBufferManager {
// Widget to attached/being attach WaylandWindow.
gfx::AcceleratedWidget widget = gfx::kNullAcceleratedWidget;
// A buffer swap result once the buffer is committed.
gfx::SwapResult swap_result;
// A feedback, which is received if a presentation feedback protocol is
// supported.
gfx::PresentationFeedback feedback;
// Params that are used to create a wl_buffer.
zwp_linux_buffer_params_v1* params = nullptr;
......@@ -99,6 +109,10 @@ class WaylandBufferManager {
// operation.
wl::Object<wl_callback> wl_frame_callback;
// A presentation feedback provided by the Wayland server once frame is
// shown.
wl::Object<wp_presentation_feedback> wp_presentation_feedback;
DISALLOW_COPY_AND_ASSIGN(Buffer);
};
......@@ -121,6 +135,8 @@ class WaylandBufferManager {
void CreateSucceededInternal(struct zwp_linux_buffer_params_v1* params,
struct wl_buffer* new_buffer);
void OnBufferSwapped(Buffer* buffer);
// zwp_linux_dmabuf_v1_listener
static void Modifiers(void* data,
struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf,
......@@ -143,6 +159,25 @@ class WaylandBufferManager {
wl_callback* callback,
uint32_t time);
// wp_presentation_feedback_listener
static void FeedbackSyncOutput(
void* data,
struct wp_presentation_feedback* wp_presentation_feedback,
struct wl_output* output);
static void FeedbackPresented(
void* data,
struct wp_presentation_feedback* wp_presentation_feedback,
uint32_t tv_sec_hi,
uint32_t tv_sec_lo,
uint32_t tv_nsec,
uint32_t refresh,
uint32_t seq_hi,
uint32_t seq_lo,
uint32_t flags);
static void FeedbackDiscarded(
void* data,
struct wp_presentation_feedback* wp_presentation_feedback);
// Stores announced buffer formats supported by the compositor.
std::vector<gfx::BufferFormat> supported_buffer_formats_;
......
......@@ -24,12 +24,13 @@ static_assert(XDG_SHELL_VERSION_CURRENT == 5, "Unsupported xdg-shell version");
namespace ui {
namespace {
const uint32_t kMaxCompositorVersion = 4;
const uint32_t kMaxLinuxDmabufVersion = 1;
const uint32_t kMaxSeatVersion = 4;
const uint32_t kMaxShmVersion = 1;
const uint32_t kMaxXdgShellVersion = 1;
const uint32_t kMaxDeviceManagerVersion = 3;
constexpr uint32_t kMaxCompositorVersion = 4;
constexpr uint32_t kMaxLinuxDmabufVersion = 1;
constexpr uint32_t kMaxSeatVersion = 4;
constexpr uint32_t kMaxShmVersion = 1;
constexpr uint32_t kMaxXdgShellVersion = 1;
constexpr uint32_t kMaxDeviceManagerVersion = 3;
constexpr uint32_t kMaxWpPresentationVersion = 1;
std::unique_ptr<WaylandDataSource> CreateWaylandDataSource(
WaylandDataDeviceManager* data_device_manager,
......@@ -439,6 +440,10 @@ void WaylandConnection::Global(void* data,
registry, name, std::min(version, kMaxLinuxDmabufVersion));
connection->buffer_manager_.reset(
new WaylandBufferManager(zwp_linux_dmabuf.release(), connection));
} else if (!connection->presentation_ &&
(strcmp(interface, "wp_presentation") == 0)) {
connection->presentation_ =
wl::Bind<wp_presentation>(registry, name, kMaxWpPresentationVersion);
}
connection->ScheduleFlush();
......
......@@ -74,6 +74,7 @@ class WaylandConnection : public PlatformEventSource,
zxdg_shell_v6* shell_v6() { return shell_v6_.get(); }
wl_seat* seat() { return seat_.get(); }
wl_data_device* data_device() { return data_device_->data_device(); }
wp_presentation* presentation() const { return presentation_.get(); }
WaylandWindow* GetWindow(gfx::AcceleratedWidget widget);
WaylandWindow* GetCurrentFocusedWindow();
......@@ -182,6 +183,7 @@ class WaylandConnection : public PlatformEventSource,
wl::Object<wl_shm> shm_;
wl::Object<xdg_shell> shell_;
wl::Object<zxdg_shell_v6> shell_v6_;
wl::Object<wp_presentation> presentation_;
std::unique_ptr<WaylandDataDeviceManager> data_device_manager_;
std::unique_ptr<WaylandDataDevice> data_device_;
......
......@@ -5,6 +5,7 @@
#include "ui/ozone/platform/wayland/wayland_object.h"
#include <linux-dmabuf-unstable-v1-client-protocol.h>
#include <presentation-time-client-protocol.h>
#include <wayland-client.h>
#include <xdg-shell-unstable-v5-client-protocol.h>
#include <xdg-shell-unstable-v6-client-protocol.h>
......@@ -126,6 +127,16 @@ void (*ObjectTraits<wl_subsurface>::deleter)(wl_subsurface*) =
const wl_interface* ObjectTraits<wl_touch>::interface = &wl_touch_interface;
void (*ObjectTraits<wl_touch>::deleter)(wl_touch*) = &delete_touch;
const wl_interface* ObjectTraits<wp_presentation>::interface =
&wp_presentation_interface;
void (*ObjectTraits<wp_presentation>::deleter)(wp_presentation*) =
&wp_presentation_destroy;
const wl_interface* ObjectTraits<struct wp_presentation_feedback>::interface =
&wp_presentation_feedback_interface;
void (*ObjectTraits<struct wp_presentation_feedback>::deleter)(
struct wp_presentation_feedback*) = &wp_presentation_feedback_destroy;
const wl_interface* ObjectTraits<xdg_shell>::interface = &xdg_shell_interface;
void (*ObjectTraits<xdg_shell>::deleter)(xdg_shell*) = &xdg_shell_destroy;
......
......@@ -27,6 +27,8 @@ struct wl_subcompositor;
struct wl_subsurface;
struct wl_surface;
struct wl_touch;
struct wp_presentation;
struct wp_presentation_feedback;
struct xdg_shell;
struct xdg_surface;
struct xdg_popup;
......@@ -156,6 +158,18 @@ struct ObjectTraits<wl_touch> {
static void (*deleter)(wl_touch*);
};
template <>
struct ObjectTraits<wp_presentation> {
static const wl_interface* interface;
static void (*deleter)(wp_presentation*);
};
template <>
struct ObjectTraits<wp_presentation_feedback> {
static const wl_interface* interface;
static void (*deleter)(wp_presentation_feedback*);
};
template <>
struct ObjectTraits<xdg_shell> {
static const wl_interface* interface;
......
......@@ -20,12 +20,14 @@ class SharedMemory;
namespace gfx {
class Size;
enum class SwapResult;
struct PresentationFeedback;
}
namespace wl {
// Corresponds to mojom::WaylandConnection::ScheduleBufferSwapCallback.
using BufferSwapCallback = base::OnceCallback<void(gfx::SwapResult)>;
using BufferSwapCallback =
base::OnceCallback<void(gfx::SwapResult, const gfx::PresentationFeedback&)>;
wl_buffer* CreateSHMBuffer(const gfx::Size& size,
base::SharedMemory* shared_memory,
......
......@@ -7,6 +7,7 @@ module ui.ozone.mojom;
import "mojo/public/mojom/base/file.mojom";
import "mojo/public/mojom/base/file_path.mojom";
import "ui/gfx/mojo/accelerated_widget.mojom";
import "ui/gfx/mojo/presentation_feedback.mojom";
import "ui/gfx/mojo/swap_result.mojom";
// Used by the GPU for communication with a WaylandConnection on the browser
......@@ -28,7 +29,8 @@ interface WaylandConnection {
// Swaps wl_buffers for a WaylandWindow with the following |widget|.
ScheduleBufferSwap(gfx.mojom.AcceleratedWidget widget, uint32 buffer_id)
=> (gfx.mojom.SwapResult swap_result);
=> (gfx.mojom.SwapResult swap_result,
gfx.mojom.PresentationFeedback feedback);
};
// Used by the browser process to provide the GPU process with a mojo ptr to a
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment