diff options
author | ccameron@chromium.org <ccameron@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-09 09:42:53 +0000 |
---|---|---|
committer | ccameron@chromium.org <ccameron@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-09 09:42:53 +0000 |
commit | c62a5e2cc0dfdbf6fc42d6c93322764dbc3001be (patch) | |
tree | 032ca2b7cd9cd1f1c5ec96d81f81ba26770a4d7c /content | |
parent | 7da0f76c8e5c659f36d20de2b16ffdd70d4391a7 (diff) | |
download | chromium_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.cc | 178 | ||||
-rw-r--r-- | content/common/gpu/gpu_memory_manager.cc | 4 | ||||
-rw-r--r-- | content/common/gpu/gpu_messages.h | 2 | ||||
-rw-r--r-- | content/content_tests.gypi | 1 | ||||
-rw-r--r-- | content/public/common/gpu_memory_stats.cc | 4 | ||||
-rw-r--r-- | content/public/common/gpu_memory_stats.h | 6 | ||||
-rw-r--r-- | content/test/data/gpu/mem_css3d.html | 52 | ||||
-rw-r--r-- | content/test/data/gpu/mem_webgl.html | 136 |
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> + |