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