1 // Copyright 2014 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.
5 #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h"
7 #include "base/atomic_sequence_num.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/synchronization/waitable_event.h"
11 #include "base/threading/thread_restrictions.h"
12 #include "base/trace_event/process_memory_dump.h"
13 #include "base/trace_event/trace_event.h"
14 #include "content/common/gpu/client/gpu_memory_buffer_factory_host.h"
15 #include "content/common/gpu/client/gpu_memory_buffer_impl.h"
16 #include "content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h"
17 #include "content/public/browser/browser_thread.h"
22 BrowserGpuMemoryBufferManager
* g_gpu_memory_buffer_manager
= nullptr;
24 // Global atomic to generate gpu memory buffer unique IDs.
25 base::StaticAtomicSequenceNumber g_next_gpu_memory_buffer_id
;
27 const char kMemoryAllocatorName
[] = "gpumemorybuffer";
31 struct BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferRequest
{
32 AllocateGpuMemoryBufferRequest(const gfx::Size
& size
,
33 gfx::GpuMemoryBuffer::Format format
,
34 gfx::GpuMemoryBuffer::Usage usage
,
42 surface_id(surface_id
) {}
43 ~AllocateGpuMemoryBufferRequest() {}
44 base::WaitableEvent event
;
46 gfx::GpuMemoryBuffer::Format format
;
47 gfx::GpuMemoryBuffer::Usage usage
;
50 scoped_ptr
<gfx::GpuMemoryBuffer
> result
;
53 BrowserGpuMemoryBufferManager::BrowserGpuMemoryBufferManager(
54 GpuMemoryBufferFactoryHost
* gpu_memory_buffer_factory_host
,
56 : gpu_memory_buffer_factory_host_(gpu_memory_buffer_factory_host
),
57 gpu_client_id_(gpu_client_id
),
58 weak_ptr_factory_(this) {
59 DCHECK(!g_gpu_memory_buffer_manager
);
60 g_gpu_memory_buffer_manager
= this;
63 BrowserGpuMemoryBufferManager::~BrowserGpuMemoryBufferManager() {
64 g_gpu_memory_buffer_manager
= nullptr;
68 BrowserGpuMemoryBufferManager
* BrowserGpuMemoryBufferManager::current() {
69 return g_gpu_memory_buffer_manager
;
72 scoped_ptr
<gfx::GpuMemoryBuffer
>
73 BrowserGpuMemoryBufferManager::AllocateGpuMemoryBuffer(
74 const gfx::Size
& size
,
75 gfx::GpuMemoryBuffer::Format format
,
76 gfx::GpuMemoryBuffer::Usage usage
) {
77 return AllocateGpuMemoryBufferCommon(size
, format
, usage
, 0);
80 scoped_ptr
<gfx::GpuMemoryBuffer
>
81 BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForScanout(
82 const gfx::Size
& size
,
83 gfx::GpuMemoryBuffer::Format format
,
85 DCHECK_GT(surface_id
, 0);
86 return AllocateGpuMemoryBufferCommon(
87 size
, format
, gfx::GpuMemoryBuffer::SCANOUT
, surface_id
);
90 scoped_ptr
<gfx::GpuMemoryBuffer
>
91 BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferCommon(
92 const gfx::Size
& size
,
93 gfx::GpuMemoryBuffer::Format format
,
94 gfx::GpuMemoryBuffer::Usage usage
,
96 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO
));
98 // Fallback to shared memory buffer if |format| and |usage| are not supported
100 if (!gpu_memory_buffer_factory_host_
->IsGpuMemoryBufferConfigurationSupported(
102 DCHECK(GpuMemoryBufferImplSharedMemory::IsFormatSupported(format
));
103 DCHECK_EQ(usage
, gfx::GpuMemoryBuffer::MAP
);
104 return GpuMemoryBufferImplSharedMemory::Create(
105 g_next_gpu_memory_buffer_id
.GetNext(), size
, format
);
108 AllocateGpuMemoryBufferRequest
request(size
, format
, usage
, gpu_client_id_
,
110 BrowserThread::PostTask(
113 base::Bind(&BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferOnIO
,
114 base::Unretained(this), // Safe as we wait for result below.
115 base::Unretained(&request
)));
117 // We're blocking the UI thread, which is generally undesirable.
118 TRACE_EVENT0("browser",
119 "BrowserGpuMemoryBufferManager::AllocateGpuMemoryBuffer");
120 base::ThreadRestrictions::ScopedAllowWait allow_wait
;
121 request
.event
.Wait();
122 return request
.result
.Pass();
125 void BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForChildProcess(
126 const gfx::Size
& size
,
127 gfx::GpuMemoryBuffer::Format format
,
128 gfx::GpuMemoryBuffer::Usage usage
,
129 base::ProcessHandle child_process_handle
,
131 const AllocationCallback
& callback
) {
132 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
134 gfx::GpuMemoryBufferId new_id
= g_next_gpu_memory_buffer_id
.GetNext();
136 BufferMap
& buffers
= clients_
[child_client_id
];
137 DCHECK(buffers
.find(new_id
) == buffers
.end());
139 // Fallback to shared memory buffer if |format| and |usage| are not supported
141 if (!gpu_memory_buffer_factory_host_
->IsGpuMemoryBufferConfigurationSupported(
143 // Early out if we cannot fallback to shared memory buffer.
144 if (!GpuMemoryBufferImplSharedMemory::IsFormatSupported(format
) ||
145 !GpuMemoryBufferImplSharedMemory::IsSizeValidForFormat(size
, format
) ||
146 usage
!= gfx::GpuMemoryBuffer::MAP
) {
147 callback
.Run(gfx::GpuMemoryBufferHandle());
151 buffers
[new_id
] = BufferInfo(size
, format
, gfx::SHARED_MEMORY_BUFFER
);
152 callback
.Run(GpuMemoryBufferImplSharedMemory::AllocateForChildProcess(
153 new_id
, size
, format
, child_process_handle
));
157 // Note: Handling of cases where the child process is removed before the
158 // allocation completes is less subtle if we set the buffer type to
159 // EMPTY_BUFFER here and verify that this has not changed when allocation
161 buffers
[new_id
] = BufferInfo(size
, format
, gfx::EMPTY_BUFFER
);
163 gpu_memory_buffer_factory_host_
->CreateGpuMemoryBuffer(
164 new_id
, size
, format
, usage
, child_client_id
, 0,
165 base::Bind(&BrowserGpuMemoryBufferManager::
166 GpuMemoryBufferAllocatedForChildProcess
,
167 weak_ptr_factory_
.GetWeakPtr(), child_client_id
, callback
));
170 gfx::GpuMemoryBuffer
*
171 BrowserGpuMemoryBufferManager::GpuMemoryBufferFromClientBuffer(
172 ClientBuffer buffer
) {
173 return GpuMemoryBufferImpl::FromClientBuffer(buffer
);
176 void BrowserGpuMemoryBufferManager::SetDestructionSyncPoint(
177 gfx::GpuMemoryBuffer
* buffer
,
179 static_cast<GpuMemoryBufferImpl
*>(buffer
)
180 ->set_destruction_sync_point(sync_point
);
183 bool BrowserGpuMemoryBufferManager::OnMemoryDump(
184 base::trace_event::ProcessMemoryDump
* pmd
) {
185 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
187 for (const auto& client
: clients_
) {
188 for (const auto& buffer
: client
.second
) {
189 if (buffer
.second
.type
== gfx::EMPTY_BUFFER
)
192 base::trace_event::MemoryAllocatorDump
* dump
= pmd
->CreateAllocatorDump(
193 base::StringPrintf("%s/%d", kMemoryAllocatorName
, buffer
.first
));
197 size_t buffer_size_in_bytes
= 0;
198 // Note: BufferSizeInBytes returns an approximated size for the buffer
199 // but the factory can be made to return the exact size if this
200 // approximation is not good enough.
201 bool valid_size
= GpuMemoryBufferImpl::BufferSizeInBytes(
202 buffer
.second
.size
, buffer
.second
.format
, &buffer_size_in_bytes
);
205 dump
->AddScalar(base::trace_event::MemoryAllocatorDump::kNameOuterSize
,
206 base::trace_event::MemoryAllocatorDump::kUnitsBytes
,
207 buffer_size_in_bytes
);
214 void BrowserGpuMemoryBufferManager::ChildProcessDeletedGpuMemoryBuffer(
215 gfx::GpuMemoryBufferId id
,
216 base::ProcessHandle child_process_handle
,
219 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
220 DCHECK(clients_
.find(child_client_id
) != clients_
.end());
222 BufferMap
& buffers
= clients_
[child_client_id
];
224 BufferMap::iterator buffer_it
= buffers
.find(id
);
225 if (buffer_it
== buffers
.end()) {
226 LOG(ERROR
) << "Invalid GpuMemoryBuffer ID for child process.";
230 // This can happen if a child process managed to trigger a call to this while
231 // a buffer is in the process of being allocated.
232 if (buffer_it
->second
.type
== gfx::EMPTY_BUFFER
) {
233 LOG(ERROR
) << "Invalid GpuMemoryBuffer type.";
237 // Buffers allocated using the factory need to be destroyed through the
239 if (buffer_it
->second
.type
!= gfx::SHARED_MEMORY_BUFFER
) {
240 gpu_memory_buffer_factory_host_
->DestroyGpuMemoryBuffer(id
,
245 buffers
.erase(buffer_it
);
248 void BrowserGpuMemoryBufferManager::ProcessRemoved(
249 base::ProcessHandle process_handle
,
251 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
253 ClientMap::iterator client_it
= clients_
.find(client_id
);
254 if (client_it
== clients_
.end())
257 for (const auto& buffer
: client_it
->second
) {
258 // This might happen if buffer is currenlty in the process of being
259 // allocated. The buffer will in that case be cleaned up when allocation
261 if (buffer
.second
.type
== gfx::EMPTY_BUFFER
)
264 // Skip shared memory buffers as they were not allocated using the factory.
265 if (buffer
.second
.type
== gfx::SHARED_MEMORY_BUFFER
)
268 gpu_memory_buffer_factory_host_
->DestroyGpuMemoryBuffer(buffer
.first
,
272 clients_
.erase(client_it
);
275 void BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferOnIO(
276 AllocateGpuMemoryBufferRequest
* request
) {
277 // Note: Unretained is safe as this is only used for synchronous allocation
278 // from a non-IO thread.
279 gpu_memory_buffer_factory_host_
->CreateGpuMemoryBuffer(
280 g_next_gpu_memory_buffer_id
.GetNext(), request
->size
, request
->format
,
281 request
->usage
, request
->client_id
, request
->surface_id
,
282 base::Bind(&BrowserGpuMemoryBufferManager::GpuMemoryBufferAllocatedOnIO
,
283 base::Unretained(this), base::Unretained(request
)));
286 void BrowserGpuMemoryBufferManager::GpuMemoryBufferAllocatedOnIO(
287 AllocateGpuMemoryBufferRequest
* request
,
288 const gfx::GpuMemoryBufferHandle
& handle
) {
289 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
291 // Early out if factory failed to allocate the buffer.
292 if (handle
.is_null()) {
293 request
->event
.Signal();
297 DCHECK_NE(handle
.type
, gfx::SHARED_MEMORY_BUFFER
);
298 request
->result
= GpuMemoryBufferImpl::CreateFromHandle(
302 base::Bind(&BrowserGpuMemoryBufferManager::GpuMemoryBufferDeleted
,
303 weak_ptr_factory_
.GetWeakPtr(),
305 request
->client_id
));
306 request
->event
.Signal();
309 void BrowserGpuMemoryBufferManager::GpuMemoryBufferDeleted(
310 gfx::GpuMemoryBufferId id
,
313 gpu_memory_buffer_factory_host_
->DestroyGpuMemoryBuffer(id
,
318 void BrowserGpuMemoryBufferManager::GpuMemoryBufferAllocatedForChildProcess(
320 const AllocationCallback
& callback
,
321 const gfx::GpuMemoryBufferHandle
& handle
) {
322 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
324 ClientMap::iterator client_it
= clients_
.find(child_client_id
);
326 // This can happen if the child process is removed while the buffer is being
328 if (client_it
== clients_
.end()) {
329 if (!handle
.is_null()) {
330 gpu_memory_buffer_factory_host_
->DestroyGpuMemoryBuffer(
331 handle
.id
, child_client_id
, 0);
333 callback
.Run(gfx::GpuMemoryBufferHandle());
337 BufferMap
& buffers
= client_it
->second
;
339 BufferMap::iterator buffer_it
= buffers
.find(handle
.id
);
340 DCHECK(buffer_it
!= buffers
.end());
341 DCHECK_EQ(buffer_it
->second
.type
, gfx::EMPTY_BUFFER
);
343 if (handle
.is_null()) {
344 buffers
.erase(buffer_it
);
345 callback
.Run(gfx::GpuMemoryBufferHandle());
349 // The factory should never return a shared memory backed buffer.
350 DCHECK_NE(handle
.type
, gfx::SHARED_MEMORY_BUFFER
);
352 // Store the type of this buffer so it can be cleaned up if the child
353 // process is removed.
354 buffer_it
->second
.type
= handle
.type
;
356 callback
.Run(handle
);
359 } // namespace content