summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authorccameron@chromium.org <ccameron@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-09 09:42:53 +0000
committerccameron@chromium.org <ccameron@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-09 09:42:53 +0000
commitc62a5e2cc0dfdbf6fc42d6c93322764dbc3001be (patch)
tree032ca2b7cd9cd1f1c5ec96d81f81ba26770a4d7c /content
parent7da0f76c8e5c659f36d20de2b16ffdd70d4391a7 (diff)
downloadchromium_src-c62a5e2cc0dfdbf6fc42d6c93322764dbc3001be.zip
chromium_src-c62a5e2cc0dfdbf6fc42d6c93322764dbc3001be.tar.gz
chromium_src-c62a5e2cc0dfdbf6fc42d6c93322764dbc3001be.tar.bz2
Add a pass/fail test to verify that GPU memory usage doesn't grow beyond an acceptable level when using CSS (managed memory) and WebGL (unmanaged memory).
BUG=135525 Review URL: https://chromiumcodereview.appspot.com/11667030 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@175753 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r--content/browser/gpu/gpu_memory_test.cc178
-rw-r--r--content/common/gpu/gpu_memory_manager.cc4
-rw-r--r--content/common/gpu/gpu_messages.h2
-rw-r--r--content/content_tests.gypi1
-rw-r--r--content/public/common/gpu_memory_stats.cc4
-rw-r--r--content/public/common/gpu_memory_stats.h6
-rw-r--r--content/test/data/gpu/mem_css3d.html52
-rw-r--r--content/test/data/gpu/mem_webgl.html136
8 files changed, 382 insertions, 1 deletions
diff --git a/content/browser/gpu/gpu_memory_test.cc b/content/browser/gpu/gpu_memory_test.cc
new file mode 100644
index 0000000..6bde5f7
--- /dev/null
+++ b/content/browser/gpu/gpu_memory_test.cc
@@ -0,0 +1,178 @@
+// 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/callback.h"
+#include "base/command_line.h"
+#include "base/path_service.h"
+#include "content/public/browser/gpu_data_manager.h"
+#include "content/public/browser/gpu_data_manager_observer.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/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"
+
+namespace {
+
+// Run the tests with a memory limit of 256MB, and give
+// and extra 24MB 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();
+ 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),
+ has_used_first_shell_(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_CSS3D,
+ PAGE_WEBGL,
+ };
+
+ // Load a page and consume a specified amount of GPU memory.
+ void LoadPage(content::Shell* shell_to_load,
+ PageType page_type,
+ size_t mb_to_use) {
+ FilePath url;
+ switch (page_type) {
+ case PAGE_CSS3D:
+ url = gpu_test_dir_.AppendASCII("mem_css3d.html");
+ break;
+ case PAGE_WEBGL:
+ url = gpu_test_dir_.AppendASCII("mem_webgl.html");
+ 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::ExecuteScript(
+ shell_to_load->web_contents(), js_call.str()));
+ ASSERT_TRUE(message_queue.WaitForMessage(&message));
+ EXPECT_EQ("\"DONE_USE_GPU_MEMORY\"", message);
+ }
+
+ // Create a new tab.
+ content::Shell* CreateNewTab() {
+ // 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 =
+ has_used_first_shell_ ? CreateBrowser() : shell();
+ has_used_first_shell_ = true;
+ visible_shells_.insert(new_shell);
+ return new_shell;
+ }
+
+ void SetTabBackgrounded(content::Shell* shell_to_background) {
+ ASSERT_TRUE(
+ visible_shells_.find(shell_to_background) != visible_shells_.end());
+ visible_shells_.erase(shell_to_background);
+ shell_to_background->web_contents()->WasHidden();
+ }
+
+ size_t GetMemoryUsageMbytes() {
+ // 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.
+ GpuMemoryBytesAllocatedObserver observer;
+ observer.GetBytesAllocated();
+ return observer.GetBytesAllocated() / 1048576;
+ }
+
+ bool AllowTestsToRun() const {
+ return allow_tests_to_run_;
+ }
+
+ private:
+ bool allow_tests_to_run_;
+ std::set<content::Shell*> visible_shells_;
+ bool has_used_first_shell_;
+ FilePath gpu_test_dir_;
+};
+
+} // namespace
diff --git a/content/common/gpu/gpu_memory_manager.cc b/content/common/gpu/gpu_memory_manager.cc
index 2f80bd0..c25414b 100644
--- a/content/common/gpu/gpu_memory_manager.cc
+++ b/content/common/gpu/gpu_memory_manager.cc
@@ -421,6 +421,10 @@ void GpuMemoryManager::GetVideoMemoryUsageStats(
base::GetCurrentProcId()].video_memory = GetCurrentUsage();
video_memory_usage_stats->process_map[
base::GetCurrentProcId()].has_duplicates = true;
+
+ video_memory_usage_stats->bytes_allocated = GetCurrentUsage();
+ video_memory_usage_stats->bytes_allocated_historical_max =
+ bytes_allocated_historical_max_;
}
void GpuMemoryManager::SetWindowCount(uint32 window_count) {
diff --git a/content/common/gpu/gpu_messages.h b/content/common/gpu/gpu_messages.h
index 00e421d..b9558cc 100644
--- a/content/common/gpu/gpu_messages.h
+++ b/content/common/gpu/gpu_messages.h
@@ -162,6 +162,8 @@ IPC_STRUCT_TRAITS_END()
IPC_STRUCT_TRAITS_BEGIN(content::GPUVideoMemoryUsageStats)
IPC_STRUCT_TRAITS_MEMBER(process_map)
+ IPC_STRUCT_TRAITS_MEMBER(bytes_allocated)
+ IPC_STRUCT_TRAITS_MEMBER(bytes_allocated_historical_max)
IPC_STRUCT_TRAITS_END()
IPC_STRUCT_TRAITS_BEGIN(content::GPUMemoryUmaStats)
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 9142ec6..40e5f69 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -685,6 +685,7 @@
'browser/fileapi/blob_layout_browsertest.cc',
'browser/fileapi/file_system_browsertest.cc',
'browser/gpu/gpu_crash_browsertest.cc',
+ 'browser/gpu/gpu_memory_test.cc',
'browser/gpu/webgl_conformance_test.cc',
'browser/gpu/webgl_conformance_test_list_autogen.h',
'browser/in_process_webkit/indexed_db_browsertest.cc',
diff --git a/content/public/common/gpu_memory_stats.cc b/content/public/common/gpu_memory_stats.cc
index b593ab4..2b4e0fa 100644
--- a/content/public/common/gpu_memory_stats.cc
+++ b/content/public/common/gpu_memory_stats.cc
@@ -6,7 +6,9 @@
namespace content {
-GPUVideoMemoryUsageStats::GPUVideoMemoryUsageStats() {
+GPUVideoMemoryUsageStats::GPUVideoMemoryUsageStats()
+ : bytes_allocated(0),
+ bytes_allocated_historical_max(0) {
}
GPUVideoMemoryUsageStats::~GPUVideoMemoryUsageStats() {
diff --git a/content/public/common/gpu_memory_stats.h b/content/public/common/gpu_memory_stats.h
index 5ae6e97..e5e22f8 100644
--- a/content/public/common/gpu_memory_stats.h
+++ b/content/public/common/gpu_memory_stats.h
@@ -36,6 +36,12 @@ struct CONTENT_EXPORT GPUVideoMemoryUsageStats {
// A map of processes to their GPU resource consumption
ProcessMap process_map;
+
+ // The total amount of GPU memory allocated at the time of the request.
+ size_t bytes_allocated;
+
+ // The maximum amount of GPU memory ever allocated at once.
+ size_t bytes_allocated_historical_max;
};
} // namespace content
diff --git a/content/test/data/gpu/mem_css3d.html b/content/test/data/gpu/mem_css3d.html
new file mode 100644
index 0000000..d1d3ead
--- /dev/null
+++ b/content/test/data/gpu/mem_css3d.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>GPU Memory Test: Use N MB of GPU Memory with 3D CSS</title>
+<style>
+.block {
+ background: #FF0000;
+ font-size: 150px;
+ height: 512px;
+ position: absolute;
+ width: 512px;
+
+}
+.perspective
+{
+ border: 1px solid black;
+ height: 512px;
+ text-align: center;
+ width: 512px;
+ -webkit-perspective: 600px;
+ -webkit-perspective-origin: center center;
+ -webkit-transform-style: preserve-3d;
+}
+</style>
+<script type="text/javascript">
+// Generate n 3D CSS elements that are each about 1 MB in size
+function useGpuMemory(mb_to_use) {
+ n = mb_to_use;
+ var blocks = document.getElementById("blocks");
+ var blockArray = document.getElementsByClassName("block");
+ for (var i = 0; i < n; i++) {
+ var block = document.createElement("div");
+ block.className = "block";
+ block.style.WebkitTransform = "translate3d(0px, 0px, " + (i-n+1) + "px)";
+ block.style.opacity = 0.1;
+ block.style.background = "#00FF00";
+ block.textContent = i;
+ blocks.appendChild(block);
+ }
+
+ // Touch offsetTop to trigger a layout.
+ document.body.offsetTop;
+
+ // Signal back to the test runner that we're done allocating memory.
+ domAutomationController.send("DONE_USE_GPU_MEMORY");
+}
+</script>
+</head>
+<body>
+<div id="blocks" class="perspective"/>
+</body>
+</html>
diff --git a/content/test/data/gpu/mem_webgl.html b/content/test/data/gpu/mem_webgl.html
new file mode 100644
index 0000000..6246d34
--- /dev/null
+++ b/content/test/data/gpu/mem_webgl.html
@@ -0,0 +1,136 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>GPU Memory Test: Use N MB of GPU Memory with WebGL</title>
+
+<script id="vertex-shader" type="x-shader/x-vertex">
+attribute vec2 a_position;
+varying vec2 v_position;
+void main() {
+ v_position = a_position;
+ gl_Position = vec4(a_position, 0, 1);
+}
+</script>
+
+<script id="fragment-shader" type="x-shader/x-fragment">
+precision mediump float;
+uniform sampler2D u_image;
+varying vec2 v_position;
+void main() {
+ gl_FragColor = texture2D(u_image, v_position);
+}
+</script>
+
+<script>
+var gl = null;
+var shaderProgram = null;
+var textures = [];
+var fbos = [];
+var t = 0.0;
+var n = 1;
+
+// Create n textures of about 1MB each.
+function useGpuMemory(mb_to_use)
+{
+ n = mb_to_use;
+
+ var canvas = document.getElementById("canvas_id");
+ gl = canvas.getContext("experimental-webgl");
+ if (!gl) {
+ throw "Unable to fetch WebGL rendering context for Canvas";
+ }
+
+ // Create n textures that are each about 1MB and FBOs to render to them.
+ for (var i = 0; i < n; ++i) {
+ var texture = gl.createTexture();
+ var fbo = gl.createFramebuffer();
+ textures.push(texture);
+ fbos.push(fbo);
+
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 512, 512, 0,
+ gl.RGBA, gl.UNSIGNED_BYTE, null)
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER,
+ gl.COLOR_ATTACHMENT0,
+ gl.TEXTURE_2D,
+ texture,
+ 0);
+ }
+
+ // Create a shader that samples a 2D image.
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader,
+ document.getElementById("vertex-shader").textContent);
+ gl.compileShader(vertexShader);
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader,
+ document.getElementById("fragment-shader").textContent);
+ gl.compileShader(fragmentShader);
+
+ shaderProgram = gl.createProgram();
+ gl.attachShader(shaderProgram, vertexShader);
+ gl.attachShader(shaderProgram, fragmentShader);
+ gl.linkProgram(shaderProgram);
+ gl.useProgram(shaderProgram);
+
+ // Bind a vertex buffer with a single triangle
+ var buffer = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+ var bufferData = new Float32Array([-1.0, -1.0, 1.0, -1.0, -1.0, 1.0]);
+ gl.bufferData(gl.ARRAY_BUFFER, bufferData, gl.STATIC_DRAW);
+ gl.enableVertexAttribArray(shaderProgram.a_position);
+ gl.vertexAttribPointer(shaderProgram.a_position, 2, gl.FLOAT, false, 0, 0);
+
+ // Signal back to the test runner that we're done allocating memory.
+ domAutomationController.send("DONE_USE_GPU_MEMORY");
+
+ // Start the event loop.
+ tick();
+}
+
+function drawScene()
+{
+ // Render a solid color to each texture.
+ for (var i = 0; i < n; ++i) {
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbos[i]);
+ gl.viewport(0, 0, 512, 512);
+ gl.clearColor(0.0, Math.sin(t/60.0)*0.25 + 0.75*(i+1.0)/n, 0.0, 1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ }
+
+ // Draw these textures to the screen, offset by 1 pixel increments
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ gl.viewport(0, 0, 256, 256);
+ gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ for (var i = 0; i < n; ++i) {
+ gl.viewport(i, i, 256-i, 256-i);
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, textures[i]);
+ gl.uniform1i(shaderProgram.u_image, 0);
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
+ }
+
+ t += 1;
+}
+
+function tick()
+{
+ window.webkitRequestAnimationFrame(tick);
+ drawScene();
+}
+</script>
+</head>
+
+<body>
+<canvas id="canvas_id" width=256px height=256px style="width:256px; height:256px;"/>
+</body>
+</html>
+