diff options
-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', |