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