Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / renderer_host / display_link_mac.cc
blob79920f6f10bc1a903a817ae6946a8ecde422f8b5
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"
11 namespace base {
13 template<>
14 struct ScopedTypeRefTraits<CVDisplayLinkRef> {
15 static void Retain(CVDisplayLinkRef object) {
16 CVDisplayLinkRetain(object);
18 static void Release(CVDisplayLinkRef object) {
19 CVDisplayLinkRelease(object);
23 } // namespace base
25 namespace content {
27 // static
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()) {
33 return found->second;
36 CVReturn ret = kCVReturnSuccess;
38 base::ScopedTypeRef<CVDisplayLinkRef> display_link;
39 ret = CVDisplayLinkCreateWithCGDisplay(
40 display_id,
41 display_link.InitializeInto());
42 if (ret != kCVReturnSuccess) {
43 LOG(ERROR) << "CVDisplayLinkCreateWithActiveCGDisplays failed: " << ret;
44 return NULL;
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_,
52 &DisplayLinkCallback,
53 display_link_mac.get());
54 if (ret != kCVReturnSuccess) {
55 LOG(ERROR) << "CVDisplayLinkSetOutputCallback failed: " << ret;
56 return NULL;
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: "
74 << register_error;
76 display_map_.Get().insert(std::make_pair(display_id_, this));
79 DisplayLinkMac::~DisplayLinkMac() {
80 StopDisplayLink();
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: "
91 << remove_error;
95 bool DisplayLinkMac::GetVSyncParameters(
96 base::TimeTicks* timebase, base::TimeDelta* interval) {
97 if (!timebase_and_interval_valid_) {
98 StartOrContinueDisplayLink();
99 return false;
102 *timebase = timebase_;
103 *interval = interval_;
104 return true;
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.";
118 return;
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;
127 StopDisplayLink();
130 void DisplayLinkMac::StartOrContinueDisplayLink() {
131 if (CVDisplayLinkIsRunning(display_link_))
132 return;
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_))
142 return;
144 CVReturn ret = CVDisplayLinkStop(display_link_);
145 if (ret != kCVReturnSuccess) {
146 LOG(ERROR) << "CVDisplayLinkStop failed: " << ret;
150 // static
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,
157 void* context) {
158 TRACE_EVENT0("browser", "DisplayLinkMac::DisplayLinkCallback");
159 DisplayLinkMac* display_link_mac = static_cast<DisplayLinkMac*>(context);
160 RenderWidgetResizeHelper::Get()->task_runner()->PostTask(
161 FROM_HERE,
162 base::Bind(&DisplayLinkMac::Tick, display_link_mac, *output_time));
163 return kCVReturnSuccess;
166 // static
167 void DisplayLinkMac::DisplayReconfigurationCallBack(
168 CGDirectDisplayID display,
169 CGDisplayChangeSummaryFlags flags,
170 void* user_info) {
171 DisplayMap::iterator found = display_map_.Get().find(display);
172 if (found == display_map_.Get().end())
173 return;
174 DisplayLinkMac* display_link_mac = found->second;
175 display_link_mac->timebase_and_interval_valid_ = false;
178 // static
179 base::LazyInstance<DisplayLinkMac::DisplayMap>
180 DisplayLinkMac::display_map_ = LAZY_INSTANCE_INITIALIZER;
182 } // content