Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1073)

Unified Diff: content/browser/gpu/gpu_memory_test.cc

Issue 11667030: Add GPU memory usage contents browser test (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Contents browser test ready for review Created 7 years, 12 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/gpu/gpu_memory_test.cc
diff --git a/content/browser/gpu/gpu_memory_test.cc b/content/browser/gpu/gpu_memory_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..3d5bb3eae761345227556e96193c37d4c2c8699a
--- /dev/null
+++ b/content/browser/gpu/gpu_memory_test.cc
@@ -0,0 +1,309 @@
+// Copyright (c) 2012 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/basictypes.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
+#include "base/utf_string_conversions.h"
+#include "content/public/browser/gpu_data_manager.h"
+#include "content/public/browser/gpu_data_manager_observer.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_paths.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/url_constants.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+#include "content/shell/shell.h"
+#include "content/test/content_browser_test.h"
+#include "content/test/content_browser_test_utils.h"
+#include "gpu/command_buffer/service/gpu_switches.h"
+#include "net/base/net_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Run the tests with a memory limit of 256MB, and give
+// and extra 16MB of wiggle-room for over-allocation.
+const char *kMemoryLimitSwitch = "256";
+const size_t kMemoryLimit = 256;
+const size_t kSingleTabLimit = 128;
+const size_t kSingleTabMinimum = 64;
+const size_t kWiggleRoom = 24;
+
+// Observer to report GPU memory usage when requested.
+class GpuMemoryBytesAllocatedObserver
+ : public content::GpuDataManagerObserver {
+ public:
+ GpuMemoryBytesAllocatedObserver()
+ : bytes_allocated_(0) {
+ }
+
+ virtual ~GpuMemoryBytesAllocatedObserver() {
+ }
+
+ virtual void OnGpuInfoUpdate() OVERRIDE {}
+
+ virtual void OnVideoMemoryUsageStatsUpdate(
+ const content::GPUVideoMemoryUsageStats& video_memory_usage_stats)
+ OVERRIDE {
+ bytes_allocated_ = video_memory_usage_stats.bytes_allocated;
+ message_loop_runner_->Quit();
+ }
+
+ size_t GetBytesAllocated() {
+ message_loop_runner_ = new content::MessageLoopRunner;
+ content::GpuDataManager::GetInstance()->AddObserver(this);
+ content::GpuDataManager::GetInstance()->
+ RequestVideoMemoryUsageStatsUpdate();
+ message_loop_runner_->Run();
+ // Will break after OnVideoMemoryUsageStatsUpdate
+ content::GpuDataManager::GetInstance()->RemoveObserver(this);
+ message_loop_runner_ = NULL;
+ return bytes_allocated_;
+ }
+
+ private:
+ size_t bytes_allocated_;
+ scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
+};
+
+class GpuMemoryTest : public content::ContentBrowserTest {
+ public:
+ GpuMemoryTest() : allow_tests_to_run_(false) {
+ }
+ virtual ~GpuMemoryTest() {
+ }
+
+ virtual void SetUpInProcessBrowserTestFixture() {
+ FilePath test_dir;
+ ASSERT_TRUE(PathService::Get(content::DIR_TEST_DATA, &test_dir));
+ gpu_test_dir_ = test_dir.AppendASCII("gpu");
+ }
+
+ virtual void SetUpCommandLine(CommandLine* command_line) {
+ command_line->AppendSwitch(switches::kEnableLogging);
+ command_line->AppendSwitch(switches::kForceCompositingMode);
+ command_line->AppendSwitchASCII(switches::kForceGpuMemAvailableMb,
+ kMemoryLimitSwitch);
+ // Only run this on GPU bots for now. These tests should work with
+ // any GPU process, but may be slow.
+ if (command_line->HasSwitch(switches::kUseGpuInTests)) {
+ allow_tests_to_run_ = true;
+ }
+ // Don't enable these tests on Android just yet (they use lots of memory and
+ // may not be stable).
+#if defined(OS_ANDROID)
+ allow_tests_to_run_ = false;
+#endif
+ }
+
+ enum PageType {
+ PAGE_SIMPLE,
+ PAGE_CSS3D,
+ PAGE_WEBGL,
+ };
+
+ void LoadPage(content::Shell* shell_to_load,
+ PageType page_type,
+ size_t mb_to_use) {
+ FilePath url;
+ switch (page_type) {
+ case PAGE_SIMPLE:
+ url = gpu_test_dir_.AppendASCII("mem_simple.html");
+ break;
+ case PAGE_CSS3D:
+ url = gpu_test_dir_.AppendASCII("mem_css3d.html");
+ break;
+ case PAGE_WEBGL:
+ url = gpu_test_dir_.AppendASCII("mem_webgl.html");
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ content::NavigateToURL(shell_to_load, net::FilePathToFileURL(url));
+ std::ostringstream js_call;
+ js_call << "useGpuMemory(";
+ js_call << mb_to_use;
+ js_call << ");";
+ content::DOMMessageQueue message_queue;
+ std::string message;
+ ASSERT_TRUE(content::ExecuteJavaScript(
+ shell_to_load->web_contents()->GetRenderViewHost(),
+ "",
+ js_call.str()));
+ ASSERT_TRUE(message_queue.WaitForMessage(&message));
+ EXPECT_EQ("\"DONE\"", message);
+ }
+
+ size_t GetMemoryUsageMbytes() {
+ GpuMemoryBytesAllocatedObserver observer;
+ observer.GetBytesAllocated();
+ return observer.GetBytesAllocated()/1048576;
+ }
+
+ void ExpectMemoryUsageGE(size_t mbytes_expected) {
+ // TODO: This should wait until all effects of memory management complete.
+ // We will need to wait until all
+ // 1. pending commits from the main thread to the impl thread in the
+ // compositor complete (for visible compositors).
+ // 2. allocations that the renderer's impl thread will make due to the
+ // compositor and WebGL are completed.
+ // 3. pending GpuMemoryManager::Manage() calls to manage are made.
+ // 4. renderers' OnMemoryAllocationChanged callbacks in response to
+ // manager are made.
+ // Each step in this sequence can cause trigger the next (as a 1-2-3-4-1
+ // cycle), so we will need to pump this cycle until it stabilizes.
+
+ // The above is a big undertaking, so, instead, we wait for the condition
+ // to be satisfied, risking that we will give a false PASS if it happens
+ // to be being transiently satisfied.
+ WaitForMemoryStableInRange(mbytes_expected, static_cast<size_t>(-1));
+ }
+
+ void ExpectMemoryUsageLE(size_t mbytes_expected) {
+ WaitForMemoryStableInRange(0, mbytes_expected);
+ }
+
+ content::Shell* CreateShell() {
+ // The ContentBrowserTest will create one shell by default, use that one
+ // first so that we don't confuse the memory manager into thinking there
+ // are more windows than there are.
+ content::Shell* new_shell = shells_.empty() ? shell() : CreateBrowser();
+ shells_.push_back(new_shell);
+ return new_shell;
+ }
+
+ bool AllowTestsToRun() const {
+ return allow_tests_to_run_;
+ }
+
+ private:
+
+ void WaitForMemoryStableInRange(size_t low, size_t high) {
+ // Wait until we get the same vaue in the expected interval 20 times in
+ // a row. By waiting for the value to settle, we increase the chance that
+ // if we are in the process of plowing through the interval, we will
+ // not give a false pass.
+ size_t same_value_observed_count = 0;
+ size_t mbytes_last_observed = 0;
+ while (same_value_observed_count < 20) {
+ size_t mbytes_observed = GetMemoryUsageMbytes();
+ if (low <= mbytes_observed && mbytes_observed <= high) {
+ if (mbytes_observed != mbytes_last_observed)
+ same_value_observed_count = 0;
+ else
+ same_value_observed_count++;
+ mbytes_last_observed = mbytes_observed;
+ }
+ }
+ }
+
+ std::vector<content::Shell*> shells_;
+ FilePath gpu_test_dir_;
+ bool allow_tests_to_run_;
+};
+
+// When trying to load something that doesn't fit into our total GPU memory
+// limit, we shouldn't exceed that limit.
+IN_PROC_BROWSER_TEST_F(GpuMemoryTest, Simple) {
nduca 2013/01/05 05:14:42 Here and elsewhere the test name should say imply
ccameron 2013/01/07 21:36:34 Good point -- I've updated the test names to not s
+ if (!AllowTestsToRun())
+ return;
+
+ content::Shell* shell1 = CreateShell();
+ LoadPage(shell1, PAGE_CSS3D, kMemoryLimit);
+ // Make sure that the CSS3D page triggers allocation of at least 64MB,
+ // otherwise the test doesn't test anything.
+ ExpectMemoryUsageGE(kSingleTabLimit - kWiggleRoom);
+ // Make sure that we stay below the memory limit.
+ ExpectMemoryUsageLE(kMemoryLimit + kWiggleRoom);
+}
+
+// This should remain true if we load the same big page in three windows.
+IN_PROC_BROWSER_TEST_F(GpuMemoryTest, MultiWindow) {
+ if (!AllowTestsToRun())
+ return;
+
+ // Load three instances of a heavy page in visible windows.
+ content::Shell* shell1 = CreateShell();
+ LoadPage(shell1, PAGE_CSS3D, kMemoryLimit);
+ ExpectMemoryUsageGE(kSingleTabLimit - kWiggleRoom);
+ ExpectMemoryUsageLE(kMemoryLimit + kWiggleRoom);
+ content::Shell* shell2 = CreateShell();
+ LoadPage(shell2, PAGE_CSS3D, kMemoryLimit);
+ ExpectMemoryUsageGE(kMemoryLimit - kWiggleRoom);
+ ExpectMemoryUsageLE(kMemoryLimit + kWiggleRoom);
+ content::Shell* shell3 = CreateShell();
+ LoadPage(shell3, PAGE_CSS3D, kMemoryLimit);
+ ExpectMemoryUsageGE(kMemoryLimit - kWiggleRoom);
+ ExpectMemoryUsageLE(kMemoryLimit + kWiggleRoom);
+}
+
+// This should remain true if we load the same big page in multiple tabs.
+IN_PROC_BROWSER_TEST_F(GpuMemoryTest, MultiTab) {
+ if (!AllowTestsToRun())
+ return;
+
+ content::Shell* shell1 = CreateShell();
+ // Load and then background two tabs, then load a foregrounded tab.
+ LoadPage(shell1, PAGE_CSS3D, kMemoryLimit);
+ ExpectMemoryUsageGE(kSingleTabLimit - kWiggleRoom);
+ shell1->web_contents()->WasHidden();
+ ExpectMemoryUsageLE(0u);
+
+ content::Shell* shell2 = CreateShell();
+ LoadPage(shell2, PAGE_CSS3D, kMemoryLimit);
+ ExpectMemoryUsageGE(kSingleTabLimit - kWiggleRoom);
+ shell2->web_contents()->WasHidden();
+ ExpectMemoryUsageLE(0u);
+
+ content::Shell* shell3 = CreateShell();
+ LoadPage(shell3, PAGE_CSS3D, kMemoryLimit);
+ ExpectMemoryUsageGE(kSingleTabLimit - kWiggleRoom);
+ ExpectMemoryUsageLE(kMemoryLimit + kWiggleRoom);
+}
+
+// Load a page with heavy WebGL, then background it, and make sure that
+// the WebGL page's allocation causes the CSS page to use less memory
+IN_PROC_BROWSER_TEST_F(GpuMemoryTest, WebGL) {
+ if (!AllowTestsToRun())
+ return;
+
+ content::Shell* shell1 = CreateShell();
+ LoadPage(shell1, PAGE_WEBGL, kMemoryLimit - kSingleTabMinimum);
+ // Make sure that the WEBGL page triggers allocation of at least what
+ // WebGL allocated, otherwise the test doesn't test anything.
+ ExpectMemoryUsageGE(kMemoryLimit - kSingleTabMinimum);
+ // Background that tab, and load heavy content in a foreground tab.
+ shell1->web_contents()->WasHidden();
+ content::Shell* shell2 = CreateShell();
+ LoadPage(shell2, PAGE_CSS3D, kMemoryLimit);
+ // Make sure we are still under the limit, but that we get close
+ ExpectMemoryUsageGE(kMemoryLimit - kWiggleRoom);
+ ExpectMemoryUsageLE(kMemoryLimit + kWiggleRoom);
+}
+
+// Make sure that we don't waste memory on backgrounded tabs that can't
+// be rendered fully.
+IN_PROC_BROWSER_TEST_F(GpuMemoryTest, Background) {
+ if (!AllowTestsToRun())
+ return;
+
+ content::Shell* shell = CreateShell();
+ LoadPage(shell, PAGE_CSS3D, kMemoryLimit);
+ // Make sure that the CSS3D page triggers allocation of at least 64MB,
+ // otherwise the test doesn't test anything.
+ ExpectMemoryUsageGE(kSingleTabLimit - kWiggleRoom);
+ // Background the tab. Because it is too big to be kept in memory,
+ // its allocation should drop to zero.
+ shell->web_contents()->WasHidden();
+ ExpectMemoryUsageLE(0u);
+}
+
+} // namespace

Powered by Google App Engine
This is Rietveld 408576698