blob: 1945d2bcabae374891df90e0339080160485e170 [file] [log] [blame]
[email protected]f5fca212014-02-05 18:23:001// 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.
4
ccameron9913d6462015-06-08 22:07:575#include "ui/accelerated_widget_mac/display_link_mac.h"
[email protected]f5fca212014-02-05 18:23:006
[email protected]f5fca212014-02-05 18:23:007#include "base/logging.h"
ccameronefa5e1b2015-06-09 18:47:028#include "base/message_loop/message_loop.h"
ssid3e765612015-01-28 04:03:429#include "base/trace_event/trace_event.h"
[email protected]f5fca212014-02-05 18:23:0010
[email protected]3d09c4132014-02-11 14:24:4011namespace base {
12
13template<>
14struct ScopedTypeRefTraits<CVDisplayLinkRef> {
15 static void Retain(CVDisplayLinkRef object) {
16 CVDisplayLinkRetain(object);
17 }
18 static void Release(CVDisplayLinkRef object) {
19 CVDisplayLinkRelease(object);
20 }
21};
22
23} // namespace base
24
ccameron9913d6462015-06-08 22:07:5725namespace ui {
[email protected]f5fca212014-02-05 18:23:0026
27// static
[email protected]c6d21652014-03-04 08:23:1928scoped_refptr<DisplayLinkMac> DisplayLinkMac::GetForDisplay(
29 CGDirectDisplayID display_id) {
ccameron01ae4cbc2015-08-10 10:44:1630 if (!display_id)
31 return nullptr;
32
[email protected]c6d21652014-03-04 08:23:1933 // 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()) {
36 return found->second;
37 }
38
[email protected]f5fca212014-02-05 18:23:0039 CVReturn ret = kCVReturnSuccess;
40
[email protected]3d09c4132014-02-11 14:24:4041 base::ScopedTypeRef<CVDisplayLinkRef> display_link;
[email protected]c6d21652014-03-04 08:23:1942 ret = CVDisplayLinkCreateWithCGDisplay(
43 display_id,
44 display_link.InitializeInto());
[email protected]3d09c4132014-02-11 14:24:4045 if (ret != kCVReturnSuccess) {
46 LOG(ERROR) << "CVDisplayLinkCreateWithActiveCGDisplays failed: " << ret;
47 return NULL;
[email protected]f5fca212014-02-05 18:23:0048 }
49
[email protected]3d09c4132014-02-11 14:24:4050 scoped_refptr<DisplayLinkMac> display_link_mac;
ccameronefa5e1b2015-06-09 18:47:0251 display_link_mac = new DisplayLinkMac(display_id, display_link);
[email protected]f5fca212014-02-05 18:23:0052 ret = CVDisplayLinkSetOutputCallback(
53 display_link_mac->display_link_,
54 &DisplayLinkCallback,
55 display_link_mac.get());
56 if (ret != kCVReturnSuccess) {
57 LOG(ERROR) << "CVDisplayLinkSetOutputCallback failed: " << ret;
58 return NULL;
59 }
60
61 return display_link_mac;
62}
63
[email protected]3d09c4132014-02-11 14:24:4064DisplayLinkMac::DisplayLinkMac(
[email protected]c6d21652014-03-04 08:23:1965 CGDirectDisplayID display_id,
[email protected]3d09c4132014-02-11 14:24:4066 base::ScopedTypeRef<CVDisplayLinkRef> display_link)
ccameronefa5e1b2015-06-09 18:47:0267 : main_thread_task_runner_(
68 base::MessageLoop::current()->task_runner()),
ccameron9913d6462015-06-08 22:07:5769 display_id_(display_id),
[email protected]c6d21652014-03-04 08:23:1970 display_link_(display_link),
[email protected]3d09c4132014-02-11 14:24:4071 timebase_and_interval_valid_(false) {
[email protected]c6d21652014-03-04 08:23:1972 DCHECK(display_map_.Get().find(display_id) == display_map_.Get().end());
ccameron26d78d92015-04-01 17:33:5273 if (display_map_.Get().empty()) {
74 CGError register_error = CGDisplayRegisterReconfigurationCallback(
75 DisplayReconfigurationCallBack, nullptr);
76 DPLOG_IF(ERROR, register_error != kCGErrorSuccess)
77 << "CGDisplayRegisterReconfigurationCallback: "
78 << register_error;
79 }
[email protected]c6d21652014-03-04 08:23:1980 display_map_.Get().insert(std::make_pair(display_id_, this));
[email protected]f5fca212014-02-05 18:23:0081}
82
83DisplayLinkMac::~DisplayLinkMac() {
ccameron26d78d92015-04-01 17:33:5284 StopDisplayLink();
[email protected]c6d21652014-03-04 08:23:1985
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);
ccameron26d78d92015-04-01 17:33:5290 if (display_map_.Get().empty()) {
91 CGError remove_error = CGDisplayRemoveReconfigurationCallback(
92 DisplayReconfigurationCallBack, nullptr);
93 DPLOG_IF(ERROR, remove_error != kCGErrorSuccess)
94 << "CGDisplayRemoveReconfigurationCallback: "
95 << remove_error;
96 }
[email protected]f5fca212014-02-05 18:23:0097}
98
99bool DisplayLinkMac::GetVSyncParameters(
100 base::TimeTicks* timebase, base::TimeDelta* interval) {
ccameron26d78d92015-04-01 17:33:52101 if (!timebase_and_interval_valid_) {
102 StartOrContinueDisplayLink();
[email protected]f5fca212014-02-05 18:23:00103 return false;
ccameron26d78d92015-04-01 17:33:52104 }
[email protected]f5fca212014-02-05 18:23:00105
106 *timebase = timebase_;
107 *interval = interval_;
108 return true;
109}
110
ccameronc8429f22015-08-17 20:02:41111void DisplayLinkMac::NotifyCurrentTime(const base::TimeTicks& now) {
112 if (now >= recalculate_time_)
113 StartOrContinueDisplayLink();
114}
115
ccameron26d78d92015-04-01 17:33:52116void DisplayLinkMac::Tick(const CVTimeStamp& cv_time) {
ccameron9913d6462015-06-08 22:07:57117 TRACE_EVENT0("ui", "DisplayLinkMac::Tick");
[email protected]f5fca212014-02-05 18:23:00118
119 // Verify that videoRefreshPeriod is 32 bits.
ccameron26d78d92015-04-01 17:33:52120 DCHECK((cv_time.videoRefreshPeriod & ~0xffffFFFFull) == 0ull);
[email protected]f5fca212014-02-05 18:23:00121
122 // Verify that the numerator and denominator make some sense.
ccameron26d78d92015-04-01 17:33:52123 uint32 numerator = static_cast<uint32>(cv_time.videoRefreshPeriod);
124 uint32 denominator = cv_time.videoTimeScale;
[email protected]f5fca212014-02-05 18:23:00125 if (numerator <= 0 || denominator <= 0) {
126 LOG(WARNING) << "Unexpected numerator or denominator, bailing.";
127 return;
128 }
129
130 timebase_ = base::TimeTicks::FromInternalValue(
ccameron26d78d92015-04-01 17:33:52131 cv_time.hostTime / 1000);
[email protected]f5fca212014-02-05 18:23:00132 interval_ = base::TimeDelta::FromMicroseconds(
133 1000000 * static_cast<int64>(numerator) / denominator);
134 timebase_and_interval_valid_ = true;
ccameron26d78d92015-04-01 17:33:52135
ccameronc8429f22015-08-17 20:02:41136 // Don't restart the display link for 10 seconds.
137 recalculate_time_ = base::TimeTicks::Now() +
138 base::TimeDelta::FromSeconds(10);
ccameron26d78d92015-04-01 17:33:52139 StopDisplayLink();
[email protected]f5fca212014-02-05 18:23:00140}
141
142void DisplayLinkMac::StartOrContinueDisplayLink() {
[email protected]f5fca212014-02-05 18:23:00143 if (CVDisplayLinkIsRunning(display_link_))
144 return;
145
146 CVReturn ret = CVDisplayLinkStart(display_link_);
147 if (ret != kCVReturnSuccess) {
148 LOG(ERROR) << "CVDisplayLinkStart failed: " << ret;
149 }
150}
151
152void DisplayLinkMac::StopDisplayLink() {
153 if (!CVDisplayLinkIsRunning(display_link_))
154 return;
155
156 CVReturn ret = CVDisplayLinkStop(display_link_);
157 if (ret != kCVReturnSuccess) {
158 LOG(ERROR) << "CVDisplayLinkStop failed: " << ret;
159 }
160}
161
ccameron26d78d92015-04-01 17:33:52162// static
[email protected]f5fca212014-02-05 18:23:00163CVReturn DisplayLinkMac::DisplayLinkCallback(
164 CVDisplayLinkRef display_link,
165 const CVTimeStamp* now,
166 const CVTimeStamp* output_time,
167 CVOptionFlags flags_in,
168 CVOptionFlags* flags_out,
169 void* context) {
ccameron9913d6462015-06-08 22:07:57170 TRACE_EVENT0("ui", "DisplayLinkMac::DisplayLinkCallback");
[email protected]f5fca212014-02-05 18:23:00171 DisplayLinkMac* display_link_mac = static_cast<DisplayLinkMac*>(context);
ccameron9913d6462015-06-08 22:07:57172 display_link_mac->main_thread_task_runner_->PostTask(
ccameron26d78d92015-04-01 17:33:52173 FROM_HERE,
174 base::Bind(&DisplayLinkMac::Tick, display_link_mac, *output_time));
[email protected]f5fca212014-02-05 18:23:00175 return kCVReturnSuccess;
176}
177
[email protected]c6d21652014-03-04 08:23:19178// static
ccameron26d78d92015-04-01 17:33:52179void DisplayLinkMac::DisplayReconfigurationCallBack(
180 CGDirectDisplayID display,
181 CGDisplayChangeSummaryFlags flags,
182 void* user_info) {
183 DisplayMap::iterator found = display_map_.Get().find(display);
184 if (found == display_map_.Get().end())
185 return;
186 DisplayLinkMac* display_link_mac = found->second;
187 display_link_mac->timebase_and_interval_valid_ = false;
188}
189
190// static
[email protected]c6d21652014-03-04 08:23:19191base::LazyInstance<DisplayLinkMac::DisplayMap>
192 DisplayLinkMac::display_map_ = LAZY_INSTANCE_INITIALIZER;
193
ccameron9913d6462015-06-08 22:07:57194} // ui
[email protected]f5fca212014-02-05 18:23:00195