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.
4
5#include "content/browser/renderer_host/display_link_mac.h"
6
7#include "base/debug/trace_event.h"
8#include "base/logging.h"
9
10namespace base {
11
12template<>
13struct ScopedTypeRefTraits<CVDisplayLinkRef> {
14  static void Retain(CVDisplayLinkRef object) {
15    CVDisplayLinkRetain(object);
16  }
17  static void Release(CVDisplayLinkRef object) {
18    CVDisplayLinkRelease(object);
19  }
20};
21
22}  // namespace base
23
24namespace content {
25
26// static
27scoped_refptr<DisplayLinkMac> DisplayLinkMac::GetForDisplay(
28    CGDirectDisplayID display_id) {
29  // Return the existing display link for this display, if it exists.
30  DisplayMap::iterator found = display_map_.Get().find(display_id);
31  if (found != display_map_.Get().end()) {
32    return found->second;
33  }
34
35  CVReturn ret = kCVReturnSuccess;
36
37  base::ScopedTypeRef<CVDisplayLinkRef> display_link;
38  ret = CVDisplayLinkCreateWithCGDisplay(
39      display_id,
40      display_link.InitializeInto());
41  if (ret != kCVReturnSuccess) {
42    LOG(ERROR) << "CVDisplayLinkCreateWithActiveCGDisplays failed: " << ret;
43    return NULL;
44  }
45
46  scoped_refptr<DisplayLinkMac> display_link_mac;
47  display_link_mac = new DisplayLinkMac(display_id, display_link);
48
49  ret = CVDisplayLinkSetOutputCallback(
50      display_link_mac->display_link_,
51      &DisplayLinkCallback,
52      display_link_mac.get());
53  if (ret != kCVReturnSuccess) {
54    LOG(ERROR) << "CVDisplayLinkSetOutputCallback failed: " << ret;
55    return NULL;
56  }
57
58  return display_link_mac;
59}
60
61DisplayLinkMac::DisplayLinkMac(
62    CGDirectDisplayID display_id,
63    base::ScopedTypeRef<CVDisplayLinkRef> display_link)
64      : display_id_(display_id),
65        display_link_(display_link),
66        stop_timer_(
67            FROM_HERE, base::TimeDelta::FromSeconds(1),
68            this, &DisplayLinkMac::StopDisplayLink),
69        timebase_and_interval_valid_(false) {
70  DCHECK(display_map_.Get().find(display_id) == display_map_.Get().end());
71  display_map_.Get().insert(std::make_pair(display_id_, this));
72}
73
74DisplayLinkMac::~DisplayLinkMac() {
75  if (CVDisplayLinkIsRunning(display_link_))
76    CVDisplayLinkStop(display_link_);
77
78  DisplayMap::iterator found = display_map_.Get().find(display_id_);
79  DCHECK(found != display_map_.Get().end());
80  DCHECK(found->second == this);
81  display_map_.Get().erase(found);
82}
83
84bool DisplayLinkMac::GetVSyncParameters(
85    base::TimeTicks* timebase, base::TimeDelta* interval) {
86  StartOrContinueDisplayLink();
87
88  base::AutoLock lock(lock_);
89  if (!timebase_and_interval_valid_)
90    return false;
91
92  *timebase = timebase_;
93  *interval = interval_;
94  return true;
95}
96
97void DisplayLinkMac::Tick(const CVTimeStamp* cv_time) {
98  TRACE_EVENT0("browser", "DisplayLinkMac::GetVSyncParameters");
99  base::AutoLock lock(lock_);
100
101  // Verify that videoRefreshPeriod is 32 bits.
102  DCHECK((cv_time->videoRefreshPeriod & ~0xffffFFFFull) == 0ull);
103
104  // Verify that the numerator and denominator make some sense.
105  uint32 numerator = static_cast<uint32>(cv_time->videoRefreshPeriod);
106  uint32 denominator = cv_time->videoTimeScale;
107  if (numerator <= 0 || denominator <= 0) {
108    LOG(WARNING) << "Unexpected numerator or denominator, bailing.";
109    return;
110  }
111
112  timebase_ = base::TimeTicks::FromInternalValue(
113      cv_time->hostTime / 1000);
114  interval_ = base::TimeDelta::FromMicroseconds(
115      1000000 * static_cast<int64>(numerator) / denominator);
116  timebase_and_interval_valid_ = true;
117}
118
119void DisplayLinkMac::StartOrContinueDisplayLink() {
120  // Reset the timer, so that the display link won't be turned off for another
121  // second.
122  stop_timer_.Reset();
123
124  if (CVDisplayLinkIsRunning(display_link_))
125    return;
126
127  CVReturn ret = CVDisplayLinkStart(display_link_);
128  if (ret != kCVReturnSuccess) {
129    LOG(ERROR) << "CVDisplayLinkStart failed: " << ret;
130  }
131}
132
133void DisplayLinkMac::StopDisplayLink() {
134  if (!CVDisplayLinkIsRunning(display_link_))
135    return;
136
137  CVReturn ret = CVDisplayLinkStop(display_link_);
138  if (ret != kCVReturnSuccess) {
139    LOG(ERROR) << "CVDisplayLinkStop failed: " << ret;
140  }
141}
142
143CVReturn DisplayLinkMac::DisplayLinkCallback(
144    CVDisplayLinkRef display_link,
145    const CVTimeStamp* now,
146    const CVTimeStamp* output_time,
147    CVOptionFlags flags_in,
148    CVOptionFlags* flags_out,
149    void* context) {
150  DisplayLinkMac* display_link_mac = static_cast<DisplayLinkMac*>(context);
151  display_link_mac->Tick(output_time);
152  return kCVReturnSuccess;
153}
154
155// static
156base::LazyInstance<DisplayLinkMac::DisplayMap>
157    DisplayLinkMac::display_map_ = LAZY_INSTANCE_INITIALIZER;
158
159}  // content
160
161