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 "content/browser/renderer_host/display_link_mac.h"
7 #include "base/logging.h"
8 #include "base/trace_event/trace_event.h"
9 #include "content/browser/renderer_host/render_widget_resize_helper.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
) {
30 // Return the existing display link for this display, if it exists.
31 DisplayMap::iterator found
= display_map_
.Get().find(display_id
);
32 if (found
!= display_map_
.Get().end()) {
36 CVReturn ret
= kCVReturnSuccess
;
38 base::ScopedTypeRef
<CVDisplayLinkRef
> display_link
;
39 ret
= CVDisplayLinkCreateWithCGDisplay(
41 display_link
.InitializeInto());
42 if (ret
!= kCVReturnSuccess
) {
43 LOG(ERROR
) << "CVDisplayLinkCreateWithActiveCGDisplays failed: " << ret
;
47 scoped_refptr
<DisplayLinkMac
> display_link_mac
;
48 display_link_mac
= new DisplayLinkMac(display_id
, display_link
);
50 ret
= CVDisplayLinkSetOutputCallback(
51 display_link_mac
->display_link_
,
53 display_link_mac
.get());
54 if (ret
!= kCVReturnSuccess
) {
55 LOG(ERROR
) << "CVDisplayLinkSetOutputCallback failed: " << ret
;
59 return display_link_mac
;
62 DisplayLinkMac::DisplayLinkMac(
63 CGDirectDisplayID display_id
,
64 base::ScopedTypeRef
<CVDisplayLinkRef
> display_link
)
65 : display_id_(display_id
),
66 display_link_(display_link
),
67 timebase_and_interval_valid_(false) {
68 DCHECK(display_map_
.Get().find(display_id
) == display_map_
.Get().end());
69 if (display_map_
.Get().empty()) {
70 CGError register_error
= CGDisplayRegisterReconfigurationCallback(
71 DisplayReconfigurationCallBack
, nullptr);
72 DPLOG_IF(ERROR
, register_error
!= kCGErrorSuccess
)
73 << "CGDisplayRegisterReconfigurationCallback: "
76 display_map_
.Get().insert(std::make_pair(display_id_
, this));
79 DisplayLinkMac::~DisplayLinkMac() {
82 DisplayMap::iterator found
= display_map_
.Get().find(display_id_
);
83 DCHECK(found
!= display_map_
.Get().end());
84 DCHECK(found
->second
== this);
85 display_map_
.Get().erase(found
);
86 if (display_map_
.Get().empty()) {
87 CGError remove_error
= CGDisplayRemoveReconfigurationCallback(
88 DisplayReconfigurationCallBack
, nullptr);
89 DPLOG_IF(ERROR
, remove_error
!= kCGErrorSuccess
)
90 << "CGDisplayRemoveReconfigurationCallback: "
95 bool DisplayLinkMac::GetVSyncParameters(
96 base::TimeTicks
* timebase
, base::TimeDelta
* interval
) {
97 if (!timebase_and_interval_valid_
) {
98 StartOrContinueDisplayLink();
102 *timebase
= timebase_
;
103 *interval
= interval_
;
107 void DisplayLinkMac::Tick(const CVTimeStamp
& cv_time
) {
108 TRACE_EVENT0("browser", "DisplayLinkMac::Tick");
110 // Verify that videoRefreshPeriod is 32 bits.
111 DCHECK((cv_time
.videoRefreshPeriod
& ~0xffffFFFFull
) == 0ull);
113 // Verify that the numerator and denominator make some sense.
114 uint32 numerator
= static_cast<uint32
>(cv_time
.videoRefreshPeriod
);
115 uint32 denominator
= cv_time
.videoTimeScale
;
116 if (numerator
<= 0 || denominator
<= 0) {
117 LOG(WARNING
) << "Unexpected numerator or denominator, bailing.";
121 timebase_
= base::TimeTicks::FromInternalValue(
122 cv_time
.hostTime
/ 1000);
123 interval_
= base::TimeDelta::FromMicroseconds(
124 1000000 * static_cast<int64
>(numerator
) / denominator
);
125 timebase_and_interval_valid_
= true;
130 void DisplayLinkMac::StartOrContinueDisplayLink() {
131 if (CVDisplayLinkIsRunning(display_link_
))
134 CVReturn ret
= CVDisplayLinkStart(display_link_
);
135 if (ret
!= kCVReturnSuccess
) {
136 LOG(ERROR
) << "CVDisplayLinkStart failed: " << ret
;
140 void DisplayLinkMac::StopDisplayLink() {
141 if (!CVDisplayLinkIsRunning(display_link_
))
144 CVReturn ret
= CVDisplayLinkStop(display_link_
);
145 if (ret
!= kCVReturnSuccess
) {
146 LOG(ERROR
) << "CVDisplayLinkStop failed: " << ret
;
151 CVReturn
DisplayLinkMac::DisplayLinkCallback(
152 CVDisplayLinkRef display_link
,
153 const CVTimeStamp
* now
,
154 const CVTimeStamp
* output_time
,
155 CVOptionFlags flags_in
,
156 CVOptionFlags
* flags_out
,
158 TRACE_EVENT0("browser", "DisplayLinkMac::DisplayLinkCallback");
159 DisplayLinkMac
* display_link_mac
= static_cast<DisplayLinkMac
*>(context
);
160 RenderWidgetResizeHelper::Get()->task_runner()->PostTask(
162 base::Bind(&DisplayLinkMac::Tick
, display_link_mac
, *output_time
));
163 return kCVReturnSuccess
;
167 void DisplayLinkMac::DisplayReconfigurationCallBack(
168 CGDirectDisplayID display
,
169 CGDisplayChangeSummaryFlags flags
,
171 DisplayMap::iterator found
= display_map_
.Get().find(display
);
172 if (found
== display_map_
.Get().end())
174 DisplayLinkMac
* display_link_mac
= found
->second
;
175 display_link_mac
->timebase_and_interval_valid_
= false;
179 base::LazyInstance
<DisplayLinkMac::DisplayMap
>
180 DisplayLinkMac::display_map_
= LAZY_INSTANCE_INITIALIZER
;