diff options
author | hush@chromium.org <hush@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-03 02:17:07 +0000 |
---|---|---|
committer | hush@chromium.org <hush@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-03 02:17:07 +0000 |
commit | 7d67302a01daf37df42a5526654b84ccd7b244cd (patch) | |
tree | cb1ab023895919fb194cc83618f8f68fc59f85e0 /android_webview/browser | |
parent | fa34edcaf8d6f22221f797590371417644c15930 (diff) | |
download | chromium_src-7d67302a01daf37df42a5526654b84ccd7b244cd.zip chromium_src-7d67302a01daf37df42a5526654b84ccd7b244cd.tar.gz chromium_src-7d67302a01daf37df42a5526654b84ccd7b244cd.tar.bz2 |
Global GPU memory manager for android webview
BUG=354155
Review URL: https://codereview.chromium.org/226363004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@268006 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'android_webview/browser')
-rw-r--r-- | android_webview/browser/browser_view_renderer.cc | 151 | ||||
-rw-r--r-- | android_webview/browser/browser_view_renderer.h | 39 | ||||
-rw-r--r-- | android_webview/browser/global_tile_manager.cc | 135 | ||||
-rw-r--r-- | android_webview/browser/global_tile_manager.h | 74 | ||||
-rw-r--r-- | android_webview/browser/global_tile_manager_client.h | 26 | ||||
-rw-r--r-- | android_webview/browser/global_tile_manager_unittest.cc | 157 | ||||
-rw-r--r-- | android_webview/browser/hardware_renderer.cc | 101 | ||||
-rw-r--r-- | android_webview/browser/hardware_renderer.h | 9 | ||||
-rw-r--r-- | android_webview/browser/shared_renderer_state.cc | 26 | ||||
-rw-r--r-- | android_webview/browser/shared_renderer_state.h | 10 |
10 files changed, 627 insertions, 101 deletions
diff --git a/android_webview/browser/browser_view_renderer.cc b/android_webview/browser/browser_view_renderer.cc index c6188e3..7db9816 100644 --- a/android_webview/browser/browser_view_renderer.cc +++ b/android_webview/browser/browser_view_renderer.cc @@ -9,11 +9,15 @@ #include "android_webview/public/browser/draw_gl.h" #include "base/android/jni_android.h" #include "base/auto_reset.h" +#include "base/command_line.h" #include "base/debug/trace_event.h" #include "base/logging.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "content/public/browser/android/synchronous_compositor.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/web_contents.h" +#include "content/public/common/content_switches.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkPicture.h" @@ -23,6 +27,8 @@ using base::android::AttachCurrentThread; using base::android::JavaRef; using base::android::ScopedJavaLocalRef; +using content::BrowserThread; +using content::SynchronousCompositorMemoryPolicy; namespace android_webview { @@ -30,6 +36,18 @@ namespace { const int64 kFallbackTickTimeoutInMilliseconds = 20; +// Used to calculate memory allocation. Determined experimentally. +const size_t kMemoryMultiplier = 10; +const size_t kBytesPerPixel = 4; +const size_t kMemoryAllocationStep = 5 * 1024 * 1024; + +// Used to calculate tile allocation. Determined experimentally. +const size_t kTileMultiplier = 12; +const size_t kTileAllocationStep = 20; +// This will be set by static function CalculateTileMemoryPolicy() during init. +// See AwMainDelegate::BasicStartupComplete. +size_t g_tile_area; + class AutoResetWithLock { public: AutoResetWithLock(gfx::Vector2dF* scoped_variable, @@ -57,6 +75,22 @@ class AutoResetWithLock { } // namespace +// static +void BrowserViewRenderer::CalculateTileMemoryPolicy() { + CommandLine* cl = CommandLine::ForCurrentProcess(); + const char kDefaultTileSize[] = "384"; + + if (!cl->HasSwitch(switches::kDefaultTileWidth)) + cl->AppendSwitchASCII(switches::kDefaultTileWidth, kDefaultTileSize); + + if (!cl->HasSwitch(switches::kDefaultTileHeight)) + cl->AppendSwitchASCII(switches::kDefaultTileHeight, kDefaultTileSize); + + size_t tile_size; + base::StringToSizeT(kDefaultTileSize, &tile_size); + g_tile_area = tile_size * tile_size; +} + BrowserViewRenderer::BrowserViewRenderer( BrowserViewRendererClient* client, SharedRendererState* shared_renderer_state, @@ -90,6 +124,99 @@ BrowserViewRenderer::BrowserViewRenderer( BrowserViewRenderer::~BrowserViewRenderer() { content::SynchronousCompositor::SetClientForWebContents(web_contents_, NULL); + // OnDetachedFromWindow should be called before the destructor, so the memory + // policy should have already been updated. +} + +// This function updates the cached memory policy in shared renderer state, as +// well as the tile resource allocation in GlobalTileManager. +void BrowserViewRenderer::TrimMemory(const int level, const bool visible) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // Constants from Android ComponentCallbacks2. + enum { + TRIM_MEMORY_RUNNING_LOW = 10, + TRIM_MEMORY_UI_HIDDEN = 20, + TRIM_MEMORY_BACKGROUND = 40, + }; + + // Not urgent enough. TRIM_MEMORY_UI_HIDDEN is treated specially because + // it does not indicate memory pressure, but merely that the app is + // backgrounded. + if (level < TRIM_MEMORY_RUNNING_LOW || level == TRIM_MEMORY_UI_HIDDEN) + return; + + // Do not release resources on view we expect to get DrawGL soon. + if (level < TRIM_MEMORY_BACKGROUND && visible) + return; + + // Just set the memory limit to 0 and drop all tiles. This will be reset to + // normal levels in the next DrawGL call. + SynchronousCompositorMemoryPolicy zero_policy; + if (shared_renderer_state_->GetMemoryPolicy() == zero_policy) + return; + + TRACE_EVENT0("android_webview", "BrowserViewRenderer::TrimMemory"); + + RequestMemoryPolicy(zero_policy); + EnforceMemoryPolicyImmediately(zero_policy); +} + +SynchronousCompositorMemoryPolicy +BrowserViewRenderer::CalculateDesiredMemoryPolicy() { + SynchronousCompositorMemoryPolicy policy; + size_t width = draw_gl_input_.global_visible_rect.width(); + size_t height = draw_gl_input_.global_visible_rect.height(); + policy.bytes_limit = kMemoryMultiplier * kBytesPerPixel * width * height; + // Round up to a multiple of kMemoryAllocationStep. + policy.bytes_limit = + (policy.bytes_limit / kMemoryAllocationStep + 1) * kMemoryAllocationStep; + + size_t tiles = std::max(width * height * kTileMultiplier / g_tile_area, 1u); + // Round up to a multiple of kTileAllocationStep. The minimum number of tiles + // is also kTileAllocationStep. + tiles = (tiles + kTileAllocationStep - 1) / kTileAllocationStep * + kTileAllocationStep; + policy.num_resources_limit = tiles; + return policy; +} + +// This function updates the cached memory policy in shared renderer state, as +// well as the tile resource allocation in GlobalTileManager. +void BrowserViewRenderer::RequestMemoryPolicy( + SynchronousCompositorMemoryPolicy& new_policy) { + // This will be used in SetNumTiles. + num_bytes_ = new_policy.bytes_limit; + + GlobalTileManager* manager = GlobalTileManager::GetInstance(); + + // The following line will call BrowserViewRenderer::SetTilesNum(). + manager->RequestTiles(new_policy.num_resources_limit, tile_manager_key_); +} + +void BrowserViewRenderer::SetNumTiles(size_t num_tiles, + bool effective_immediately) { + if (num_tiles == num_tiles_) + return; + num_tiles_ = num_tiles; + + SynchronousCompositorMemoryPolicy new_policy; + new_policy.num_resources_limit = num_tiles_; + new_policy.bytes_limit = num_bytes_; + shared_renderer_state_->SetMemoryPolicy(new_policy); + + if (effective_immediately) + EnforceMemoryPolicyImmediately(new_policy); +} + +void BrowserViewRenderer::EnforceMemoryPolicyImmediately( + SynchronousCompositorMemoryPolicy new_policy) { + shared_renderer_state_->GetCompositor()->SetMemoryPolicy(new_policy); + ForceFakeCompositeSW(); + shared_renderer_state_->SetMemoryPolicyDirty(false); +} + +size_t BrowserViewRenderer::GetNumTiles() const { + return shared_renderer_state_->GetMemoryPolicy().num_resources_limit; } bool BrowserViewRenderer::OnDraw(jobject java_canvas, @@ -106,10 +233,23 @@ bool BrowserViewRenderer::OnDraw(jobject java_canvas, return false; if (is_hardware_canvas && attached_to_window_) { shared_renderer_state_->SetDrawGLInput(draw_gl_input_); + + SynchronousCompositorMemoryPolicy old_policy = + shared_renderer_state_->GetMemoryPolicy(); + SynchronousCompositorMemoryPolicy new_policy = + CalculateDesiredMemoryPolicy(); + RequestMemoryPolicy(new_policy); // We should be performing a hardware draw here. If we don't have the // compositor yet or if RequestDrawGL fails, it means we failed this draw // and thus return false here to clear to background color for this draw. - return has_compositor_ && client_->RequestDrawGL(java_canvas, false); + bool did_draw_gl = + has_compositor_ && client_->RequestDrawGL(java_canvas, false); + if (did_draw_gl) + GlobalTileManager::GetInstance()->DidUse(tile_manager_key_); + else + RequestMemoryPolicy(old_policy); + + return did_draw_gl; } // Perform a software draw return DrawSWInternal(java_canvas, clip); @@ -230,11 +370,18 @@ void BrowserViewRenderer::OnAttachedToWindow(int width, int height) { attached_to_window_ = true; width_ = width; height_ = height; + tile_manager_key_ = GlobalTileManager::GetInstance()->PushBack(this); } void BrowserViewRenderer::OnDetachedFromWindow() { TRACE_EVENT0("android_webview", "BrowserViewRenderer::OnDetachedFromWindow"); attached_to_window_ = false; + SynchronousCompositorMemoryPolicy zero_policy; + RequestMemoryPolicy(zero_policy); + GlobalTileManager::GetInstance()->Remove(tile_manager_key_); + // The hardware resources are released in the destructor of hardware renderer, + // so we don't need to do it here. + // See AwContents::ReleaseHardwareDrawOnRenderThread(JNIEnv*, jobject). } bool BrowserViewRenderer::IsAttachedToWindow() const { @@ -268,6 +415,8 @@ void BrowserViewRenderer::DidDestroyCompositor( DCHECK(ui_task_runner_->BelongsToCurrentThread()); has_compositor_ = false; shared_renderer_state_->SetCompositorOnUiThread(NULL); + SynchronousCompositorMemoryPolicy zero_policy; + DCHECK(shared_renderer_state_->GetMemoryPolicy() == zero_policy); } void BrowserViewRenderer::SetContinuousInvalidate(bool invalidate) { diff --git a/android_webview/browser/browser_view_renderer.h b/android_webview/browser/browser_view_renderer.h index da82fce..1843644 100644 --- a/android_webview/browser/browser_view_renderer.h +++ b/android_webview/browser/browser_view_renderer.h @@ -5,6 +5,8 @@ #ifndef ANDROID_WEBVIEW_BROWSER_BROWSER_VIEW_RENDERER_H_ #define ANDROID_WEBVIEW_BROWSER_BROWSER_VIEW_RENDERER_H_ +#include "android_webview/browser/global_tile_manager.h" +#include "android_webview/browser/global_tile_manager_client.h" #include "android_webview/browser/shared_renderer_state.h" #include "base/android/scoped_java_ref.h" #include "base/callback.h" @@ -22,6 +24,7 @@ struct AwDrawSWFunctionTable; namespace content { class ContentViewCore; class SynchronousCompositor; +struct SynchronousCompositorMemoryPolicy; class WebContents; } @@ -52,8 +55,11 @@ class BrowserViewRendererJavaHelper { // Interface for all the WebView-specific content rendering operations. // Provides software and hardware rendering and the Capture Picture API. -class BrowserViewRenderer : public content::SynchronousCompositorClient { +class BrowserViewRenderer : public content::SynchronousCompositorClient, + public GlobalTileManagerClient { public: + static void CalculateTileMemoryPolicy(); + BrowserViewRenderer( BrowserViewRendererClient* client, SharedRendererState* shared_renderer_state, @@ -101,10 +107,12 @@ class BrowserViewRenderer : public content::SynchronousCompositorClient { bool IsVisible() const; gfx::Rect GetScreenRect() const; - // Force invoke the compositor to run produce a 1x1 software frame that is - // immediately discarded. This is a hack to force invoke parts of the - // compositor that are not directly exposed here. - void ForceFakeCompositeSW(); + // Set the memory policy in shared renderer state and request the tiles from + // GlobalTileManager. The actually amount of memory allowed by + // GlobalTileManager may not be equal to what's requested in |policy|. + void RequestMemoryPolicy(content::SynchronousCompositorMemoryPolicy& policy); + + void TrimMemory(const int level, const bool visible); // SynchronousCompositorClient overrides virtual void DidInitializeCompositor( @@ -127,6 +135,11 @@ class BrowserViewRenderer : public content::SynchronousCompositorClient { gfx::Vector2dF latest_overscroll_delta, gfx::Vector2dF current_fling_velocity) OVERRIDE; + // GlobalTileManagerClient overrides + virtual size_t GetNumTiles() const OVERRIDE; + virtual void SetNumTiles(size_t num_tiles, + bool effective_immediately) OVERRIDE; + private: // Checks the continuous invalidate and block invalidate state, and schedule // invalidates appropriately. If |force_invalidate| is true, then send a view @@ -140,8 +153,17 @@ class BrowserViewRenderer : public content::SynchronousCompositorClient { // then we keep ticking the SynchronousCompositor so it can make progress. void FallbackTickFired(); + // Force invoke the compositor to run produce a 1x1 software frame that is + // immediately discarded. This is a hack to force invoke parts of the + // compositor that are not directly exposed here. + void ForceFakeCompositeSW(); + + void EnforceMemoryPolicyImmediately( + content::SynchronousCompositorMemoryPolicy policy); + gfx::Vector2d max_scroll_offset() const; + content::SynchronousCompositorMemoryPolicy CalculateDesiredMemoryPolicy(); // For debug tracing or logging. Return the string representation of this // view renderer's state and the |draw_info| if provided. std::string ToString(AwDrawGLInfo* draw_info) const; @@ -199,6 +221,13 @@ class BrowserViewRenderer : public content::SynchronousCompositorClient { // spot over a period of time). gfx::Vector2dF overscroll_rounding_error_; + GlobalTileManager::Key tile_manager_key_; + + // The following 2 are used to construct a memory policy and set the memory + // policy on the shared_renderer_state_ atomically. + size_t num_tiles_; + size_t num_bytes_; + DISALLOW_COPY_AND_ASSIGN(BrowserViewRenderer); }; diff --git a/android_webview/browser/global_tile_manager.cc b/android_webview/browser/global_tile_manager.cc new file mode 100644 index 0000000..efc3039 --- /dev/null +++ b/android_webview/browser/global_tile_manager.cc @@ -0,0 +1,135 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "android_webview/browser/global_tile_manager.h" +#include "android_webview/browser/global_tile_manager_client.h" +#include "base/lazy_instance.h" + +namespace android_webview { + +namespace { +base::LazyInstance<GlobalTileManager>::Leaky g_tile_manager = + LAZY_INSTANCE_INITIALIZER; +// The soft limit of the number of file descriptors per process is 1024 on +// Android and gralloc buffers may not be the only thing that uses file +// descriptors. For each tile, there is a gralloc buffer backing it, which +// uses 2 FDs. +const size_t kNumTilesLimit = 450; + +} // namespace + +// static +GlobalTileManager* GlobalTileManager::GetInstance() { + return g_tile_manager.Pointer(); +} + +void GlobalTileManager::Remove(Key key) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + DCHECK(mru_list_.end() != key); + + total_allocated_tiles_ -= (*key)->GetNumTiles(); + mru_list_.erase(key); + DCHECK(IsConsistent()); +} + +size_t GlobalTileManager::Evict(size_t desired_num_tiles, Key key) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + size_t total_evicted_tiles = 0; + + // Evicts from the least recent drawn view, until the disired number of tiles + // can be reclaimed, or until we've evicted all inactive views. + ListType::reverse_iterator it; + for (it = mru_list_.rbegin(); it != mru_list_.rend(); it++) { + // key represents the view that requested the eviction, so we don't need to + // evict the requester itself. And we only evict the inactive views, + // which are all the views after the requester. + if (*it == *key) + break; + + size_t evicted_tiles = (*it)->GetNumTiles(); + (*it)->SetNumTiles(0, true); + + total_evicted_tiles += evicted_tiles; + if (total_evicted_tiles >= desired_num_tiles) + break; + } + + return total_evicted_tiles; +} + +void GlobalTileManager::RequestTiles(size_t new_num_of_tiles, Key key) { + DCHECK(IsConsistent()); + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + size_t old_num_of_tiles = (*key)->GetNumTiles(); + size_t num_of_active_views = std::distance(mru_list_.begin(), key) + 1; + size_t tiles_per_view_limit; + if (num_of_active_views == 0) + tiles_per_view_limit = kNumTilesLimit; + else + tiles_per_view_limit = kNumTilesLimit / num_of_active_views; + new_num_of_tiles = std::min(new_num_of_tiles, tiles_per_view_limit); + size_t new_total_allocated_tiles = + total_allocated_tiles_ - old_num_of_tiles + new_num_of_tiles; + // Has enough tiles to satisfy the request. + if (new_total_allocated_tiles <= kNumTilesLimit) { + total_allocated_tiles_ = new_total_allocated_tiles; + (*key)->SetNumTiles(new_num_of_tiles, false); + return; + } + + // Does not have enough tiles. Now evict other clients' tiles. + size_t tiles_left = kNumTilesLimit - total_allocated_tiles_; + + size_t evicted_tiles = Evict(new_total_allocated_tiles - kNumTilesLimit, key); + if (evicted_tiles >= new_total_allocated_tiles - kNumTilesLimit) { + new_total_allocated_tiles -= evicted_tiles; + total_allocated_tiles_ = new_total_allocated_tiles; + (*key)->SetNumTiles(new_num_of_tiles, false); + return; + } else { + total_allocated_tiles_ = kNumTilesLimit; + (*key)->SetNumTiles(tiles_left + old_num_of_tiles + evicted_tiles, false); + return; + } +} + +GlobalTileManager::Key GlobalTileManager::PushBack( + GlobalTileManagerClient* client) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + DCHECK(mru_list_.end() == + std::find(mru_list_.begin(), mru_list_.end(), client)); + mru_list_.push_back(client); + Key back = mru_list_.end(); + back--; + return back; +} + +void GlobalTileManager::DidUse(Key key) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + DCHECK(mru_list_.end() != key); + + mru_list_.splice(mru_list_.begin(), mru_list_, key); +} + +GlobalTileManager::GlobalTileManager() { + total_allocated_tiles_ = 0; +} + +GlobalTileManager::~GlobalTileManager() { +} + +bool GlobalTileManager::IsConsistent() const { + size_t total_tiles = 0; + ListType::const_iterator it; + for (it = mru_list_.begin(); it != mru_list_.end(); it++) { + total_tiles += (*it)->GetNumTiles(); + } + + bool is_consistent = + (total_tiles <= kNumTilesLimit && total_tiles == total_allocated_tiles_); + + return is_consistent; +} + +} // namespace webview diff --git a/android_webview/browser/global_tile_manager.h b/android_webview/browser/global_tile_manager.h new file mode 100644 index 0000000..5cf2fc4 --- /dev/null +++ b/android_webview/browser/global_tile_manager.h @@ -0,0 +1,74 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ANDROID_WEBVIEW_BROWSER_GLOBAL_TILE_MANAGER_H_ +#define ANDROID_WEBVIEW_BROWSER_GLOBAL_TILE_MANAGER_H_ + +#include <list> +#include "base/basictypes.h" +#include "base/lazy_instance.h" +#include "base/sequence_checker.h" +#include "base/synchronization/lock.h" + +namespace android_webview { + +class GlobalTileManagerClient; + +// A global tile manager that keeps track of the number of tile resources. Each +// tile needs file descriptors (typically 2) and there is a soft limit of 1024 +// file descriptors per Android process. The GlobalTileManager does not keep +// track of how many tiles each individual view is actually using. The purpose +// of GlobalTileManager is to behave gracefully (as in not crashing) when the +// embedder of webview creates a lot of webviews and draw them at the same time. +class GlobalTileManager { + private: + typedef std::list<GlobalTileManagerClient*> ListType; + + public: + typedef ListType::iterator Key; + static GlobalTileManager* GetInstance(); + + // Requests the |num_of_tiles| from the available global pool. Calls + // GlobalTileManagerClient.SetNumTiles after the manager determines how many + // tiles are available for the client. The tile policy on the clients are not + // immediately enforced, unless |effective_immediately| is true. If the + // number of tiles left are not enough to satisfy the request, the manager + // will evict tiles allocated to other clients. + void RequestTiles(size_t new_num_of_tiles, Key key); + + Key PushBack(GlobalTileManagerClient* client); + + // |key| must be already in manager. Move the tile manager client + // corresponding to |key| to most recent. This function should be called after + // RequestTiles. + void DidUse(Key key); + + void Remove(Key key); + + private: + friend struct base::DefaultLazyInstanceTraits<GlobalTileManager>; + GlobalTileManager(); + ~GlobalTileManager(); + + // Continues evicting the inactive views until freeing up at least amount of + // tiles specified by |desired_num_tiles| to draw a view specified by |key|, + // or until all inactive views have been evicted. Returns the amount of + // memory that was actually evicted. This function is called when a + // request cannot be satisfied. + size_t Evict(size_t desired_num_tiles, Key key); + + // Check that the sum of all client's tiles is equal to + // total_allocated_tiles_. + bool IsConsistent() const; + + size_t total_allocated_tiles_; + ListType mru_list_; + base::SequenceChecker sequence_checker_; + + DISALLOW_COPY_AND_ASSIGN(GlobalTileManager); +}; + +} // namespace android_webview + +#endif // ANDROID_WEBVIEW_BROWSER_GLOBAL_TILE_MANAGER_H_ diff --git a/android_webview/browser/global_tile_manager_client.h b/android_webview/browser/global_tile_manager_client.h new file mode 100644 index 0000000..ec4dad6 --- /dev/null +++ b/android_webview/browser/global_tile_manager_client.h @@ -0,0 +1,26 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ANDROID_WEBVIEW_BROWSER_GLOBAL_TILE_MANAGER_CLIENT_H_ +#define ANDROID_WEBVIEW_BROWSER_GLOBAL_TILE_MANAGER_CLIENT_H_ + +namespace android_webview { +// GlobalTileManagerClient requests tile resources from GlobalTileManager. +class GlobalTileManagerClient { + public: + // Get the number of tiles allocated to the client. + virtual size_t GetNumTiles() const = 0; + + // Set the number of tiles allocated to the client. When + // |effective_immediately| is true, the client will enforce its tile policy + // immediately. + virtual void SetNumTiles(size_t num_tiles, bool effective_immediately) = 0; + + protected: + virtual ~GlobalTileManagerClient() {} +}; + +} // namespace android_webview + +#endif // ANDROID_WEBVIEW_BROWSER_GLOBAL_TILE_MANAGER_CLIENT_H_ diff --git a/android_webview/browser/global_tile_manager_unittest.cc b/android_webview/browser/global_tile_manager_unittest.cc new file mode 100644 index 0000000..9f09084 --- /dev/null +++ b/android_webview/browser/global_tile_manager_unittest.cc @@ -0,0 +1,157 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <algorithm> +#include "android_webview/browser/global_tile_manager.h" +#include "android_webview/browser/global_tile_manager_client.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +// This should be the same as the one defined global_tile_manager.cc +const size_t kNumTilesLimit = 450; +const size_t kDefaultNumTiles = 150; + +} // namespace + +using android_webview::GlobalTileManager; +using android_webview::GlobalTileManagerClient; +using testing::Test; + +class MockGlobalTileManagerClient : public GlobalTileManagerClient { + public: + virtual size_t GetNumTiles() const OVERRIDE { return num_tiles_; } + virtual void SetNumTiles(size_t num_tiles, + bool effective_immediately) OVERRIDE { + num_tiles_ = num_tiles; + } + + MockGlobalTileManagerClient() { + num_tiles_ = 0; + tile_request_ = kDefaultNumTiles; + key_ = GlobalTileManager::GetInstance()->PushBack(this); + } + + virtual ~MockGlobalTileManagerClient() { + GlobalTileManager::GetInstance()->Remove(key_); + } + + size_t GetTileRequest() { return tile_request_; } + GlobalTileManager::Key GetKey() { return key_; } + + private: + size_t num_tiles_; + size_t tile_request_; + GlobalTileManager::Key key_; +}; + +class GlobalTileManagerTest : public Test { + public: + virtual void SetUp() {} + + GlobalTileManager* manager() { return GlobalTileManager::GetInstance(); } +}; + +TEST_F(GlobalTileManagerTest, RequestTilesUnderLimit) { + MockGlobalTileManagerClient clients[2]; + + for (size_t i = 0; i < 2; i++) { + manager()->RequestTiles(clients[i].GetTileRequest(), clients[i].GetKey()); + manager()->DidUse(clients[i].GetKey()); + + // Ensure clients get what they asked for when the manager is under tile + // limit. + EXPECT_EQ(clients[i].GetNumTiles(), kDefaultNumTiles); + } +} + +TEST_F(GlobalTileManagerTest, EvictHappensWhenOverLimit) { + MockGlobalTileManagerClient clients[4]; + + for (size_t i = 0; i < 4; i++) { + manager()->RequestTiles(clients[i].GetTileRequest(), clients[i].GetKey()); + manager()->DidUse(clients[i].GetKey()); + } + + size_t total_tiles = 0; + for (size_t i = 0; i < 4; i++) { + total_tiles += clients[i].GetNumTiles(); + } + + // Ensure that eviction happened and kept the total number of tiles within + // kNumTilesLimit. + EXPECT_LE(total_tiles, kNumTilesLimit); +} + +TEST_F(GlobalTileManagerTest, RandomizedStressRequests) { + MockGlobalTileManagerClient clients[100]; + size_t index[100]; + for (size_t i = 0; i < 100; i++) { + index[i] = i; + } + + // Simulate a random request order of clients. + std::random_shuffle(&index[0], &index[99]); + + for (size_t i = 0; i < 100; i++) { + size_t j = index[i]; + manager()->RequestTiles(clients[j].GetTileRequest(), clients[j].GetKey()); + manager()->DidUse(clients[j].GetKey()); + } + + size_t total_tiles = 0; + for (size_t i = 0; i < 100; i++) { + total_tiles += clients[i].GetNumTiles(); + } + + // Ensure that eviction happened and kept the total number of tiles within + // kNumTilesLimit. + EXPECT_LE(total_tiles, kNumTilesLimit); +} + +TEST_F(GlobalTileManagerTest, FixedOrderedRequests) { + MockGlobalTileManagerClient clients[10]; + + // 10 clients requesting resources in a fixed order. Do that for 5 rounds. + for (int round = 0; round < 5; round++) { + for (size_t i = 0; i < 10; i++) { + manager()->RequestTiles(clients[i].GetTileRequest(), clients[i].GetKey()); + manager()->DidUse(clients[i].GetKey()); + } + } + + // Ensure that the total tiles are divided evenly among all clients. + for (size_t i = 0; i < 10; i++) { + EXPECT_EQ(clients[i].GetNumTiles(), kNumTilesLimit / 10); + } +} + +TEST_F(GlobalTileManagerTest, FixedOrderedRequestsWithInactiveClients) { + MockGlobalTileManagerClient clients[20]; + + // 20 clients request resources. + for (size_t i = 0; i < 20; i++) { + manager()->RequestTiles(clients[i].GetTileRequest(), clients[i].GetKey()); + manager()->DidUse(clients[i].GetKey()); + } + + // Now the last 10 clients become inactive. Only the first 10 clients remain + // active resource requesters. + // 10 clients requesting resources in a fixed order. Do that for 5 rounds. + for (int round = 0; round < 5; round++) { + for (size_t i = 0; i < 10; i++) { + manager()->RequestTiles(clients[i].GetTileRequest(), clients[i].GetKey()); + manager()->DidUse(clients[i].GetKey()); + } + } + + // Ensure that the total tiles are divided evenly among all clients. + for (size_t i = 0; i < 10; i++) { + EXPECT_EQ(clients[i].GetNumTiles(), kNumTilesLimit / 10); + } + + // Ensure that the inactive tiles are evicted. + for (size_t i = 11; i < 20; i++) { + EXPECT_EQ(clients[i].GetNumTiles(), 0u); + } +} diff --git a/android_webview/browser/hardware_renderer.cc b/android_webview/browser/hardware_renderer.cc index c48e4c7..f1528ec 100644 --- a/android_webview/browser/hardware_renderer.cc +++ b/android_webview/browser/hardware_renderer.cc @@ -6,31 +6,21 @@ #include "android_webview/browser/aw_gl_surface.h" #include "android_webview/browser/browser_view_renderer_client.h" -#include "android_webview/browser/gl_view_renderer_manager.h" #include "android_webview/browser/scoped_app_gl_state_restore.h" #include "android_webview/public/browser/draw_gl.h" -#include "base/command_line.h" #include "base/debug/trace_event.h" #include "base/strings/string_number_conversions.h" +#include "content/public/browser/android/synchronous_compositor.h" #include "content/public/browser/browser_thread.h" -#include "content/public/common/content_switches.h" #include "gpu/command_buffer/service/shader_translator_cache.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/transform.h" -using content::BrowserThread; - namespace android_webview { namespace { -// Used to calculate memory and resource allocation. Determined experimentally. -const size_t g_memory_multiplier = 10; -const size_t g_num_gralloc_limit = 150; -const size_t kBytesPerPixel = 4; -const size_t kMemoryAllocationStep = 5 * 1024 * 1024; - base::LazyInstance<scoped_refptr<internal::DeferredGpuCommandService> > g_service = LAZY_INSTANCE_INITIALIZER; @@ -39,7 +29,7 @@ base::LazyInstance<scoped_refptr<internal::DeferredGpuCommandService> > HardwareRenderer::HardwareRenderer(SharedRendererState* state) : shared_renderer_state_(state), last_egl_context_(eglGetCurrentContext()), - manager_key_(GLViewRendererManager::GetInstance()->PushBack( + renderer_manager_key_(GLViewRendererManager::GetInstance()->PushBack( shared_renderer_state_)) { DCHECK(last_egl_context_); if (!g_service.Get()) { @@ -59,8 +49,8 @@ HardwareRenderer::HardwareRenderer(SharedRendererState* state) } HardwareRenderer::~HardwareRenderer() { - GLViewRendererManager* mru = GLViewRendererManager::GetInstance(); - mru->Remove(manager_key_); + GLViewRendererManager* render_manager = GLViewRendererManager::GetInstance(); + render_manager->Remove(renderer_manager_key_); ScopedAppGLStateRestore state_restore( ScopedAppGLStateRestore::MODE_RESOURCE_MANAGEMENT); @@ -72,7 +62,7 @@ HardwareRenderer::~HardwareRenderer() { bool HardwareRenderer::DrawGL(AwDrawGLInfo* draw_info, DrawGLResult* result) { TRACE_EVENT0("android_webview", "HardwareRenderer::DrawGL"); - GLViewRendererManager::GetInstance()->DidDrawGL(manager_key_); + GLViewRendererManager::GetInstance()->DidDrawGL(renderer_manager_key_); // We need to watch if the current Android context has changed and enforce // a clean-up in the compositor. @@ -94,18 +84,7 @@ bool HardwareRenderer::DrawGL(AwDrawGLInfo* draw_info, DrawGLResult* result) { // Should only need to access SharedRendererState in kModeDraw and kModeSync. const DrawGLInput input = shared_renderer_state_->GetDrawGLInput(); - - // Update memory budget. This will no-op in compositor if the policy has not - // changed since last draw. - content::SynchronousCompositorMemoryPolicy policy; - policy.bytes_limit = g_memory_multiplier * kBytesPerPixel * - input.global_visible_rect.width() * - input.global_visible_rect.height(); - // Round up to a multiple of kMemoryAllocationStep. - policy.bytes_limit = - (policy.bytes_limit / kMemoryAllocationStep + 1) * kMemoryAllocationStep; - policy.num_resources_limit = g_num_gralloc_limit; - SetMemoryPolicy(policy); + SetCompositorMemoryPolicy(); gl_surface_->SetBackingFrameBufferObject( state_restore.framebuffer_binding_ext()); @@ -142,68 +121,14 @@ bool HardwareRenderer::DrawGL(AwDrawGLInfo* draw_info, DrawGLResult* result) { return did_draw; } -bool HardwareRenderer::TrimMemory(int level, bool visible) { - // Constants from Android ComponentCallbacks2. - enum { - TRIM_MEMORY_RUNNING_LOW = 10, - TRIM_MEMORY_UI_HIDDEN = 20, - TRIM_MEMORY_BACKGROUND = 40, - }; - - // Not urgent enough. TRIM_MEMORY_UI_HIDDEN is treated specially because - // it does not indicate memory pressure, but merely that the app is - // backgrounded. - if (level < TRIM_MEMORY_RUNNING_LOW || level == TRIM_MEMORY_UI_HIDDEN) - return false; - - // Do not release resources on view we expect to get DrawGL soon. - if (level < TRIM_MEMORY_BACKGROUND && visible) - return false; - - if (!eglGetCurrentContext()) { - NOTREACHED(); - return false; +void HardwareRenderer::SetCompositorMemoryPolicy() { + if (shared_renderer_state_->IsMemoryPolicyDirty()) { + content::SynchronousCompositorMemoryPolicy policy = + shared_renderer_state_->GetMemoryPolicy(); + // Memory policy is set by BrowserViewRenderer on UI thread. + shared_renderer_state_->GetCompositor()->SetMemoryPolicy(policy); + shared_renderer_state_->SetMemoryPolicyDirty(false); } - - DCHECK_EQ(last_egl_context_, eglGetCurrentContext()); - - // Just set the memory limit to 0 and drop all tiles. This will be reset to - // normal levels in the next DrawGL call. - content::SynchronousCompositorMemoryPolicy policy; - policy.bytes_limit = 0; - policy.num_resources_limit = 0; - if (memory_policy_ == policy) - return false; - - TRACE_EVENT0("android_webview", "HardwareRenderer::TrimMemory"); - ScopedAppGLStateRestore state_restore( - ScopedAppGLStateRestore::MODE_RESOURCE_MANAGEMENT); - internal::ScopedAllowGL allow_gl; - - SetMemoryPolicy(policy); - return true; -} - -void HardwareRenderer::SetMemoryPolicy( - content::SynchronousCompositorMemoryPolicy& new_policy) { - if (memory_policy_ == new_policy) - return; - - memory_policy_ = new_policy; - shared_renderer_state_->GetCompositor()-> - SetMemoryPolicy(memory_policy_); -} - -// static -void HardwareRenderer::CalculateTileMemoryPolicy() { - CommandLine* cl = CommandLine::ForCurrentProcess(); - - const char kDefaultTileSize[] = "384"; - if (!cl->HasSwitch(switches::kDefaultTileWidth)) - cl->AppendSwitchASCII(switches::kDefaultTileWidth, kDefaultTileSize); - - if (!cl->HasSwitch(switches::kDefaultTileHeight)) - cl->AppendSwitchASCII(switches::kDefaultTileHeight, kDefaultTileSize); } namespace internal { diff --git a/android_webview/browser/hardware_renderer.h b/android_webview/browser/hardware_renderer.h index 09580eb..b290058e 100644 --- a/android_webview/browser/hardware_renderer.h +++ b/android_webview/browser/hardware_renderer.h @@ -12,7 +12,6 @@ #include "base/lazy_instance.h" #include "base/memory/ref_counted.h" #include "base/threading/thread_local.h" -#include "content/public/browser/android/synchronous_compositor.h" struct AwDrawGLInfo; @@ -30,15 +29,12 @@ class HardwareRenderer { explicit HardwareRenderer(SharedRendererState* state); ~HardwareRenderer(); - static void CalculateTileMemoryPolicy(); - bool DrawGL(AwDrawGLInfo* draw_info, DrawGLResult* result); - bool TrimMemory(int level, bool visible); private: friend class internal::DeferredGpuCommandService; - void SetMemoryPolicy(content::SynchronousCompositorMemoryPolicy& new_policy); + void SetCompositorMemoryPolicy(); SharedRendererState* shared_renderer_state_; @@ -46,9 +42,8 @@ class HardwareRenderer { EGLContext last_egl_context_; scoped_refptr<AwGLSurface> gl_surface_; - content::SynchronousCompositorMemoryPolicy memory_policy_; - GLViewRendererManager::Key manager_key_; + GLViewRendererManager::Key renderer_manager_key_; DISALLOW_COPY_AND_ASSIGN(HardwareRenderer); }; diff --git a/android_webview/browser/shared_renderer_state.cc b/android_webview/browser/shared_renderer_state.cc index ffc91ea..a4e3873 100644 --- a/android_webview/browser/shared_renderer_state.cc +++ b/android_webview/browser/shared_renderer_state.cc @@ -21,6 +21,7 @@ SharedRendererState::SharedRendererState( client_on_ui_(client), weak_factory_on_ui_thread_(this), ui_thread_weak_ptr_(weak_factory_on_ui_thread_.GetWeakPtr()), + memory_policy_dirty_(false), hardware_initialized_(false) { DCHECK(ui_loop_->BelongsToCurrentThread()); DCHECK(client_on_ui_); @@ -59,6 +60,21 @@ content::SynchronousCompositor* SharedRendererState::GetCompositor() { return compositor_; } +void SharedRendererState::SetMemoryPolicy( + const content::SynchronousCompositorMemoryPolicy new_policy) { + base::AutoLock lock(lock_); + if (memory_policy_ != new_policy) { + memory_policy_ = new_policy; + memory_policy_dirty_ = true; + } +} + +content::SynchronousCompositorMemoryPolicy +SharedRendererState::GetMemoryPolicy() const { + base::AutoLock lock(lock_); + return memory_policy_; +} + void SharedRendererState::SetDrawGLInput(const DrawGLInput& input) { base::AutoLock lock(lock_); draw_gl_input_ = input; @@ -102,4 +118,14 @@ bool SharedRendererState::IsHardwareInitialized() const { return hardware_initialized_; } +void SharedRendererState::SetMemoryPolicyDirty(bool is_dirty) { + base::AutoLock lock(lock_); + memory_policy_dirty_ = is_dirty; +} + +bool SharedRendererState::IsMemoryPolicyDirty() const { + base::AutoLock lock(lock_); + return memory_policy_dirty_; +} + } // namespace android_webview diff --git a/android_webview/browser/shared_renderer_state.h b/android_webview/browser/shared_renderer_state.h index c2c1eee..9c90731 100644 --- a/android_webview/browser/shared_renderer_state.h +++ b/android_webview/browser/shared_renderer_state.h @@ -55,6 +55,12 @@ class SharedRendererState { // This function can be called on both UI and RT thread. content::SynchronousCompositor* GetCompositor(); + + void SetMemoryPolicy(const content::SynchronousCompositorMemoryPolicy policy); + content::SynchronousCompositorMemoryPolicy GetMemoryPolicy() const; + + void SetMemoryPolicyDirty(bool is_dirty); + bool IsMemoryPolicyDirty() const; void SetDrawGLInput(const DrawGLInput& input); DrawGLInput GetDrawGLInput() const; @@ -78,6 +84,10 @@ class SharedRendererState { // Accessed by both UI and RT thread. mutable base::Lock lock_; content::SynchronousCompositor* compositor_; + content::SynchronousCompositorMemoryPolicy memory_policy_; + // Set to true when SetMemoryPolicy called with a different memory policy. + // Set to false when memory policy is read and enforced to compositor. + bool memory_policy_dirty_; DrawGLInput draw_gl_input_; std::queue<base::Closure> closure_queue_; bool hardware_initialized_; |