diff options
author | ccameron@chromium.org <ccameron@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-25 01:48:51 +0000 |
---|---|---|
committer | ccameron@chromium.org <ccameron@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-25 01:48:51 +0000 |
commit | 6e415ff81a1e29024ccfdd5548e7cd6ba4de450e (patch) | |
tree | f0abc09c013a79897f0a3d9985181a101a23060c | |
parent | dd8a3c5d6cba7edcb1c52ebe2d02c9552c90ebd2 (diff) | |
download | chromium_src-6e415ff81a1e29024ccfdd5548e7cd6ba4de450e.zip chromium_src-6e415ff81a1e29024ccfdd5548e7cd6ba4de450e.tar.gz chromium_src-6e415ff81a1e29024ccfdd5548e7cd6ba4de450e.tar.bz2 |
Make software compositing work on Mac.
Rename the FramebufferMemoryManager class to
SoftwareFrameMemoryManager. Pull out common members
to Aura and Mac and move them to SoftwareFramebufferManager.
Rename the FramebufferHolder to SoftwareFrame.
This is still disabled unless --enable-software-compositing is passed
in and has not been thoroughly tested.
BUG=286038
Review URL: https://codereview.chromium.org/25942002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@230900 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | content/browser/renderer_host/frame_memory_manager.cc | 66 | ||||
-rw-r--r-- | content/browser/renderer_host/frame_memory_manager.h | 44 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host_view_aura.cc | 132 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host_view_aura.h | 16 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host_view_mac.h | 16 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host_view_mac.mm | 114 | ||||
-rw-r--r-- | content/browser/renderer_host/software_frame_manager.cc | 243 | ||||
-rw-r--r-- | content/browser/renderer_host/software_frame_manager.h | 105 | ||||
-rw-r--r-- | content/browser/renderer_host/software_frame_manager_unittest.cc | 255 | ||||
-rw-r--r-- | content/content_browser.gypi | 4 | ||||
-rw-r--r-- | content/content_tests.gypi | 1 |
11 files changed, 765 insertions, 231 deletions
diff --git a/content/browser/renderer_host/frame_memory_manager.cc b/content/browser/renderer_host/frame_memory_manager.cc deleted file mode 100644 index 708bddf..0000000 --- a/content/browser/renderer_host/frame_memory_manager.cc +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2013 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 "content/browser/renderer_host/frame_memory_manager.h" - -#include "base/logging.h" -#include "base/memory/singleton.h" -#include "base/sys_info.h" - -namespace content { - -namespace { - -size_t MaxNumberOfSavedFrames() { - return std::min(5, 2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256)); -} - -} // namespace - -FrameMemoryManager* FrameMemoryManager::GetInstance() { - return Singleton<FrameMemoryManager>::get(); -} - -void FrameMemoryManager::AddFrame(FrameContainer* frame, bool visible) { - RemoveFrame(frame); - if (visible) - visible_frames_.insert(frame); - else - hidden_frames_.push_front(frame); - CullHiddenFrames(); -} - -void FrameMemoryManager::RemoveFrame(FrameContainer* frame) { - visible_frames_.erase(frame); - hidden_frames_.remove(frame); -} - -void FrameMemoryManager::SetFrameVisibility(FrameContainer* frame, - bool visible) { - if (visible) { - hidden_frames_.remove(frame); - visible_frames_.insert(frame); - } else { - visible_frames_.erase(frame); - hidden_frames_.push_front(frame); - CullHiddenFrames(); - } -} - -FrameMemoryManager::FrameMemoryManager() {} - -FrameMemoryManager::~FrameMemoryManager() {} - -void FrameMemoryManager::CullHiddenFrames() { - while (!hidden_frames_.empty() && - hidden_frames_.size() + visible_frames_.size() > - MaxNumberOfSavedFrames()) { - size_t old_size = hidden_frames_.size(); - // Should remove self from list. - hidden_frames_.back()->ReleaseCurrentFrame(); - DCHECK_EQ(hidden_frames_.size() + 1, old_size); - } -} - -} // namespace content diff --git a/content/browser/renderer_host/frame_memory_manager.h b/content/browser/renderer_host/frame_memory_manager.h deleted file mode 100644 index ce3fc25..0000000 --- a/content/browser/renderer_host/frame_memory_manager.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2013 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 CONTENT_BROWSER_RENDERER_HOST_FRAME_MEMORY_MANAGER_H_ -#define CONTENT_BROWSER_RENDERER_HOST_FRAME_MEMORY_MANAGER_H_ - -#include <list> -#include <set> - -#include "base/basictypes.h" - -template <typename T> struct DefaultSingletonTraits; - -namespace content { - -class FrameContainer { - public: - virtual void ReleaseCurrentFrame() = 0; -}; - -class FrameMemoryManager { - public: - static FrameMemoryManager* GetInstance(); - - void AddFrame(FrameContainer*, bool visible); - void RemoveFrame(FrameContainer*); - void SetFrameVisibility(FrameContainer*, bool visible); - - private: - FrameMemoryManager(); - ~FrameMemoryManager(); - void CullHiddenFrames(); - friend struct DefaultSingletonTraits<FrameMemoryManager>; - - std::set<FrameContainer*> visible_frames_; - std::list<FrameContainer*> hidden_frames_; - - DISALLOW_COPY_AND_ASSIGN(FrameMemoryManager); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_RENDERER_HOST_FRAME_MEMORY_MANAGER_H_ diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc index 4d616bb..fab01ac 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc @@ -93,35 +93,6 @@ using WebKit::WebTouchEvent; namespace content { -void ReleaseMailbox(scoped_refptr<MemoryHolder> holder, - unsigned sync_point, - bool lost_resource) {} - -class MemoryHolder : public base::RefCounted<MemoryHolder> { - public: - MemoryHolder(scoped_ptr<base::SharedMemory> shared_memory, - gfx::Size frame_size, - base::Callback<void()> callback) - : shared_memory_(shared_memory.Pass()), - frame_size_(frame_size), - callback_(callback) {} - - void GetMailbox(cc::TextureMailbox* mailbox, - scoped_ptr<cc::SingleReleaseCallback>* release_callback) { - *mailbox = cc::TextureMailbox(shared_memory_.get(), frame_size_); - *release_callback = cc::SingleReleaseCallback::Create( - base::Bind(ReleaseMailbox, make_scoped_refptr(this))); - } - - private: - friend class base::RefCounted<MemoryHolder>; - ~MemoryHolder() { callback_.Run(); } - - scoped_ptr<base::SharedMemory> shared_memory_; - gfx::Size frame_size_; - base::Callback<void()> callback_; -}; - namespace { void MailboxReleaseCallback(scoped_ptr<base::SharedMemory> shared_memory, @@ -603,7 +574,8 @@ RenderWidgetHostViewAura::RenderWidgetHostViewAura(RenderWidgetHost* host) can_lock_compositor_(YES), cursor_visibility_state_in_renderer_(UNKNOWN), paint_observer_(NULL), - touch_editing_client_(NULL) { + touch_editing_client_(NULL), + weak_ptr_factory_(this) { host_->SetView(this); window_observer_.reset(new WindowObserver(this)); aura::client::SetTooltipText(window_, &tooltip_); @@ -614,6 +586,8 @@ RenderWidgetHostViewAura::RenderWidgetHostViewAura(RenderWidgetHost* host) #if defined(OS_WIN) transient_observer_.reset(new TransientWindowObserver(this)); #endif + software_frame_manager_.reset(new SoftwareFrameManager( + weak_ptr_factory_.GetWeakPtr())); } //////////////////////////////////////////////////////////////////////////////// @@ -698,8 +672,7 @@ void RenderWidgetHostViewAura::WasShown() { if (!host_->is_hidden()) return; host_->WasShown(); - if (framebuffer_holder_) - FrameMemoryManager::GetInstance()->SetFrameVisibility(this, true); + software_frame_manager_->SetVisibility(true); aura::RootWindow* root = window_->GetRootWindow(); if (root) { @@ -727,9 +700,7 @@ void RenderWidgetHostViewAura::WasHidden() { if (!host_ || host_->is_hidden()) return; host_->WasHidden(); - if (framebuffer_holder_) - FrameMemoryManager::GetInstance()->SetFrameVisibility(this, false); - + software_frame_manager_->SetVisibility(false); released_front_lock_ = NULL; #if defined(OS_WIN) @@ -1288,12 +1259,12 @@ void RenderWidgetHostViewAura::UpdateExternalTexture() { current_frame_size_ = ConvertSizeToDIP( current_surface_->device_scale_factor(), current_surface_->size()); CheckResizeLock(); - framebuffer_holder_ = NULL; - FrameMemoryManager::GetInstance()->RemoveFrame(this); - } else if (is_compositing_active && framebuffer_holder_) { + software_frame_manager_->DiscardCurrentFrame(); + } else if (is_compositing_active && + software_frame_manager_->HasCurrentFrame()) { cc::TextureMailbox mailbox; scoped_ptr<cc::SingleReleaseCallback> callback; - framebuffer_holder_->GetMailbox(&mailbox, &callback); + software_frame_manager_->GetCurrentFrameMailbox(&mailbox, &callback); window_->layer()->SetTextureMailbox(mailbox, callback.Pass(), last_swapped_surface_scale_factor_); @@ -1304,8 +1275,7 @@ void RenderWidgetHostViewAura::UpdateExternalTexture() { window_->layer()->SetShowPaintedContent(); resize_lock_.reset(); host_->WasResized(); - framebuffer_holder_ = NULL; - FrameMemoryManager::GetInstance()->RemoveFrame(this); + software_frame_manager_->DiscardCurrentFrame(); } } @@ -1440,8 +1410,7 @@ void RenderWidgetHostViewAura::SwapDelegatedFrame( gfx::Rect damage_rect_in_dip = ConvertRectToDIP(frame_device_scale_factor, damage_rect); - framebuffer_holder_ = NULL; - FrameMemoryManager::GetInstance()->RemoveFrame(this); + software_frame_manager_->DiscardCurrentFrame(); if (ShouldSkipFrame(frame_size_in_dip)) { cc::CompositorFrameAck ack; @@ -1575,38 +1544,14 @@ void RenderWidgetHostViewAura::SwapSoftwareFrame( return; } - const size_t size_in_bytes = 4 * frame_size.GetArea(); -#ifdef OS_WIN - scoped_ptr<base::SharedMemory> shared_memory( - new base::SharedMemory(frame_data->handle, true, - host_->GetProcess()->GetHandle())); -#else - scoped_ptr<base::SharedMemory> shared_memory( - new base::SharedMemory(frame_data->handle, true)); -#endif - -#ifdef OS_WIN - if (!shared_memory->Map(0)) { - DLOG(ERROR) << "Unable to map renderer memory."; - RecordAction(UserMetricsAction("BadMessageTerminate_RWHVA1")); - host_->GetProcess()->ReceivedBadMessage(); - return; - } - - if (shared_memory->mapped_size() < size_in_bytes) { - DLOG(ERROR) << "Shared memory too small for given rectangle"; - RecordAction(UserMetricsAction("BadMessageTerminate_RWHVA2")); + if (!software_frame_manager_->SwapToNewFrame( + output_surface_id, + frame_data.get(), + frame_device_scale_factor, + host_->GetProcess()->GetHandle())) { host_->GetProcess()->ReceivedBadMessage(); return; } -#else - if (!shared_memory->Map(size_in_bytes)) { - DLOG(ERROR) << "Unable to map renderer memory."; - RecordAction(UserMetricsAction("BadMessageTerminate_RWHVA1")); - host_->GetProcess()->ReceivedBadMessage(); - return; - } -#endif if (last_swapped_surface_size_ != frame_size) { DLOG_IF(ERROR, damage_rect != gfx::Rect(frame_size)) @@ -1615,17 +1560,9 @@ void RenderWidgetHostViewAura::SwapSoftwareFrame( last_swapped_surface_size_ = frame_size; last_swapped_surface_scale_factor_ = frame_device_scale_factor; - scoped_refptr<MemoryHolder> holder(new MemoryHolder( - shared_memory.Pass(), - frame_size, - base::Bind(&RenderWidgetHostViewAura::ReleaseSoftwareFrame, - AsWeakPtr(), - output_surface_id, - frame_data->id))); - framebuffer_holder_.swap(holder); cc::TextureMailbox mailbox; scoped_ptr<cc::SingleReleaseCallback> callback; - framebuffer_holder_->GetMailbox(&mailbox, &callback); + software_frame_manager_->GetCurrentFrameMailbox(&mailbox, &callback); DCHECK(mailbox.IsSharedMemory()); current_frame_size_ = frame_size_in_dip; @@ -1648,7 +1585,8 @@ void RenderWidgetHostViewAura::SwapSoftwareFrame( if (paint_observer_) paint_observer_->OnUpdateCompositorContent(); DidReceiveFrameFromRenderer(); - FrameMemoryManager::GetInstance()->AddFrame(this, !host_->is_hidden()); + + software_frame_manager_->SwapToNewFrameComplete(!host_->is_hidden()); } void RenderWidgetHostViewAura::SendSoftwareFrameAck(uint32 output_surface_id) { @@ -1756,8 +1694,7 @@ void RenderWidgetHostViewAura::BuffersSwapped( const BufferPresentedCallback& ack_callback) { scoped_refptr<ui::Texture> previous_texture(current_surface_); const gfx::Rect surface_rect = gfx::Rect(surface_size); - framebuffer_holder_ = NULL; - FrameMemoryManager::GetInstance()->RemoveFrame(this); + software_frame_manager_->DiscardCurrentFrame(); if (!SwapBuffersPrepare(surface_rect, surface_scale_factor, @@ -3070,16 +3007,21 @@ void RenderWidgetHostViewAura::OnRootWindowHostMoved( UpdateScreenInfo(window_); } -void RenderWidgetHostViewAura::ReleaseCurrentFrame() { - if (framebuffer_holder_.get() && !current_surface_.get()) { - framebuffer_holder_ = NULL; - ui::Compositor* compositor = GetCompositor(); - if (compositor) { - AddOnCommitCallbackAndDisableLocks(base::Bind( - &RenderWidgetHostViewAura::SendReclaimSoftwareFrames, AsWeakPtr())); - } - UpdateExternalTexture(); +//////////////////////////////////////////////////////////////////////////////// +// RenderWidgetHostViewAura, SoftwareFrameManagerClient implementation: + +void RenderWidgetHostViewAura::SoftwareFrameWasFreed( + uint32 output_surface_id, unsigned frame_id) { + ReleaseSoftwareFrame(output_surface_id, frame_id); +} + +void RenderWidgetHostViewAura::ReleaseReferencesToSoftwareFrame() { + ui::Compositor* compositor = GetCompositor(); + if (compositor) { + AddOnCommitCallbackAndDisableLocks(base::Bind( + &RenderWidgetHostViewAura::SendReclaimSoftwareFrames, AsWeakPtr())); } + UpdateExternalTexture(); } //////////////////////////////////////////////////////////////////////////////// @@ -3253,10 +3195,6 @@ RenderWidgetHostViewAura::~RenderWidgetHostViewAura() { // Aura root window and we don't have a way to get an input method object // associated with the window, but just in case. DetachFromInputMethod(); - FrameMemoryManager::GetInstance()->RemoveFrame(this); - // The destruction of the holder may call back into the RWHVA, so do it - // early. - framebuffer_holder_ = NULL; if (resource_collection_.get()) resource_collection_->SetClient(NULL); diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h index 77b5db4..f333eed 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.h +++ b/content/browser/renderer_host/render_widget_host_view_aura.h @@ -20,8 +20,8 @@ #include "cc/resources/texture_mailbox.h" #include "content/browser/accessibility/browser_accessibility_manager.h" #include "content/browser/aura/image_transport_factory.h" -#include "content/browser/renderer_host/frame_memory_manager.h" #include "content/browser/renderer_host/render_widget_host_view_base.h" +#include "content/browser/renderer_host/software_frame_manager.h" #include "content/common/content_export.h" #include "content/common/gpu/client/gl_helper.h" #include "third_party/skia/include/core/SkRegion.h" @@ -59,7 +59,6 @@ class Texture; } namespace content { -class MemoryHolder; class RenderWidgetHostImpl; class RenderWidgetHostView; class ResizeLock; @@ -78,7 +77,7 @@ class CONTENT_EXPORT RenderWidgetHostViewAura public aura::client::CursorClientObserver, public ImageTransportFactoryObserver, public BrowserAccessibilityDelegate, - public FrameContainer, + public SoftwareFrameManagerClient, public base::SupportsWeakPtr<RenderWidgetHostViewAura>, public cc::DelegatedFrameResourceCollectionClient { public: @@ -332,8 +331,10 @@ class CONTENT_EXPORT RenderWidgetHostViewAura virtual void OnRootWindowHostMoved(const aura::RootWindow* root, const gfx::Point& new_origin) OVERRIDE; - // FrameContainer implementation: - virtual void ReleaseCurrentFrame() OVERRIDE; + // SoftwareFrameManagerClient implementation: + virtual void SoftwareFrameWasFreed( + uint32 output_surface_id, unsigned frame_id) OVERRIDE; + virtual void ReleaseReferencesToSoftwareFrame() OVERRIDE; bool CanCopyToBitmap() const; @@ -613,8 +614,8 @@ class CONTENT_EXPORT RenderWidgetHostViewAura // The current frontbuffer texture. scoped_refptr<ui::Texture> current_surface_; - // This holds the current software framebuffer. - scoped_refptr<MemoryHolder> framebuffer_holder_; + // This holds the current software framebuffer, if any. + scoped_ptr<SoftwareFrameManager> software_frame_manager_; // With delegated renderer, this is the last output surface, used to // disambiguate resources with the same id coming from different output @@ -754,6 +755,7 @@ class CONTENT_EXPORT RenderWidgetHostViewAura }; scoped_ptr<ReleasedFrameInfo> released_software_frame_; + base::WeakPtrFactory<RenderWidgetHostViewAura> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAura); }; diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h index 6a9caaa..da16bc2 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.h +++ b/content/browser/renderer_host/render_widget_host_view_mac.h @@ -18,6 +18,7 @@ #include "base/time/time.h" #include "content/browser/accessibility/browser_accessibility_delegate_mac.h" #include "content/browser/renderer_host/render_widget_host_view_base.h" +#include "content/browser/renderer_host/software_frame_manager.h" #include "content/common/edit_command.h" #import "content/public/browser/render_widget_host_view_mac_base.h" #include "ipc/ipc_sender.h" @@ -196,7 +197,8 @@ class RenderWidgetHostImpl; // // RenderWidgetHostView class hierarchy described in render_widget_host_view.h. class RenderWidgetHostViewMac : public RenderWidgetHostViewBase, - public IPC::Sender { + public IPC::Sender, + public SoftwareFrameManagerClient { public: virtual ~RenderWidgetHostViewMac(); @@ -283,6 +285,8 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase, virtual void BeginFrameSubscription( scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) OVERRIDE; virtual void EndFrameSubscription() OVERRIDE; + virtual void OnSwapCompositorFrame( + uint32 output_surface_id, scoped_ptr<cc::CompositorFrame> frame) OVERRIDE; virtual void OnAcceleratedCompositingStateChange() OVERRIDE; virtual void OnAccessibilityEvents( const std::vector<AccessibilityHostMsg_EventParams>& params @@ -316,6 +320,11 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase, // IPC::Sender implementation. virtual bool Send(IPC::Message* message) OVERRIDE; + // SoftwareFrameManagerClient implementation: + virtual void SoftwareFrameWasFreed( + uint32 output_surface_id, unsigned frame_id) OVERRIDE; + virtual void ReleaseReferencesToSoftwareFrame() OVERRIDE; + // Forwards the mouse event to the renderer. void ForwardMouseEvent(const WebKit::WebMouseEvent& event); @@ -412,6 +421,9 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase, scoped_ptr<CompositingIOSurfaceMac> compositing_iosurface_; scoped_refptr<CompositingIOSurfaceContext> compositing_iosurface_context_; + // This holds the current software compositing framebuffer, if any. + scoped_ptr<SoftwareFrameManager> software_frame_manager_; + // Whether to allow overlapping views. bool allow_overlapping_views_; @@ -552,6 +564,8 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase, // Subscriber that listens to frame presentation events. scoped_ptr<RenderWidgetHostViewFrameSubscriber> frame_subscriber_; + base::WeakPtrFactory<RenderWidgetHostViewMac> + software_frame_weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewMac); }; diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm index 02c66aa..d632390 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm @@ -46,6 +46,7 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/native_web_keyboard_event.h" #import "content/public/browser/render_widget_host_view_mac_delegate.h" +#include "content/public/browser/user_metrics.h" #include "content/public/common/content_switches.h" #include "skia/ext/platform_canvas.h" #include "third_party/WebKit/public/web/WebInputEvent.h" @@ -426,7 +427,10 @@ RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget) weak_factory_(this), fullscreen_parent_host_view_(NULL), pending_swap_buffers_acks_weak_factory_(this), - next_swap_ack_time_(base::Time::Now()) { + next_swap_ack_time_(base::Time::Now()), + software_frame_weak_ptr_factory_(this) { + software_frame_manager_.reset(new SoftwareFrameManager( + software_frame_weak_ptr_factory_.GetWeakPtr())); // |cocoa_view_| owns us and we will be deleted when |cocoa_view_| // goes away. Since we autorelease it, our caller must put // |GetNativeView()| into the view hierarchy right after calling us. @@ -732,6 +736,7 @@ void RenderWidgetHostViewMac::WasShown() { if (web_contents_switch_paint_time_.is_null()) web_contents_switch_paint_time_ = base::TimeTicks::Now(); render_widget_host_->WasShown(); + software_frame_manager_->SetVisibility(true); // We're messing with the window, so do this to ensure no flashes. if (!use_core_animation_) @@ -751,6 +756,7 @@ void RenderWidgetHostViewMac::WasHidden() { // If we have a renderer, then inform it that we are being hidden so it can // reduce its resource utilization. render_widget_host_->WasHidden(); + software_frame_manager_->SetVisibility(false); // There can be a transparent flash as this view is removed and the next is // added, because of OSX windowing races between displaying the contents of @@ -1650,17 +1656,50 @@ void RenderWidgetHostViewMac::AcceleratedSurfaceRelease() { bool RenderWidgetHostViewMac::HasAcceleratedSurface( const gfx::Size& desired_size) { - return last_frame_was_accelerated_ && - compositing_iosurface_ && - compositing_iosurface_->HasIOSurface() && - (desired_size.IsEmpty() || - compositing_iosurface_->dip_io_surface_size() == desired_size); + if (last_frame_was_accelerated_) { + return compositing_iosurface_ && + compositing_iosurface_->HasIOSurface() && + (desired_size.IsEmpty() || + compositing_iosurface_->dip_io_surface_size() == desired_size); + } else { + return (software_frame_manager_->HasCurrentFrame() && + (desired_size.IsEmpty() || + software_frame_manager_->GetCurrentFrameSizeInDIP() == + desired_size)); + } + return false; } void RenderWidgetHostViewMac::AboutToWaitForBackingStoreMsg() { AckPendingSwapBuffers(); } +void RenderWidgetHostViewMac::OnSwapCompositorFrame( + uint32 output_surface_id, scoped_ptr<cc::CompositorFrame> frame) { + // Only software compositor frames are accepted. + if (!frame->software_frame_data) { + DLOG(ERROR) << "Received unexpected frame type."; + RecordAction(UserMetricsAction( + "BadMessageTerminate_UnexpectedFrameType")); + render_widget_host_->GetProcess()->ReceivedBadMessage(); + return; + } + + GotSoftwareFrame(); + if (!software_frame_manager_->SwapToNewFrame( + output_surface_id, + frame->software_frame_data.get(), + frame->metadata.device_scale_factor, + render_widget_host_->GetProcess()->GetHandle())) { + render_widget_host_->GetProcess()->ReceivedBadMessage(); + return; + } + software_frame_manager_->SwapToNewFrameComplete( + !render_widget_host_->is_hidden()); + + [cocoa_view_ setNeedsDisplay:YES]; +} + void RenderWidgetHostViewMac::OnAcceleratedCompositingStateChange() { } @@ -1741,6 +1780,19 @@ bool RenderWidgetHostViewMac::Send(IPC::Message* message) { return false; } +void RenderWidgetHostViewMac::SoftwareFrameWasFreed( + uint32 output_surface_id, unsigned frame_id) { + cc::CompositorFrameAck ack; + ack.last_software_frame_id = frame_id; + RenderWidgetHostImpl::SendReclaimCompositorResources( + render_widget_host_->GetRoutingID(), + output_surface_id, + render_widget_host_->GetProcess()->GetID(), + ack); +} + +void RenderWidgetHostViewMac::ReleaseReferencesToSoftwareFrame() { +} void RenderWidgetHostViewMac::ShutdownHost() { weak_factory_.InvalidateWeakPtrs(); @@ -1764,6 +1816,7 @@ void RenderWidgetHostViewMac::GotAcceleratedFrame() { // Delete software backingstore. BackingStoreManager::RemoveBackingStore(render_widget_host_); + software_frame_manager_->DiscardCurrentFrame(); } } @@ -2718,29 +2771,62 @@ void RenderWidgetHostViewMac::FrameSwapped() { - (void)drawBackingStore:(BackingStoreMac*)backingStore dirtyRect:(CGRect)dirtyRect inContext:(CGContextRef)context { - if (backingStore) { + content::SoftwareFrameManager* software_frame_manager = + renderWidgetHostView_->software_frame_manager_.get(); + // There should never be both a legacy software and software composited + // frame. + DCHECK(!backingStore || !software_frame_manager->HasCurrentFrame()); + + if (backingStore || software_frame_manager->HasCurrentFrame()) { // Note: All coordinates are in view units, not pixels. - gfx::Rect bitmapRect(0, 0, - backingStore->size().width(), - backingStore->size().height()); + gfx::Rect bitmapRect( + software_frame_manager->HasCurrentFrame() ? + software_frame_manager->GetCurrentFrameSizeInDIP() : + backingStore->size()); // Specify the proper y offset to ensure that the view is rooted to the // upper left corner. This can be negative, if the window was resized // smaller and the renderer hasn't yet repainted. - int yOffset = NSHeight([self bounds]) - backingStore->size().height(); + int yOffset = NSHeight([self bounds]) - bitmapRect.height(); NSRect nsDirtyRect = NSRectFromCGRect(dirtyRect); const gfx::Rect damagedRect([self flipNSRectToRect:nsDirtyRect]); gfx::Rect paintRect = gfx::IntersectRects(bitmapRect, damagedRect); if (!paintRect.IsEmpty()) { - // if we have a CGLayer, draw that into the window - if (backingStore->cg_layer()) { + if (software_frame_manager->HasCurrentFrame()) { + // If a software compositor framebuffer is present, draw using that. + gfx::Size sizeInPixels = + software_frame_manager->GetCurrentFrameSizeInPixels(); + base::ScopedCFTypeRef<CGDataProviderRef> dataProvider( + CGDataProviderCreateWithData( + NULL, + software_frame_manager->GetCurrentFramePixels(), + 4 * sizeInPixels.width() * sizeInPixels.height(), + NULL)); + base::ScopedCFTypeRef<CGImageRef> image( + CGImageCreate( + sizeInPixels.width(), + sizeInPixels.height(), + 8, + 32, + 4 * sizeInPixels.width(), + base::mac::GetSystemColorSpace(), + kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, + dataProvider, + NULL, + false, + kCGRenderingIntentDefault)); + CGRect imageRect = bitmapRect.ToCGRect(); + imageRect.origin.y = yOffset; + CGContextDrawImage(context, imageRect, image); + } else if (backingStore->cg_layer()) { + // If we have a CGLayer, draw that into the window // TODO: add clipping to dirtyRect if it improves drawing performance. CGContextDrawLayerAtPoint(context, CGPointMake(0.0, yOffset), backingStore->cg_layer()); } else { - // if we haven't created a layer yet, draw the cached bitmap into + // If we haven't created a layer yet, draw the cached bitmap into // the window. The CGLayer will be created the next time the renderer // paints. base::ScopedCFTypeRef<CGImageRef> image( diff --git a/content/browser/renderer_host/software_frame_manager.cc b/content/browser/renderer_host/software_frame_manager.cc new file mode 100644 index 0000000..ac6fc3c --- /dev/null +++ b/content/browser/renderer_host/software_frame_manager.cc @@ -0,0 +1,243 @@ +// Copyright 2013 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 "content/browser/renderer_host/software_frame_manager.h" + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/sys_info.h" +#include "content/browser/renderer_host/dip_util.h" +#include "content/public/browser/user_metrics.h" + +namespace { + +void ReleaseMailbox(scoped_refptr<content::SoftwareFrame> frame, + unsigned sync_point, + bool lost_resource) {} + +} // namespace + +namespace content { + +//////////////////////////////////////////////////////////////////////////////// +// SoftwareFrame + +class CONTENT_EXPORT SoftwareFrame : public base::RefCounted<SoftwareFrame> { + private: + friend class base::RefCounted<SoftwareFrame>; + friend class SoftwareFrameManager; + + SoftwareFrame( + base::WeakPtr<SoftwareFrameManagerClient> frame_manager_client, + uint32 output_surface_id, + unsigned frame_id, + gfx::Size frame_size_dip, + gfx::Size frame_size_pixels, + scoped_ptr<base::SharedMemory> shared_memory); + ~SoftwareFrame(); + + base::WeakPtr<SoftwareFrameManagerClient> frame_manager_client_; + const uint32 output_surface_id_; + const unsigned frame_id_; + const gfx::Size frame_size_dip_; + const gfx::Size frame_size_pixels_; + scoped_ptr<base::SharedMemory> shared_memory_; + + DISALLOW_COPY_AND_ASSIGN(SoftwareFrame); +}; + +SoftwareFrame::SoftwareFrame( + base::WeakPtr<SoftwareFrameManagerClient> frame_manager_client, + uint32 output_surface_id, + unsigned frame_id, + gfx::Size frame_size_dip, + gfx::Size frame_size_pixels, + scoped_ptr<base::SharedMemory> shared_memory) + : frame_manager_client_(frame_manager_client), + output_surface_id_(output_surface_id), + frame_id_(frame_id), + frame_size_dip_(frame_size_dip), + frame_size_pixels_(frame_size_pixels), + shared_memory_(shared_memory.Pass()) {} + +SoftwareFrame::~SoftwareFrame() { + if (frame_manager_client_) { + frame_manager_client_->SoftwareFrameWasFreed( + output_surface_id_, frame_id_); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// SoftwareFrameManager + +SoftwareFrameManager::SoftwareFrameManager( + base::WeakPtr<SoftwareFrameManagerClient> client) + : client_(client) {} + +SoftwareFrameManager::~SoftwareFrameManager() { + DiscardCurrentFrame(); +} + +bool SoftwareFrameManager::SwapToNewFrame( + uint32 output_surface_id, + const cc::SoftwareFrameData* frame_data, + float frame_device_scale_factor, + base::ProcessHandle process_handle) { + +#ifdef OS_WIN + scoped_ptr<base::SharedMemory> shared_memory( + new base::SharedMemory(frame_data->handle, true, + process_handle)); +#else + scoped_ptr<base::SharedMemory> shared_memory( + new base::SharedMemory(frame_data->handle, true)); +#endif + + // The NULL handle is used in testing. + if (base::SharedMemory::IsHandleValid(shared_memory->handle())) { + const size_t size_in_bytes = 4 * frame_data->size.GetArea(); +#ifdef OS_WIN + if (!shared_memory->Map(0)) { + DLOG(ERROR) << "Unable to map renderer memory."; + RecordAction(UserMetricsAction( + "BadMessageTerminate_SharedMemoryManager1")); + return false; + } + + if (shared_memory->mapped_size() < size_in_bytes) { + DLOG(ERROR) << "Shared memory too small for given rectangle"; + RecordAction(UserMetricsAction( + "BadMessageTerminate_SharedMemoryManager2")); + return false; + } +#else + if (!shared_memory->Map(size_in_bytes)) { + DLOG(ERROR) << "Unable to map renderer memory."; + RecordAction(UserMetricsAction( + "BadMessageTerminate_SharedMemoryManager1")); + return false; + } +#endif + } + + scoped_refptr<SoftwareFrame> next_frame(new SoftwareFrame( + client_, + output_surface_id, + frame_data->id, + ConvertSizeToDIP(frame_device_scale_factor, frame_data->size), + frame_data->size, + shared_memory.Pass())); + current_frame_.swap(next_frame); + return true; +} + +bool SoftwareFrameManager::HasCurrentFrame() const { + return current_frame_.get() ? true : false; +} + +void SoftwareFrameManager::DiscardCurrentFrame() { + if (!HasCurrentFrame()) + return; + current_frame_ = NULL; + SoftwareFrameMemoryManager::GetInstance()->RemoveFrame(this); +} + +void SoftwareFrameManager::SwapToNewFrameComplete(bool visible) { + DCHECK(HasCurrentFrame()); + SoftwareFrameMemoryManager::GetInstance()->AddFrame(this, visible); +} + +void SoftwareFrameManager::SetVisibility(bool visible) { + if (HasCurrentFrame()) { + SoftwareFrameMemoryManager::GetInstance()->SetFrameVisibility(this, + visible); + } +} + +void SoftwareFrameManager::GetCurrentFrameMailbox( + cc::TextureMailbox* mailbox, + scoped_ptr<cc::SingleReleaseCallback>* callback) { + DCHECK(HasCurrentFrame()); + *mailbox = cc::TextureMailbox( + current_frame_->shared_memory_.get(), current_frame_->frame_size_pixels_); + *callback = cc::SingleReleaseCallback::Create( + base::Bind(ReleaseMailbox, current_frame_)); +} + +const void* SoftwareFrameManager::GetCurrentFramePixels() const { + DCHECK(HasCurrentFrame()); + DCHECK(base::SharedMemory::IsHandleValid( + current_frame_->shared_memory_->handle())); + return current_frame_->shared_memory_->memory(); +} + +gfx::Size SoftwareFrameManager::GetCurrentFrameSizeInPixels() const { + DCHECK(HasCurrentFrame()); + return current_frame_->frame_size_pixels_; +} + +gfx::Size SoftwareFrameManager::GetCurrentFrameSizeInDIP() const { + DCHECK(HasCurrentFrame()); + return current_frame_->frame_size_dip_; +} + +void SoftwareFrameManager::EvictCurrentFrame() { + DCHECK(HasCurrentFrame()); + DiscardCurrentFrame(); + if (client_) + client_->ReleaseReferencesToSoftwareFrame(); +} + +//////////////////////////////////////////////////////////////////////////////// +// SoftwareFrameMemoryManager + +SoftwareFrameMemoryManager* SoftwareFrameMemoryManager::GetInstance() { + return Singleton<SoftwareFrameMemoryManager>::get(); +} + +void SoftwareFrameMemoryManager::AddFrame(SoftwareFrameManager* frame, + bool visible) { + RemoveFrame(frame); + if (visible) + visible_frames_.insert(frame); + else + hidden_frames_.push_front(frame); + CullHiddenFrames(); +} + +void SoftwareFrameMemoryManager::RemoveFrame(SoftwareFrameManager* frame) { + visible_frames_.erase(frame); + hidden_frames_.remove(frame); +} + +void SoftwareFrameMemoryManager::SetFrameVisibility(SoftwareFrameManager* frame, + bool visible) { + if (visible) { + hidden_frames_.remove(frame); + visible_frames_.insert(frame); + } else { + visible_frames_.erase(frame); + hidden_frames_.push_front(frame); + CullHiddenFrames(); + } +} + +SoftwareFrameMemoryManager::SoftwareFrameMemoryManager() + : max_number_of_saved_frames_( + std::min(5, 2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256))) {} + +SoftwareFrameMemoryManager::~SoftwareFrameMemoryManager() {} + +void SoftwareFrameMemoryManager::CullHiddenFrames() { + while (!hidden_frames_.empty() && + hidden_frames_.size() + visible_frames_.size() > + max_number_of_saved_frames()) { + size_t old_size = hidden_frames_.size(); + // Should remove self from list. + hidden_frames_.back()->EvictCurrentFrame(); + DCHECK_EQ(hidden_frames_.size() + 1, old_size); + } +} + +} // namespace content diff --git a/content/browser/renderer_host/software_frame_manager.h b/content/browser/renderer_host/software_frame_manager.h new file mode 100644 index 0000000..d7f186f --- /dev/null +++ b/content/browser/renderer_host/software_frame_manager.h @@ -0,0 +1,105 @@ +// Copyright 2013 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 CONTENT_BROWSER_RENDERER_HOST_SOFTWARE_FRAME_MANAGER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_SOFTWARE_FRAME_MANAGER_H_ + +#include <list> +#include <set> + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/shared_memory.h" +#include "base/memory/singleton.h" +#include "base/memory/weak_ptr.h" +#include "cc/output/software_frame_data.h" +#include "cc/resources/single_release_callback.h" +#include "cc/resources/texture_mailbox.h" +#include "content/common/content_export.h" +#include "ui/gfx/size.h" + +namespace content { +class SoftwareFrame; +class SoftwareFrameMemoryManager; + +class CONTENT_EXPORT SoftwareFrameManagerClient { + public: + // Called when the memory for the current software frame was freed. + virtual void SoftwareFrameWasFreed( + uint32 output_surface_id, unsigned frame_id) = 0; + + // Called when the SoftwareFrameMemoryManager has requested that the frame + // be evicted. Upon receiving this callback, the client should release any + // references that it may hold to the current frame, to ensure that its memory + // is freed expediently. + virtual void ReleaseReferencesToSoftwareFrame() = 0; +}; + +class CONTENT_EXPORT SoftwareFrameManager { + public: + explicit SoftwareFrameManager( + base::WeakPtr<SoftwareFrameManagerClient> client); + ~SoftwareFrameManager(); + + // Swaps to a new frame from shared memory. This frame is guaranteed to + // not be evicted until SwapToNewFrameComplete is called. + bool SwapToNewFrame( + uint32 output_surface_id, + const cc::SoftwareFrameData* frame_data, + float frame_device_scale_factor, + base::ProcessHandle process_handle); + void SwapToNewFrameComplete(bool visible); + void SetVisibility(bool visible); + bool HasCurrentFrame() const; + void DiscardCurrentFrame(); + void GetCurrentFrameMailbox( + cc::TextureMailbox* mailbox, + scoped_ptr<cc::SingleReleaseCallback>* callback); + const void* GetCurrentFramePixels() const; + gfx::Size GetCurrentFrameSizeInPixels() const; + gfx::Size GetCurrentFrameSizeInDIP() const; + + private: + friend class SoftwareFrameMemoryManager; + + // Called by SoftwareFrameMemoryManager to demand that the current frame + // be evicted. + void EvictCurrentFrame(); + + base::WeakPtr<SoftwareFrameManagerClient> client_; + + // This holds the current software framebuffer. + scoped_refptr<SoftwareFrame> current_frame_; + + DISALLOW_COPY_AND_ASSIGN(SoftwareFrameManager); +}; + +class CONTENT_EXPORT SoftwareFrameMemoryManager { + public: + static SoftwareFrameMemoryManager* GetInstance(); + + void AddFrame(SoftwareFrameManager*, bool visible); + void RemoveFrame(SoftwareFrameManager*); + void SetFrameVisibility(SoftwareFrameManager*, bool visible); + + size_t max_number_of_saved_frames() const { + return max_number_of_saved_frames_; + } + + private: + SoftwareFrameMemoryManager(); + ~SoftwareFrameMemoryManager(); + void CullHiddenFrames(); + friend struct DefaultSingletonTraits<SoftwareFrameMemoryManager>; + + std::set<SoftwareFrameManager*> visible_frames_; + std::list<SoftwareFrameManager*> hidden_frames_; + size_t max_number_of_saved_frames_; + + DISALLOW_COPY_AND_ASSIGN(SoftwareFrameMemoryManager); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_SOFTWARE_FRAME_MANAGER_H_ diff --git a/content/browser/renderer_host/software_frame_manager_unittest.cc b/content/browser/renderer_host/software_frame_manager_unittest.cc new file mode 100644 index 0000000..eadbfb6 --- /dev/null +++ b/content/browser/renderer_host/software_frame_manager_unittest.cc @@ -0,0 +1,255 @@ +// Copyright 2013 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 "content/browser/renderer_host/software_frame_manager.h" + +#include <vector> + +#include "base/sys_info.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { + +class FakeSoftwareFrameManagerClient : public SoftwareFrameManagerClient { + public: + FakeSoftwareFrameManagerClient() + : evicted_count_(0), weak_ptr_factory_(this) { + software_frame_manager_.reset(new SoftwareFrameManager( + weak_ptr_factory_.GetWeakPtr())); + } + virtual ~FakeSoftwareFrameManagerClient() {} + virtual void SoftwareFrameWasFreed( + uint32 output_surface_id, unsigned frame_id) OVERRIDE { + freed_frames_.push_back(std::make_pair(output_surface_id, frame_id)); + } + virtual void ReleaseReferencesToSoftwareFrame() OVERRIDE { + ++evicted_count_; + } + + bool SwapToNewFrame(uint32 output_surface, unsigned frame_id) { + cc::SoftwareFrameData frame; + frame.id = frame_id; + frame.size = gfx::Size(1, 1); + frame.damage_rect = gfx::Rect(frame.size); + frame.handle = base::SharedMemory::NULLHandle(); + return software_frame_manager_->SwapToNewFrame( + output_surface, &frame, 1.0, base::GetCurrentProcessHandle()); + } + + SoftwareFrameManager* software_frame_manager() { + return software_frame_manager_.get(); + } + size_t freed_frame_count() const { return freed_frames_.size(); } + size_t evicted_frame_count() const { return evicted_count_; } + + private: + std::vector<std::pair<uint32,unsigned> > freed_frames_; + size_t evicted_count_; + + scoped_ptr<SoftwareFrameManager> software_frame_manager_; + base::WeakPtrFactory<FakeSoftwareFrameManagerClient> + weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(FakeSoftwareFrameManagerClient); +}; + +class SoftwareFrameManagerTest : public testing::Test { + public: + SoftwareFrameManagerTest() {} + void AllocateClients(size_t num_clients) { + for (size_t i = 0; i < num_clients; ++i) + clients_.push_back(new FakeSoftwareFrameManagerClient); + } + void FreeClients() { + for (size_t i = 0; i < clients_.size(); ++i) + delete clients_[i]; + clients_.clear(); + } + size_t MaxNumberOfSavedFrames() const { + size_t result = SoftwareFrameMemoryManager::GetInstance()-> + max_number_of_saved_frames(); + return result; + } + + protected: + std::vector<FakeSoftwareFrameManagerClient*> clients_; + + private: + DISALLOW_COPY_AND_ASSIGN(SoftwareFrameManagerTest); +}; + +TEST_F(SoftwareFrameManagerTest, DoNotEvictVisible) { + // Create twice as many frames as are allowed. + AllocateClients(2 * MaxNumberOfSavedFrames()); + + // Swap a visible frame to all clients_. Because they are all visible, + // the should not be evicted. + for (size_t i = 0; i < clients_.size(); ++i) { + bool swap_result = clients_[i]->SwapToNewFrame( + static_cast<uint32>(i), 0); + clients_[i]->software_frame_manager()->SwapToNewFrameComplete(true); + EXPECT_TRUE(swap_result); + EXPECT_EQ(0u, clients_[i]->evicted_frame_count()); + EXPECT_EQ(0u, clients_[i]->freed_frame_count()); + } + for (size_t i = 0; i < clients_.size(); ++i) { + EXPECT_EQ(0u, clients_[i]->evicted_frame_count()); + EXPECT_EQ(0u, clients_[i]->freed_frame_count()); + } + + // Swap another frame and make sure the original was freed (but not evicted). + for (size_t i = 0; i < clients_.size(); ++i) { + bool swap_result = clients_[i]->SwapToNewFrame( + static_cast<uint32>(i), 1); + clients_[i]->software_frame_manager()->SwapToNewFrameComplete(true); + EXPECT_TRUE(swap_result); + EXPECT_EQ(0u, clients_[i]->evicted_frame_count()); + EXPECT_EQ(1u, clients_[i]->freed_frame_count()); + } + for (size_t i = 0; i < clients_.size(); ++i) { + EXPECT_EQ(0u, clients_[i]->evicted_frame_count()); + EXPECT_EQ(1u, clients_[i]->freed_frame_count()); + } + + // Mark the frames as nonvisible and make sure they start getting evicted. + for (size_t i = 0; i < clients_.size(); ++i) { + clients_[i]->software_frame_manager()->SetVisibility(false); + if (clients_.size() - i > MaxNumberOfSavedFrames()) { + EXPECT_EQ(1u, clients_[i]->evicted_frame_count()); + EXPECT_EQ(2u, clients_[i]->freed_frame_count()); + } else { + EXPECT_EQ(0u, clients_[i]->evicted_frame_count()); + EXPECT_EQ(1u, clients_[i]->freed_frame_count()); + } + } + + // Clean up. + FreeClients(); +} + +TEST_F(SoftwareFrameManagerTest, DoNotEvictDuringSwap) { + // Create twice as many frames as are allowed. + AllocateClients(2 * MaxNumberOfSavedFrames()); + + // Swap a visible frame to all clients_. Because they are all visible, + // the should not be evicted. + for (size_t i = 0; i < clients_.size(); ++i) { + bool swap_result = clients_[i]->SwapToNewFrame(static_cast<uint32>(i), 0); + clients_[i]->software_frame_manager()->SwapToNewFrameComplete(true); + EXPECT_TRUE(swap_result); + EXPECT_EQ(0u, clients_[i]->evicted_frame_count()); + EXPECT_EQ(0u, clients_[i]->freed_frame_count()); + } + for (size_t i = 0; i < clients_.size(); ++i) { + EXPECT_EQ(0u, clients_[i]->evicted_frame_count()); + EXPECT_EQ(0u, clients_[i]->freed_frame_count()); + } + + // Now create a test non-visible client, and swap a non-visible frame in. + scoped_ptr<FakeSoftwareFrameManagerClient> test_client( + new FakeSoftwareFrameManagerClient); + test_client->software_frame_manager()->SetVisibility(false); + { + bool swap_result = test_client->SwapToNewFrame( + static_cast<uint32>(500), 0); + EXPECT_TRUE(swap_result); + EXPECT_EQ(0u, test_client->evicted_frame_count()); + EXPECT_EQ(0u, test_client->freed_frame_count()); + test_client->software_frame_manager()->SwapToNewFrameComplete(false); + EXPECT_EQ(1u, test_client->evicted_frame_count()); + EXPECT_EQ(1u, test_client->freed_frame_count()); + } + + // Clean up. + FreeClients(); +} + +TEST_F(SoftwareFrameManagerTest, Cleanup) { + // Create twice as many frames as are allowed. + AllocateClients(2 * MaxNumberOfSavedFrames()); + + // Swap a visible frame to all clients_. Because they are all visible, + // the should not be evicted. + for (size_t i = 0; i < clients_.size(); ++i) { + bool swap_result = clients_[i]->SwapToNewFrame(static_cast<uint32>(i), 0); + clients_[i]->software_frame_manager()->SwapToNewFrameComplete(true); + EXPECT_TRUE(swap_result); + EXPECT_EQ(0u, clients_[i]->evicted_frame_count()); + EXPECT_EQ(0u, clients_[i]->freed_frame_count()); + } + + // Destroy them. + FreeClients(); + + // Create the maximum number of frames, all non-visible. They should not + // be evicted, because the previous frames were cleaned up at destruction. + AllocateClients(MaxNumberOfSavedFrames()); + for (size_t i = 0; i < clients_.size(); ++i) { + cc::SoftwareFrameData frame; + bool swap_result = clients_[i]->SwapToNewFrame(static_cast<uint32>(i), 0); + clients_[i]->software_frame_manager()->SwapToNewFrameComplete(true); + EXPECT_TRUE(swap_result); + EXPECT_EQ(0u, clients_[i]->evicted_frame_count()); + EXPECT_EQ(0u, clients_[i]->freed_frame_count()); + } + for (size_t i = 0; i < clients_.size(); ++i) { + EXPECT_EQ(0u, clients_[i]->evicted_frame_count()); + EXPECT_EQ(0u, clients_[i]->freed_frame_count()); + } + + // Clean up. + FreeClients(); +} + +TEST_F(SoftwareFrameManagerTest, EvictVersusFree) { + // Create twice as many frames as are allowed and swap a visible frame to all + // clients_. Because they are all visible, the should not be evicted. + AllocateClients(2 * MaxNumberOfSavedFrames()); + for (size_t i = 0; i < clients_.size(); ++i) { + clients_[i]->SwapToNewFrame(static_cast<uint32>(i), 0); + clients_[i]->software_frame_manager()->SwapToNewFrameComplete(true); + } + + // Create a test client with a frame that is not evicted. + scoped_ptr<FakeSoftwareFrameManagerClient> test_client( + new FakeSoftwareFrameManagerClient); + bool swap_result = test_client->SwapToNewFrame(static_cast<uint32>(500), 0); + EXPECT_TRUE(swap_result); + test_client->software_frame_manager()->SwapToNewFrameComplete(true); + EXPECT_EQ(0u, test_client->evicted_frame_count()); + EXPECT_EQ(0u, test_client->freed_frame_count()); + + // Take out a reference on the current frame and make the memory manager + // evict it. The frame will not be freed until this reference is released. + cc::TextureMailbox mailbox; + scoped_ptr<cc::SingleReleaseCallback> callback; + test_client->software_frame_manager()->GetCurrentFrameMailbox( + &mailbox, &callback); + test_client->software_frame_manager()->SetVisibility(false); + EXPECT_EQ(1u, test_client->evicted_frame_count()); + EXPECT_EQ(0u, test_client->freed_frame_count()); + + // Swap a few frames. The frames will be freed as they are swapped out. + for (size_t frame = 0; frame < 10; ++frame) { + bool swap_result = test_client->SwapToNewFrame( + static_cast<uint32>(500), 1 + static_cast<int>(frame)); + EXPECT_TRUE(swap_result); + test_client->software_frame_manager()->SwapToNewFrameComplete(true); + EXPECT_EQ(frame, test_client->freed_frame_count()); + EXPECT_EQ(1u, test_client->evicted_frame_count()); + } + + // The reference to the frame that we didn't free is in the callback + // object. It will go away when the callback is destroyed. + EXPECT_EQ(9u, test_client->freed_frame_count()); + EXPECT_EQ(1u, test_client->evicted_frame_count()); + callback->Run(0, false); + callback.reset(); + EXPECT_EQ(10u, test_client->freed_frame_count()); + EXPECT_EQ(1u, test_client->evicted_frame_count()); + + FreeClients(); +} + +} // namespace content diff --git a/content/content_browser.gypi b/content/content_browser.gypi index aec6539..06cefae 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -789,8 +789,8 @@ 'browser/renderer_host/dip_util.h', 'browser/renderer_host/file_utilities_message_filter.cc', 'browser/renderer_host/file_utilities_message_filter.h', - 'browser/renderer_host/frame_memory_manager.cc', - 'browser/renderer_host/frame_memory_manager.h', + 'browser/renderer_host/software_frame_manager.cc', + 'browser/renderer_host/software_frame_manager.h', 'browser/renderer_host/frame_tree.cc', 'browser/renderer_host/frame_tree.h', 'browser/renderer_host/frame_tree_node.cc', diff --git a/content/content_tests.gypi b/content/content_tests.gypi index 3ee001c..09673f5 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -442,6 +442,7 @@ 'browser/renderer_host/render_widget_host_view_guest_unittest.cc', 'browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm', 'browser/renderer_host/render_widget_host_view_mac_unittest.mm', + 'browser/renderer_host/software_frame_manager_unittest.cc', 'browser/renderer_host/synthetic_gesture_controller_unittest.cc', 'browser/renderer_host/text_input_client_mac_unittest.mm', 'browser/renderer_host/web_input_event_aura_unittest.cc', |