#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"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "ui/gfx/vector2d_conversions.h"
using base::android::AttachCurrentThread;
using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;
+using content::BrowserThread;
+using content::SynchronousCompositorMemoryPolicy;
namespace android_webview {
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,
} // 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,
compositor_needs_continuous_invalidate_(false),
block_invalidates_(false),
width_(0),
- height_(0) {
+ height_(0),
+ num_tiles_(0u),
+ num_bytes_(0u) {
CHECK(web_contents_);
content::SynchronousCompositor::SetClientForWebContents(web_contents_, this);
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 = width * height * kTileMultiplier / g_tile_area;
+ // Round up to a multiple of kTileAllocationStep. The minimum number of tiles
+ // is also kTileAllocationStep.
+ tiles = (tiles / kTileAllocationStep + 1) * 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,
draw_gl_input_.frame_id++;
draw_gl_input_.scroll_offset = scroll;
draw_gl_input_.global_visible_rect = global_visible_rect;
+ draw_gl_input_.width = width_;
+ draw_gl_input_.height = height_;
if (clear_view_)
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);
+ 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);
TRACE_EVENT0("android_webview", "BrowserViewRenderer::CapturePicture");
// Return empty Picture objects for empty SkPictures.
- skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture);
if (width <= 0 || height <= 0) {
- return picture;
+ return skia::AdoptRef(new SkPicture);
}
// Reset scroll back to the origin, will go back to the old
// value when scroll_reset is out of scope.
AutoResetWithLock scroll_reset(
- &scroll_offset_dip_, gfx::Vector2dF(), scroll_offset_dip_lock_);
+ &scroll_offset_dip_, gfx::Vector2dF(), render_thread_lock_);
- SkCanvas* rec_canvas = picture->beginRecording(width, height, 0);
+ SkPictureRecorder recorder;
+ SkCanvas* rec_canvas = recorder.beginRecording(width, height, NULL, 0);
if (has_compositor_)
CompositeSW(rec_canvas);
- picture->endRecording();
- return picture;
+ return skia::AdoptRef(recorder.endRecording());
}
void BrowserViewRenderer::EnableOnNewPicture(bool enabled) {
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 {
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) {
- if (!ui_task_runner_->BelongsToCurrentThread()) {
- ui_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&BrowserViewRenderer::SetContinuousInvalidate,
- ui_thread_weak_ptr_,
- invalidate));
- return;
+ {
+ base::AutoLock lock(render_thread_lock_);
+ if (compositor_needs_continuous_invalidate_ == invalidate)
+ return;
+
+ TRACE_EVENT_INSTANT1("android_webview",
+ "BrowserViewRenderer::SetContinuousInvalidate",
+ TRACE_EVENT_SCOPE_THREAD,
+ "invalidate",
+ invalidate);
+ compositor_needs_continuous_invalidate_ = invalidate;
}
- if (compositor_needs_continuous_invalidate_ == invalidate)
- return;
- TRACE_EVENT_INSTANT1("android_webview",
- "BrowserViewRenderer::SetContinuousInvalidate",
- TRACE_EVENT_SCOPE_THREAD,
- "invalidate",
- invalidate);
- compositor_needs_continuous_invalidate_ = invalidate;
- EnsureContinuousInvalidation(false);
+ if (ui_task_runner_->BelongsToCurrentThread()) {
+ EnsureContinuousInvalidation(false);
+ return;
+ }
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&BrowserViewRenderer::EnsureContinuousInvalidation,
+ ui_thread_weak_ptr_,
+ false));
}
void BrowserViewRenderer::SetDipScale(float dip_scale) {
DCHECK_LE(scroll_offset_dip.y(), max_scroll_offset_dip_.y());
{
- base::AutoLock lock(scroll_offset_dip_lock_);
+ base::AutoLock lock(render_thread_lock_);
if (scroll_offset_dip_ == scroll_offset_dip)
return;
}
if (has_compositor_)
- shared_renderer_state_->CompositorDidChangeRootLayerScrollOffset();
+ shared_renderer_state_->GetCompositor()->
+ DidChangeRootLayerScrollOffset();
}
void BrowserViewRenderer::DidUpdateContent() {
}
{
- base::AutoLock lock(scroll_offset_dip_lock_);
+ base::AutoLock lock(render_thread_lock_);
// TOOD(mkosiba): Add a DCHECK to say that this does _not_ get called during
// DrawGl when http://crbug.com/249972 is fixed.
if (scroll_offset_dip_ == scroll_offset_dip)
}
gfx::Vector2dF BrowserViewRenderer::GetTotalRootLayerScrollOffset() {
- base::AutoLock lock(scroll_offset_dip_lock_);
+ base::AutoLock lock(render_thread_lock_);
return scroll_offset_dip_;
}
bool BrowserViewRenderer::CompositeSW(SkCanvas* canvas) {
DCHECK(has_compositor_);
- bool result = shared_renderer_state_->CompositorDemandDrawSw(canvas);
+ bool result = shared_renderer_state_->GetCompositor()->
+ DemandDrawSw(canvas);
DidComposite(false);
return result;
}