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/lazy_instance.h"
10 #include "base/synchronization/waitable_event.h"
11 #include "base/threading/thread_restrictions.h"
12 #include "base/trace_event/trace_event.h"
13 #include "content/common/gpu/client/gpu_memory_buffer_factory_host.h"
14 #include "content/common/gpu/client/gpu_memory_buffer_impl.h"
15 #include "content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h"
16 #include "content/public/browser/browser_thread.h"
21 BrowserGpuMemoryBufferManager
* g_gpu_memory_buffer_manager
= nullptr;
23 // Global atomic to generate gpu memory buffer unique IDs.
24 base::StaticAtomicSequenceNumber g_next_gpu_memory_buffer_id
;
28 struct BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferRequest
{
29 AllocateGpuMemoryBufferRequest(const gfx::Size
& size
,
30 gfx::GpuMemoryBuffer::Format format
,
31 gfx::GpuMemoryBuffer::Usage usage
,
39 surface_id(surface_id
) {}
40 ~AllocateGpuMemoryBufferRequest() {}
41 base::WaitableEvent event
;
43 gfx::GpuMemoryBuffer::Format format
;
44 gfx::GpuMemoryBuffer::Usage usage
;
47 scoped_ptr
<gfx::GpuMemoryBuffer
> result
;
50 BrowserGpuMemoryBufferManager::BrowserGpuMemoryBufferManager(
51 GpuMemoryBufferFactoryHost
* gpu_memory_buffer_factory_host
,
53 : gpu_memory_buffer_factory_host_(gpu_memory_buffer_factory_host
),
54 gpu_client_id_(gpu_client_id
),
55 weak_ptr_factory_(this) {
56 DCHECK(!g_gpu_memory_buffer_manager
);
57 g_gpu_memory_buffer_manager
= this;
60 BrowserGpuMemoryBufferManager::~BrowserGpuMemoryBufferManager() {
61 g_gpu_memory_buffer_manager
= nullptr;
65 BrowserGpuMemoryBufferManager
* BrowserGpuMemoryBufferManager::current() {
66 return g_gpu_memory_buffer_manager
;
69 scoped_ptr
<gfx::GpuMemoryBuffer
>
70 BrowserGpuMemoryBufferManager::AllocateGpuMemoryBuffer(
71 const gfx::Size
& size
,
72 gfx::GpuMemoryBuffer::Format format
,
73 gfx::GpuMemoryBuffer::Usage usage
) {
74 return AllocateGpuMemoryBufferCommon(size
, format
, usage
, 0);
77 scoped_ptr
<gfx::GpuMemoryBuffer
>
78 BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForScanout(
79 const gfx::Size
& size
,
80 gfx::GpuMemoryBuffer::Format format
,
82 DCHECK_GT(surface_id
, 0);
83 return AllocateGpuMemoryBufferCommon(
84 size
, format
, gfx::GpuMemoryBuffer::SCANOUT
, surface_id
);
87 scoped_ptr
<gfx::GpuMemoryBuffer
>
88 BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferCommon(
89 const gfx::Size
& size
,
90 gfx::GpuMemoryBuffer::Format format
,
91 gfx::GpuMemoryBuffer::Usage usage
,
93 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO
));
95 // Fallback to shared memory buffer if |format| and |usage| are not supported
97 if (!gpu_memory_buffer_factory_host_
->IsGpuMemoryBufferConfigurationSupported(
99 DCHECK(GpuMemoryBufferImplSharedMemory::IsFormatSupported(format
));
100 DCHECK_EQ(usage
, gfx::GpuMemoryBuffer::MAP
);
101 return GpuMemoryBufferImplSharedMemory::Create(
102 g_next_gpu_memory_buffer_id
.GetNext(), size
, format
);
105 AllocateGpuMemoryBufferRequest
request(size
, format
, usage
, gpu_client_id_
,
107 BrowserThread::PostTask(
110 base::Bind(&BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferOnIO
,
111 base::Unretained(this), // Safe as we wait for result below.
112 base::Unretained(&request
)));
114 // We're blocking the UI thread, which is generally undesirable.
115 TRACE_EVENT0("browser",
116 "BrowserGpuMemoryBufferManager::AllocateGpuMemoryBuffer");
117 base::ThreadRestrictions::ScopedAllowWait allow_wait
;
118 request
.event
.Wait();
119 return request
.result
.Pass();
122 void BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForChildProcess(
123 const gfx::Size
& size
,
124 gfx::GpuMemoryBuffer::Format format
,
125 gfx::GpuMemoryBuffer::Usage usage
,
126 base::ProcessHandle child_process_handle
,
128 const AllocationCallback
& callback
) {
129 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
131 gfx::GpuMemoryBufferId new_id
= g_next_gpu_memory_buffer_id
.GetNext();
133 BufferMap
& buffers
= clients_
[child_client_id
];
134 DCHECK(buffers
.find(new_id
) == buffers
.end());
136 // Fallback to shared memory buffer if |format| and |usage| are not supported
138 if (!gpu_memory_buffer_factory_host_
->IsGpuMemoryBufferConfigurationSupported(
140 // Early out if we cannot fallback to shared memory buffer.
141 if (!GpuMemoryBufferImplSharedMemory::IsFormatSupported(format
) ||
142 !GpuMemoryBufferImplSharedMemory::IsSizeValidForFormat(size
, format
) ||
143 usage
!= gfx::GpuMemoryBuffer::MAP
) {
144 callback
.Run(gfx::GpuMemoryBufferHandle());
148 buffers
[new_id
] = gfx::SHARED_MEMORY_BUFFER
;
149 callback
.Run(GpuMemoryBufferImplSharedMemory::AllocateForChildProcess(
150 new_id
, size
, format
, child_process_handle
));
154 // Note: Handling of cases where the child process is removed before the
155 // allocation completes is less subtle if we set the buffer type to
156 // EMPTY_BUFFER here and verify that this has not changed when allocation
158 buffers
[new_id
] = gfx::EMPTY_BUFFER
;
160 gpu_memory_buffer_factory_host_
->CreateGpuMemoryBuffer(
161 new_id
, size
, format
, usage
, child_client_id
, 0,
162 base::Bind(&BrowserGpuMemoryBufferManager::
163 GpuMemoryBufferAllocatedForChildProcess
,
164 weak_ptr_factory_
.GetWeakPtr(), child_client_id
, callback
));
167 gfx::GpuMemoryBuffer
*
168 BrowserGpuMemoryBufferManager::GpuMemoryBufferFromClientBuffer(
169 ClientBuffer buffer
) {
170 return GpuMemoryBufferImpl::FromClientBuffer(buffer
);
173 void BrowserGpuMemoryBufferManager::SetDestructionSyncPoint(
174 gfx::GpuMemoryBuffer
* buffer
,
176 static_cast<GpuMemoryBufferImpl
*>(buffer
)
177 ->set_destruction_sync_point(sync_point
);
180 void BrowserGpuMemoryBufferManager::ChildProcessDeletedGpuMemoryBuffer(
181 gfx::GpuMemoryBufferId id
,
182 base::ProcessHandle child_process_handle
,
185 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
186 DCHECK(clients_
.find(child_client_id
) != clients_
.end());
188 BufferMap
& buffers
= clients_
[child_client_id
];
190 BufferMap::iterator buffer_it
= buffers
.find(id
);
191 if (buffer_it
== buffers
.end()) {
192 LOG(ERROR
) << "Invalid GpuMemoryBuffer ID for child process.";
196 // This can happen if a child process managed to trigger a call to this while
197 // a buffer is in the process of being allocated.
198 if (buffer_it
->second
== gfx::EMPTY_BUFFER
) {
199 LOG(ERROR
) << "Invalid GpuMemoryBuffer type.";
203 // Buffers allocated using the factory need to be destroyed through the
205 if (buffer_it
->second
!= gfx::SHARED_MEMORY_BUFFER
) {
206 gpu_memory_buffer_factory_host_
->DestroyGpuMemoryBuffer(id
,
211 buffers
.erase(buffer_it
);
214 void BrowserGpuMemoryBufferManager::ProcessRemoved(
215 base::ProcessHandle process_handle
,
217 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
219 ClientMap::iterator client_it
= clients_
.find(client_id
);
220 if (client_it
== clients_
.end())
223 for (auto &buffer_it
: client_it
->second
) {
224 // This might happen if buffer is currenlty in the process of being
225 // allocated. The buffer will in that case be cleaned up when allocation
227 if (buffer_it
.second
== gfx::EMPTY_BUFFER
)
230 // Skip shared memory buffers as they were not allocated using the factory.
231 if (buffer_it
.second
== gfx::SHARED_MEMORY_BUFFER
)
234 gpu_memory_buffer_factory_host_
->DestroyGpuMemoryBuffer(buffer_it
.first
,
239 clients_
.erase(client_it
);
242 void BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferOnIO(
243 AllocateGpuMemoryBufferRequest
* request
) {
244 // Note: Unretained is safe as this is only used for synchronous allocation
245 // from a non-IO thread.
246 gpu_memory_buffer_factory_host_
->CreateGpuMemoryBuffer(
247 g_next_gpu_memory_buffer_id
.GetNext(), request
->size
, request
->format
,
248 request
->usage
, request
->client_id
, request
->surface_id
,
249 base::Bind(&BrowserGpuMemoryBufferManager::GpuMemoryBufferAllocatedOnIO
,
250 base::Unretained(this), base::Unretained(request
)));
253 void BrowserGpuMemoryBufferManager::GpuMemoryBufferAllocatedOnIO(
254 AllocateGpuMemoryBufferRequest
* request
,
255 const gfx::GpuMemoryBufferHandle
& handle
) {
256 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
258 // Early out if factory failed to allocate the buffer.
259 if (handle
.is_null()) {
260 request
->event
.Signal();
264 DCHECK_NE(handle
.type
, gfx::SHARED_MEMORY_BUFFER
);
265 request
->result
= GpuMemoryBufferImpl::CreateFromHandle(
269 base::Bind(&BrowserGpuMemoryBufferManager::GpuMemoryBufferDeleted
,
270 weak_ptr_factory_
.GetWeakPtr(),
272 request
->client_id
));
273 request
->event
.Signal();
276 void BrowserGpuMemoryBufferManager::GpuMemoryBufferDeleted(
277 gfx::GpuMemoryBufferId id
,
280 gpu_memory_buffer_factory_host_
->DestroyGpuMemoryBuffer(id
,
285 void BrowserGpuMemoryBufferManager::GpuMemoryBufferAllocatedForChildProcess(
287 const AllocationCallback
& callback
,
288 const gfx::GpuMemoryBufferHandle
& handle
) {
289 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
291 ClientMap::iterator client_it
= clients_
.find(child_client_id
);
293 // This can happen if the child process is removed while the buffer is being
295 if (client_it
== clients_
.end()) {
296 if (!handle
.is_null()) {
297 gpu_memory_buffer_factory_host_
->DestroyGpuMemoryBuffer(
298 handle
.id
, child_client_id
, 0);
300 callback
.Run(gfx::GpuMemoryBufferHandle());
304 BufferMap
& buffers
= client_it
->second
;
306 BufferMap::iterator buffer_it
= buffers
.find(handle
.id
);
307 DCHECK(buffer_it
!= buffers
.end());
308 DCHECK_EQ(buffer_it
->second
, gfx::EMPTY_BUFFER
);
310 if (handle
.is_null()) {
311 buffers
.erase(buffer_it
);
312 callback
.Run(gfx::GpuMemoryBufferHandle());
316 // The factory should never return a shared memory backed buffer.
317 DCHECK_NE(handle
.type
, gfx::SHARED_MEMORY_BUFFER
);
319 // Store the type of this buffer so it can be cleaned up if the child
320 // process is removed.
321 buffer_it
->second
= handle
.type
;
323 callback
.Run(handle
);
326 } // namespace content