Home | History | Annotate | Download | only in gpu
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/common/gpu/gpu_memory_manager.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/bind.h"
     10 #include "base/command_line.h"
     11 #include "base/debug/trace_event.h"
     12 #include "base/message_loop/message_loop.h"
     13 #include "base/process/process_handle.h"
     14 #include "base/strings/string_number_conversions.h"
     15 #include "content/common/gpu/gpu_channel_manager.h"
     16 #include "content/common/gpu/gpu_memory_manager_client.h"
     17 #include "content/common/gpu/gpu_memory_tracking.h"
     18 #include "content/common/gpu/gpu_memory_uma_stats.h"
     19 #include "content/common/gpu/gpu_messages.h"
     20 #include "gpu/command_buffer/common/gpu_memory_allocation.h"
     21 #include "gpu/command_buffer/service/gpu_switches.h"
     22 
     23 using gpu::MemoryAllocation;
     24 
     25 namespace content {
     26 namespace {
     27 
     28 const int kDelayedScheduleManageTimeoutMs = 67;
     29 
     30 const uint64 kBytesAllocatedUnmanagedStep = 16 * 1024 * 1024;
     31 
     32 void TrackValueChanged(uint64 old_size, uint64 new_size, uint64* total_size) {
     33   DCHECK(new_size > old_size || *total_size >= (old_size - new_size));
     34   *total_size += (new_size - old_size);
     35 }
     36 
     37 }
     38 
     39 GpuMemoryManager::GpuMemoryManager(
     40     GpuChannelManager* channel_manager,
     41     uint64 max_surfaces_with_frontbuffer_soft_limit)
     42     : channel_manager_(channel_manager),
     43       manage_immediate_scheduled_(false),
     44       disable_schedule_manage_(false),
     45       max_surfaces_with_frontbuffer_soft_limit_(
     46           max_surfaces_with_frontbuffer_soft_limit),
     47       client_hard_limit_bytes_(0),
     48       bytes_allocated_managed_current_(0),
     49       bytes_allocated_unmanaged_current_(0),
     50       bytes_allocated_historical_max_(0)
     51 { }
     52 
     53 GpuMemoryManager::~GpuMemoryManager() {
     54   DCHECK(tracking_groups_.empty());
     55   DCHECK(clients_visible_mru_.empty());
     56   DCHECK(clients_nonvisible_mru_.empty());
     57   DCHECK(clients_nonsurface_.empty());
     58   DCHECK(!bytes_allocated_managed_current_);
     59   DCHECK(!bytes_allocated_unmanaged_current_);
     60 }
     61 
     62 void GpuMemoryManager::UpdateAvailableGpuMemory() {
     63   // If the value was overridden on the command line, use the specified value.
     64   static bool client_hard_limit_bytes_overridden =
     65       base::CommandLine::ForCurrentProcess()->HasSwitch(
     66           switches::kForceGpuMemAvailableMb);
     67   if (client_hard_limit_bytes_overridden) {
     68     base::StringToUint64(
     69         base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
     70             switches::kForceGpuMemAvailableMb),
     71         &client_hard_limit_bytes_);
     72     client_hard_limit_bytes_ *= 1024 * 1024;
     73     return;
     74   }
     75 
     76 #if defined(OS_ANDROID)
     77   // On non-Android, we use an operating system query when possible.
     78   // We do not have a reliable concept of multiple GPUs existing in
     79   // a system, so just be safe and go with the minimum encountered.
     80   uint64 bytes_min = 0;
     81 
     82   // Only use the clients that are visible, because otherwise the set of clients
     83   // we are querying could become extremely large.
     84   for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
     85       it != clients_visible_mru_.end();
     86       ++it) {
     87     const GpuMemoryManagerClientState* client_state = *it;
     88     if (!client_state->has_surface_)
     89       continue;
     90     if (!client_state->visible_)
     91       continue;
     92 
     93     uint64 bytes = 0;
     94     if (client_state->client_->GetTotalGpuMemory(&bytes)) {
     95       if (!bytes_min || bytes < bytes_min)
     96         bytes_min = bytes;
     97     }
     98   }
     99 
    100   client_hard_limit_bytes_ = bytes_min;
    101   // Clamp the observed value to a specific range on Android.
    102   client_hard_limit_bytes_ = std::max(client_hard_limit_bytes_,
    103                                       static_cast<uint64>(16 * 1024 * 1024));
    104   client_hard_limit_bytes_ = std::min(client_hard_limit_bytes_,
    105                                       static_cast<uint64>(256 * 1024 * 1024));
    106 #else
    107   // Ignore what the system said and give all clients the same maximum
    108   // allocation on desktop platforms.
    109   client_hard_limit_bytes_ = 512 * 1024 * 1024;
    110 #endif
    111 }
    112 
    113 void GpuMemoryManager::ScheduleManage(
    114     ScheduleManageTime schedule_manage_time) {
    115   if (disable_schedule_manage_)
    116     return;
    117   if (manage_immediate_scheduled_)
    118     return;
    119   if (schedule_manage_time == kScheduleManageNow) {
    120     base::MessageLoop::current()->PostTask(
    121         FROM_HERE, base::Bind(&GpuMemoryManager::Manage, AsWeakPtr()));
    122     manage_immediate_scheduled_ = true;
    123     if (!delayed_manage_callback_.IsCancelled())
    124       delayed_manage_callback_.Cancel();
    125   } else {
    126     if (!delayed_manage_callback_.IsCancelled())
    127       return;
    128     delayed_manage_callback_.Reset(base::Bind(&GpuMemoryManager::Manage,
    129                                               AsWeakPtr()));
    130     base::MessageLoop::current()->PostDelayedTask(
    131         FROM_HERE,
    132         delayed_manage_callback_.callback(),
    133         base::TimeDelta::FromMilliseconds(kDelayedScheduleManageTimeoutMs));
    134   }
    135 }
    136 
    137 void GpuMemoryManager::TrackMemoryAllocatedChange(
    138     GpuMemoryTrackingGroup* tracking_group,
    139     uint64 old_size,
    140     uint64 new_size,
    141     gpu::gles2::MemoryTracker::Pool tracking_pool) {
    142   TrackValueChanged(old_size, new_size, &tracking_group->size_);
    143   switch (tracking_pool) {
    144     case gpu::gles2::MemoryTracker::kManaged:
    145       TrackValueChanged(old_size, new_size, &bytes_allocated_managed_current_);
    146       break;
    147     case gpu::gles2::MemoryTracker::kUnmanaged:
    148       TrackValueChanged(old_size,
    149                         new_size,
    150                         &bytes_allocated_unmanaged_current_);
    151       break;
    152     default:
    153       NOTREACHED();
    154       break;
    155   }
    156   if (new_size != old_size) {
    157     TRACE_COUNTER1("gpu",
    158                    "GpuMemoryUsage",
    159                    GetCurrentUsage());
    160   }
    161 
    162   if (GetCurrentUsage() > bytes_allocated_historical_max_ +
    163                           kBytesAllocatedUnmanagedStep) {
    164       bytes_allocated_historical_max_ = GetCurrentUsage();
    165       // If we're blowing into new memory usage territory, spam the browser
    166       // process with the most up-to-date information about our memory usage.
    167       SendUmaStatsToBrowser();
    168   }
    169 }
    170 
    171 bool GpuMemoryManager::EnsureGPUMemoryAvailable(uint64 /* size_needed */) {
    172   // TODO: Check if there is enough space. Lose contexts until there is.
    173   return true;
    174 }
    175 
    176 GpuMemoryManagerClientState* GpuMemoryManager::CreateClientState(
    177     GpuMemoryManagerClient* client,
    178     bool has_surface,
    179     bool visible) {
    180   TrackingGroupMap::iterator tracking_group_it =
    181       tracking_groups_.find(client->GetMemoryTracker());
    182   DCHECK(tracking_group_it != tracking_groups_.end());
    183   GpuMemoryTrackingGroup* tracking_group = tracking_group_it->second;
    184 
    185   GpuMemoryManagerClientState* client_state = new GpuMemoryManagerClientState(
    186       this, client, tracking_group, has_surface, visible);
    187   AddClientToList(client_state);
    188   ScheduleManage(kScheduleManageNow);
    189   return client_state;
    190 }
    191 
    192 void GpuMemoryManager::OnDestroyClientState(
    193     GpuMemoryManagerClientState* client_state) {
    194   RemoveClientFromList(client_state);
    195   ScheduleManage(kScheduleManageLater);
    196 }
    197 
    198 void GpuMemoryManager::SetClientStateVisible(
    199     GpuMemoryManagerClientState* client_state, bool visible) {
    200   DCHECK(client_state->has_surface_);
    201   if (client_state->visible_ == visible)
    202     return;
    203 
    204   RemoveClientFromList(client_state);
    205   client_state->visible_ = visible;
    206   AddClientToList(client_state);
    207   ScheduleManage(visible ? kScheduleManageNow : kScheduleManageLater);
    208 }
    209 
    210 uint64 GpuMemoryManager::GetClientMemoryUsage(
    211     const GpuMemoryManagerClient* client) const {
    212   TrackingGroupMap::const_iterator tracking_group_it =
    213       tracking_groups_.find(client->GetMemoryTracker());
    214   DCHECK(tracking_group_it != tracking_groups_.end());
    215   return tracking_group_it->second->GetSize();
    216 }
    217 
    218 GpuMemoryTrackingGroup* GpuMemoryManager::CreateTrackingGroup(
    219     base::ProcessId pid, gpu::gles2::MemoryTracker* memory_tracker) {
    220   GpuMemoryTrackingGroup* tracking_group = new GpuMemoryTrackingGroup(
    221       pid, memory_tracker, this);
    222   DCHECK(!tracking_groups_.count(tracking_group->GetMemoryTracker()));
    223   tracking_groups_.insert(std::make_pair(tracking_group->GetMemoryTracker(),
    224                                          tracking_group));
    225   return tracking_group;
    226 }
    227 
    228 void GpuMemoryManager::OnDestroyTrackingGroup(
    229     GpuMemoryTrackingGroup* tracking_group) {
    230   DCHECK(tracking_groups_.count(tracking_group->GetMemoryTracker()));
    231   tracking_groups_.erase(tracking_group->GetMemoryTracker());
    232 }
    233 
    234 void GpuMemoryManager::GetVideoMemoryUsageStats(
    235     GPUVideoMemoryUsageStats* video_memory_usage_stats) const {
    236   // For each context group, assign its memory usage to its PID
    237   video_memory_usage_stats->process_map.clear();
    238   for (TrackingGroupMap::const_iterator i =
    239        tracking_groups_.begin(); i != tracking_groups_.end(); ++i) {
    240     const GpuMemoryTrackingGroup* tracking_group = i->second;
    241     video_memory_usage_stats->process_map[
    242         tracking_group->GetPid()].video_memory += tracking_group->GetSize();
    243   }
    244 
    245   // Assign the total across all processes in the GPU process
    246   video_memory_usage_stats->process_map[
    247       base::GetCurrentProcId()].video_memory = GetCurrentUsage();
    248   video_memory_usage_stats->process_map[
    249       base::GetCurrentProcId()].has_duplicates = true;
    250 
    251   video_memory_usage_stats->bytes_allocated = GetCurrentUsage();
    252   video_memory_usage_stats->bytes_allocated_historical_max =
    253       bytes_allocated_historical_max_;
    254 }
    255 
    256 void GpuMemoryManager::Manage() {
    257   manage_immediate_scheduled_ = false;
    258   delayed_manage_callback_.Cancel();
    259 
    260   // Update the amount of GPU memory available on the system.
    261   UpdateAvailableGpuMemory();
    262 
    263   // Determine which clients are "hibernated" (which determines the
    264   // distribution of frontbuffers and memory among clients that don't have
    265   // surfaces).
    266   SetClientsHibernatedState();
    267 
    268   // Assign memory allocations to clients that have surfaces.
    269   AssignSurfacesAllocations();
    270 
    271   // Assign memory allocations to clients that don't have surfaces.
    272   AssignNonSurfacesAllocations();
    273 
    274   SendUmaStatsToBrowser();
    275 }
    276 
    277 void GpuMemoryManager::AssignSurfacesAllocations() {
    278   // Send that allocation to the clients.
    279   ClientStateList clients = clients_visible_mru_;
    280   clients.insert(clients.end(),
    281                  clients_nonvisible_mru_.begin(),
    282                  clients_nonvisible_mru_.end());
    283   for (ClientStateList::const_iterator it = clients.begin();
    284        it != clients.end();
    285        ++it) {
    286     GpuMemoryManagerClientState* client_state = *it;
    287 
    288     // Populate and send the allocation to the client
    289     MemoryAllocation allocation;
    290     allocation.bytes_limit_when_visible = client_hard_limit_bytes_;
    291 #if defined(OS_ANDROID)
    292     // On Android, because there is only one visible tab at any time, allow
    293     // that renderer to cache as much as it can.
    294     allocation.priority_cutoff_when_visible =
    295         MemoryAllocation::CUTOFF_ALLOW_EVERYTHING;
    296 #else
    297     // On desktop platforms, instruct the renderers to cache only a smaller
    298     // set, to play nice with other renderers and other applications. If this
    299     // if not done, then the system can become unstable.
    300     // http://crbug.com/145600 (Linux)
    301     // http://crbug.com/141377 (Mac)
    302     allocation.priority_cutoff_when_visible =
    303         MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE;
    304 #endif
    305 
    306     client_state->client_->SetMemoryAllocation(allocation);
    307     client_state->client_->SuggestHaveFrontBuffer(!client_state->hibernated_);
    308   }
    309 }
    310 
    311 void GpuMemoryManager::AssignNonSurfacesAllocations() {
    312   for (ClientStateList::const_iterator it = clients_nonsurface_.begin();
    313        it != clients_nonsurface_.end();
    314        ++it) {
    315     GpuMemoryManagerClientState* client_state = *it;
    316     MemoryAllocation allocation;
    317 
    318     if (!client_state->hibernated_) {
    319       allocation.bytes_limit_when_visible = client_hard_limit_bytes_;
    320       allocation.priority_cutoff_when_visible =
    321           MemoryAllocation::CUTOFF_ALLOW_EVERYTHING;
    322     }
    323 
    324     client_state->client_->SetMemoryAllocation(allocation);
    325   }
    326 }
    327 
    328 void GpuMemoryManager::SetClientsHibernatedState() const {
    329   // Re-set all tracking groups as being hibernated.
    330   for (TrackingGroupMap::const_iterator it = tracking_groups_.begin();
    331        it != tracking_groups_.end();
    332        ++it) {
    333     GpuMemoryTrackingGroup* tracking_group = it->second;
    334     tracking_group->hibernated_ = true;
    335   }
    336   // All clients with surfaces that are visible are non-hibernated.
    337   uint64 non_hibernated_clients = 0;
    338   for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
    339        it != clients_visible_mru_.end();
    340        ++it) {
    341     GpuMemoryManagerClientState* client_state = *it;
    342     client_state->hibernated_ = false;
    343     client_state->tracking_group_->hibernated_ = false;
    344     non_hibernated_clients++;
    345   }
    346   // Then an additional few clients with surfaces are non-hibernated too, up to
    347   // a fixed limit.
    348   for (ClientStateList::const_iterator it = clients_nonvisible_mru_.begin();
    349        it != clients_nonvisible_mru_.end();
    350        ++it) {
    351     GpuMemoryManagerClientState* client_state = *it;
    352     if (non_hibernated_clients < max_surfaces_with_frontbuffer_soft_limit_) {
    353       client_state->hibernated_ = false;
    354       client_state->tracking_group_->hibernated_ = false;
    355       non_hibernated_clients++;
    356     } else {
    357       client_state->hibernated_ = true;
    358     }
    359   }
    360   // Clients that don't have surfaces are non-hibernated if they are
    361   // in a GL share group with a non-hibernated surface.
    362   for (ClientStateList::const_iterator it = clients_nonsurface_.begin();
    363        it != clients_nonsurface_.end();
    364        ++it) {
    365     GpuMemoryManagerClientState* client_state = *it;
    366     client_state->hibernated_ = client_state->tracking_group_->hibernated_;
    367   }
    368 }
    369 
    370 void GpuMemoryManager::SendUmaStatsToBrowser() {
    371   if (!channel_manager_)
    372     return;
    373   GPUMemoryUmaStats params;
    374   params.bytes_allocated_current = GetCurrentUsage();
    375   params.bytes_allocated_max = bytes_allocated_historical_max_;
    376   params.bytes_limit = client_hard_limit_bytes_;
    377   params.client_count = clients_visible_mru_.size() +
    378                         clients_nonvisible_mru_.size() +
    379                         clients_nonsurface_.size();
    380   params.context_group_count = tracking_groups_.size();
    381   channel_manager_->Send(new GpuHostMsg_GpuMemoryUmaStats(params));
    382 }
    383 
    384 GpuMemoryManager::ClientStateList* GpuMemoryManager::GetClientList(
    385     GpuMemoryManagerClientState* client_state) {
    386   if (client_state->has_surface_) {
    387     if (client_state->visible_)
    388       return &clients_visible_mru_;
    389     else
    390       return &clients_nonvisible_mru_;
    391   }
    392   return &clients_nonsurface_;
    393 }
    394 
    395 void GpuMemoryManager::AddClientToList(
    396     GpuMemoryManagerClientState* client_state) {
    397   DCHECK(!client_state->list_iterator_valid_);
    398   ClientStateList* client_list = GetClientList(client_state);
    399   client_state->list_iterator_ = client_list->insert(
    400       client_list->begin(), client_state);
    401   client_state->list_iterator_valid_ = true;
    402 }
    403 
    404 void GpuMemoryManager::RemoveClientFromList(
    405     GpuMemoryManagerClientState* client_state) {
    406   DCHECK(client_state->list_iterator_valid_);
    407   ClientStateList* client_list = GetClientList(client_state);
    408   client_list->erase(client_state->list_iterator_);
    409   client_state->list_iterator_valid_ = false;
    410 }
    411 
    412 }  // namespace content
    413