blob: 5dfd3fb7876ff42ab440ef4a19b13b27007c7e3b [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 <algorithm>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/message_loop/message_loop.h"
#include "base/process/process_handle.h"
#include "base/strings/string_number_conversions.h"
#include "content/common/gpu/gpu_channel_manager.h"
#include "content/common/gpu/gpu_memory_manager_client.h"
#include "content/common/gpu/gpu_memory_tracking.h"
#include "content/common/gpu/gpu_memory_uma_stats.h"
#include "content/common/gpu/gpu_messages.h"
#include "gpu/command_buffer/common/gpu_memory_allocation.h"
#include "gpu/command_buffer/service/gpu_switches.h"
using gpu::ManagedMemoryStats;
using gpu::MemoryAllocation;
namespace content {
namespace {
const int kDelayedScheduleManageTimeoutMs = 67;
const uint64 kBytesAllocatedUnmanagedStep = 16 * 1024 * 1024;
void TrackValueChanged(uint64 old_size, uint64 new_size, uint64* total_size) {
DCHECK(new_size > old_size || *total_size >= (old_size - new_size));
*total_size += (new_size - old_size);
}
template<typename T>
T RoundUp(T n, T mul) {
return ((n + mul - 1) / mul) * mul;
}
template<typename T>
T RoundDown(T n, T mul) {
return (n / mul) * mul;
}
}
GpuMemoryManager::GpuMemoryManager(
GpuChannelManager* channel_manager,
uint64 max_surfaces_with_frontbuffer_soft_limit)
: channel_manager_(channel_manager),
manage_immediate_scheduled_(false),
max_surfaces_with_frontbuffer_soft_limit_(
max_surfaces_with_frontbuffer_soft_limit),
priority_cutoff_(MemoryAllocation::CUTOFF_ALLOW_EVERYTHING),
bytes_available_gpu_memory_(0),
bytes_available_gpu_memory_overridden_(false),
bytes_minimum_per_client_(0),
bytes_default_per_client_(0),
bytes_allocated_managed_current_(0),
bytes_allocated_unmanaged_current_(0),
bytes_allocated_historical_max_(0),
bytes_allocated_unmanaged_high_(0),
bytes_allocated_unmanaged_low_(0),
bytes_unmanaged_limit_step_(kBytesAllocatedUnmanagedStep),
disable_schedule_manage_(false)
{
CommandLine* command_line = CommandLine::ForCurrentProcess();
// Use a more conservative memory allocation policy on Linux and Mac because
// the platform is unstable when under memory pressure.
// http://crbug.com/145600 (Linux)
// http://crbug.com/141377 (Mac)
#if defined(OS_MACOSX) || (defined(OS_LINUX) && !defined(OS_CHROMEOS))
priority_cutoff_ = MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE;
#endif
#if defined(OS_ANDROID)
bytes_default_per_client_ = 8 * 1024 * 1024;
bytes_minimum_per_client_ = 8 * 1024 * 1024;
#elif defined(OS_CHROMEOS)
bytes_default_per_client_ = 64 * 1024 * 1024;
bytes_minimum_per_client_ = 4 * 1024 * 1024;
#elif defined(OS_MACOSX)
bytes_default_per_client_ = 128 * 1024 * 1024;
bytes_minimum_per_client_ = 128 * 1024 * 1024;
#else
bytes_default_per_client_ = 64 * 1024 * 1024;
bytes_minimum_per_client_ = 64 * 1024 * 1024;
#endif
if (command_line->HasSwitch(switches::kForceGpuMemAvailableMb)) {
base::StringToUint64(
command_line->GetSwitchValueASCII(switches::kForceGpuMemAvailableMb),
&bytes_available_gpu_memory_);
bytes_available_gpu_memory_ *= 1024 * 1024;
bytes_available_gpu_memory_overridden_ = true;
} else
bytes_available_gpu_memory_ = GetDefaultAvailableGpuMemory();
}
GpuMemoryManager::~GpuMemoryManager() {
DCHECK(tracking_groups_.empty());
DCHECK(clients_visible_mru_.empty());
DCHECK(clients_nonvisible_mru_.empty());
DCHECK(clients_nonsurface_.empty());
DCHECK(!bytes_allocated_managed_current_);
DCHECK(!bytes_allocated_unmanaged_current_);
}
uint64 GpuMemoryManager::GetAvailableGpuMemory() const {
// Allow unmanaged allocations to over-subscribe by at most (high_ - low_)
// before restricting managed (compositor) memory based on unmanaged usage.
if (bytes_allocated_unmanaged_low_ > bytes_available_gpu_memory_)
return 0;
return bytes_available_gpu_memory_ - bytes_allocated_unmanaged_low_;
}
uint64 GpuMemoryManager::GetDefaultAvailableGpuMemory() const {
#if defined(OS_ANDROID)
return 16 * 1024 * 1024;
#elif defined(OS_CHROMEOS)
return 1024 * 1024 * 1024;
#else
return 256 * 1024 * 1024;
#endif
}
uint64 GpuMemoryManager::GetMaximumTotalGpuMemory() const {
#if defined(OS_ANDROID)
return 256 * 1024 * 1024;
#else
return 1024 * 1024 * 1024;
#endif
}
uint64 GpuMemoryManager::GetMaximumClientAllocation() const {
#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
return bytes_available_gpu_memory_;
#else
// This is to avoid allowing a single page on to use a full 256MB of memory
// (the current total limit). Long-scroll pages will hit this limit,
// resulting in instability on some platforms (e.g, issue 141377).
return bytes_available_gpu_memory_ / 2;
#endif
}
uint64 GpuMemoryManager::CalcAvailableFromGpuTotal(uint64 total_gpu_memory) {
#if defined(OS_ANDROID)
// We don't need to reduce the total on Android, since
// the total is an estimate to begin with.
return total_gpu_memory;
#else
// Allow Chrome to use 75% of total GPU memory, or all-but-64MB of GPU
// memory, whichever is less.
return std::min(3 * total_gpu_memory / 4, total_gpu_memory - 64*1024*1024);
#endif
}
void GpuMemoryManager::UpdateAvailableGpuMemory() {
// If the amount of video memory to use was specified at the command
// line, never change it.
if (bytes_available_gpu_memory_overridden_)
return;
// On non-Android, we use an operating system query when possible.
// We do not have a reliable concept of multiple GPUs existing in
// a system, so just be safe and go with the minimum encountered.
uint64 bytes_min = 0;
// Only use the clients that are visible, because otherwise the set of clients
// we are querying could become extremely large.
for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
it != clients_visible_mru_.end();
++it) {
const GpuMemoryManagerClientState* client_state = *it;
if (!client_state->has_surface_)
continue;
if (!client_state->visible_)
continue;
uint64 bytes = 0;
if (client_state->client_->GetTotalGpuMemory(&bytes)) {
if (!bytes_min || bytes < bytes_min)
bytes_min = bytes;
}
}
if (!bytes_min)
return;
bytes_available_gpu_memory_ = CalcAvailableFromGpuTotal(bytes_min);
// Never go below the default allocation
bytes_available_gpu_memory_ = std::max(bytes_available_gpu_memory_,
GetDefaultAvailableGpuMemory());
// Never go above the maximum.
bytes_available_gpu_memory_ = std::min(bytes_available_gpu_memory_,
GetMaximumTotalGpuMemory());
}
void GpuMemoryManager::UpdateUnmanagedMemoryLimits() {
// Set the limit to be [current_, current_ + step_ / 4), with the endpoints
// of the intervals rounded down and up to the nearest step_, to avoid
// thrashing the interval.
bytes_allocated_unmanaged_high_ = RoundUp(
bytes_allocated_unmanaged_current_ + bytes_unmanaged_limit_step_ / 4,
bytes_unmanaged_limit_step_);
bytes_allocated_unmanaged_low_ = RoundDown(
bytes_allocated_unmanaged_current_,
bytes_unmanaged_limit_step_);
}
void GpuMemoryManager::ScheduleManage(
ScheduleManageTime schedule_manage_time) {
if (disable_schedule_manage_)
return;
if (manage_immediate_scheduled_)
return;
if (schedule_manage_time == kScheduleManageNow) {
base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&GpuMemoryManager::Manage, AsWeakPtr()));
manage_immediate_scheduled_ = true;
if (!delayed_manage_callback_.IsCancelled())
delayed_manage_callback_.Cancel();
} else {
if (!delayed_manage_callback_.IsCancelled())
return;
delayed_manage_callback_.Reset(base::Bind(&GpuMemoryManager::Manage,
AsWeakPtr()));
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
delayed_manage_callback_.callback(),
base::TimeDelta::FromMilliseconds(kDelayedScheduleManageTimeoutMs));
}
}
void GpuMemoryManager::TrackMemoryAllocatedChange(
GpuMemoryTrackingGroup* tracking_group,
uint64 old_size,
uint64 new_size,
gpu::gles2::MemoryTracker::Pool tracking_pool) {
TrackValueChanged(old_size, new_size, &tracking_group->size_);
switch (tracking_pool) {
case gpu::gles2::MemoryTracker::kManaged:
TrackValueChanged(old_size, new_size, &bytes_allocated_managed_current_);
break;
case gpu::gles2::MemoryTracker::kUnmanaged:
TrackValueChanged(old_size,
new_size,
&bytes_allocated_unmanaged_current_);
break;
default:
NOTREACHED();
break;
}
if (new_size != old_size) {
TRACE_COUNTER1("gpu",
"GpuMemoryUsage",
GetCurrentUsage());
}
// If we've gone past our current limit on unmanaged memory, schedule a
// re-manage to take int account the unmanaged memory.
if (bytes_allocated_unmanaged_current_ >= bytes_allocated_unmanaged_high_)
ScheduleManage(kScheduleManageNow);
if (bytes_allocated_unmanaged_current_ < bytes_allocated_unmanaged_low_)
ScheduleManage(kScheduleManageLater);
if (GetCurrentUsage() > bytes_allocated_historical_max_) {
bytes_allocated_historical_max_ = GetCurrentUsage();
// If we're blowing into new memory usage territory, spam the browser
// process with the most up-to-date information about our memory usage.
SendUmaStatsToBrowser();
}
}
bool GpuMemoryManager::EnsureGPUMemoryAvailable(uint64 /* size_needed */) {
// TODO: Check if there is enough space. Lose contexts until there is.
return true;
}
GpuMemoryManagerClientState* GpuMemoryManager::CreateClientState(
GpuMemoryManagerClient* client,
bool has_surface,
bool visible) {
TrackingGroupMap::iterator tracking_group_it =
tracking_groups_.find(client->GetMemoryTracker());
DCHECK(tracking_group_it != tracking_groups_.end());
GpuMemoryTrackingGroup* tracking_group = tracking_group_it->second;
GpuMemoryManagerClientState* client_state = new GpuMemoryManagerClientState(
this, client, tracking_group, has_surface, visible);
AddClientToList(client_state);
ScheduleManage(kScheduleManageNow);
return client_state;
}
void GpuMemoryManager::OnDestroyClientState(
GpuMemoryManagerClientState* client_state) {
RemoveClientFromList(client_state);
ScheduleManage(kScheduleManageLater);
}
void GpuMemoryManager::SetClientStateVisible(
GpuMemoryManagerClientState* client_state, bool visible) {
DCHECK(client_state->has_surface_);
if (client_state->visible_ == visible)
return;
RemoveClientFromList(client_state);
client_state->visible_ = visible;
AddClientToList(client_state);
ScheduleManage(visible ? kScheduleManageNow : kScheduleManageLater);
}
void GpuMemoryManager::SetClientStateManagedMemoryStats(
GpuMemoryManagerClientState* client_state,
const ManagedMemoryStats& stats)
{
client_state->managed_memory_stats_ = stats;
// If this is the first time that stats have been received for this
// client, use them immediately.
if (!client_state->managed_memory_stats_received_) {
client_state->managed_memory_stats_received_ = true;
ScheduleManage(kScheduleManageNow);
return;
}
// If these statistics sit outside of the range that we used in our
// computation of memory allocations then recompute the allocations.
if (client_state->managed_memory_stats_.bytes_nice_to_have >
client_state->bytes_nicetohave_limit_high_) {
ScheduleManage(kScheduleManageNow);
} else if (client_state->managed_memory_stats_.bytes_nice_to_have <
client_state->bytes_nicetohave_limit_low_) {
ScheduleManage(kScheduleManageLater);
}
}
uint64 GpuMemoryManager::GetClientMemoryUsage(
const GpuMemoryManagerClient* client) const{
TrackingGroupMap::const_iterator tracking_group_it =
tracking_groups_.find(client->GetMemoryTracker());
DCHECK(tracking_group_it != tracking_groups_.end());
return tracking_group_it->second->GetSize();
}
GpuMemoryTrackingGroup* GpuMemoryManager::CreateTrackingGroup(
base::ProcessId pid, gpu::gles2::MemoryTracker* memory_tracker) {
GpuMemoryTrackingGroup* tracking_group = new GpuMemoryTrackingGroup(
pid, memory_tracker, this);
DCHECK(!tracking_groups_.count(tracking_group->GetMemoryTracker()));
tracking_groups_.insert(std::make_pair(tracking_group->GetMemoryTracker(),
tracking_group));
return tracking_group;
}
void GpuMemoryManager::OnDestroyTrackingGroup(
GpuMemoryTrackingGroup* tracking_group) {
DCHECK(tracking_groups_.count(tracking_group->GetMemoryTracker()));
tracking_groups_.erase(tracking_group->GetMemoryTracker());
}
void GpuMemoryManager::GetVideoMemoryUsageStats(
GPUVideoMemoryUsageStats* video_memory_usage_stats) const {
// For each context group, assign its memory usage to its PID
video_memory_usage_stats->process_map.clear();
for (TrackingGroupMap::const_iterator i =
tracking_groups_.begin(); i != tracking_groups_.end(); ++i) {
const GpuMemoryTrackingGroup* tracking_group = i->second;
video_memory_usage_stats->process_map[
tracking_group->GetPid()].video_memory += tracking_group->GetSize();
}
// Assign the total across all processes in the GPU process
video_memory_usage_stats->process_map[
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::Manage() {
manage_immediate_scheduled_ = false;
delayed_manage_callback_.Cancel();
// Update the amount of GPU memory available on the system.
UpdateAvailableGpuMemory();
// Update the limit on unmanaged memory.
UpdateUnmanagedMemoryLimits();
// Determine which clients are "hibernated" (which determines the
// distribution of frontbuffers and memory among clients that don't have
// surfaces).
SetClientsHibernatedState();
// Assign memory allocations to clients that have surfaces.
AssignSurfacesAllocations();
// Assign memory allocations to clients that don't have surfaces.
AssignNonSurfacesAllocations();
SendUmaStatsToBrowser();
}
// static
uint64 GpuMemoryManager::ComputeCap(
std::vector<uint64> bytes, uint64 bytes_sum_limit)
{
size_t bytes_size = bytes.size();
uint64 bytes_sum = 0;
if (bytes_size == 0)
return std::numeric_limits<uint64>::max();
// Sort and add up all entries
std::sort(bytes.begin(), bytes.end());
for (size_t i = 0; i < bytes_size; ++i)
bytes_sum += bytes[i];
// As we go through the below loop, let bytes_partial_sum be the
// sum of bytes[0] + ... + bytes[bytes_size - i - 1]
uint64 bytes_partial_sum = bytes_sum;
// Try using each entry as a cap, and see where we get cut off.
for (size_t i = 0; i < bytes_size; ++i) {
// Try limiting cap to bytes[bytes_size - i - 1]
uint64 test_cap = bytes[bytes_size - i - 1];
uint64 bytes_sum_with_test_cap = i * test_cap + bytes_partial_sum;
// If that fits, raise test_cap to give an even distribution to the
// last i entries.
if (bytes_sum_with_test_cap <= bytes_sum_limit) {
if (i == 0)
return std::numeric_limits<uint64>::max();
else
return test_cap + (bytes_sum_limit - bytes_sum_with_test_cap) / i;
} else {
bytes_partial_sum -= test_cap;
}
}
// If we got here, then we can't fully accommodate any of the clients,
// so distribute bytes_sum_limit evenly.
return bytes_sum_limit / bytes_size;
}
uint64 GpuMemoryManager::ComputeClientAllocationWhenVisible(
GpuMemoryManagerClientState* client_state,
uint64 bytes_above_required_cap,
uint64 bytes_above_minimum_cap,
uint64 bytes_overall_cap) {
ManagedMemoryStats* stats = &client_state->managed_memory_stats_;
if (!client_state->managed_memory_stats_received_)
return GetDefaultClientAllocation();
uint64 bytes_required = 9 * stats->bytes_required / 8;
bytes_required = std::min(bytes_required, GetMaximumClientAllocation());
bytes_required = std::max(bytes_required, GetMinimumClientAllocation());
uint64 bytes_nicetohave = 4 * stats->bytes_nice_to_have / 3;
bytes_nicetohave = std::min(bytes_nicetohave, GetMaximumClientAllocation());
bytes_nicetohave = std::max(bytes_nicetohave, GetMinimumClientAllocation());
bytes_nicetohave = std::max(bytes_nicetohave, bytes_required);
uint64 allocation = GetMinimumClientAllocation();
allocation += std::min(bytes_required - GetMinimumClientAllocation(),
bytes_above_minimum_cap);
allocation += std::min(bytes_nicetohave - bytes_required,
bytes_above_required_cap);
allocation = std::min(allocation,
bytes_overall_cap);
return allocation;
}
void GpuMemoryManager::ComputeVisibleSurfacesAllocations() {
uint64 bytes_available_total = GetAvailableGpuMemory();
uint64 bytes_above_required_cap = std::numeric_limits<uint64>::max();
uint64 bytes_above_minimum_cap = std::numeric_limits<uint64>::max();
uint64 bytes_overall_cap_visible = GetMaximumClientAllocation();
// Compute memory usage at three levels
// - painting everything that is nicetohave for visible clients
// - painting only what that is visible
// - giving every client the minimum allocation
uint64 bytes_nicetohave_visible = 0;
uint64 bytes_required_visible = 0;
uint64 bytes_minimum_visible = 0;
for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
it != clients_visible_mru_.end();
++it) {
GpuMemoryManagerClientState* client_state = *it;
client_state->bytes_allocation_ideal_nicetohave_ =
ComputeClientAllocationWhenVisible(
client_state,
bytes_above_required_cap,
bytes_above_minimum_cap,
bytes_overall_cap_visible);
client_state->bytes_allocation_ideal_required_ =
ComputeClientAllocationWhenVisible(
client_state,
0,
bytes_above_minimum_cap,
bytes_overall_cap_visible);
client_state->bytes_allocation_ideal_minimum_ =
ComputeClientAllocationWhenVisible(
client_state,
0,
0,
bytes_overall_cap_visible);
bytes_nicetohave_visible +=
client_state->bytes_allocation_ideal_nicetohave_;
bytes_required_visible +=
client_state->bytes_allocation_ideal_required_;
bytes_minimum_visible +=
client_state->bytes_allocation_ideal_minimum_;
}
// Determine which of those three points we can satisfy, and limit
// bytes_above_required_cap and bytes_above_minimum_cap to not go
// over the limit.
if (bytes_minimum_visible > bytes_available_total) {
bytes_above_required_cap = 0;
bytes_above_minimum_cap = 0;
} else if (bytes_required_visible > bytes_available_total) {
std::vector<uint64> bytes_to_fit;
for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
it != clients_visible_mru_.end();
++it) {
GpuMemoryManagerClientState* client_state = *it;
bytes_to_fit.push_back(client_state->bytes_allocation_ideal_required_ -
client_state->bytes_allocation_ideal_minimum_);
}
bytes_above_required_cap = 0;
bytes_above_minimum_cap = ComputeCap(
bytes_to_fit, bytes_available_total - bytes_minimum_visible);
} else if (bytes_nicetohave_visible > bytes_available_total) {
std::vector<uint64> bytes_to_fit;
for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
it != clients_visible_mru_.end();
++it) {
GpuMemoryManagerClientState* client_state = *it;
bytes_to_fit.push_back(client_state->bytes_allocation_ideal_nicetohave_ -
client_state->bytes_allocation_ideal_required_);
}
bytes_above_required_cap = ComputeCap(
bytes_to_fit, bytes_available_total - bytes_required_visible);
bytes_above_minimum_cap = std::numeric_limits<uint64>::max();
}
// Given those computed limits, set the actual memory allocations for the
// visible clients, tracking the largest allocation and the total allocation
// for future use.
uint64 bytes_allocated_visible = 0;
uint64 bytes_allocated_max_client_allocation = 0;
for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
it != clients_visible_mru_.end();
++it) {
GpuMemoryManagerClientState* client_state = *it;
client_state->bytes_allocation_when_visible_ =
ComputeClientAllocationWhenVisible(
client_state,
bytes_above_required_cap,
bytes_above_minimum_cap,
bytes_overall_cap_visible);
bytes_allocated_visible += client_state->bytes_allocation_when_visible_;
bytes_allocated_max_client_allocation = std::max(
bytes_allocated_max_client_allocation,
client_state->bytes_allocation_when_visible_);
}
// Set the limit for nonvisible clients for when they become visible.
// Use the same formula, with a lowered overall cap in case any of the
// currently-nonvisible clients are much more resource-intensive than any
// of the existing clients.
uint64 bytes_overall_cap_nonvisible = bytes_allocated_max_client_allocation;
if (bytes_available_total > bytes_allocated_visible) {
bytes_overall_cap_nonvisible +=
bytes_available_total - bytes_allocated_visible;
}
bytes_overall_cap_nonvisible = std::min(bytes_overall_cap_nonvisible,
GetMaximumClientAllocation());
for (ClientStateList::const_iterator it = clients_nonvisible_mru_.begin();
it != clients_nonvisible_mru_.end();
++it) {
GpuMemoryManagerClientState* client_state = *it;
client_state->bytes_allocation_when_visible_ =
ComputeClientAllocationWhenVisible(
client_state,
bytes_above_required_cap,
bytes_above_minimum_cap,
bytes_overall_cap_nonvisible);
}
}
void GpuMemoryManager::DistributeRemainingMemoryToVisibleSurfaces() {
uint64 bytes_available_total = GetAvailableGpuMemory();
uint64 bytes_allocated_total = 0;
for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
it != clients_visible_mru_.end();
++it) {
GpuMemoryManagerClientState* client_state = *it;
bytes_allocated_total += client_state->bytes_allocation_when_visible_;
}
if (bytes_allocated_total >= bytes_available_total)
return;
std::vector<uint64> bytes_extra_requests;
for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
it != clients_visible_mru_.end();
++it) {
GpuMemoryManagerClientState* client_state = *it;
CHECK(GetMaximumClientAllocation() >=
client_state->bytes_allocation_when_visible_);
uint64 bytes_extra = GetMaximumClientAllocation() -
client_state->bytes_allocation_when_visible_;
bytes_extra_requests.push_back(bytes_extra);
}
uint64 bytes_extra_cap = ComputeCap(
bytes_extra_requests, bytes_available_total - bytes_allocated_total);
for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
it != clients_visible_mru_.end();
++it) {
GpuMemoryManagerClientState* client_state = *it;
uint64 bytes_extra = GetMaximumClientAllocation() -
client_state->bytes_allocation_when_visible_;
client_state->bytes_allocation_when_visible_ += std::min(
bytes_extra, bytes_extra_cap);
}
}
void GpuMemoryManager::AssignSurfacesAllocations() {
// Compute allocation when for all clients.
ComputeVisibleSurfacesAllocations();
// Distribute the remaining memory to visible clients.
DistributeRemainingMemoryToVisibleSurfaces();
// Send that allocation to the clients.
ClientStateList clients = clients_visible_mru_;
clients.insert(clients.end(),
clients_nonvisible_mru_.begin(),
clients_nonvisible_mru_.end());
for (ClientStateList::const_iterator it = clients.begin();
it != clients.end();
++it) {
GpuMemoryManagerClientState* client_state = *it;
// Re-assign memory limits to this client when its "nice to have" bucket
// grows or shrinks by 1/4.
client_state->bytes_nicetohave_limit_high_ =
5 * client_state->managed_memory_stats_.bytes_nice_to_have / 4;
client_state->bytes_nicetohave_limit_low_ =
3 * client_state->managed_memory_stats_.bytes_nice_to_have / 4;
// Populate and send the allocation to the client
MemoryAllocation allocation;
allocation.bytes_limit_when_visible =
client_state->bytes_allocation_when_visible_;
allocation.priority_cutoff_when_visible = priority_cutoff_;
client_state->client_->SetMemoryAllocation(allocation);
client_state->client_->SuggestHaveFrontBuffer(!client_state->hibernated_);
}
}
void GpuMemoryManager::AssignNonSurfacesAllocations() {
for (ClientStateList::const_iterator it = clients_nonsurface_.begin();
it != clients_nonsurface_.end();
++it) {
GpuMemoryManagerClientState* client_state = *it;
MemoryAllocation allocation;
if (!client_state->hibernated_) {
allocation.bytes_limit_when_visible =
GetMinimumClientAllocation();
allocation.priority_cutoff_when_visible =
MemoryAllocation::CUTOFF_ALLOW_EVERYTHING;
}
client_state->client_->SetMemoryAllocation(allocation);
}
}
void GpuMemoryManager::SetClientsHibernatedState() const {
// Re-set all tracking groups as being hibernated.
for (TrackingGroupMap::const_iterator it = tracking_groups_.begin();
it != tracking_groups_.end();
++it) {
GpuMemoryTrackingGroup* tracking_group = it->second;
tracking_group->hibernated_ = true;
}
// All clients with surfaces that are visible are non-hibernated.
uint64 non_hibernated_clients = 0;
for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
it != clients_visible_mru_.end();
++it) {
GpuMemoryManagerClientState* client_state = *it;
client_state->hibernated_ = false;
client_state->tracking_group_->hibernated_ = false;
non_hibernated_clients++;
}
// Then an additional few clients with surfaces are non-hibernated too, up to
// a fixed limit.
for (ClientStateList::const_iterator it = clients_nonvisible_mru_.begin();
it != clients_nonvisible_mru_.end();
++it) {
GpuMemoryManagerClientState* client_state = *it;
if (non_hibernated_clients < max_surfaces_with_frontbuffer_soft_limit_) {
client_state->hibernated_ = false;
client_state->tracking_group_->hibernated_ = false;
non_hibernated_clients++;
} else {
client_state->hibernated_ = true;
}
}
// Clients that don't have surfaces are non-hibernated if they are
// in a GL share group with a non-hibernated surface.
for (ClientStateList::const_iterator it = clients_nonsurface_.begin();
it != clients_nonsurface_.end();
++it) {
GpuMemoryManagerClientState* client_state = *it;
client_state->hibernated_ = client_state->tracking_group_->hibernated_;
}
}
void GpuMemoryManager::SendUmaStatsToBrowser() {
if (!channel_manager_)
return;
GPUMemoryUmaStats params;
params.bytes_allocated_current = GetCurrentUsage();
params.bytes_allocated_max = bytes_allocated_historical_max_;
params.bytes_limit = bytes_available_gpu_memory_;
params.client_count = clients_visible_mru_.size() +
clients_nonvisible_mru_.size() +
clients_nonsurface_.size();
params.context_group_count = tracking_groups_.size();
channel_manager_->Send(new GpuHostMsg_GpuMemoryUmaStats(params));
}
GpuMemoryManager::ClientStateList* GpuMemoryManager::GetClientList(
GpuMemoryManagerClientState* client_state) {
if (client_state->has_surface_) {
if (client_state->visible_)
return &clients_visible_mru_;
else
return &clients_nonvisible_mru_;
}
return &clients_nonsurface_;
}
void GpuMemoryManager::AddClientToList(
GpuMemoryManagerClientState* client_state) {
DCHECK(!client_state->list_iterator_valid_);
ClientStateList* client_list = GetClientList(client_state);
client_state->list_iterator_ = client_list->insert(
client_list->begin(), client_state);
client_state->list_iterator_valid_ = true;
}
void GpuMemoryManager::RemoveClientFromList(
GpuMemoryManagerClientState* client_state) {
DCHECK(client_state->list_iterator_valid_);
ClientStateList* client_list = GetClientList(client_state);
client_list->erase(client_state->list_iterator_);
client_state->list_iterator_valid_ = false;
}
} // namespace content