blob: 18ebce20786d51f829ce98f503e23f9e1e4f7db0 [file] [log] [blame]
// 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 "content/common/gpu/gpu_memory_manager.h"
#include "content/common/gpu/gpu_memory_manager_client.h"
#include "content/common/gpu/gpu_memory_tracking.h"
#include "gpu/command_buffer/common/gpu_memory_allocation.h"
#include "ui/gfx/size_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
using gpu::MemoryAllocation;
class FakeMemoryTracker : public gpu::gles2::MemoryTracker {
public:
void TrackMemoryAllocatedChange(
size_t /* old_size */,
size_t /* new_size */,
gpu::gles2::MemoryTracker::Pool /* pool */) override {}
bool EnsureGPUMemoryAvailable(size_t /* size_needed */) override {
return true;
}
private:
~FakeMemoryTracker() override {}
};
namespace content {
// This class is used to collect all stub assignments during a
// Manage() call.
class ClientAssignmentCollector {
public:
struct ClientMemoryStat {
MemoryAllocation allocation;
};
typedef base::hash_map<GpuMemoryManagerClient*, ClientMemoryStat>
ClientMemoryStatMap;
static const ClientMemoryStatMap& GetClientStatsForLastManage() {
return client_memory_stats_for_last_manage_;
}
static void ClearAllStats() {
client_memory_stats_for_last_manage_.clear();
}
static void AddClientStat(GpuMemoryManagerClient* client,
const MemoryAllocation& allocation) {
DCHECK(!client_memory_stats_for_last_manage_.count(client));
client_memory_stats_for_last_manage_[client].allocation = allocation;
}
private:
static ClientMemoryStatMap client_memory_stats_for_last_manage_;
};
ClientAssignmentCollector::ClientMemoryStatMap
ClientAssignmentCollector::client_memory_stats_for_last_manage_;
class FakeClient : public GpuMemoryManagerClient {
public:
GpuMemoryManager* memmgr_;
bool suggest_have_frontbuffer_;
MemoryAllocation allocation_;
uint64 total_gpu_memory_;
gfx::Size surface_size_;
GpuMemoryManagerClient* share_group_;
scoped_refptr<gpu::gles2::MemoryTracker> memory_tracker_;
scoped_ptr<GpuMemoryTrackingGroup> tracking_group_;
scoped_ptr<GpuMemoryManagerClientState> client_state_;
// This will create a client with no surface
FakeClient(GpuMemoryManager* memmgr, GpuMemoryManagerClient* share_group)
: memmgr_(memmgr),
suggest_have_frontbuffer_(false),
total_gpu_memory_(0),
share_group_(share_group),
memory_tracker_(NULL) {
if (!share_group_) {
memory_tracker_ = new FakeMemoryTracker();
tracking_group_.reset(
memmgr_->CreateTrackingGroup(0, memory_tracker_.get()));
}
client_state_.reset(memmgr_->CreateClientState(this, false, true));
}
// This will create a client with a surface
FakeClient(GpuMemoryManager* memmgr, int32 surface_id, bool visible)
: memmgr_(memmgr),
suggest_have_frontbuffer_(false),
total_gpu_memory_(0),
share_group_(NULL),
memory_tracker_(NULL) {
memory_tracker_ = new FakeMemoryTracker();
tracking_group_.reset(
memmgr_->CreateTrackingGroup(0, memory_tracker_.get()));
client_state_.reset(
memmgr_->CreateClientState(this, surface_id != 0, visible));
}
~FakeClient() override {
client_state_.reset();
tracking_group_.reset();
memory_tracker_ = NULL;
}
void SetMemoryAllocation(const MemoryAllocation& alloc) override {
allocation_ = alloc;
ClientAssignmentCollector::AddClientStat(this, alloc);
}
void SuggestHaveFrontBuffer(bool suggest_have_frontbuffer) override {
suggest_have_frontbuffer_ = suggest_have_frontbuffer;
}
bool GetTotalGpuMemory(uint64* bytes) override {
if (total_gpu_memory_) {
*bytes = total_gpu_memory_;
return true;
}
return false;
}
void SetTotalGpuMemory(uint64 bytes) { total_gpu_memory_ = bytes; }
gpu::gles2::MemoryTracker* GetMemoryTracker() const override {
if (share_group_)
return share_group_->GetMemoryTracker();
return memory_tracker_.get();
}
gfx::Size GetSurfaceSize() const override { return surface_size_; }
void SetSurfaceSize(gfx::Size size) { surface_size_ = size; }
void SetVisible(bool visible) {
client_state_->SetVisible(visible);
}
uint64 BytesWhenVisible() const {
return allocation_.bytes_limit_when_visible;
}
};
class GpuMemoryManagerTest : public testing::Test {
protected:
static const uint64 kFrontbufferLimitForTest = 3;
GpuMemoryManagerTest()
: memmgr_(0, kFrontbufferLimitForTest) {
memmgr_.TestingDisableScheduleManage();
}
void SetUp() override {}
static int32 GenerateUniqueSurfaceId() {
static int32 surface_id_ = 1;
return surface_id_++;
}
bool IsAllocationForegroundForSurfaceYes(
const MemoryAllocation& alloc) {
return true;
}
bool IsAllocationBackgroundForSurfaceYes(
const MemoryAllocation& alloc) {
return true;
}
bool IsAllocationHibernatedForSurfaceYes(
const MemoryAllocation& alloc) {
return true;
}
bool IsAllocationForegroundForSurfaceNo(
const MemoryAllocation& alloc) {
return alloc.bytes_limit_when_visible != 0;
}
bool IsAllocationBackgroundForSurfaceNo(
const MemoryAllocation& alloc) {
return alloc.bytes_limit_when_visible != 0;
}
bool IsAllocationHibernatedForSurfaceNo(
const MemoryAllocation& alloc) {
return alloc.bytes_limit_when_visible == 0;
}
void Manage() {
ClientAssignmentCollector::ClearAllStats();
memmgr_.Manage();
}
GpuMemoryManager memmgr_;
};
// Test GpuMemoryManager::Manage basic functionality.
// Expect memory allocation to set suggest_have_frontbuffer/backbuffer
// according to visibility and last used time for stubs with surface.
// Expect memory allocation to be shared according to share groups for stubs
// without a surface.
TEST_F(GpuMemoryManagerTest, TestManageBasicFunctionality) {
// Test stubs with surface.
FakeClient stub1(&memmgr_, GenerateUniqueSurfaceId(), true),
stub2(&memmgr_, GenerateUniqueSurfaceId(), false);
Manage();
EXPECT_TRUE(IsAllocationForegroundForSurfaceYes(stub1.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceYes(stub2.allocation_));
// Test stubs without surface, with share group of 1 stub.
FakeClient stub3(&memmgr_, &stub1), stub4(&memmgr_, &stub2);
Manage();
EXPECT_TRUE(IsAllocationForegroundForSurfaceYes(stub1.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceYes(stub2.allocation_));
EXPECT_TRUE(IsAllocationForegroundForSurfaceNo(stub3.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceNo(stub4.allocation_));
// Test stub without surface, with share group of multiple stubs.
FakeClient stub5(&memmgr_ , &stub2);
Manage();
EXPECT_TRUE(IsAllocationForegroundForSurfaceNo(stub4.allocation_));
}
// Test GpuMemoryManager::Manage functionality: changing visibility.
// Expect memory allocation to set suggest_have_frontbuffer/backbuffer
// according to visibility and last used time for stubs with surface.
// Expect memory allocation to be shared according to share groups for stubs
// without a surface.
TEST_F(GpuMemoryManagerTest, TestManageChangingVisibility) {
FakeClient stub1(&memmgr_, GenerateUniqueSurfaceId(), true),
stub2(&memmgr_, GenerateUniqueSurfaceId(), false);
FakeClient stub3(&memmgr_, &stub1), stub4(&memmgr_, &stub2);
FakeClient stub5(&memmgr_ , &stub2);
Manage();
EXPECT_TRUE(IsAllocationForegroundForSurfaceYes(stub1.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceYes(stub2.allocation_));
EXPECT_TRUE(IsAllocationForegroundForSurfaceNo(stub3.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceNo(stub4.allocation_));
EXPECT_TRUE(IsAllocationForegroundForSurfaceNo(stub5.allocation_));
stub1.SetVisible(false);
stub2.SetVisible(true);
Manage();
EXPECT_TRUE(IsAllocationBackgroundForSurfaceYes(stub1.allocation_));
EXPECT_TRUE(IsAllocationForegroundForSurfaceYes(stub2.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceNo(stub3.allocation_));
EXPECT_TRUE(IsAllocationForegroundForSurfaceNo(stub4.allocation_));
EXPECT_TRUE(IsAllocationForegroundForSurfaceNo(stub5.allocation_));
}
// Test GpuMemoryManager::Manage functionality: Test more than threshold number
// of visible stubs.
// Expect all allocations to continue to have frontbuffer.
TEST_F(GpuMemoryManagerTest, TestManageManyVisibleStubs) {
FakeClient stub1(&memmgr_, GenerateUniqueSurfaceId(), true),
stub2(&memmgr_, GenerateUniqueSurfaceId(), true),
stub3(&memmgr_, GenerateUniqueSurfaceId(), true),
stub4(&memmgr_, GenerateUniqueSurfaceId(), true);
FakeClient stub5(&memmgr_ , &stub1), stub6(&memmgr_ , &stub2);
FakeClient stub7(&memmgr_ , &stub2);
Manage();
EXPECT_TRUE(IsAllocationForegroundForSurfaceYes(stub1.allocation_));
EXPECT_TRUE(IsAllocationForegroundForSurfaceYes(stub2.allocation_));
EXPECT_TRUE(IsAllocationForegroundForSurfaceYes(stub3.allocation_));
EXPECT_TRUE(IsAllocationForegroundForSurfaceYes(stub4.allocation_));
EXPECT_TRUE(IsAllocationForegroundForSurfaceNo(stub5.allocation_));
EXPECT_TRUE(IsAllocationForegroundForSurfaceNo(stub6.allocation_));
EXPECT_TRUE(IsAllocationForegroundForSurfaceNo(stub7.allocation_));
}
// Test GpuMemoryManager::Manage functionality: Test more than threshold number
// of not visible stubs.
// Expect the stubs surpassing the threshold to not have a backbuffer.
TEST_F(GpuMemoryManagerTest, TestManageManyNotVisibleStubs) {
FakeClient stub1(&memmgr_, GenerateUniqueSurfaceId(), true),
stub2(&memmgr_, GenerateUniqueSurfaceId(), true),
stub3(&memmgr_, GenerateUniqueSurfaceId(), true),
stub4(&memmgr_, GenerateUniqueSurfaceId(), true);
stub4.SetVisible(false);
stub3.SetVisible(false);
stub2.SetVisible(false);
stub1.SetVisible(false);
FakeClient stub5(&memmgr_ , &stub1), stub6(&memmgr_ , &stub4);
FakeClient stub7(&memmgr_ , &stub1);
Manage();
EXPECT_TRUE(IsAllocationBackgroundForSurfaceYes(stub1.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceYes(stub2.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceYes(stub3.allocation_));
EXPECT_TRUE(IsAllocationHibernatedForSurfaceYes(stub4.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceNo(stub5.allocation_));
EXPECT_TRUE(IsAllocationHibernatedForSurfaceNo(stub6.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceNo(stub7.allocation_));
}
// Test GpuMemoryManager::Manage functionality: Test changing the last used
// time of stubs when doing so causes change in which stubs surpass threshold.
// Expect frontbuffer to be dropped for the older stub.
TEST_F(GpuMemoryManagerTest, TestManageChangingLastUsedTime) {
FakeClient stub1(&memmgr_, GenerateUniqueSurfaceId(), true),
stub2(&memmgr_, GenerateUniqueSurfaceId(), true),
stub3(&memmgr_, GenerateUniqueSurfaceId(), true),
stub4(&memmgr_, GenerateUniqueSurfaceId(), true);
FakeClient stub5(&memmgr_ , &stub3), stub6(&memmgr_ , &stub4);
FakeClient stub7(&memmgr_ , &stub3);
// Make stub4 be the least-recently-used client
stub4.SetVisible(false);
stub3.SetVisible(false);
stub2.SetVisible(false);
stub1.SetVisible(false);
Manage();
EXPECT_TRUE(IsAllocationBackgroundForSurfaceYes(stub1.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceYes(stub2.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceYes(stub3.allocation_));
EXPECT_TRUE(IsAllocationHibernatedForSurfaceYes(stub4.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceNo(stub5.allocation_));
EXPECT_TRUE(IsAllocationHibernatedForSurfaceNo(stub6.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceNo(stub7.allocation_));
// Make stub3 become the least-recently-used client.
stub2.SetVisible(true);
stub2.SetVisible(false);
stub4.SetVisible(true);
stub4.SetVisible(false);
Manage();
EXPECT_TRUE(IsAllocationBackgroundForSurfaceYes(stub1.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceYes(stub2.allocation_));
EXPECT_TRUE(IsAllocationHibernatedForSurfaceYes(stub3.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceYes(stub4.allocation_));
EXPECT_TRUE(IsAllocationHibernatedForSurfaceNo(stub5.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceNo(stub6.allocation_));
EXPECT_TRUE(IsAllocationHibernatedForSurfaceNo(stub7.allocation_));
}
// Test GpuMemoryManager::Manage functionality: Test changing importance of
// enough stubs so that every stub in share group crosses threshold.
// Expect memory allocation of the stubs without surface to share memory
// allocation with the most visible stub in share group.
TEST_F(GpuMemoryManagerTest, TestManageChangingImportanceShareGroup) {
FakeClient stub_ignore_a(&memmgr_, GenerateUniqueSurfaceId(), true),
stub_ignore_b(&memmgr_, GenerateUniqueSurfaceId(), false),
stub_ignore_c(&memmgr_, GenerateUniqueSurfaceId(), false);
FakeClient stub1(&memmgr_, GenerateUniqueSurfaceId(), false),
stub2(&memmgr_, GenerateUniqueSurfaceId(), false);
FakeClient stub3(&memmgr_, &stub2), stub4(&memmgr_, &stub2);
// stub1 and stub2 keep their non-hibernated state because they're
// either visible or the 2 most recently used clients (through the
// first three checks).
stub1.SetVisible(true);
stub2.SetVisible(true);
Manage();
EXPECT_TRUE(IsAllocationForegroundForSurfaceYes(stub1.allocation_));
EXPECT_TRUE(IsAllocationForegroundForSurfaceYes(stub2.allocation_));
EXPECT_TRUE(IsAllocationForegroundForSurfaceNo(stub3.allocation_));
EXPECT_TRUE(IsAllocationForegroundForSurfaceNo(stub4.allocation_));
stub1.SetVisible(false);
Manage();
EXPECT_TRUE(IsAllocationBackgroundForSurfaceYes(stub1.allocation_));
EXPECT_TRUE(IsAllocationForegroundForSurfaceYes(stub2.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceNo(stub3.allocation_));
EXPECT_TRUE(IsAllocationForegroundForSurfaceNo(stub4.allocation_));
stub2.SetVisible(false);
Manage();
EXPECT_TRUE(IsAllocationBackgroundForSurfaceYes(stub1.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceYes(stub2.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceNo(stub3.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceNo(stub4.allocation_));
// stub_ignore_b will cause stub1 to become hibernated (because
// stub_ignore_a, stub_ignore_b, and stub2 are all non-hibernated and more
// important).
stub_ignore_b.SetVisible(true);
stub_ignore_b.SetVisible(false);
Manage();
EXPECT_TRUE(IsAllocationHibernatedForSurfaceYes(stub1.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceYes(stub2.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceNo(stub3.allocation_));
EXPECT_TRUE(IsAllocationBackgroundForSurfaceNo(stub4.allocation_));
// stub_ignore_c will cause stub2 to become hibernated (because
// stub_ignore_a, stub_ignore_b, and stub_ignore_c are all non-hibernated
// and more important).
stub_ignore_c.SetVisible(true);
stub_ignore_c.SetVisible(false);
Manage();
EXPECT_TRUE(IsAllocationHibernatedForSurfaceYes(stub1.allocation_));
EXPECT_TRUE(IsAllocationHibernatedForSurfaceYes(stub2.allocation_));
EXPECT_TRUE(IsAllocationHibernatedForSurfaceNo(stub3.allocation_));
EXPECT_TRUE(IsAllocationHibernatedForSurfaceNo(stub4.allocation_));
}
} // namespace content