| // 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/android/application_status_listener.h" | 
 | #include "base/android/build_info.h" | 
 | #include "base/base_switches.h" | 
 | #include "base/bind.h" | 
 | #include "base/bind_helpers.h" | 
 | #include "base/test/scoped_feature_list.h" | 
 | #include "components/viz/common/features.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/public/common/content_switches.h" | 
 | #include "content/public/common/gpu_stream_constants.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/config/gpu_finch_features.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 { | 
 |  | 
 | enum class CompositorImplMode { | 
 |   kNormal, | 
 |   kViz, | 
 |   kVizSkDDL, | 
 | }; | 
 |  | 
 | class CompositorImplBrowserTest | 
 |     : public testing::WithParamInterface<CompositorImplMode>, | 
 |       public ContentBrowserTest { | 
 |  public: | 
 |   CompositorImplBrowserTest() {} | 
 |  | 
 |   void SetUp() override { | 
 |     switch (GetParam()) { | 
 |       case CompositorImplMode::kNormal: | 
 |         break; | 
 |       case CompositorImplMode::kViz: | 
 |         scoped_feature_list_.InitAndEnableFeature( | 
 |             features::kVizDisplayCompositor); | 
 |         break; | 
 |       case CompositorImplMode::kVizSkDDL: | 
 |         scoped_feature_list_.InitWithFeatures( | 
 |             {features::kVizDisplayCompositor, features::kUseSkiaRenderer, | 
 |              features::kDefaultEnableOopRasterization}, | 
 |             {}); | 
 |         break; | 
 |     } | 
 |  | 
 |     ContentBrowserTest::SetUp(); | 
 |   } | 
 |  | 
 |  protected: | 
 |   void SetUpOnMainThread() override { | 
 |     ASSERT_TRUE(embedded_test_server()->Start()); | 
 |     net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); | 
 |     https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath()); | 
 |     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: | 
 |   base::test::ScopedFeatureList scoped_feature_list_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(CompositorImplBrowserTest); | 
 | }; | 
 |  | 
 | INSTANTIATE_TEST_SUITE_P(P, | 
 |                          CompositorImplBrowserTest, | 
 |                          ::testing::Values(CompositorImplMode::kNormal, | 
 |                                            CompositorImplMode::kViz, | 
 |                                            CompositorImplMode::kVizSkDDL)); | 
 |  | 
 | class CompositorImplLowEndBrowserTest : public CompositorImplBrowserTest { | 
 |  public: | 
 |   void SetUpCommandLine(base::CommandLine* command_line) override { | 
 |     command_line->AppendSwitch(switches::kEnableLowEndDeviceMode); | 
 |     command_line->AppendSwitch(switches::kInProcessGPU); | 
 |     content::ContentBrowserTest::SetUpCommandLine(command_line); | 
 |   } | 
 | }; | 
 |  | 
 | // Viz on android is not yet compatible with in-process GPU. Only run in | 
 | // kNormal mode. | 
 | // TODO(ericrk): Make this work everywhere. https://crbug.com/851643 | 
 | INSTANTIATE_TEST_SUITE_P(P, | 
 |                          CompositorImplLowEndBrowserTest, | 
 |                          ::testing::Values(CompositorImplMode::kNormal)); | 
 |  | 
 | // 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_P(CompositorImplLowEndBrowserTest, | 
 |                        CompositorImplDropsResourcesOnBackground) { | 
 |   // This test makes invalid assumptions when surface synchronization is | 
 |   // enabled. The compositor lock is obsolete, and inspecting frames | 
 |   // from the CompositorImpl does not guarantee renderer CompositorFrames | 
 |   // are ready. | 
 |   if (features::IsSurfaceSynchronizationEnabled()) | 
 |     return; | 
 |  | 
 |   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); | 
 |   base::android::ApplicationStatusListener::NotifyApplicationStateChange( | 
 |       base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES); | 
 |   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); | 
 |   base::android::ApplicationStatusListener::NotifyApplicationStateChange( | 
 |       base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES); | 
 |   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()); | 
 | } | 
 |  | 
 | // RunLoop implementation that runs until it observes a swap with size. | 
 | class CompositorSwapRunLoop { | 
 |  public: | 
 |   CompositorSwapRunLoop(CompositorImpl* compositor) : compositor_(compositor) { | 
 |     compositor_->SetSwapCompletedWithSizeCallbackForTesting(base::BindRepeating( | 
 |         &CompositorSwapRunLoop::DidSwap, base::Unretained(this))); | 
 |   } | 
 |   ~CompositorSwapRunLoop() { | 
 |     compositor_->SetSwapCompletedWithSizeCallbackForTesting(base::DoNothing()); | 
 |   } | 
 |  | 
 |   void RunUntilSwap() { run_loop_.Run(); } | 
 |  | 
 |  private: | 
 |   void DidSwap(const gfx::Size& pixel_size) { | 
 |     EXPECT_FALSE(pixel_size.IsEmpty()); | 
 |     run_loop_.Quit(); | 
 |   } | 
 |  | 
 |   CompositorImpl* compositor_; | 
 |   base::RunLoop run_loop_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(CompositorSwapRunLoop); | 
 | }; | 
 |  | 
 | IN_PROC_BROWSER_TEST_P(CompositorImplBrowserTest, | 
 |                        CompositorImplReceivesSwapCallbacks) { | 
 |   // OOP-R is required for this test to succeed with SkDDL, but is disabled on | 
 |   // Android L and lower. | 
 |   if (GetParam() == CompositorImplMode::kVizSkDDL && | 
 |       base::android::BuildInfo::GetInstance()->sdk_int() < | 
 |           base::android::SDK_VERSION_MARSHMALLOW) { | 
 |     return; | 
 |   } | 
 |   CompositorSwapRunLoop(compositor_impl()).RunUntilSwap(); | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace content |