summaryrefslogtreecommitdiffstats
path: root/android_webview/browser
diff options
context:
space:
mode:
authorhush@chromium.org <hush@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-03 02:17:07 +0000
committerhush@chromium.org <hush@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-03 02:17:07 +0000
commit7d67302a01daf37df42a5526654b84ccd7b244cd (patch)
treecb1ab023895919fb194cc83618f8f68fc59f85e0 /android_webview/browser
parentfa34edcaf8d6f22221f797590371417644c15930 (diff)
downloadchromium_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.cc151
-rw-r--r--android_webview/browser/browser_view_renderer.h39
-rw-r--r--android_webview/browser/global_tile_manager.cc135
-rw-r--r--android_webview/browser/global_tile_manager.h74
-rw-r--r--android_webview/browser/global_tile_manager_client.h26
-rw-r--r--android_webview/browser/global_tile_manager_unittest.cc157
-rw-r--r--android_webview/browser/hardware_renderer.cc101
-rw-r--r--android_webview/browser/hardware_renderer.h9
-rw-r--r--android_webview/browser/shared_renderer_state.cc26
-rw-r--r--android_webview/browser/shared_renderer_state.h10
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_;