diff options
author | ccameron <ccameron@chromium.org> | 2015-06-08 15:07:57 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-06-08 22:09:29 +0000 |
commit | 9913d64654735b8c947495d6ead02edf3d6071e2 (patch) | |
tree | e6799b7f7cc4b734a641fd317f53b7c013d55a21 /ui | |
parent | 39c5af24ab4e14ece3d7c97b7c4d027151ede176 (diff) | |
download | chromium_src-9913d64654735b8c947495d6ead02edf3d6071e2.zip chromium_src-9913d64654735b8c947495d6ead02edf3d6071e2.tar.gz chromium_src-9913d64654735b8c947495d6ead02edf3d6071e2.tar.bz2 |
Mac: Move DisplayLinkMac from content to ui
This logically belongs in ui::, because Mac views will need to be
synced to their display as well.
There is a bit of ugliness in this change in that the task runner for
display-link callbacks is provided as an argument to creation. It will
be moved to be an argument to a function to add callbacks in an upcoming
patch.
BUG=496484
Review URL: https://codereview.chromium.org/1163353002
Cr-Commit-Position: refs/heads/master@{#333357}
Diffstat (limited to 'ui')
-rw-r--r-- | ui/accelerated_widget_mac/BUILD.gn | 2 | ||||
-rw-r--r-- | ui/accelerated_widget_mac/accelerated_widget_mac.gyp | 6 | ||||
-rw-r--r-- | ui/accelerated_widget_mac/display_link_mac.cc | 185 | ||||
-rw-r--r-- | ui/accelerated_widget_mac/display_link_mac.h | 85 |
4 files changed, 276 insertions, 2 deletions
diff --git a/ui/accelerated_widget_mac/BUILD.gn b/ui/accelerated_widget_mac/BUILD.gn index 0c066dd..2ca4299 100644 --- a/ui/accelerated_widget_mac/BUILD.gn +++ b/ui/accelerated_widget_mac/BUILD.gn @@ -8,6 +8,8 @@ component("accelerated_widget_mac") { "accelerated_widget_mac.h", "accelerated_widget_mac.mm", "accelerated_widget_mac_export.h", + "display_link_mac.cc", + "display_link_mac.h", "io_surface_context.h", "io_surface_context.mm", "io_surface_layer.h", diff --git a/ui/accelerated_widget_mac/accelerated_widget_mac.gyp b/ui/accelerated_widget_mac/accelerated_widget_mac.gyp index d46b2e629..2e13b11 100644 --- a/ui/accelerated_widget_mac/accelerated_widget_mac.gyp +++ b/ui/accelerated_widget_mac/accelerated_widget_mac.gyp @@ -15,12 +15,14 @@ 'accelerated_widget_mac.h', 'accelerated_widget_mac.mm', 'accelerated_widget_mac_export.h', + 'display_link_mac.cc', + 'display_link_mac.h', 'io_surface_context.h', 'io_surface_context.mm', 'io_surface_layer.h', 'io_surface_layer.mm', - "io_surface_ns_gl_surface.h", - "io_surface_ns_gl_surface.mm", + 'io_surface_ns_gl_surface.h', + 'io_surface_ns_gl_surface.mm', 'io_surface_texture.h', 'io_surface_texture.mm', 'software_layer.h', diff --git a/ui/accelerated_widget_mac/display_link_mac.cc b/ui/accelerated_widget_mac/display_link_mac.cc new file mode 100644 index 0000000..94aa454 --- /dev/null +++ b/ui/accelerated_widget_mac/display_link_mac.cc @@ -0,0 +1,185 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/accelerated_widget_mac/display_link_mac.h" + +#include "base/logging.h" +#include "base/trace_event/trace_event.h" + +namespace base { + +template<> +struct ScopedTypeRefTraits<CVDisplayLinkRef> { + static void Retain(CVDisplayLinkRef object) { + CVDisplayLinkRetain(object); + } + static void Release(CVDisplayLinkRef object) { + CVDisplayLinkRelease(object); + } +}; + +} // namespace base + +namespace ui { + +// static +scoped_refptr<DisplayLinkMac> DisplayLinkMac::GetForDisplay( + scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner, + CGDirectDisplayID display_id) { + // Return the existing display link for this display, if it exists. + DisplayMap::iterator found = display_map_.Get().find(display_id); + if (found != display_map_.Get().end()) { + return found->second; + } + + CVReturn ret = kCVReturnSuccess; + + base::ScopedTypeRef<CVDisplayLinkRef> display_link; + ret = CVDisplayLinkCreateWithCGDisplay( + display_id, + display_link.InitializeInto()); + if (ret != kCVReturnSuccess) { + LOG(ERROR) << "CVDisplayLinkCreateWithActiveCGDisplays failed: " << ret; + return NULL; + } + + scoped_refptr<DisplayLinkMac> display_link_mac; + display_link_mac = new DisplayLinkMac( + main_thread_task_runner, display_id, display_link); + ret = CVDisplayLinkSetOutputCallback( + display_link_mac->display_link_, + &DisplayLinkCallback, + display_link_mac.get()); + if (ret != kCVReturnSuccess) { + LOG(ERROR) << "CVDisplayLinkSetOutputCallback failed: " << ret; + return NULL; + } + + return display_link_mac; +} + +DisplayLinkMac::DisplayLinkMac( + scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner, + CGDirectDisplayID display_id, + base::ScopedTypeRef<CVDisplayLinkRef> display_link) + : main_thread_task_runner_(main_thread_task_runner), + display_id_(display_id), + display_link_(display_link), + timebase_and_interval_valid_(false) { + DCHECK(display_map_.Get().find(display_id) == display_map_.Get().end()); + if (display_map_.Get().empty()) { + CGError register_error = CGDisplayRegisterReconfigurationCallback( + DisplayReconfigurationCallBack, nullptr); + DPLOG_IF(ERROR, register_error != kCGErrorSuccess) + << "CGDisplayRegisterReconfigurationCallback: " + << register_error; + } + display_map_.Get().insert(std::make_pair(display_id_, this)); +} + +DisplayLinkMac::~DisplayLinkMac() { + StopDisplayLink(); + + DisplayMap::iterator found = display_map_.Get().find(display_id_); + DCHECK(found != display_map_.Get().end()); + DCHECK(found->second == this); + display_map_.Get().erase(found); + if (display_map_.Get().empty()) { + CGError remove_error = CGDisplayRemoveReconfigurationCallback( + DisplayReconfigurationCallBack, nullptr); + DPLOG_IF(ERROR, remove_error != kCGErrorSuccess) + << "CGDisplayRemoveReconfigurationCallback: " + << remove_error; + } +} + +bool DisplayLinkMac::GetVSyncParameters( + base::TimeTicks* timebase, base::TimeDelta* interval) { + if (!timebase_and_interval_valid_) { + StartOrContinueDisplayLink(); + return false; + } + + *timebase = timebase_; + *interval = interval_; + return true; +} + +void DisplayLinkMac::Tick(const CVTimeStamp& cv_time) { + TRACE_EVENT0("ui", "DisplayLinkMac::Tick"); + + // Verify that videoRefreshPeriod is 32 bits. + DCHECK((cv_time.videoRefreshPeriod & ~0xffffFFFFull) == 0ull); + + // Verify that the numerator and denominator make some sense. + uint32 numerator = static_cast<uint32>(cv_time.videoRefreshPeriod); + uint32 denominator = cv_time.videoTimeScale; + if (numerator <= 0 || denominator <= 0) { + LOG(WARNING) << "Unexpected numerator or denominator, bailing."; + return; + } + + timebase_ = base::TimeTicks::FromInternalValue( + cv_time.hostTime / 1000); + interval_ = base::TimeDelta::FromMicroseconds( + 1000000 * static_cast<int64>(numerator) / denominator); + timebase_and_interval_valid_ = true; + + StopDisplayLink(); +} + +void DisplayLinkMac::StartOrContinueDisplayLink() { + if (CVDisplayLinkIsRunning(display_link_)) + return; + + CVReturn ret = CVDisplayLinkStart(display_link_); + if (ret != kCVReturnSuccess) { + LOG(ERROR) << "CVDisplayLinkStart failed: " << ret; + } +} + +void DisplayLinkMac::StopDisplayLink() { + if (!CVDisplayLinkIsRunning(display_link_)) + return; + + CVReturn ret = CVDisplayLinkStop(display_link_); + if (ret != kCVReturnSuccess) { + LOG(ERROR) << "CVDisplayLinkStop failed: " << ret; + } +} + +// static +CVReturn DisplayLinkMac::DisplayLinkCallback( + CVDisplayLinkRef display_link, + const CVTimeStamp* now, + const CVTimeStamp* output_time, + CVOptionFlags flags_in, + CVOptionFlags* flags_out, + void* context) { + TRACE_EVENT0("ui", "DisplayLinkMac::DisplayLinkCallback"); + DisplayLinkMac* display_link_mac = static_cast<DisplayLinkMac*>(context); + display_link_mac->main_thread_task_runner_->PostTask( + FROM_HERE, + base::Bind(&DisplayLinkMac::Tick, display_link_mac, *output_time)); + return kCVReturnSuccess; +} + +// static +void DisplayLinkMac::DisplayReconfigurationCallBack( + CGDirectDisplayID display, + CGDisplayChangeSummaryFlags flags, + void* user_info) { + DisplayMap::iterator found = display_map_.Get().find(display); + if (found == display_map_.Get().end()) + return; + DisplayLinkMac* display_link_mac = found->second; + display_link_mac->timebase_and_interval_valid_ = false; +} + +// static +base::LazyInstance<DisplayLinkMac::DisplayMap> + DisplayLinkMac::display_map_ = LAZY_INSTANCE_INITIALIZER; + +} // ui + diff --git a/ui/accelerated_widget_mac/display_link_mac.h b/ui/accelerated_widget_mac/display_link_mac.h new file mode 100644 index 0000000..24aad6e --- /dev/null +++ b/ui/accelerated_widget_mac/display_link_mac.h @@ -0,0 +1,85 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_ACCELERATED_WIDGET_MAC_DISPLAY_LINK_MAC_H_ +#define UI_ACCELERATED_WIDGET_MAC_DISPLAY_LINK_MAC_H_ + +#include <QuartzCore/CVDisplayLink.h> +#include <map> + +#include "base/lazy_instance.h" +#include "base/mac/scoped_typeref.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "base/time/time.h" +#include "base/timer/timer.h" +#include "ui/accelerated_widget_mac/accelerated_widget_mac_export.h" + +namespace ui { + +class ACCELERATED_WIDGET_MAC_EXPORT DisplayLinkMac : + public base::RefCounted<DisplayLinkMac> { + public: + static scoped_refptr<DisplayLinkMac> GetForDisplay( + scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner, + CGDirectDisplayID display_id); + + // Get vsync scheduling parameters. + bool GetVSyncParameters( + base::TimeTicks* timebase, + base::TimeDelta* interval); + + private: + friend class base::RefCounted<DisplayLinkMac>; + + DisplayLinkMac( + scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner, + CGDirectDisplayID display_id, + base::ScopedTypeRef<CVDisplayLinkRef> display_link); + virtual ~DisplayLinkMac(); + + void StartOrContinueDisplayLink(); + void StopDisplayLink(); + void Tick(const CVTimeStamp& time); + + // Called by the system on the display link thread, and posts a call to Tick + // to the UI thread. + static CVReturn DisplayLinkCallback( + CVDisplayLinkRef display_link, + const CVTimeStamp* now, + const CVTimeStamp* output_time, + CVOptionFlags flags_in, + CVOptionFlags* flags_out, + void* context); + + // This is called whenever the display is reconfigured, and marks that the + // vsync parameters must be recalculated. + static void DisplayReconfigurationCallBack( + CGDirectDisplayID display, + CGDisplayChangeSummaryFlags flags, + void* user_info); + + // The task runner from which to post tasks to run on the main thread. + scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_; + + // The display that this display link is attached to. + CGDirectDisplayID display_id_; + + // CVDisplayLink for querying VSync timing info. + base::ScopedTypeRef<CVDisplayLinkRef> display_link_; + + // VSync parameters computed during Tick. + bool timebase_and_interval_valid_; + base::TimeTicks timebase_; + base::TimeDelta interval_; + + // Each display link instance consumes a non-negligible number of cycles, so + // make all display links on the same screen share the same object. + typedef std::map<CGDirectDisplayID, DisplayLinkMac*> DisplayMap; + static base::LazyInstance<DisplayMap> display_map_; +}; + +} // ui + +#endif // UI_ACCELERATED_WIDGET_MAC_DISPLAY_LINK_MAC_H_ |