Commit 73cfd3fc authored by Eric Karl's avatar Eric Karl Committed by Commit Bot

Trigger GPU context dropping on low-end from Browser proc

We drop GL contexts on low-end devices when we go to the background.
This is currently initiated by the GPU process, which kills all GPU
channels, eventually causing context-loss in the various clients.

While the current approach works, it makes it difficult to avoid
rendering artifacts on app resume. As context-loss is lazily
identified, it's possible for the browser and renderer to be notified
at very different times. This can lead to races where the browser will
attempt to draw with invalid renderer resources.

To avoid these issues, this CL causes the context-dropping to be
triggered from the browser itself. This allows the browser to drop
renderer resources at the same time, greatly reducing (but
unfortunately not eliminating) the possibility of a race.

If we still see these issues with any frequency, further follow-up
work, leveraging Surface Synchronization, will likely be able to
fully eliminate these cases.

Bug: 792120
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel
Change-Id: I1c0f2c6000fc9c1f4171952b09484101a220654c
Reviewed-on: https://chromium-review.googlesource.com/959605Reviewed-by: default avatarSadrul Chowdhury <sadrul@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarenne <enne@chromium.org>
Reviewed-by: default avatarKenneth Russell <kbr@chromium.org>
Reviewed-by: default avatarKhushal <khushalsagar@chromium.org>
Commit-Queue: Eric Karl <ericrk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#545879}
parent 8a861942
......@@ -165,4 +165,8 @@ void FrameEvictionManager::PurgeMemory(int percentage) {
CullUnlockedFrames(std::max(1, (saved_frame_limit * percentage) / 100));
}
void FrameEvictionManager::PurgeAllUnlockedFrames() {
CullUnlockedFrames(0);
}
} // namespace viz
......@@ -52,6 +52,9 @@ class VIZ_CLIENT_EXPORT FrameEvictionManager
void OnMemoryPressure(
base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
// Purges all unlocked frames, allowing us to reclaim resources.
void PurgeAllUnlockedFrames();
private:
FrameEvictionManager();
~FrameEvictionManager() override;
......
......@@ -117,6 +117,8 @@ class TestGpuService : public mojom::GpuService {
void DestroyAllChannels() override {}
void OnBackgrounded() override {}
void Crash() override {}
void Hang() override {}
......
......@@ -674,6 +674,21 @@ void GpuServiceImpl::DestroyAllChannels() {
gpu_channel_manager_->DestroyAllChannels();
}
void GpuServiceImpl::OnBackgrounded() {
// Currently only called on Android.
#if defined(OS_ANDROID)
if (io_runner_->BelongsToCurrentThread()) {
main_runner_->PostTask(
FROM_HERE, base::BindOnce(&GpuServiceImpl::OnBackgrounded, weak_ptr_));
return;
}
DVLOG(1) << "GPU: Performing background cleanup";
gpu_channel_manager_->OnApplicationBackgrounded();
#else
NOTREACHED();
#endif
}
void GpuServiceImpl::Crash() {
DCHECK(io_runner_->BelongsToCurrentThread());
DVLOG(1) << "GPU: Simulating GPU crash";
......
......@@ -187,6 +187,7 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate,
void WakeUpGpu() override;
void GpuSwitched() override;
void DestroyAllChannels() override;
void OnBackgrounded() override;
void Crash() override;
void Hang() override;
void ThrowJavaException() override;
......
......@@ -15,6 +15,7 @@
#include "content/public/browser/gpu_utils.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/content_browser_test.h"
#include "content/test/gpu_browsertest_helpers.h"
#include "gpu/ipc/client/command_buffer_proxy_impl.h"
#include "gpu/ipc/client/gpu_channel_host.h"
#include "services/ui/public/cpp/gpu/context_provider_command_buffer.h"
......@@ -27,50 +28,6 @@
namespace {
scoped_refptr<ui::ContextProviderCommandBuffer> CreateContext(
scoped_refptr<gpu::GpuChannelHost> gpu_channel_host) {
gpu::GpuChannelEstablishFactory* factory =
content::BrowserMainLoop::GetInstance()->gpu_channel_establish_factory();
// This is for an offscreen context, so the default framebuffer doesn't need
// any alpha, depth, stencil, antialiasing.
gpu::ContextCreationAttribs attributes;
attributes.alpha_size = -1;
attributes.depth_size = 0;
attributes.stencil_size = 0;
attributes.samples = 0;
attributes.sample_buffers = 0;
attributes.bind_generates_resource = false;
constexpr bool automatic_flushes = false;
constexpr bool support_locking = false;
return base::MakeRefCounted<ui::ContextProviderCommandBuffer>(
std::move(gpu_channel_host), factory->GetGpuMemoryBufferManager(),
content::kGpuStreamIdDefault, content::kGpuStreamPriorityDefault,
gpu::kNullSurfaceHandle, GURL(), automatic_flushes, support_locking,
gpu::SharedMemoryLimits(), attributes, nullptr,
ui::command_buffer_metrics::OFFSCREEN_CONTEXT_FOR_TESTING);
}
void OnEstablishedGpuChannel(
const base::Closure& quit_closure,
scoped_refptr<gpu::GpuChannelHost>* retvalue,
scoped_refptr<gpu::GpuChannelHost> established_host) {
if (retvalue)
*retvalue = std::move(established_host);
quit_closure.Run();
}
scoped_refptr<gpu::GpuChannelHost> EstablishGpuChannelSyncRunLoop() {
gpu::GpuChannelEstablishFactory* factory =
content::BrowserMainLoop::GetInstance()->gpu_channel_establish_factory();
CHECK(factory);
base::RunLoop run_loop;
scoped_refptr<gpu::GpuChannelHost> gpu_channel_host;
factory->EstablishGpuChannel(base::BindOnce(
&OnEstablishedGpuChannel, run_loop.QuitClosure(), &gpu_channel_host));
run_loop.Run();
return gpu_channel_host;
}
// RunLoop implementation that runs until it observes OnContextLost().
class ContextLostRunLoop : public viz::ContextLostObserver {
public:
......@@ -101,10 +58,11 @@ class ContextTestBase : public content::ContentBrowserTest {
return;
scoped_refptr<gpu::GpuChannelHost> gpu_channel_host =
EstablishGpuChannelSyncRunLoop();
content::GpuBrowsertestEstablishGpuChannelSyncRunLoop();
CHECK(gpu_channel_host);
provider_ = CreateContext(std::move(gpu_channel_host));
provider_ =
content::GpuBrowsertestCreateContext(std::move(gpu_channel_host));
auto result = provider_->BindToCurrentThread();
CHECK_EQ(result, gpu::ContextResult::kSuccess);
gl_ = provider_->ContextGL();
......@@ -180,7 +138,7 @@ class BrowserGpuChannelHostFactoryTest : public ContentBrowserTest {
}
void EstablishAndWait() {
gpu_channel_host_ = EstablishGpuChannelSyncRunLoop();
gpu_channel_host_ = content::GpuBrowsertestEstablishGpuChannelSyncRunLoop();
}
gpu::GpuChannelHost* GetGpuChannel() { return gpu_channel_host_.get(); }
......@@ -275,7 +233,7 @@ IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest,
// Step 2: verify that holding onto the provider's GrContext will
// retain the host after provider is destroyed.
scoped_refptr<ui::ContextProviderCommandBuffer> provider =
CreateContext(GetGpuChannel());
content::GpuBrowsertestCreateContext(GetGpuChannel());
ASSERT_EQ(provider->BindToCurrentThread(), gpu::ContextResult::kSuccess);
sk_sp<GrContext> gr_context = sk_ref_sp(provider->GrContext());
......@@ -322,7 +280,7 @@ IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest,
scoped_refptr<gpu::GpuChannelHost> host = GetGpuChannel();
scoped_refptr<ui::ContextProviderCommandBuffer> provider =
CreateContext(GetGpuChannel());
content::GpuBrowsertestCreateContext(GetGpuChannel());
ContextLostRunLoop run_loop(provider.get());
ASSERT_EQ(provider->BindToCurrentThread(), gpu::ContextResult::kSuccess);
GpuProcessHost::CallOnIO(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
......
......@@ -35,6 +35,7 @@
#include "cc/resources/ui_resource_manager.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_settings.h"
#include "components/viz/client/frame_eviction_manager.h"
#include "components/viz/common/features.h"
#include "components/viz/common/gl_helper.h"
#include "components/viz/common/gpu/context_provider.h"
......@@ -54,6 +55,7 @@
#include "content/browser/browser_main_loop.h"
#include "content/browser/compositor/surface_utils.h"
#include "content/browser/gpu/compositor_util.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/common/gpu_stream_constants.h"
#include "content/public/browser/android/compositor.h"
......@@ -659,11 +661,13 @@ void CompositorImpl::SetVisible(bool visible) {
root_window_->GetBeginFrameSource());
}
display_.reset();
EnqueueLowEndBackgroundCleanup();
} else {
host_->SetVisible(true);
has_submitted_frame_since_became_visible_ = false;
if (layer_tree_frame_sink_request_pending_)
HandlePendingLayerTreeFrameSinkRequest();
low_end_background_cleanup_task_.Cancel();
}
}
......@@ -1004,4 +1008,35 @@ void CompositorImpl::OnCompositorLockStateChanged(bool locked) {
host_->SetDeferCommits(locked);
}
void CompositorImpl::EnqueueLowEndBackgroundCleanup() {
if (base::SysInfo::IsLowEndDevice()) {
low_end_background_cleanup_task_.Reset(
base::BindOnce(&CompositorImpl::DoLowEndBackgroundCleanup,
weak_factory_.GetWeakPtr()));
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, low_end_background_cleanup_task_.callback(),
base::TimeDelta::FromSeconds(5));
}
}
void CompositorImpl::DoLowEndBackgroundCleanup() {
// When we become visible, we immediately cancel the callback that runs this
// code.
DCHECK(!host_->IsVisible());
// First, evict all unlocked frames, allowing resources to be reclaimed.
viz::FrameEvictionManager::GetInstance()->PurgeAllUnlockedFrames();
// Next, notify the GPU process to do background processing, which will
// lose all renderer contexts.
content::GpuProcessHost::CallOnIO(
content::GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
false /* force_create */,
base::BindRepeating([](content::GpuProcessHost* host) {
if (host) {
host->gpu_service()->OnBackgrounded();
}
}));
}
} // namespace content
......@@ -77,6 +77,10 @@ class CONTENT_EXPORT CompositorImpl
void DeleteUIResource(cc::UIResourceId resource_id) override;
bool SupportsETC1NonPowerOfTwo() const override;
// Test functions:
bool IsLockedForTesting() const { return lock_manager_.IsLocked(); }
void SetVisibleForTesting(bool visible) { SetVisible(visible); }
private:
// Compositor implementation.
void SetRootWindow(gfx::NativeWindow root_window) override;
......@@ -160,6 +164,11 @@ class CONTENT_EXPORT CompositorImpl
void DetachRootWindow();
// Helper functions to perform delayed cleanup after the compositor is no
// longer visible on low-end devices.
void EnqueueLowEndBackgroundCleanup();
void DoLowEndBackgroundCleanup();
viz::FrameSinkId frame_sink_id_;
// root_layer_ is the persistent internal root layer, while subroot_layer_
......@@ -206,6 +215,11 @@ class CONTENT_EXPORT CompositorImpl
pending_child_frame_sink_ids_;
ui::CompositorLockManager lock_manager_;
bool has_submitted_frame_since_became_visible_ = false;
// A task which runs cleanup tasks on low-end Android after a delay. Enqueued
// when we hide, canceled when we're shown.
base::CancelableOnceClosure low_end_background_cleanup_task_;
base::WeakPtrFactory<CompositorImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(CompositorImpl);
......
// Copyright 2017 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 "base/base_switches.h"
#include "content/browser/browser_main_loop.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/browser/renderer_host/compositor_impl_android.h"
#include "content/browser/renderer_host/render_widget_host_view_android.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/gpu_stream_constants.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "content/test/gpu_browsertest_helpers.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/ipc/client/gpu_channel_host.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "ui/android/window_android.h"
#include "url/gurl.h"
namespace content {
namespace {
class CompositorImplLowEndBrowserTest : public ContentBrowserTest {
public:
CompositorImplLowEndBrowserTest() {}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(switches::kEnableLowEndDeviceMode);
command_line->AppendSwitch(switches::kInProcessGPU);
content::ContentBrowserTest::SetUpCommandLine(command_line);
}
protected:
void SetUpOnMainThread() override {
ASSERT_TRUE(embedded_test_server()->Start());
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.ServeFilesFromSourceDirectory("content/test/data");
ASSERT_TRUE(https_server.Start());
GURL http_url(embedded_test_server()->GetURL("/title1.html"));
ASSERT_TRUE(NavigateToURL(shell(), http_url));
}
ui::WindowAndroid* window() const {
return web_contents()->GetTopLevelNativeWindow();
}
CompositorImpl* compositor_impl() const {
return static_cast<CompositorImpl*>(window()->GetCompositor());
}
WebContentsImpl* web_contents() const {
return static_cast<WebContentsImpl*>(shell()->web_contents());
}
RenderWidgetHostViewAndroid* render_widget_host_view_android() const {
return static_cast<RenderWidgetHostViewAndroid*>(
web_contents()->GetRenderWidgetHostView());
}
private:
DISALLOW_COPY_AND_ASSIGN(CompositorImplLowEndBrowserTest);
};
// RunLoop implementation that calls glFlush() every second until it observes
// OnContextLost().
class ContextLostRunLoop : public viz::ContextLostObserver {
public:
ContextLostRunLoop(viz::ContextProvider* context_provider)
: context_provider_(context_provider) {
context_provider_->AddObserver(this);
}
~ContextLostRunLoop() override { context_provider_->RemoveObserver(this); }
void RunUntilContextLost() {
CheckForContextLoss();
run_loop_.Run();
}
void CheckForContextLoss() {
if (did_lose_context_) {
run_loop_.Quit();
return;
}
context_provider_->ContextGL()->Flush();
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ContextLostRunLoop::CheckForContextLoss,
base::Unretained(this)),
base::TimeDelta::FromSeconds(1));
}
private:
// viz::LostContextProvider:
void OnContextLost() override { did_lose_context_ = true; }
viz::ContextProvider* const context_provider_;
bool did_lose_context_ = false;
base::RunLoop run_loop_;
DISALLOW_COPY_AND_ASSIGN(ContextLostRunLoop);
};
// RunLoop implementation that runs until it observes a compositor frame.
class CompositorFrameRunLoop : public ui::WindowAndroidObserver {
public:
CompositorFrameRunLoop(ui::WindowAndroid* window) : window_(window) {
window_->AddObserver(this);
}
~CompositorFrameRunLoop() override { window_->RemoveObserver(this); }
void RunUntilFrame() { run_loop_.Run(); }
private:
// ui::WindowAndroidObserver:
void OnCompositingDidCommit() override { run_loop_.Quit(); }
void OnRootWindowVisibilityChanged(bool visible) override {}
void OnAttachCompositor() override {}
void OnDetachCompositor() override {}
void OnActivityStopped() override {}
void OnActivityStarted() override {}
ui::WindowAndroid* const window_;
base::RunLoop run_loop_;
DISALLOW_COPY_AND_ASSIGN(CompositorFrameRunLoop);
};
IN_PROC_BROWSER_TEST_F(CompositorImplLowEndBrowserTest,
CompositorImplDropsResourcesOnBackground) {
auto* rwhva = render_widget_host_view_android();
auto* compositor = compositor_impl();
auto context = GpuBrowsertestCreateContext(
GpuBrowsertestEstablishGpuChannelSyncRunLoop());
context->BindToCurrentThread();
CompositorFrameRunLoop(window()).RunUntilFrame();
EXPECT_TRUE(rwhva->HasValidFrame());
ContextLostRunLoop run_loop(context.get());
compositor->SetVisibleForTesting(false);
rwhva->OnRootWindowVisibilityChanged(false);
rwhva->Hide();
// Ensure that context is eventually dropped and at that point we do not have
// a valid frame.
run_loop.RunUntilContextLost();
EXPECT_FALSE(rwhva->HasValidFrame());
// Become visible again:
compositor->SetVisibleForTesting(true);
rwhva->Show();
rwhva->OnRootWindowVisibilityChanged(true);
// We should have taken the compositor lock on resume.
EXPECT_TRUE(compositor->IsLockedForTesting());
EXPECT_FALSE(rwhva->HasValidFrame());
// The compositor should eventually be unlocked and produce a frame.
CompositorFrameRunLoop(window()).RunUntilFrame();
EXPECT_FALSE(compositor->IsLockedForTesting());
EXPECT_TRUE(rwhva->HasValidFrame());
}
} // namespace
} // namespace content
......@@ -200,6 +200,8 @@ jumbo_static_library("test_support") {
"fake_plugin_service.h",
"fake_renderer_compositor_frame_sink.cc",
"fake_renderer_compositor_frame_sink.h",
"gpu_browsertest_helpers.cc",
"gpu_browsertest_helpers.h",
"mock_background_sync_controller.cc",
"mock_background_sync_controller.h",
"mock_google_streaming_server.cc",
......@@ -325,6 +327,7 @@ jumbo_static_library("test_support") {
"//mojo/edk",
"//net:test_support",
"//services/device/public/mojom",
"//services/ui/public/cpp/gpu",
# TODO(jam): remove this by adding a public header for the NetworkContext
# public testing method.
......@@ -1021,6 +1024,7 @@ test("content_browsertests") {
"../browser/android/render_widget_host_connector_browsertest.cc",
"../browser/android/render_widget_host_connector_browsertest.h",
"../browser/media/session/audio_focus_delegate_android_browsertest.cc",
"../browser/renderer_host/compositor_impl_android_browsertest.cc",
"../shell/android/browsertests_apk/content_browser_tests_jni_onload.cc",
]
sources -= [
......
......@@ -47,4 +47,8 @@ specific_include_rules = {
"+content/shell/common/shell_switches.h",
"+services/ui/public/cpp/gpu",
],
"gpu_browsertest_helpers.cc": [
"+services/ui/public/cpp/gpu/command_buffer_metrics.h",
"+services/ui/public/cpp/gpu/context_provider_command_buffer.h",
],
}
// Copyright 2018 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/test/gpu_browsertest_helpers.h"
#include "base/callback.h"
#include "base/run_loop.h"
#include "content/browser/browser_main_loop.h"
#include "content/common/gpu_stream_constants.h"
#include "gpu/command_buffer/client/shared_memory_limits.h"
#include "gpu/command_buffer/common/context_creation_attribs.h"
#include "gpu/ipc/client/gpu_channel_host.h"
#include "gpu/ipc/common/surface_handle.h"
#include "services/ui/public/cpp/gpu/command_buffer_metrics.h"
#include "services/ui/public/cpp/gpu/context_provider_command_buffer.h"
#include "url/gurl.h"
namespace content {
namespace {
void OnEstablishedGpuChannel(
const base::RepeatingClosure& quit_closure,
scoped_refptr<gpu::GpuChannelHost>* retvalue,
scoped_refptr<gpu::GpuChannelHost> established_host) {
if (retvalue)
*retvalue = std::move(established_host);
quit_closure.Run();
}
} // namespace
scoped_refptr<gpu::GpuChannelHost>
GpuBrowsertestEstablishGpuChannelSyncRunLoop() {
gpu::GpuChannelEstablishFactory* factory =
content::BrowserMainLoop::GetInstance()->gpu_channel_establish_factory();
CHECK(factory);
base::RunLoop run_loop;
scoped_refptr<gpu::GpuChannelHost> gpu_channel_host;
factory->EstablishGpuChannel(base::BindOnce(
&OnEstablishedGpuChannel, run_loop.QuitClosure(), &gpu_channel_host));
run_loop.Run();
return gpu_channel_host;
}
scoped_refptr<ui::ContextProviderCommandBuffer> GpuBrowsertestCreateContext(
scoped_refptr<gpu::GpuChannelHost> gpu_channel_host) {
gpu::GpuChannelEstablishFactory* factory =
content::BrowserMainLoop::GetInstance()->gpu_channel_establish_factory();
// This is for an offscreen context, so the default framebuffer doesn't need
// any alpha, depth, stencil, antialiasing.
gpu::ContextCreationAttribs attributes;
attributes.alpha_size = -1;
attributes.depth_size = 0;
attributes.stencil_size = 0;
attributes.samples = 0;
attributes.sample_buffers = 0;
attributes.bind_generates_resource = false;
constexpr bool automatic_flushes = false;
constexpr bool support_locking = false;
return base::MakeRefCounted<ui::ContextProviderCommandBuffer>(
std::move(gpu_channel_host), factory->GetGpuMemoryBufferManager(),
content::kGpuStreamIdDefault, content::kGpuStreamPriorityDefault,
gpu::kNullSurfaceHandle, GURL(), automatic_flushes, support_locking,
gpu::SharedMemoryLimits(), attributes, nullptr,
ui::command_buffer_metrics::OFFSCREEN_CONTEXT_FOR_TESTING);
}
} // namespace content
// Copyright 2018 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_TEST_GPU_BROWSERTEST_HELPERS_H_
#define CONTENT_TEST_GPU_BROWSERTEST_HELPERS_H_
#include "base/memory/scoped_refptr.h"
namespace gpu {
class GpuChannelHost;
}
namespace ui {
class ContextProviderCommandBuffer;
}
namespace content {
// Synchronously establishes a connection to the GPU process and returns the
// GpuChannelHost.
scoped_refptr<gpu::GpuChannelHost>
GpuBrowsertestEstablishGpuChannelSyncRunLoop();
// Creates a new ContextProviderCommandBuffer using the provided
// GpuChannelHost.
scoped_refptr<ui::ContextProviderCommandBuffer> GpuBrowsertestCreateContext(
scoped_refptr<gpu::GpuChannelHost> gpu_channel_host);
} // namespace content
#endif // CONTENT_TEST_GPU_BROWSERTEST_HELPERS_H_
......@@ -73,31 +73,15 @@ GpuChannelManager::GpuChannelManager(
shader_translator_cache_(gpu_preferences_),
gpu_memory_buffer_factory_(gpu_memory_buffer_factory),
gpu_feature_info_(gpu_feature_info),
#if defined(OS_ANDROID)
is_backgrounded_for_testing_(false),
#endif
exiting_for_lost_context_(false),
activity_flags_(std::move(activity_flags)),
memory_pressure_listener_(
base::Bind(&GpuChannelManager::HandleMemoryPressure,
base::Unretained(this))),
weak_factory_(this) {
// |application_status_listener_| must be created on the right task runner.
DCHECK(task_runner->BelongsToCurrentThread());
DCHECK(io_task_runner);
DCHECK(scheduler);
#if defined(OS_ANDROID)
// ApplicationStatusListener does not currently support being used from
// non-browser processes. Enable this optimization only for low-end, where GPU
// is run in-process.
if (base::SysInfo::IsLowEndDevice()) {
// Runs on GPU main thread and unregisters when the listener is destroyed,
// So, Unretained is fine here.
application_status_listener_.emplace(base::Bind(
&GpuChannelManager::OnApplicationStateChange, base::Unretained(this)));
}
#endif
}
GpuChannelManager::~GpuChannelManager() {
......@@ -270,43 +254,7 @@ void GpuChannelManager::DoWakeUpGpu() {
DidAccessGpu();
}
void GpuChannelManager::OnApplicationStateChange(
base::android::ApplicationState state) {
// TODO(ericrk): Temporarily disable the context release logic due to
// https://crbug.com/792120. Re-enable when the fix lands.
return;
if (state != base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES) {
return;
}
// Clear the GL context on low-end devices after a 5 second delay, so that we
// don't clear in case the user pressed the home or recents button by mistake
// and got back to Chrome quickly.
const int64_t kDelayToClearContextMs = 5000;
task_runner_->PostDelayedTask(
FROM_HERE,
base::Bind(&GpuChannelManager::OnApplicationBackgrounded,
weak_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kDelayToClearContextMs));
return;
}
void GpuChannelManager::OnApplicationBackgroundedForTesting() {
is_backgrounded_for_testing_ = true;
OnApplicationBackgrounded();
}
void GpuChannelManager::OnApplicationBackgrounded() {
// Check if the app is still in background after the delay.
auto state = base::android::ApplicationStatusListener::GetState();
if (state != base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES &&
state != base::android::APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES &&
!is_backgrounded_for_testing_) {
return;
}
// Delete all the GL contexts when the channel does not use WebGL and Chrome
// goes to background on low-end devices.
std::vector<int> channels_to_clear;
......
......@@ -33,10 +33,6 @@
#include "ui/gl/gl_surface.h"
#include "url/gurl.h"
#if defined(OS_ANDROID)
#include "base/android/application_status_listener.h"
#endif
namespace gl {
class GLShareGroup;
}
......@@ -126,10 +122,7 @@ class GPU_IPC_SERVICE_EXPORT GpuChannelManager {
#if defined(OS_ANDROID)
void DidAccessGpu();
void OnApplicationStateChange(base::android::ApplicationState state);
void OnApplicationBackgroundedForTesting();
void OnApplicationBackgrounded();
#endif
bool is_exiting_for_lost_context() { return exiting_for_lost_context_; }
......@@ -147,8 +140,6 @@ class GPU_IPC_SERVICE_EXPORT GpuChannelManager {
#if defined(OS_ANDROID)
void ScheduleWakeUpGpu();
void DoWakeUpGpu();
void OnApplicationBackgrounded();
#endif
void HandleMemoryPressure(
......@@ -189,10 +180,6 @@ class GPU_IPC_SERVICE_EXPORT GpuChannelManager {
// transport surfaces.
base::TimeTicks last_gpu_access_time_;
base::TimeTicks begin_wake_up_time_;
base::Optional<base::android::ApplicationStatusListener>
application_status_listener_;
bool is_backgrounded_for_testing_;
#endif
// Set during intentional GPU process shutdown.
......
......@@ -18,8 +18,8 @@ class GpuChannelManagerTest : public GpuChannelTestCommon {
~GpuChannelManagerTest() override = default;
#if defined(OS_ANDROID)
void TestOnApplicationStateChange(ContextType type,
bool should_destroy_channel) {
void TestApplicationBackgrounded(ContextType type,
bool should_destroy_channel) {
ASSERT_TRUE(channel_manager());
int32_t kClientId = 1;
......@@ -47,7 +47,7 @@ class GpuChannelManagerTest : public GpuChannelTestCommon {
CommandBufferStub* stub = channel->LookupCommandBuffer(kRouteId);
EXPECT_TRUE(stub);
channel_manager()->OnApplicationBackgroundedForTesting();
channel_manager()->OnApplicationBackgrounded();
channel = channel_manager()->LookupChannel(kClientId);
if (should_destroy_channel) {
......@@ -71,13 +71,14 @@ TEST_F(GpuChannelManagerTest, EstablishChannel) {
}
#if defined(OS_ANDROID)
TEST_F(GpuChannelManagerTest, OnLowEndBackgroundedWithoutWebGL) {
TestOnApplicationStateChange(CONTEXT_TYPE_OPENGLES2, true);
TEST_F(GpuChannelManagerTest, OnBackgroundedWithoutWebGL) {
TestApplicationBackgrounded(CONTEXT_TYPE_OPENGLES2, true);
}
TEST_F(GpuChannelManagerTest, OnLowEndBackgroundedWithWebGL) {
TestOnApplicationStateChange(CONTEXT_TYPE_WEBGL2, false);
TEST_F(GpuChannelManagerTest, OnBackgroundedWithWebGL) {
TestApplicationBackgrounded(CONTEXT_TYPE_WEBGL2, false);
}
#endif
} // namespace gpu
......@@ -85,6 +85,11 @@ interface GpuService {
DestroyAllChannels();
// Called by the browser when the application is backgrounded. The GPU can use
// this message to perform appropriate cleanup. Note that this is currently
// only invoked on low-end Android.
OnBackgrounded();
Crash();
Hang();
ThrowJavaException();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment