Save errno for logging before potentially overwriting it.
[chromium-blink-merge.git] / content / browser / gpu / gpu_memory_test.cc
blobfd85739b86468ceebcd289786f31af5d9f483906
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.
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"
22 namespace content {
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;
31 // Observer to report GPU memory usage when requested.
32 class GpuMemoryBytesAllocatedObserver : public GpuDataManagerObserver {
33 public:
34 GpuMemoryBytesAllocatedObserver()
35 : bytes_allocated_(0) {
38 virtual ~GpuMemoryBytesAllocatedObserver() {
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();
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_;
57 private:
58 size_t bytes_allocated_;
59 scoped_refptr<MessageLoopRunner> message_loop_runner_;
62 class GpuMemoryTest : public ContentBrowserTest {
63 public:
64 GpuMemoryTest()
65 : allow_tests_to_run_(false),
66 has_used_first_shell_(false) {
68 virtual ~GpuMemoryTest() {
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");
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;
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
94 enum PageType {
95 PAGE_CSS3D,
96 PAGE_WEBGL,
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;
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);
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;
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();
143 bool MemoryUsageInRange(size_t low, size_t high) {
144 FinishGpuMemoryChanges();
145 size_t memory_usage_bytes = GetMemoryUsageMbytes();
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;
160 return (low <= memory_usage_bytes && memory_usage_bytes <= high);
163 bool AllowTestsToRun() const {
164 return allow_tests_to_run_;
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.
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);
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.
205 size_t GetMemoryUsageMbytes() {
206 GpuMemoryBytesAllocatedObserver observer;
207 observer.GetBytesAllocated();
208 return observer.GetBytesAllocated() / 1048576;
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_;
218 // When trying to load something that doesn't fit into our total GPU memory
219 // limit, we shouldn't exceed that limit.
220 IN_PROC_BROWSER_TEST_F(GpuMemoryTest, SingleWindowDoesNotExceedLimit) {
221 if (!AllowTestsToRun())
222 return;
224 Shell* tab = CreateNewTab();
225 LoadPage(tab, PAGE_CSS3D, kMemoryLimitMB);
226 // Make sure that the CSS3D page maxes out a single tab's budget (otherwise
227 // the test doesn't test anything) but still stays under the limit.
228 EXPECT_TRUE(MemoryUsageInRange(
229 kSingleTabLimitMB - kWiggleRoomMB,
230 kMemoryLimitMB + kWiggleRoomMB));
233 } // namespace content