blob: d04bfa665894eab1d1e2ec7fb25541125f774343 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/exo/wayland/clients/simple.h"
#include <presentation-time-client-protocol.h>
#include "base/command_line.h"
#include "base/containers/circular_deque.h"
#include "base/stl_util.h"
#include "base/time/time.h"
#include "components/exo/wayland/clients/client_helper.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrContext.h"
#include "ui/gl/gl_bindings.h"
namespace exo {
namespace wayland {
namespace clients {
namespace {
void FrameCallback(void* data, wl_callback* callback, uint32_t time) {
bool* frame_callback_pending = static_cast<bool*>(data);
*frame_callback_pending = false;
}
struct Frame {
base::TimeTicks submit_time;
std::unique_ptr<struct wp_presentation_feedback> feedback;
};
struct Presentation {
base::circular_deque<Frame> submitted_frames;
Simple::PresentationFeedback feedback;
};
void FeedbackSyncOutput(void* data,
struct wp_presentation_feedback* feedback,
wl_output* output) {}
void FeedbackPresented(void* data,
struct wp_presentation_feedback* 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) {
Presentation* presentation = static_cast<Presentation*>(data);
DCHECK_GT(presentation->submitted_frames.size(), 0u);
Frame& frame = presentation->submitted_frames.front();
DCHECK_EQ(frame.feedback.get(), feedback);
int64_t seconds = (static_cast<int64_t>(tv_sec_hi) << 32) + tv_sec_lo;
int64_t microseconds = seconds * base::Time::kMicrosecondsPerSecond +
tv_nsec / base::Time::kNanosecondsPerMicrosecond;
base::TimeTicks presentation_time =
base::TimeTicks() + base::TimeDelta::FromMicroseconds(microseconds);
presentation->feedback.total_presentation_latency +=
presentation_time - frame.submit_time;
++presentation->feedback.num_frames_presented;
presentation->submitted_frames.pop_front();
}
void FeedbackDiscarded(void* data, struct wp_presentation_feedback* feedback) {
Presentation* presentation = static_cast<Presentation*>(data);
DCHECK_GT(presentation->submitted_frames.size(), 0u);
auto it = std::find_if(
presentation->submitted_frames.begin(),
presentation->submitted_frames.end(),
[feedback](Frame& frame) { return frame.feedback.get() == feedback; });
DCHECK(it != presentation->submitted_frames.end());
presentation->submitted_frames.erase(it);
}
} // namespace
Simple::Simple() = default;
void Simple::Run(int frames, PresentationFeedback* feedback) {
wl_callback_listener frame_listener = {FrameCallback};
wp_presentation_feedback_listener feedback_listener = {
FeedbackSyncOutput, FeedbackPresented, FeedbackDiscarded};
Presentation presentation;
int frame_count = 0;
std::unique_ptr<wl_callback> frame_callback;
bool frame_callback_pending = false;
do {
if (frame_callback_pending)
continue;
if (frame_count == frames)
break;
Buffer* buffer = DequeueBuffer();
if (!buffer)
continue;
SkCanvas* canvas = buffer->sk_surface->getCanvas();
static const SkColor kColors[] = {SK_ColorRED, SK_ColorBLACK};
canvas->clear(kColors[++frame_count % base::size(kColors)]);
if (gr_context_) {
gr_context_->flush();
glFinish();
}
wl_surface_set_buffer_scale(surface_.get(), scale_);
wl_surface_set_buffer_transform(surface_.get(), transform_);
wl_surface_damage(surface_.get(), 0, 0, surface_size_.width(),
surface_size_.height());
wl_surface_attach(surface_.get(), buffer->buffer.get(), 0, 0);
// Set up the frame callback.
frame_callback_pending = true;
frame_callback.reset(wl_surface_frame(surface_.get()));
wl_callback_add_listener(frame_callback.get(), &frame_listener,
&frame_callback_pending);
// Set up presentation feedback.
Frame frame;
frame.feedback.reset(
wp_presentation_feedback(globals_.presentation.get(), surface_.get()));
wp_presentation_feedback_add_listener(frame.feedback.get(),
&feedback_listener, &presentation);
frame.submit_time = base::TimeTicks::Now();
presentation.submitted_frames.push_back(std::move(frame));
wl_surface_commit(surface_.get());
wl_display_flush(display_.get());
} while (wl_display_dispatch(display_.get()) != -1);
if (feedback)
*feedback = presentation.feedback;
}
} // namespace clients
} // namespace wayland
} // namespace exo