Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / common / gpu / gpu_memory_manager.cc
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