15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// found in the LICENSE file.
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/browser/renderer_host/display_link_mac.h"
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/debug/trace_event.h"
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/logging.h"
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace base {
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)template<>
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)struct ScopedTypeRefTraits<CVDisplayLinkRef> {
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  static void Retain(CVDisplayLinkRef object) {
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    CVDisplayLinkRetain(object);
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  static void Release(CVDisplayLinkRef object) {
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    CVDisplayLinkRelease(object);
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)};
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace base
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace content {
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// static
27a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)scoped_refptr<DisplayLinkMac> DisplayLinkMac::GetForDisplay(
28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    CGDirectDisplayID display_id) {
29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Return the existing display link for this display, if it exists.
30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DisplayMap::iterator found = display_map_.Get().find(display_id);
31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (found != display_map_.Get().end()) {
32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return found->second;
33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CVReturn ret = kCVReturnSuccess;
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::ScopedTypeRef<CVDisplayLinkRef> display_link;
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ret = CVDisplayLinkCreateWithCGDisplay(
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      display_id,
40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      display_link.InitializeInto());
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (ret != kCVReturnSuccess) {
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "CVDisplayLinkCreateWithActiveCGDisplays failed: " << ret;
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return NULL;
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_refptr<DisplayLinkMac> display_link_mac;
47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  display_link_mac = new DisplayLinkMac(display_id, display_link);
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ret = CVDisplayLinkSetOutputCallback(
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      display_link_mac->display_link_,
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      &DisplayLinkCallback,
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      display_link_mac.get());
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (ret != kCVReturnSuccess) {
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "CVDisplayLinkSetOutputCallback failed: " << ret;
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return NULL;
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return display_link_mac;
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)DisplayLinkMac::DisplayLinkMac(
62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    CGDirectDisplayID display_id,
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::ScopedTypeRef<CVDisplayLinkRef> display_link)
64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      : display_id_(display_id),
65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        display_link_(display_link),
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        stop_timer_(
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            FROM_HERE, base::TimeDelta::FromSeconds(1),
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            this, &DisplayLinkMac::StopDisplayLink),
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        timebase_and_interval_valid_(false) {
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK(display_map_.Get().find(display_id) == display_map_.Get().end());
71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  display_map_.Get().insert(std::make_pair(display_id_, this));
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)DisplayLinkMac::~DisplayLinkMac() {
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (CVDisplayLinkIsRunning(display_link_))
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    CVDisplayLinkStop(display_link_);
77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DisplayMap::iterator found = display_map_.Get().find(display_id_);
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK(found != display_map_.Get().end());
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK(found->second == this);
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  display_map_.Get().erase(found);
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool DisplayLinkMac::GetVSyncParameters(
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::TimeTicks* timebase, base::TimeDelta* interval) {
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  StartOrContinueDisplayLink();
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::AutoLock lock(lock_);
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!timebase_and_interval_valid_)
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return false;
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  *timebase = timebase_;
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  *interval = interval_;
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return true;
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void DisplayLinkMac::Tick(const CVTimeStamp* cv_time) {
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  TRACE_EVENT0("browser", "DisplayLinkMac::GetVSyncParameters");
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::AutoLock lock(lock_);
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Verify that videoRefreshPeriod is 32 bits.
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK((cv_time->videoRefreshPeriod & ~0xffffFFFFull) == 0ull);
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Verify that the numerator and denominator make some sense.
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  uint32 numerator = static_cast<uint32>(cv_time->videoRefreshPeriod);
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  uint32 denominator = cv_time->videoTimeScale;
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (numerator <= 0 || denominator <= 0) {
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(WARNING) << "Unexpected numerator or denominator, bailing.";
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  timebase_ = base::TimeTicks::FromInternalValue(
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      cv_time->hostTime / 1000);
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  interval_ = base::TimeDelta::FromMicroseconds(
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      1000000 * static_cast<int64>(numerator) / denominator);
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  timebase_and_interval_valid_ = true;
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void DisplayLinkMac::StartOrContinueDisplayLink() {
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Reset the timer, so that the display link won't be turned off for another
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // second.
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  stop_timer_.Reset();
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (CVDisplayLinkIsRunning(display_link_))
1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CVReturn ret = CVDisplayLinkStart(display_link_);
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (ret != kCVReturnSuccess) {
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "CVDisplayLinkStart failed: " << ret;
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void DisplayLinkMac::StopDisplayLink() {
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!CVDisplayLinkIsRunning(display_link_))
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CVReturn ret = CVDisplayLinkStop(display_link_);
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (ret != kCVReturnSuccess) {
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "CVDisplayLinkStop failed: " << ret;
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)CVReturn DisplayLinkMac::DisplayLinkCallback(
1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    CVDisplayLinkRef display_link,
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const CVTimeStamp* now,
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const CVTimeStamp* output_time,
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    CVOptionFlags flags_in,
1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    CVOptionFlags* flags_out,
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    void* context) {
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DisplayLinkMac* display_link_mac = static_cast<DisplayLinkMac*>(context);
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  display_link_mac->Tick(output_time);
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return kCVReturnSuccess;
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
155a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// static
156a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)base::LazyInstance<DisplayLinkMac::DisplayMap>
157a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    DisplayLinkMac::display_map_ = LAZY_INSTANCE_INITIALIZER;
158a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // content
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
161