// 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 #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 T RoundUp(T n, T mul) { return ((n + mul - 1) / mul) * mul; } template 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 bytes, uint64 bytes_sum_limit) { size_t bytes_size = bytes.size(); uint64 bytes_sum = 0; if (bytes_size == 0) return std::numeric_limits::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::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::max(); uint64 bytes_above_minimum_cap = std::numeric_limits::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 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 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::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 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