Home | History | Annotate | Download | only in gpu
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/callback.h"
      6 #include "base/command_line.h"
      7 #include "base/path_service.h"
      8 #include "content/public/browser/gpu_data_manager.h"
      9 #include "content/public/browser/gpu_data_manager_observer.h"
     10 #include "content/public/browser/web_contents.h"
     11 #include "content/public/common/content_paths.h"
     12 #include "content/public/common/content_switches.h"
     13 #include "content/public/test/browser_test_utils.h"
     14 #include "content/public/test/test_utils.h"
     15 #include "content/shell/shell.h"
     16 #include "content/test/content_browser_test.h"
     17 #include "content/test/content_browser_test_utils.h"
     18 #include "gpu/command_buffer/service/gpu_switches.h"
     19 #include "gpu/config/gpu_test_config.h"
     20 #include "net/base/net_util.h"
     21 
     22 namespace content {
     23 
     24 // Run the tests with a memory limit of 256MB, and give
     25 // and extra 4MB of wiggle-room for over-allocation.
     26 const char* kMemoryLimitMBSwitch = "256";
     27 const size_t kMemoryLimitMB = 256;
     28 const size_t kSingleTabLimitMB = 128;
     29 const size_t kWiggleRoomMB = 4;
     30 
     31 // Observer to report GPU memory usage when requested.
     32 class GpuMemoryBytesAllocatedObserver : public GpuDataManagerObserver {
     33  public:
     34   GpuMemoryBytesAllocatedObserver()
     35       : bytes_allocated_(0) {
     36   }
     37 
     38   virtual ~GpuMemoryBytesAllocatedObserver() {
     39   }
     40 
     41   virtual void OnVideoMemoryUsageStatsUpdate(
     42       const GPUVideoMemoryUsageStats& video_memory_usage_stats) OVERRIDE {
     43     bytes_allocated_ = video_memory_usage_stats.bytes_allocated;
     44     message_loop_runner_->Quit();
     45   }
     46 
     47   size_t GetBytesAllocated() {
     48     message_loop_runner_ = new MessageLoopRunner;
     49     GpuDataManager::GetInstance()->AddObserver(this);
     50     GpuDataManager::GetInstance()->RequestVideoMemoryUsageStatsUpdate();
     51     message_loop_runner_->Run();
     52     GpuDataManager::GetInstance()->RemoveObserver(this);
     53     message_loop_runner_ = NULL;
     54     return bytes_allocated_;
     55   }
     56 
     57  private:
     58   size_t bytes_allocated_;
     59   scoped_refptr<MessageLoopRunner> message_loop_runner_;
     60 };
     61 
     62 class GpuMemoryTest : public ContentBrowserTest {
     63  public:
     64   GpuMemoryTest()
     65       : allow_tests_to_run_(false),
     66         has_used_first_shell_(false) {
     67   }
     68   virtual ~GpuMemoryTest() {
     69   }
     70 
     71   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
     72     base::FilePath test_dir;
     73     ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &test_dir));
     74     gpu_test_dir_ = test_dir.AppendASCII("gpu");
     75   }
     76 
     77   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
     78     command_line->AppendSwitch(switches::kEnableLogging);
     79     command_line->AppendSwitch(switches::kForceCompositingMode);
     80     command_line->AppendSwitchASCII(switches::kForceGpuMemAvailableMb,
     81                                     kMemoryLimitMBSwitch);
     82     // Only run this on GPU bots for now. These tests should work with
     83     // any GPU process, but may be slow.
     84     if (command_line->HasSwitch(switches::kUseGpuInTests)) {
     85       allow_tests_to_run_ = true;
     86     }
     87     // Don't enable these tests on Android just yet (they use lots of memory and
     88     // may not be stable).
     89 #if defined(OS_ANDROID)
     90     allow_tests_to_run_ = false;
     91 #endif
     92   }
     93 
     94   enum PageType {
     95     PAGE_CSS3D,
     96     PAGE_WEBGL,
     97   };
     98 
     99   // Load a page and consume a specified amount of GPU memory.
    100   void LoadPage(Shell* tab_to_load,
    101                 PageType page_type,
    102                 size_t mb_to_use) {
    103     base::FilePath url;
    104     switch (page_type) {
    105       case PAGE_CSS3D:
    106         url = gpu_test_dir_.AppendASCII("mem_css3d.html");
    107         break;
    108       case PAGE_WEBGL:
    109         url = gpu_test_dir_.AppendASCII("mem_webgl.html");
    110         break;
    111     }
    112 
    113     NavigateToURL(tab_to_load, net::FilePathToFileURL(url));
    114     std::ostringstream js_call;
    115     js_call << "useGpuMemory(";
    116     js_call << mb_to_use;
    117     js_call << ");";
    118     std::string message;
    119     ASSERT_TRUE(ExecuteScriptInFrameAndExtractString(
    120         tab_to_load->web_contents(), std::string(), js_call.str(), &message));
    121     EXPECT_EQ("DONE_USE_GPU_MEMORY", message);
    122   }
    123 
    124   // Create a new tab.
    125   Shell* CreateNewTab() {
    126     // The ContentBrowserTest will create one shell by default, use that one
    127     // first so that we don't confuse the memory manager into thinking there
    128     // are more windows than there are.
    129     Shell* new_tab = has_used_first_shell_ ? CreateBrowser() : shell();
    130     has_used_first_shell_ = true;
    131     tabs_.insert(new_tab);
    132     visible_tabs_.insert(new_tab);
    133     return new_tab;
    134   }
    135 
    136   void SetTabBackgrounded(Shell* tab_to_background) {
    137     ASSERT_TRUE(
    138         visible_tabs_.find(tab_to_background) != visible_tabs_.end());
    139     visible_tabs_.erase(tab_to_background);
    140     tab_to_background->web_contents()->WasHidden();
    141   }
    142 
    143   bool MemoryUsageInRange(size_t low, size_t high) {
    144     FinishGpuMemoryChanges();
    145     size_t memory_usage_bytes = GetMemoryUsageMbytes();
    146 
    147     // If it's not immediately the case that low <= usage <= high, then
    148     // allow
    149     // Because we haven't implemented the full delay in FinishGpuMemoryChanges,
    150     // keep re-reading the GPU memory usage for 2 seconds before declaring
    151     // failure.
    152     base::Time start_time = base::Time::Now();
    153     while (low > memory_usage_bytes || memory_usage_bytes > high) {
    154       memory_usage_bytes = GetMemoryUsageMbytes();
    155       base::TimeDelta delta = base::Time::Now() - start_time;
    156       if (delta.InMilliseconds() >= 2000)
    157         break;
    158     }
    159 
    160     return (low <= memory_usage_bytes && memory_usage_bytes <= high);
    161   }
    162 
    163   bool AllowTestsToRun() const {
    164     return allow_tests_to_run_;
    165   }
    166 
    167  private:
    168   void FinishGpuMemoryChanges() {
    169     // This should wait until all effects of memory management complete.
    170     // We will need to wait until all
    171     // 1. pending commits from the main thread to the impl thread in the
    172     //    compositor complete (for visible compositors).
    173     // 2. allocations that the renderer's impl thread will make due to the
    174     //    compositor and WebGL are completed.
    175     // 3. pending GpuMemoryManager::Manage() calls to manage are made.
    176     // 4. renderers' OnMemoryAllocationChanged callbacks in response to
    177     //    manager are made.
    178     // Each step in this sequence can cause trigger the next (as a 1-2-3-4-1
    179     // cycle), so we will need to pump this cycle until it stabilizes.
    180 
    181     // Pump the cycle 8 times (in principle it could take an infinite number
    182     // of iterations to settle).
    183     for (size_t pump_it = 0; pump_it < 8; ++pump_it) {
    184       // Wait for a RequestAnimationFrame to complete from all visible tabs
    185       // for stage 1 of the cycle.
    186       for (std::set<Shell*>::iterator it = visible_tabs_.begin();
    187            it != visible_tabs_.end();
    188            ++it) {
    189         std::string js_call(
    190             "window.webkitRequestAnimationFrame(function() {"
    191             "  domAutomationController.setAutomationId(1);"
    192             "  domAutomationController.send(\"DONE_RAF\");"
    193             "})");
    194         std::string message;
    195         ASSERT_TRUE(ExecuteScriptInFrameAndExtractString(
    196             (*it)->web_contents(), std::string(), js_call, &message));
    197         EXPECT_EQ("DONE_RAF", message);
    198       }
    199       // TODO(ccameron): send an IPC from Browser -> Renderer (delay it until
    200       // painting finishes) -> GPU process (delay it until any pending manages
    201       // happen) -> All Renderers -> Browser to flush parts 2, 3, and 4.
    202     }
    203   }
    204 
    205   size_t GetMemoryUsageMbytes() {
    206     GpuMemoryBytesAllocatedObserver observer;
    207     observer.GetBytesAllocated();
    208     return observer.GetBytesAllocated() / 1048576;
    209   }
    210 
    211   bool allow_tests_to_run_;
    212   std::set<Shell*> tabs_;
    213   std::set<Shell*> visible_tabs_;
    214   bool has_used_first_shell_;
    215   base::FilePath gpu_test_dir_;
    216 };
    217 
    218 #if defined(OS_LINUX) && !defined(NDEBUG)
    219 // http://crbug.com/254724
    220 #define IF_NOT_DEBUG_LINUX(x) DISABLED_ ## x
    221 #else
    222 #define IF_NOT_DEBUG_LINUX(x) x
    223 #endif
    224 
    225 // When trying to load something that doesn't fit into our total GPU memory
    226 // limit, we shouldn't exceed that limit.
    227 IN_PROC_BROWSER_TEST_F(GpuMemoryTest,
    228                        IF_NOT_DEBUG_LINUX(SingleWindowDoesNotExceedLimit)) {
    229   if (!AllowTestsToRun())
    230     return;
    231 
    232   Shell* tab = CreateNewTab();
    233   LoadPage(tab, PAGE_CSS3D, kMemoryLimitMB);
    234   // Make sure that the CSS3D page maxes out a single tab's budget (otherwise
    235   // the test doesn't test anything) but still stays under the limit.
    236   EXPECT_TRUE(MemoryUsageInRange(
    237      kSingleTabLimitMB - kWiggleRoomMB,
    238      kMemoryLimitMB + kWiggleRoomMB));
    239 }
    240 
    241 }  // namespace content
    242