OLD | NEW |
| (Empty) |
1 // Copyright 2013 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 "gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.h
" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/debug/trace_event.h" | |
9 #include "base/lazy_instance.h" | |
10 #include "base/logging.h" | |
11 #include "base/synchronization/cancellation_flag.h" | |
12 #include "base/synchronization/lock.h" | |
13 #include "base/synchronization/waitable_event.h" | |
14 #include "base/threading/thread.h" | |
15 #include "gpu/command_buffer/service/async_pixel_transfer_delegate.h" | |
16 #include "gpu/command_buffer/service/safe_shared_memory_pool.h" | |
17 #include "ui/gl/gl_bindings.h" | |
18 #include "ui/gl/gl_context.h" | |
19 #include "ui/gl/gl_surface.h" | |
20 #include "ui/gl/gpu_preference.h" | |
21 #include "ui/gl/scoped_binders.h" | |
22 | |
23 namespace gpu { | |
24 | |
25 namespace { | |
26 | |
27 const char kAsyncTransferThreadName[] = "AsyncTransferThread"; | |
28 | |
29 // TODO(backer): Factor out common thread scheduling logic from the EGL and | |
30 // ShareGroup implementations. http://crbug.com/239889 | |
31 class TransferThread : public base::Thread { | |
32 public: | |
33 TransferThread() | |
34 : base::Thread(kAsyncTransferThreadName), | |
35 initialized_(false) { | |
36 Start(); | |
37 #if defined(OS_ANDROID) || defined(OS_LINUX) | |
38 SetPriority(base::kThreadPriority_Background); | |
39 #endif | |
40 } | |
41 | |
42 virtual ~TransferThread() { | |
43 // The only instance of this class was declared leaky. | |
44 NOTREACHED(); | |
45 } | |
46 | |
47 void InitializeOnMainThread(gfx::GLContext* parent_context) { | |
48 TRACE_EVENT0("gpu", "TransferThread::InitializeOnMainThread"); | |
49 if (initialized_) | |
50 return; | |
51 | |
52 base::WaitableEvent wait_for_init(true, false); | |
53 message_loop_proxy()->PostTask( | |
54 FROM_HERE, | |
55 base::Bind(&TransferThread::InitializeOnTransferThread, | |
56 base::Unretained(this), | |
57 base::Unretained(parent_context), | |
58 &wait_for_init)); | |
59 wait_for_init.Wait(); | |
60 } | |
61 | |
62 virtual void CleanUp() OVERRIDE { | |
63 surface_ = NULL; | |
64 context_ = NULL; | |
65 } | |
66 | |
67 SafeSharedMemoryPool* safe_shared_memory_pool() { | |
68 return &safe_shared_memory_pool_; | |
69 } | |
70 | |
71 private: | |
72 bool initialized_; | |
73 | |
74 scoped_refptr<gfx::GLSurface> surface_; | |
75 scoped_refptr<gfx::GLContext> context_; | |
76 SafeSharedMemoryPool safe_shared_memory_pool_; | |
77 | |
78 void InitializeOnTransferThread(gfx::GLContext* parent_context, | |
79 base::WaitableEvent* caller_wait) { | |
80 TRACE_EVENT0("gpu", "InitializeOnTransferThread"); | |
81 | |
82 if (!parent_context) { | |
83 LOG(ERROR) << "No parent context provided."; | |
84 caller_wait->Signal(); | |
85 return; | |
86 } | |
87 | |
88 surface_ = gfx::GLSurface::CreateOffscreenGLSurface(false, gfx::Size(1, 1)); | |
89 if (!surface_) { | |
90 LOG(ERROR) << "Unable to create GLSurface"; | |
91 caller_wait->Signal(); | |
92 return; | |
93 } | |
94 | |
95 // TODO(backer): This is coded for integrated GPUs. For discrete GPUs | |
96 // we would probably want to use a PBO texture upload for a true async | |
97 // upload (that would hopefully be optimized as a DMA transfer by the | |
98 // driver). | |
99 context_ = gfx::GLContext::CreateGLContext(parent_context->share_group(), | |
100 surface_, | |
101 gfx::PreferIntegratedGpu); | |
102 if (!context_) { | |
103 LOG(ERROR) << "Unable to create GLContext."; | |
104 caller_wait->Signal(); | |
105 return; | |
106 } | |
107 | |
108 context_->MakeCurrent(surface_); | |
109 initialized_ = true; | |
110 caller_wait->Signal(); | |
111 } | |
112 | |
113 DISALLOW_COPY_AND_ASSIGN(TransferThread); | |
114 }; | |
115 | |
116 base::LazyInstance<TransferThread>::Leaky | |
117 g_transfer_thread = LAZY_INSTANCE_INITIALIZER; | |
118 | |
119 base::MessageLoopProxy* transfer_message_loop_proxy() { | |
120 return g_transfer_thread.Pointer()->message_loop_proxy(); | |
121 } | |
122 | |
123 SafeSharedMemoryPool* safe_shared_memory_pool() { | |
124 return g_transfer_thread.Pointer()->safe_shared_memory_pool(); | |
125 } | |
126 | |
127 // Class which holds async pixel transfers state. | |
128 // The texture_id is accessed by either thread, but everything | |
129 // else accessed only on the main thread. | |
130 class TransferStateInternal | |
131 : public base::RefCountedThreadSafe<TransferStateInternal> { | |
132 public: | |
133 TransferStateInternal(GLuint texture_id, | |
134 const AsyncTexImage2DParams& define_params) | |
135 : texture_id_(texture_id), | |
136 transfer_completion_(true, true) { | |
137 define_params_ = define_params; | |
138 } | |
139 | |
140 // Implement AsyncPixelTransferState: | |
141 bool TransferIsInProgress() { | |
142 return !transfer_completion_.IsSignaled(); | |
143 } | |
144 | |
145 void BindTransfer() { | |
146 TRACE_EVENT2("gpu", "BindAsyncTransfer", | |
147 "width", define_params_.width, | |
148 "height", define_params_.height); | |
149 DCHECK(texture_id_); | |
150 | |
151 glBindTexture(GL_TEXTURE_2D, texture_id_); | |
152 bind_callback_.Run(); | |
153 } | |
154 | |
155 void MarkAsTransferIsInProgress() { | |
156 transfer_completion_.Reset(); | |
157 } | |
158 | |
159 void MarkAsCompleted() { | |
160 TRACE_EVENT0("gpu", "MarkAsCompleted"); | |
161 glFlush(); | |
162 transfer_completion_.Signal(); | |
163 } | |
164 | |
165 void WaitForTransferCompletion() { | |
166 TRACE_EVENT0("gpu", "WaitForTransferCompletion"); | |
167 // TODO(backer): Deschedule the channel rather than blocking the main GPU | |
168 // thread (crbug.com/240265). | |
169 transfer_completion_.Wait(); | |
170 } | |
171 | |
172 GLuint texture_id() { return texture_id_; } | |
173 | |
174 void SetBindCallback(base::Closure bind_callback) { | |
175 bind_callback_ = bind_callback; | |
176 } | |
177 | |
178 void PerformAsyncTexImage2D(AsyncTexImage2DParams tex_params, | |
179 AsyncMemoryParams mem_params, | |
180 ScopedSafeSharedMemory* safe_shared_memory) { | |
181 base::AutoLock locked(upload_lock_); | |
182 if (cancel_upload_flag_.IsSet()) | |
183 return; | |
184 | |
185 TRACE_EVENT2("gpu", | |
186 "PerformAsyncTexImage", | |
187 "width", | |
188 tex_params.width, | |
189 "height", | |
190 tex_params.height); | |
191 DCHECK_EQ(0, tex_params.level); | |
192 | |
193 void* data = | |
194 AsyncPixelTransferDelegate::GetAddress(safe_shared_memory, mem_params); | |
195 | |
196 { | |
197 TRACE_EVENT0("gpu", "glTexImage2D"); | |
198 glBindTexture(GL_TEXTURE_2D, texture_id_); | |
199 glTexImage2D(GL_TEXTURE_2D, | |
200 tex_params.level, | |
201 tex_params.internal_format, | |
202 tex_params.width, | |
203 tex_params.height, | |
204 tex_params.border, | |
205 tex_params.format, | |
206 tex_params.type, | |
207 data); | |
208 glBindTexture(GL_TEXTURE_2D, 0); | |
209 } | |
210 | |
211 MarkAsCompleted(); | |
212 } | |
213 | |
214 void PerformAsyncTexSubImage2D( | |
215 AsyncTexSubImage2DParams tex_params, | |
216 AsyncMemoryParams mem_params, | |
217 ScopedSafeSharedMemory* safe_shared_memory, | |
218 scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats) { | |
219 base::AutoLock locked(upload_lock_); | |
220 if (cancel_upload_flag_.IsSet()) | |
221 return; | |
222 | |
223 TRACE_EVENT2("gpu", | |
224 "PerformAsyncTexSubImage2D", | |
225 "width", | |
226 tex_params.width, | |
227 "height", | |
228 tex_params.height); | |
229 DCHECK_EQ(0, tex_params.level); | |
230 | |
231 base::TimeTicks begin_time; | |
232 if (texture_upload_stats) | |
233 begin_time = base::TimeTicks::HighResNow(); | |
234 | |
235 void* data = | |
236 AsyncPixelTransferDelegate::GetAddress(safe_shared_memory, mem_params); | |
237 | |
238 { | |
239 TRACE_EVENT0("gpu", "glTexSubImage2D"); | |
240 glBindTexture(GL_TEXTURE_2D, texture_id_); | |
241 glTexSubImage2D(GL_TEXTURE_2D, | |
242 tex_params.level, | |
243 tex_params.xoffset, | |
244 tex_params.yoffset, | |
245 tex_params.width, | |
246 tex_params.height, | |
247 tex_params.format, | |
248 tex_params.type, | |
249 data); | |
250 glBindTexture(GL_TEXTURE_2D, 0); | |
251 } | |
252 | |
253 MarkAsCompleted(); | |
254 | |
255 if (texture_upload_stats) { | |
256 texture_upload_stats->AddUpload(base::TimeTicks::HighResNow() - | |
257 begin_time); | |
258 } | |
259 } | |
260 | |
261 base::CancellationFlag* cancel_upload_flag() { return &cancel_upload_flag_; } | |
262 base::Lock* upload_lock() { return &upload_lock_; } | |
263 | |
264 private: | |
265 friend class base::RefCountedThreadSafe<TransferStateInternal>; | |
266 | |
267 virtual ~TransferStateInternal() { | |
268 } | |
269 | |
270 // Used to cancel pending uploads. | |
271 base::CancellationFlag cancel_upload_flag_; | |
272 base::Lock upload_lock_; | |
273 | |
274 GLuint texture_id_; | |
275 | |
276 // Definition params for texture that needs binding. | |
277 AsyncTexImage2DParams define_params_; | |
278 | |
279 // Indicates that an async transfer is in progress. | |
280 base::WaitableEvent transfer_completion_; | |
281 | |
282 // Callback to invoke when AsyncTexImage2D is complete | |
283 // and the client can safely use the texture. This occurs | |
284 // during BindCompletedAsyncTransfers(). | |
285 base::Closure bind_callback_; | |
286 }; | |
287 | |
288 void PerformNotifyCompletion( | |
289 AsyncMemoryParams mem_params, | |
290 ScopedSafeSharedMemory* safe_shared_memory, | |
291 const AsyncPixelTransferDelegate::CompletionCallback& callback) { | |
292 TRACE_EVENT0("gpu", "PerformNotifyCompletion"); | |
293 AsyncMemoryParams safe_mem_params = mem_params; | |
294 safe_mem_params.shared_memory = safe_shared_memory->shared_memory(); | |
295 callback.Run(safe_mem_params); | |
296 } | |
297 | |
298 } // namespace | |
299 | |
300 // ShareGroup needs thread-safe ref-counting, so this just wraps | |
301 // an internal thread-safe ref-counted state object. | |
302 class AsyncTransferStateImpl : public AsyncPixelTransferState { | |
303 public: | |
304 AsyncTransferStateImpl(GLuint texture_id, | |
305 const AsyncTexImage2DParams& define_params) | |
306 : internal_(new TransferStateInternal(texture_id, define_params)) {} | |
307 | |
308 virtual bool TransferIsInProgress() OVERRIDE { | |
309 return internal_->TransferIsInProgress(); | |
310 } | |
311 | |
312 TransferStateInternal* internal() { return internal_.get(); } | |
313 | |
314 private: | |
315 virtual ~AsyncTransferStateImpl() { | |
316 TRACE_EVENT0("gpu", " ~AsyncTransferStateImpl"); | |
317 base::AutoLock locked(*internal_->upload_lock()); | |
318 internal_->cancel_upload_flag()->Set(); | |
319 } | |
320 | |
321 scoped_refptr<TransferStateInternal> internal_; | |
322 }; | |
323 | |
324 AsyncPixelTransferDelegateShareGroup::AsyncPixelTransferDelegateShareGroup( | |
325 gfx::GLContext* context) { | |
326 g_transfer_thread.Pointer()->InitializeOnMainThread(context); | |
327 | |
328 // TODO(reveman): Skip this if --enable-gpu-benchmarking is not present. | |
329 texture_upload_stats_ = make_scoped_refptr(new AsyncPixelTransferUploadStats); | |
330 } | |
331 | |
332 AsyncPixelTransferDelegateShareGroup::~AsyncPixelTransferDelegateShareGroup() { | |
333 } | |
334 | |
335 AsyncPixelTransferState* | |
336 AsyncPixelTransferDelegateShareGroup:: | |
337 CreatePixelTransferState( | |
338 GLuint texture_id, | |
339 const AsyncTexImage2DParams& define_params) { | |
340 return static_cast<AsyncPixelTransferState*>( | |
341 new AsyncTransferStateImpl(texture_id, define_params)); | |
342 } | |
343 | |
344 void AsyncPixelTransferDelegateShareGroup::BindCompletedAsyncTransfers() { | |
345 scoped_ptr<gfx::ScopedTextureBinder> texture_binder; | |
346 | |
347 while (!pending_allocations_.empty()) { | |
348 if (!pending_allocations_.front().get()) { | |
349 pending_allocations_.pop_front(); | |
350 continue; | |
351 } | |
352 scoped_refptr<TransferStateInternal> state = | |
353 static_cast<AsyncTransferStateImpl*> | |
354 (pending_allocations_.front().get())->internal(); | |
355 // Terminate early, as all transfers finish in order, currently. | |
356 if (state->TransferIsInProgress()) | |
357 break; | |
358 | |
359 if (!texture_binder) | |
360 texture_binder.reset(new gfx::ScopedTextureBinder(GL_TEXTURE_2D, 0)); | |
361 | |
362 // Used to set tex info from the gles2 cmd decoder once upload has | |
363 // finished (it'll bind the texture and call a callback). | |
364 state->BindTransfer(); | |
365 | |
366 pending_allocations_.pop_front(); | |
367 } | |
368 } | |
369 | |
370 void AsyncPixelTransferDelegateShareGroup::AsyncNotifyCompletion( | |
371 const AsyncMemoryParams& mem_params, | |
372 const CompletionCallback& callback) { | |
373 DCHECK(mem_params.shared_memory); | |
374 DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size, | |
375 mem_params.shm_size); | |
376 // Post a PerformNotifyCompletion task to the upload thread. This task | |
377 // will run after all async transfers are complete. | |
378 transfer_message_loop_proxy()->PostTask( | |
379 FROM_HERE, | |
380 base::Bind(&PerformNotifyCompletion, | |
381 mem_params, | |
382 base::Owned( | |
383 new ScopedSafeSharedMemory(safe_shared_memory_pool(), | |
384 mem_params.shared_memory, | |
385 mem_params.shm_size)), | |
386 callback)); | |
387 } | |
388 | |
389 void AsyncPixelTransferDelegateShareGroup::WaitForTransferCompletion( | |
390 AsyncPixelTransferState* transfer_state) { | |
391 scoped_refptr<TransferStateInternal> state = | |
392 static_cast<AsyncTransferStateImpl*>(transfer_state)->internal(); | |
393 DCHECK(state); | |
394 DCHECK(state->texture_id()); | |
395 | |
396 if (state->TransferIsInProgress()) { | |
397 #if defined(OS_ANDROID) || defined(OS_LINUX) | |
398 g_transfer_thread.Pointer()->SetPriority(base::kThreadPriority_Display); | |
399 #endif | |
400 | |
401 state->WaitForTransferCompletion(); | |
402 DCHECK(!state->TransferIsInProgress()); | |
403 | |
404 #if defined(OS_ANDROID) || defined(OS_LINUX) | |
405 g_transfer_thread.Pointer()->SetPriority(base::kThreadPriority_Background); | |
406 #endif | |
407 } | |
408 } | |
409 | |
410 void AsyncPixelTransferDelegateShareGroup::AsyncTexImage2D( | |
411 AsyncPixelTransferState* transfer_state, | |
412 const AsyncTexImage2DParams& tex_params, | |
413 const AsyncMemoryParams& mem_params, | |
414 const base::Closure& bind_callback) { | |
415 scoped_refptr<TransferStateInternal> state = | |
416 static_cast<AsyncTransferStateImpl*>(transfer_state)->internal(); | |
417 DCHECK(mem_params.shared_memory); | |
418 DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size, | |
419 mem_params.shm_size); | |
420 DCHECK(state); | |
421 DCHECK(state->texture_id()); | |
422 DCHECK(!state->TransferIsInProgress()); | |
423 DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target); | |
424 DCHECK_EQ(tex_params.level, 0); | |
425 | |
426 // Mark the transfer in progress and save the late bind | |
427 // callback, so we can notify the client when it is bound. | |
428 pending_allocations_.push_back(transfer_state->AsWeakPtr()); | |
429 state->SetBindCallback(bind_callback); | |
430 | |
431 // Mark the transfer in progress. | |
432 state->MarkAsTransferIsInProgress(); | |
433 | |
434 // Duplicate the shared memory so there is no way we can get | |
435 // a use-after-free of the raw pixels. | |
436 transfer_message_loop_proxy()->PostTask( | |
437 FROM_HERE, | |
438 base::Bind( | |
439 &TransferStateInternal::PerformAsyncTexImage2D, | |
440 state, | |
441 tex_params, | |
442 mem_params, | |
443 base::Owned(new ScopedSafeSharedMemory(safe_shared_memory_pool(), | |
444 mem_params.shared_memory, | |
445 mem_params.shm_size)))); | |
446 } | |
447 | |
448 void AsyncPixelTransferDelegateShareGroup::AsyncTexSubImage2D( | |
449 AsyncPixelTransferState* transfer_state, | |
450 const AsyncTexSubImage2DParams& tex_params, | |
451 const AsyncMemoryParams& mem_params) { | |
452 TRACE_EVENT2("gpu", "AsyncTexSubImage2D", | |
453 "width", tex_params.width, | |
454 "height", tex_params.height); | |
455 scoped_refptr<TransferStateInternal> state = | |
456 static_cast<AsyncTransferStateImpl*>(transfer_state)->internal(); | |
457 | |
458 DCHECK(state->texture_id()); | |
459 DCHECK(!state->TransferIsInProgress()); | |
460 DCHECK(mem_params.shared_memory); | |
461 DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size, | |
462 mem_params.shm_size); | |
463 DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target); | |
464 DCHECK_EQ(tex_params.level, 0); | |
465 | |
466 // Mark the transfer in progress. | |
467 state->MarkAsTransferIsInProgress(); | |
468 | |
469 // Duplicate the shared memory so there are no way we can get | |
470 // a use-after-free of the raw pixels. | |
471 transfer_message_loop_proxy()->PostTask( | |
472 FROM_HERE, | |
473 base::Bind( | |
474 &TransferStateInternal::PerformAsyncTexSubImage2D, | |
475 state, | |
476 tex_params, | |
477 mem_params, | |
478 base::Owned(new ScopedSafeSharedMemory(safe_shared_memory_pool(), | |
479 mem_params.shared_memory, | |
480 mem_params.shm_size)), | |
481 texture_upload_stats_)); | |
482 } | |
483 | |
484 uint32 AsyncPixelTransferDelegateShareGroup::GetTextureUploadCount() { | |
485 DCHECK(texture_upload_stats_); | |
486 return texture_upload_stats_->GetStats(NULL); | |
487 } | |
488 | |
489 base::TimeDelta | |
490 AsyncPixelTransferDelegateShareGroup::GetTotalTextureUploadTime() { | |
491 DCHECK(texture_upload_stats_); | |
492 base::TimeDelta total_texture_upload_time; | |
493 texture_upload_stats_->GetStats(&total_texture_upload_time); | |
494 return total_texture_upload_time; | |
495 } | |
496 | |
497 void AsyncPixelTransferDelegateShareGroup::ProcessMorePendingTransfers() { | |
498 } | |
499 | |
500 bool AsyncPixelTransferDelegateShareGroup::NeedsProcessMorePendingTransfers() { | |
501 return false; | |
502 } | |
503 | |
504 } // namespace gpu | |
OLD | NEW |