1 // Copyright 2014 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.
5 #include "ui/accelerated_widget_mac/display_link_mac.h"
7 #include "base/logging.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/trace_event/trace_event.h"
14 struct ScopedTypeRefTraits
<CVDisplayLinkRef
> {
15 static void Retain(CVDisplayLinkRef object
) {
16 CVDisplayLinkRetain(object
);
18 static void Release(CVDisplayLinkRef object
) {
19 CVDisplayLinkRelease(object
);
28 scoped_refptr
<DisplayLinkMac
> DisplayLinkMac::GetForDisplay(
29 CGDirectDisplayID display_id
) {
33 // Return the existing display link for this display, if it exists.
34 DisplayMap::iterator found
= display_map_
.Get().find(display_id
);
35 if (found
!= display_map_
.Get().end()) {
39 CVReturn ret
= kCVReturnSuccess
;
41 base::ScopedTypeRef
<CVDisplayLinkRef
> display_link
;
42 ret
= CVDisplayLinkCreateWithCGDisplay(
44 display_link
.InitializeInto());
45 if (ret
!= kCVReturnSuccess
) {
46 LOG(ERROR
) << "CVDisplayLinkCreateWithActiveCGDisplays failed: " << ret
;
50 scoped_refptr
<DisplayLinkMac
> display_link_mac
;
51 display_link_mac
= new DisplayLinkMac(display_id
, display_link
);
52 ret
= CVDisplayLinkSetOutputCallback(
53 display_link_mac
->display_link_
,
55 display_link_mac
.get());
56 if (ret
!= kCVReturnSuccess
) {
57 LOG(ERROR
) << "CVDisplayLinkSetOutputCallback failed: " << ret
;
61 return display_link_mac
;
64 DisplayLinkMac::DisplayLinkMac(
65 CGDirectDisplayID display_id
,
66 base::ScopedTypeRef
<CVDisplayLinkRef
> display_link
)
67 : main_thread_task_runner_(
68 base::MessageLoop::current()->task_runner()),
69 display_id_(display_id
),
70 display_link_(display_link
),
71 timebase_and_interval_valid_(false) {
72 DCHECK(display_map_
.Get().find(display_id
) == display_map_
.Get().end());
73 if (display_map_
.Get().empty()) {
74 CGError register_error
= CGDisplayRegisterReconfigurationCallback(
75 DisplayReconfigurationCallBack
, nullptr);
76 DPLOG_IF(ERROR
, register_error
!= kCGErrorSuccess
)
77 << "CGDisplayRegisterReconfigurationCallback: "
80 display_map_
.Get().insert(std::make_pair(display_id_
, this));
83 DisplayLinkMac::~DisplayLinkMac() {
86 DisplayMap::iterator found
= display_map_
.Get().find(display_id_
);
87 DCHECK(found
!= display_map_
.Get().end());
88 DCHECK(found
->second
== this);
89 display_map_
.Get().erase(found
);
90 if (display_map_
.Get().empty()) {
91 CGError remove_error
= CGDisplayRemoveReconfigurationCallback(
92 DisplayReconfigurationCallBack
, nullptr);
93 DPLOG_IF(ERROR
, remove_error
!= kCGErrorSuccess
)
94 << "CGDisplayRemoveReconfigurationCallback: "
99 bool DisplayLinkMac::GetVSyncParameters(
100 base::TimeTicks
* timebase
, base::TimeDelta
* interval
) {
101 if (!timebase_and_interval_valid_
) {
102 StartOrContinueDisplayLink();
106 *timebase
= timebase_
;
107 *interval
= interval_
;
111 base::TimeTicks
DisplayLinkMac::GetNextVSyncTimeAfter(
112 const base::TimeTicks
& from
, double interval_fraction
) {
113 if (!timebase_and_interval_valid_
) {
114 StartOrContinueDisplayLink();
118 // Compute the previous vsync time.
119 base::TimeTicks previous_vsync
=
120 interval_
* ((from
- timebase_remainder_
) / interval_
) +
123 // Return |interval_fraction| through the next vsync.
124 return previous_vsync
+ (1 + interval_fraction
) * interval_
;
127 void DisplayLinkMac::Tick(const CVTimeStamp
& cv_time
) {
128 TRACE_EVENT0("ui", "DisplayLinkMac::Tick");
130 // Verify that videoRefreshPeriod is 32 bits.
131 DCHECK((cv_time
.videoRefreshPeriod
& ~0xffffFFFFull
) == 0ull);
133 // Verify that the numerator and denominator make some sense.
134 uint32 numerator
= static_cast<uint32
>(cv_time
.videoRefreshPeriod
);
135 uint32 denominator
= cv_time
.videoTimeScale
;
136 if (numerator
<= 0 || denominator
<= 0) {
137 LOG(WARNING
) << "Unexpected numerator or denominator, bailing.";
141 timebase_
= base::TimeTicks::FromInternalValue(
142 cv_time
.hostTime
/ 1000);
143 interval_
= base::TimeDelta::FromMicroseconds(
144 1000000 * static_cast<int64
>(numerator
) / denominator
);
145 // Compute |timebase_remainder_| to be the first vsync after time zero.
146 timebase_remainder_
=
147 timebase_
- interval_
* ((timebase_
- base::TimeTicks()) / interval_
);
148 timebase_and_interval_valid_
= true;
153 void DisplayLinkMac::StartOrContinueDisplayLink() {
154 if (CVDisplayLinkIsRunning(display_link_
))
157 CVReturn ret
= CVDisplayLinkStart(display_link_
);
158 if (ret
!= kCVReturnSuccess
) {
159 LOG(ERROR
) << "CVDisplayLinkStart failed: " << ret
;
163 void DisplayLinkMac::StopDisplayLink() {
164 if (!CVDisplayLinkIsRunning(display_link_
))
167 CVReturn ret
= CVDisplayLinkStop(display_link_
);
168 if (ret
!= kCVReturnSuccess
) {
169 LOG(ERROR
) << "CVDisplayLinkStop failed: " << ret
;
174 CVReturn
DisplayLinkMac::DisplayLinkCallback(
175 CVDisplayLinkRef display_link
,
176 const CVTimeStamp
* now
,
177 const CVTimeStamp
* output_time
,
178 CVOptionFlags flags_in
,
179 CVOptionFlags
* flags_out
,
181 TRACE_EVENT0("ui", "DisplayLinkMac::DisplayLinkCallback");
182 DisplayLinkMac
* display_link_mac
= static_cast<DisplayLinkMac
*>(context
);
183 display_link_mac
->main_thread_task_runner_
->PostTask(
185 base::Bind(&DisplayLinkMac::Tick
, display_link_mac
, *output_time
));
186 return kCVReturnSuccess
;
190 void DisplayLinkMac::DisplayReconfigurationCallBack(
191 CGDirectDisplayID display
,
192 CGDisplayChangeSummaryFlags flags
,
194 DisplayMap::iterator found
= display_map_
.Get().find(display
);
195 if (found
== display_map_
.Get().end())
197 DisplayLinkMac
* display_link_mac
= found
->second
;
198 display_link_mac
->timebase_and_interval_valid_
= false;
202 base::LazyInstance
<DisplayLinkMac::DisplayMap
>
203 DisplayLinkMac::display_map_
= LAZY_INSTANCE_INITIALIZER
;