blob: f74c509812fe03610ac0cb15d4cc2ede88b6bc48 [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
avi9c81217b2015-12-24 23:40:057#include <stdint.h>
8
fdoray13b955d2016-06-14 17:47:089#include "base/location.h"
[email protected]f5fca212014-02-05 18:23:0010#include "base/logging.h"
Yuri Wiitala73904e32017-07-27 00:50:2011#include "base/numerics/safe_conversions.h"
fdoray13b955d2016-06-14 17:47:0812#include "base/single_thread_task_runner.h"
13#include "base/threading/thread_task_runner_handle.h"
ssid3e765612015-01-28 04:03:4214#include "base/trace_event/trace_event.h"
[email protected]f5fca212014-02-05 18:23:0015
[email protected]3d09c4132014-02-11 14:24:4016namespace base {
17
18template<>
19struct ScopedTypeRefTraits<CVDisplayLinkRef> {
rseseke7259752015-12-23 06:46:4120 static CVDisplayLinkRef InvalidValue() { return nullptr; }
Robert Sesekcfd6ed562016-01-04 17:43:0021 static CVDisplayLinkRef Retain(CVDisplayLinkRef object) {
22 return CVDisplayLinkRetain(object);
[email protected]3d09c4132014-02-11 14:24:4023 }
24 static void Release(CVDisplayLinkRef object) {
25 CVDisplayLinkRelease(object);
26 }
27};
28
29} // namespace base
30
tapted119ecfdd2016-01-28 08:01:0131namespace {
32
33// Empty callback set during tear down.
34CVReturn VoidDisplayLinkCallback(CVDisplayLinkRef display_link,
35 const CVTimeStamp* now,
36 const CVTimeStamp* output_time,
37 CVOptionFlags flags_in,
38 CVOptionFlags* flags_out,
39 void* context) {
40 return kCVReturnSuccess;
41}
42
43} // namespace
44
ccameron9913d6462015-06-08 22:07:5745namespace ui {
[email protected]f5fca212014-02-05 18:23:0046
47// static
[email protected]c6d21652014-03-04 08:23:1948scoped_refptr<DisplayLinkMac> DisplayLinkMac::GetForDisplay(
49 CGDirectDisplayID display_id) {
ccameron01ae4cbc2015-08-10 10:44:1650 if (!display_id)
51 return nullptr;
52
[email protected]c6d21652014-03-04 08:23:1953 // Return the existing display link for this display, if it exists.
54 DisplayMap::iterator found = display_map_.Get().find(display_id);
55 if (found != display_map_.Get().end()) {
56 return found->second;
57 }
58
[email protected]f5fca212014-02-05 18:23:0059 CVReturn ret = kCVReturnSuccess;
60
[email protected]3d09c4132014-02-11 14:24:4061 base::ScopedTypeRef<CVDisplayLinkRef> display_link;
[email protected]c6d21652014-03-04 08:23:1962 ret = CVDisplayLinkCreateWithCGDisplay(
63 display_id,
64 display_link.InitializeInto());
[email protected]3d09c4132014-02-11 14:24:4065 if (ret != kCVReturnSuccess) {
66 LOG(ERROR) << "CVDisplayLinkCreateWithActiveCGDisplays failed: " << ret;
67 return NULL;
[email protected]f5fca212014-02-05 18:23:0068 }
69
[email protected]3d09c4132014-02-11 14:24:4070 scoped_refptr<DisplayLinkMac> display_link_mac;
ccameronefa5e1b2015-06-09 18:47:0271 display_link_mac = new DisplayLinkMac(display_id, display_link);
[email protected]f5fca212014-02-05 18:23:0072 ret = CVDisplayLinkSetOutputCallback(
73 display_link_mac->display_link_,
74 &DisplayLinkCallback,
75 display_link_mac.get());
76 if (ret != kCVReturnSuccess) {
77 LOG(ERROR) << "CVDisplayLinkSetOutputCallback failed: " << ret;
78 return NULL;
79 }
80
81 return display_link_mac;
82}
83
[email protected]3d09c4132014-02-11 14:24:4084DisplayLinkMac::DisplayLinkMac(
[email protected]c6d21652014-03-04 08:23:1985 CGDirectDisplayID display_id,
[email protected]3d09c4132014-02-11 14:24:4086 base::ScopedTypeRef<CVDisplayLinkRef> display_link)
fdoray13b955d2016-06-14 17:47:0887 : main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
88 display_id_(display_id),
89 display_link_(display_link),
90 timebase_and_interval_valid_(false) {
[email protected]c6d21652014-03-04 08:23:1991 DCHECK(display_map_.Get().find(display_id) == display_map_.Get().end());
ccameron26d78d92015-04-01 17:33:5292 if (display_map_.Get().empty()) {
93 CGError register_error = CGDisplayRegisterReconfigurationCallback(
94 DisplayReconfigurationCallBack, nullptr);
95 DPLOG_IF(ERROR, register_error != kCGErrorSuccess)
96 << "CGDisplayRegisterReconfigurationCallback: "
97 << register_error;
98 }
[email protected]c6d21652014-03-04 08:23:1999 display_map_.Get().insert(std::make_pair(display_id_, this));
[email protected]f5fca212014-02-05 18:23:00100}
101
102DisplayLinkMac::~DisplayLinkMac() {
ccameron26d78d92015-04-01 17:33:52103 StopDisplayLink();
[email protected]c6d21652014-03-04 08:23:19104
tapted119ecfdd2016-01-28 08:01:01105 // Usually |display_link_| holds the last reference to CVDisplayLinkRef, but
106 // that's not guaranteed, so it might not free all resources after the
107 // destructor completes. Ensure the callback is cleared out regardless to
108 // avoid possible crashes (see http://crbug.com/564780).
109 CVReturn ret = CVDisplayLinkSetOutputCallback(
110 display_link_, VoidDisplayLinkCallback, nullptr);
111 DCHECK_EQ(kCGErrorSuccess, ret);
112
[email protected]c6d21652014-03-04 08:23:19113 DisplayMap::iterator found = display_map_.Get().find(display_id_);
114 DCHECK(found != display_map_.Get().end());
115 DCHECK(found->second == this);
116 display_map_.Get().erase(found);
ccameron26d78d92015-04-01 17:33:52117 if (display_map_.Get().empty()) {
118 CGError remove_error = CGDisplayRemoveReconfigurationCallback(
119 DisplayReconfigurationCallBack, nullptr);
120 DPLOG_IF(ERROR, remove_error != kCGErrorSuccess)
121 << "CGDisplayRemoveReconfigurationCallback: "
122 << remove_error;
123 }
[email protected]f5fca212014-02-05 18:23:00124}
125
126bool DisplayLinkMac::GetVSyncParameters(
127 base::TimeTicks* timebase, base::TimeDelta* interval) {
ccameron26d78d92015-04-01 17:33:52128 if (!timebase_and_interval_valid_) {
129 StartOrContinueDisplayLink();
[email protected]f5fca212014-02-05 18:23:00130 return false;
ccameron26d78d92015-04-01 17:33:52131 }
[email protected]f5fca212014-02-05 18:23:00132
133 *timebase = timebase_;
134 *interval = interval_;
135 return true;
136}
137
ccameronc8429f22015-08-17 20:02:41138void DisplayLinkMac::NotifyCurrentTime(const base::TimeTicks& now) {
139 if (now >= recalculate_time_)
140 StartOrContinueDisplayLink();
141}
142
ccameron26d78d92015-04-01 17:33:52143void DisplayLinkMac::Tick(const CVTimeStamp& cv_time) {
ccameron9913d6462015-06-08 22:07:57144 TRACE_EVENT0("ui", "DisplayLinkMac::Tick");
[email protected]f5fca212014-02-05 18:23:00145
146 // Verify that videoRefreshPeriod is 32 bits.
ccameron26d78d92015-04-01 17:33:52147 DCHECK((cv_time.videoRefreshPeriod & ~0xffffFFFFull) == 0ull);
[email protected]f5fca212014-02-05 18:23:00148
149 // Verify that the numerator and denominator make some sense.
avi9c81217b2015-12-24 23:40:05150 uint32_t numerator = static_cast<uint32_t>(cv_time.videoRefreshPeriod);
151 uint32_t denominator = cv_time.videoTimeScale;
[email protected]f5fca212014-02-05 18:23:00152 if (numerator <= 0 || denominator <= 0) {
153 LOG(WARNING) << "Unexpected numerator or denominator, bailing.";
154 return;
155 }
156
Yuri Wiitala73904e32017-07-27 00:50:20157 base::CheckedNumeric<int64_t> interval_us(base::Time::kMicrosecondsPerSecond);
158 interval_us *= numerator;
159 interval_us /= denominator;
160 if (!interval_us.IsValid()) {
161 LOG(DFATAL) << "Bailing due to overflow: "
162 << base::Time::kMicrosecondsPerSecond << " * " << numerator
163 << " / " << denominator;
164 return;
165 }
166
167 timebase_ = base::TimeTicks::FromMachAbsoluteTime(cv_time.hostTime);
168 interval_ = base::TimeDelta::FromMicroseconds(interval_us.ValueOrDie());
[email protected]f5fca212014-02-05 18:23:00169 timebase_and_interval_valid_ = true;
ccameron26d78d92015-04-01 17:33:52170
ccameronc8429f22015-08-17 20:02:41171 // Don't restart the display link for 10 seconds.
172 recalculate_time_ = base::TimeTicks::Now() +
173 base::TimeDelta::FromSeconds(10);
ccameron26d78d92015-04-01 17:33:52174 StopDisplayLink();
[email protected]f5fca212014-02-05 18:23:00175}
176
177void DisplayLinkMac::StartOrContinueDisplayLink() {
[email protected]f5fca212014-02-05 18:23:00178 if (CVDisplayLinkIsRunning(display_link_))
179 return;
180
181 CVReturn ret = CVDisplayLinkStart(display_link_);
182 if (ret != kCVReturnSuccess) {
183 LOG(ERROR) << "CVDisplayLinkStart failed: " << ret;
184 }
185}
186
187void DisplayLinkMac::StopDisplayLink() {
188 if (!CVDisplayLinkIsRunning(display_link_))
189 return;
190
191 CVReturn ret = CVDisplayLinkStop(display_link_);
192 if (ret != kCVReturnSuccess) {
193 LOG(ERROR) << "CVDisplayLinkStop failed: " << ret;
194 }
195}
196
ccameron26d78d92015-04-01 17:33:52197// static
[email protected]f5fca212014-02-05 18:23:00198CVReturn DisplayLinkMac::DisplayLinkCallback(
199 CVDisplayLinkRef display_link,
200 const CVTimeStamp* now,
201 const CVTimeStamp* output_time,
202 CVOptionFlags flags_in,
203 CVOptionFlags* flags_out,
204 void* context) {
ccameron9913d6462015-06-08 22:07:57205 TRACE_EVENT0("ui", "DisplayLinkMac::DisplayLinkCallback");
[email protected]f5fca212014-02-05 18:23:00206 DisplayLinkMac* display_link_mac = static_cast<DisplayLinkMac*>(context);
ccameron9913d6462015-06-08 22:07:57207 display_link_mac->main_thread_task_runner_->PostTask(
ccameron26d78d92015-04-01 17:33:52208 FROM_HERE,
209 base::Bind(&DisplayLinkMac::Tick, display_link_mac, *output_time));
[email protected]f5fca212014-02-05 18:23:00210 return kCVReturnSuccess;
211}
212
[email protected]c6d21652014-03-04 08:23:19213// static
ccameron26d78d92015-04-01 17:33:52214void DisplayLinkMac::DisplayReconfigurationCallBack(
215 CGDirectDisplayID display,
216 CGDisplayChangeSummaryFlags flags,
217 void* user_info) {
218 DisplayMap::iterator found = display_map_.Get().find(display);
219 if (found == display_map_.Get().end())
220 return;
221 DisplayLinkMac* display_link_mac = found->second;
222 display_link_mac->timebase_and_interval_valid_ = false;
223}
224
225// static
scottmg5e65e3a2017-03-08 08:48:46226base::LazyInstance<DisplayLinkMac::DisplayMap>::DestructorAtExit
[email protected]c6d21652014-03-04 08:23:19227 DisplayLinkMac::display_map_ = LAZY_INSTANCE_INITIALIZER;
228
ccameron9913d6462015-06-08 22:07:57229} // ui