1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/exo/wayland/clients/simple.h"
6 
7 #include <presentation-time-client-protocol.h>
8 #include <iostream>
9 
10 #include "base/command_line.h"
11 #include "base/containers/circular_deque.h"
12 #include "base/stl_util.h"
13 #include "base/time/time.h"
14 #include "components/exo/wayland/clients/client_helper.h"
15 #include "third_party/skia/include/core/SkCanvas.h"
16 #include "third_party/skia/include/core/SkSurface.h"
17 #include "third_party/skia/include/gpu/GrContext.h"
18 #include "ui/gl/gl_bindings.h"
19 
20 namespace exo {
21 namespace wayland {
22 namespace clients {
23 namespace {
24 
FrameCallback(void * data,wl_callback * callback,uint32_t time)25 void FrameCallback(void* data, wl_callback* callback, uint32_t time) {
26   bool* frame_callback_pending = static_cast<bool*>(data);
27   *frame_callback_pending = false;
28 }
29 
30 struct Frame {
31   base::TimeTicks submit_time;
32   std::unique_ptr<struct wp_presentation_feedback> feedback;
33 };
34 
35 struct Presentation {
36   base::circular_deque<Frame> submitted_frames;
37   Simple::PresentationFeedback feedback;
38 };
39 
FeedbackSyncOutput(void * data,struct wp_presentation_feedback * feedback,wl_output * output)40 void FeedbackSyncOutput(void* data,
41                         struct wp_presentation_feedback* feedback,
42                         wl_output* output) {}
43 
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)44 void FeedbackPresented(void* data,
45                        struct wp_presentation_feedback* feedback,
46                        uint32_t tv_sec_hi,
47                        uint32_t tv_sec_lo,
48                        uint32_t tv_nsec,
49                        uint32_t refresh,
50                        uint32_t seq_hi,
51                        uint32_t seq_lo,
52                        uint32_t flags) {
53   Presentation* presentation = static_cast<Presentation*>(data);
54   DCHECK_GT(presentation->submitted_frames.size(), 0u);
55 
56   Frame& frame = presentation->submitted_frames.front();
57   DCHECK_EQ(frame.feedback.get(), feedback);
58 
59   int64_t seconds = (static_cast<int64_t>(tv_sec_hi) << 32) + tv_sec_lo;
60   int64_t microseconds = seconds * base::Time::kMicrosecondsPerSecond +
61                          tv_nsec / base::Time::kNanosecondsPerMicrosecond;
62   base::TimeTicks presentation_time =
63       base::TimeTicks() + base::TimeDelta::FromMicroseconds(microseconds);
64   presentation->feedback.total_presentation_latency +=
65       presentation_time - frame.submit_time;
66   ++presentation->feedback.num_frames_presented;
67   presentation->submitted_frames.pop_front();
68 }
69 
FeedbackDiscarded(void * data,struct wp_presentation_feedback * feedback)70 void FeedbackDiscarded(void* data, struct wp_presentation_feedback* feedback) {
71   Presentation* presentation = static_cast<Presentation*>(data);
72   DCHECK_GT(presentation->submitted_frames.size(), 0u);
73   auto it = std::find_if(
74       presentation->submitted_frames.begin(),
75       presentation->submitted_frames.end(),
76       [feedback](Frame& frame) { return frame.feedback.get() == feedback; });
77   DCHECK(it != presentation->submitted_frames.end());
78   presentation->submitted_frames.erase(it);
79 }
80 
VSyncTimingUpdate(void * data,struct zcr_vsync_timing_v1 * zcr_vsync_timing_v1,uint32_t timebase_l,uint32_t timebase_h,uint32_t interval_l,uint32_t interval_h)81 void VSyncTimingUpdate(void* data,
82                        struct zcr_vsync_timing_v1* zcr_vsync_timing_v1,
83                        uint32_t timebase_l,
84                        uint32_t timebase_h,
85                        uint32_t interval_l,
86                        uint32_t interval_h) {
87   uint64_t timebase = static_cast<uint64_t>(timebase_h) << 32 | timebase_l;
88   uint64_t interval = static_cast<uint64_t>(interval_h) << 32 | interval_l;
89   std::cout << "Received new VSyncTimingUpdate. Timebase: " << timebase
90             << ". Interval: " << interval << std::endl;
91 }
92 
93 }  // namespace
94 
95 Simple::Simple() = default;
96 
Run(int frames,const bool log_vsync_timing_updates,PresentationFeedback * feedback)97 void Simple::Run(int frames,
98                  const bool log_vsync_timing_updates,
99                  PresentationFeedback* feedback) {
100   wl_callback_listener frame_listener = {FrameCallback};
101   wp_presentation_feedback_listener feedback_listener = {
102       FeedbackSyncOutput, FeedbackPresented, FeedbackDiscarded};
103 
104   std::unique_ptr<zcr_vsync_timing_v1> vsync_timing;
105   if (log_vsync_timing_updates) {
106     if (globals_.vsync_feedback) {
107       vsync_timing.reset(zcr_vsync_feedback_v1_get_vsync_timing(
108           globals_.vsync_feedback.get(), globals_.output.get()));
109       DCHECK(vsync_timing);
110       static zcr_vsync_timing_v1_listener vsync_timing_listener = {
111           VSyncTimingUpdate};
112       zcr_vsync_timing_v1_add_listener(vsync_timing.get(),
113                                        &vsync_timing_listener, this);
114     } else {
115       LOG(WARNING)
116           << "VSync timing updates requested but zcr_vsync_feedback_v1 "
117              "protocol is not available on the server.";
118     }
119   }
120 
121   Presentation presentation;
122   int frame_count = 0;
123 
124   std::unique_ptr<wl_callback> frame_callback;
125   bool frame_callback_pending = false;
126   do {
127     if (frame_callback_pending)
128       continue;
129 
130     if (frame_count == frames)
131       break;
132 
133     Buffer* buffer = DequeueBuffer();
134     if (!buffer)
135       continue;
136 
137     SkCanvas* canvas = buffer->sk_surface->getCanvas();
138 
139     static const SkColor kColors[] = {SK_ColorRED, SK_ColorBLACK};
140     canvas->clear(kColors[++frame_count % base::size(kColors)]);
141 
142     if (gr_context_) {
143       gr_context_->flush();
144       glFinish();
145     }
146 
147     wl_surface_set_buffer_scale(surface_.get(), scale_);
148     wl_surface_set_buffer_transform(surface_.get(), transform_);
149     wl_surface_damage(surface_.get(), 0, 0, surface_size_.width(),
150                       surface_size_.height());
151     wl_surface_attach(surface_.get(), buffer->buffer.get(), 0, 0);
152 
153     // Set up the frame callback.
154     frame_callback_pending = true;
155     frame_callback.reset(wl_surface_frame(surface_.get()));
156     wl_callback_add_listener(frame_callback.get(), &frame_listener,
157                              &frame_callback_pending);
158 
159     // Set up presentation feedback.
160     Frame frame;
161     frame.feedback.reset(
162         wp_presentation_feedback(globals_.presentation.get(), surface_.get()));
163     wp_presentation_feedback_add_listener(frame.feedback.get(),
164                                           &feedback_listener, &presentation);
165     frame.submit_time = base::TimeTicks::Now();
166     presentation.submitted_frames.push_back(std::move(frame));
167 
168     wl_surface_commit(surface_.get());
169     wl_display_flush(display_.get());
170   } while (wl_display_dispatch(display_.get()) != -1);
171 
172   if (feedback)
173     *feedback = presentation.feedback;
174 }
175 
176 }  // namespace clients
177 }  // namespace wayland
178 }  // namespace exo
179