• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_egl.h"
6 
7 #include <list>
8 #include <string>
9 
10 #include "base/bind.h"
11 #include "base/debug/trace_event.h"
12 #include "base/debug/trace_event_synthetic_delay.h"
13 #include "base/lazy_instance.h"
14 #include "base/logging.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/synchronization/waitable_event.h"
17 #include "base/threading/thread.h"
18 #include "gpu/command_buffer/service/async_pixel_transfer_delegate.h"
19 #include "ui/gl/gl_context.h"
20 #include "ui/gl/gl_surface_egl.h"
21 #include "ui/gl/scoped_binders.h"
22 
23 namespace gpu {
24 
25 namespace {
26 
CheckErrors(const char * file,int line)27 bool CheckErrors(const char* file, int line) {
28   EGLint eglerror;
29   GLenum glerror;
30   bool success = true;
31   while ((eglerror = eglGetError()) != EGL_SUCCESS) {
32      LOG(ERROR) << "Async transfer EGL error at "
33                 << file << ":" << line << " " << eglerror;
34      success = false;
35   }
36   while ((glerror = glGetError()) != GL_NO_ERROR) {
37      LOG(ERROR) << "Async transfer OpenGL error at "
38                 << file << ":" << line << " " << glerror;
39      success = false;
40   }
41   return success;
42 }
43 #define CHECK_GL() CheckErrors(__FILE__, __LINE__)
44 
45 const char kAsyncTransferThreadName[] = "AsyncTransferThread";
46 
47 // Regular glTexImage2D call.
DoTexImage2D(const AsyncTexImage2DParams & tex_params,void * data)48 void DoTexImage2D(const AsyncTexImage2DParams& tex_params, void* data) {
49   glTexImage2D(
50       GL_TEXTURE_2D, tex_params.level, tex_params.internal_format,
51       tex_params.width, tex_params.height,
52       tex_params.border, tex_params.format, tex_params.type, data);
53 }
54 
55 // Regular glTexSubImage2D call.
DoTexSubImage2D(const AsyncTexSubImage2DParams & tex_params,void * data)56 void DoTexSubImage2D(const AsyncTexSubImage2DParams& tex_params, void* data) {
57   glTexSubImage2D(
58       GL_TEXTURE_2D, tex_params.level,
59       tex_params.xoffset, tex_params.yoffset,
60       tex_params.width, tex_params.height,
61       tex_params.format, tex_params.type, data);
62 }
63 
64 // Full glTexSubImage2D call, from glTexImage2D params.
DoFullTexSubImage2D(const AsyncTexImage2DParams & tex_params,void * data)65 void DoFullTexSubImage2D(const AsyncTexImage2DParams& tex_params, void* data) {
66   glTexSubImage2D(
67       GL_TEXTURE_2D, tex_params.level,
68       0, 0, tex_params.width, tex_params.height,
69       tex_params.format, tex_params.type, data);
70 }
71 
SetGlParametersForEglImageTexture()72 void SetGlParametersForEglImageTexture() {
73   // These params are needed for EGLImage creation to succeed on several
74   // Android devices. I couldn't find this requirement in the EGLImage
75   // extension spec, but several devices fail without it.
76   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
77   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
78   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
79 }
80 
PerformNotifyCompletion(AsyncMemoryParams mem_params,scoped_refptr<AsyncPixelTransferCompletionObserver> observer)81 void PerformNotifyCompletion(
82     AsyncMemoryParams mem_params,
83     scoped_refptr<AsyncPixelTransferCompletionObserver> observer) {
84   TRACE_EVENT0("gpu", "PerformNotifyCompletion");
85   observer->DidComplete(mem_params);
86 }
87 
88 class TransferThread : public base::Thread {
89  public:
TransferThread()90   TransferThread() : base::Thread(kAsyncTransferThreadName) {
91     Start();
92 #if defined(OS_ANDROID) || defined(OS_LINUX)
93     SetPriority(base::kThreadPriority_Background);
94 #endif
95   }
~TransferThread()96   virtual ~TransferThread() {
97     Stop();
98   }
99 
Init()100   virtual void Init() OVERRIDE {
101     gfx::GLShareGroup* share_group = NULL;
102     surface_ = new gfx::PbufferGLSurfaceEGL(gfx::Size(1, 1));
103     surface_->Initialize();
104     context_ = gfx::GLContext::CreateGLContext(
105         share_group, surface_.get(), gfx::PreferDiscreteGpu);
106     bool is_current = context_->MakeCurrent(surface_.get());
107     DCHECK(is_current);
108   }
109 
CleanUp()110   virtual void CleanUp() OVERRIDE {
111     surface_ = NULL;
112     context_->ReleaseCurrent(surface_.get());
113     context_ = NULL;
114   }
115 
116  private:
117   scoped_refptr<gfx::GLContext> context_;
118   scoped_refptr<gfx::GLSurface> surface_;
119 
120   DISALLOW_COPY_AND_ASSIGN(TransferThread);
121 };
122 
123 base::LazyInstance<TransferThread>
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 which holds async pixel transfers state (EGLImage).
131 // The EGLImage is accessed by either thread, but everything
132 // else accessed only on the main thread.
133 class TransferStateInternal
134     : public base::RefCountedThreadSafe<TransferStateInternal> {
135  public:
TransferStateInternal(GLuint texture_id,const AsyncTexImage2DParams & define_params,bool wait_for_uploads,bool wait_for_creation,bool use_image_preserved)136   TransferStateInternal(GLuint texture_id,
137                         const AsyncTexImage2DParams& define_params,
138                         bool wait_for_uploads,
139                         bool wait_for_creation,
140                         bool use_image_preserved)
141       : texture_id_(texture_id),
142         thread_texture_id_(0),
143         transfer_completion_(true, true),
144         egl_image_(EGL_NO_IMAGE_KHR),
145         wait_for_uploads_(wait_for_uploads),
146         wait_for_creation_(wait_for_creation),
147         use_image_preserved_(use_image_preserved) {
148     define_params_ = define_params;
149   }
150 
TransferIsInProgress()151   bool TransferIsInProgress() {
152     return !transfer_completion_.IsSignaled();
153   }
154 
BindTransfer()155   void BindTransfer() {
156     TRACE_EVENT2("gpu", "BindAsyncTransfer glEGLImageTargetTexture2DOES",
157                  "width", define_params_.width,
158                  "height", define_params_.height);
159     DCHECK(texture_id_);
160     if (EGL_NO_IMAGE_KHR == egl_image_)
161       return;
162 
163     glBindTexture(GL_TEXTURE_2D, texture_id_);
164     glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_);
165     bind_callback_.Run();
166 
167     DCHECK(CHECK_GL());
168   }
169 
CreateEglImage(GLuint texture_id)170   void CreateEglImage(GLuint texture_id) {
171     TRACE_EVENT0("gpu", "eglCreateImageKHR");
172     DCHECK(texture_id);
173     DCHECK_EQ(egl_image_, EGL_NO_IMAGE_KHR);
174 
175     EGLDisplay egl_display = eglGetCurrentDisplay();
176     EGLContext egl_context = eglGetCurrentContext();
177     EGLenum egl_target = EGL_GL_TEXTURE_2D_KHR;
178     EGLClientBuffer egl_buffer =
179         reinterpret_cast<EGLClientBuffer>(texture_id);
180 
181     EGLint image_preserved = use_image_preserved_ ? EGL_TRUE : EGL_FALSE;
182     EGLint egl_attrib_list[] = {
183         EGL_GL_TEXTURE_LEVEL_KHR, 0, // mip-level.
184         EGL_IMAGE_PRESERVED_KHR, image_preserved,
185         EGL_NONE
186     };
187     egl_image_ = eglCreateImageKHR(
188         egl_display,
189         egl_context,
190         egl_target,
191         egl_buffer,
192         egl_attrib_list);
193 
194     DLOG_IF(ERROR, EGL_NO_IMAGE_KHR == egl_image_)
195         << "eglCreateImageKHR failed";
196   }
197 
CreateEglImageOnUploadThread()198   void CreateEglImageOnUploadThread() {
199     CreateEglImage(thread_texture_id_);
200   }
201 
CreateEglImageOnMainThreadIfNeeded()202   void CreateEglImageOnMainThreadIfNeeded() {
203     if (egl_image_ == EGL_NO_IMAGE_KHR) {
204       CreateEglImage(texture_id_);
205       if (wait_for_creation_) {
206         TRACE_EVENT0("gpu", "glFinish creation");
207         glFinish();
208       }
209     }
210   }
211 
WaitForLastUpload()212   void WaitForLastUpload() {
213     // This glFinish is just a safe-guard for if uploads have some
214     // GPU action that needs to occur. We could use fences and try
215     // to do this less often. However, on older drivers fences are
216     // not always reliable (eg. Mali-400 just blocks forever).
217     if (wait_for_uploads_) {
218       TRACE_EVENT0("gpu", "glFinish");
219       glFinish();
220     }
221   }
222 
MarkAsTransferIsInProgress()223   void MarkAsTransferIsInProgress() {
224     TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("gpu.AsyncTexImage");
225     transfer_completion_.Reset();
226   }
227 
MarkAsCompleted()228   void MarkAsCompleted() {
229     TRACE_EVENT_SYNTHETIC_DELAY_END("gpu.AsyncTexImage");
230     transfer_completion_.Signal();
231   }
232 
WaitForTransferCompletion()233   void WaitForTransferCompletion() {
234     TRACE_EVENT0("gpu", "WaitForTransferCompletion");
235     // TODO(backer): Deschedule the channel rather than blocking the main GPU
236     // thread (crbug.com/240265).
237     transfer_completion_.Wait();
238   }
239 
PerformAsyncTexImage2D(AsyncTexImage2DParams tex_params,AsyncMemoryParams mem_params,scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats)240   void PerformAsyncTexImage2D(
241       AsyncTexImage2DParams tex_params,
242       AsyncMemoryParams mem_params,
243       scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats) {
244     TRACE_EVENT2("gpu",
245                  "PerformAsyncTexImage",
246                  "width",
247                  tex_params.width,
248                  "height",
249                  tex_params.height);
250     DCHECK(!thread_texture_id_);
251     DCHECK_EQ(0, tex_params.level);
252     if (EGL_NO_IMAGE_KHR != egl_image_) {
253       MarkAsCompleted();
254       return;
255     }
256 
257     void* data = mem_params.GetDataAddress();
258 
259     base::TimeTicks begin_time;
260     if (texture_upload_stats.get())
261       begin_time = base::TimeTicks::HighResNow();
262 
263     {
264       TRACE_EVENT0("gpu", "glTexImage2D no data");
265       glGenTextures(1, &thread_texture_id_);
266       glActiveTexture(GL_TEXTURE0);
267       glBindTexture(GL_TEXTURE_2D, thread_texture_id_);
268 
269       SetGlParametersForEglImageTexture();
270 
271       // If we need to use image_preserved, we pass the data with
272       // the allocation. Otherwise we use a NULL allocation to
273       // try to avoid any costs associated with creating the EGLImage.
274       if (use_image_preserved_)
275         DoTexImage2D(tex_params, data);
276       else
277         DoTexImage2D(tex_params, NULL);
278     }
279 
280     CreateEglImageOnUploadThread();
281 
282     {
283       TRACE_EVENT0("gpu", "glTexSubImage2D with data");
284 
285       // If we didn't use image_preserved, we haven't uploaded
286       // the data yet, so we do this with a full texSubImage.
287       if (!use_image_preserved_)
288         DoFullTexSubImage2D(tex_params, data);
289     }
290 
291     WaitForLastUpload();
292     MarkAsCompleted();
293 
294     DCHECK(CHECK_GL());
295     if (texture_upload_stats.get()) {
296       texture_upload_stats->AddUpload(base::TimeTicks::HighResNow() -
297                                       begin_time);
298     }
299   }
300 
PerformAsyncTexSubImage2D(AsyncTexSubImage2DParams tex_params,AsyncMemoryParams mem_params,scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats)301   void PerformAsyncTexSubImage2D(
302       AsyncTexSubImage2DParams tex_params,
303       AsyncMemoryParams mem_params,
304       scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats) {
305     TRACE_EVENT2("gpu",
306                  "PerformAsyncTexSubImage2D",
307                  "width",
308                  tex_params.width,
309                  "height",
310                  tex_params.height);
311 
312     DCHECK_NE(EGL_NO_IMAGE_KHR, egl_image_);
313     DCHECK_EQ(0, tex_params.level);
314 
315     void* data = mem_params.GetDataAddress();
316 
317     base::TimeTicks begin_time;
318     if (texture_upload_stats.get())
319       begin_time = base::TimeTicks::HighResNow();
320 
321     if (!thread_texture_id_) {
322       TRACE_EVENT0("gpu", "glEGLImageTargetTexture2DOES");
323       glGenTextures(1, &thread_texture_id_);
324       glActiveTexture(GL_TEXTURE0);
325       glBindTexture(GL_TEXTURE_2D, thread_texture_id_);
326       glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_);
327     } else {
328       glActiveTexture(GL_TEXTURE0);
329       glBindTexture(GL_TEXTURE_2D, thread_texture_id_);
330     }
331     {
332       TRACE_EVENT0("gpu", "glTexSubImage2D");
333       DoTexSubImage2D(tex_params, data);
334     }
335     WaitForLastUpload();
336     MarkAsCompleted();
337 
338     DCHECK(CHECK_GL());
339     if (texture_upload_stats.get()) {
340       texture_upload_stats->AddUpload(base::TimeTicks::HighResNow() -
341                                       begin_time);
342     }
343   }
344 
345  protected:
346   friend class base::RefCountedThreadSafe<TransferStateInternal>;
347   friend class gpu::AsyncPixelTransferDelegateEGL;
348 
DeleteTexture(GLuint id)349   static void DeleteTexture(GLuint id) {
350     glDeleteTextures(1, &id);
351   }
352 
~TransferStateInternal()353   virtual ~TransferStateInternal() {
354     if (egl_image_ != EGL_NO_IMAGE_KHR) {
355       EGLDisplay display = eglGetCurrentDisplay();
356       eglDestroyImageKHR(display, egl_image_);
357     }
358     if (thread_texture_id_) {
359       transfer_message_loop_proxy()->PostTask(FROM_HERE,
360           base::Bind(&DeleteTexture, thread_texture_id_));
361     }
362   }
363 
364   // The 'real' texture.
365   GLuint texture_id_;
366 
367   // The EGLImage sibling on the upload thread.
368   GLuint thread_texture_id_;
369 
370   // Definition params for texture that needs binding.
371   AsyncTexImage2DParams define_params_;
372 
373   // Indicates that an async transfer is in progress.
374   base::WaitableEvent transfer_completion_;
375 
376   // It would be nice if we could just create a new EGLImage for
377   // every upload, but I found that didn't work, so this stores
378   // one for the lifetime of the texture.
379   EGLImageKHR egl_image_;
380 
381   // Callback to invoke when AsyncTexImage2D is complete
382   // and the client can safely use the texture. This occurs
383   // during BindCompletedAsyncTransfers().
384   base::Closure bind_callback_;
385 
386   // Customize when we block on fences (these are work-arounds).
387   bool wait_for_uploads_;
388   bool wait_for_creation_;
389   bool use_image_preserved_;
390 };
391 
392 }  // namespace
393 
394 // Class which handles async pixel transfers using EGLImageKHR and another
395 // upload thread
396 class AsyncPixelTransferDelegateEGL
397     : public AsyncPixelTransferDelegate,
398       public base::SupportsWeakPtr<AsyncPixelTransferDelegateEGL> {
399  public:
400   AsyncPixelTransferDelegateEGL(
401       AsyncPixelTransferManagerEGL::SharedState* shared_state,
402       GLuint texture_id,
403       const AsyncTexImage2DParams& define_params);
404   virtual ~AsyncPixelTransferDelegateEGL();
405 
BindTransfer()406   void BindTransfer() { state_->BindTransfer(); }
407 
408   // Implement AsyncPixelTransferDelegate:
409   virtual void AsyncTexImage2D(
410       const AsyncTexImage2DParams& tex_params,
411       const AsyncMemoryParams& mem_params,
412       const base::Closure& bind_callback) OVERRIDE;
413   virtual void AsyncTexSubImage2D(
414       const AsyncTexSubImage2DParams& tex_params,
415       const AsyncMemoryParams& mem_params) OVERRIDE;
416   virtual bool TransferIsInProgress() OVERRIDE;
417   virtual void WaitForTransferCompletion() OVERRIDE;
418 
419  private:
420   // Returns true if a work-around was used.
421   bool WorkAroundAsyncTexImage2D(
422       const AsyncTexImage2DParams& tex_params,
423       const AsyncMemoryParams& mem_params,
424       const base::Closure& bind_callback);
425   bool WorkAroundAsyncTexSubImage2D(
426       const AsyncTexSubImage2DParams& tex_params,
427       const AsyncMemoryParams& mem_params);
428 
429   // A raw pointer is safe because the SharedState is owned by the Manager,
430   // which owns this Delegate.
431   AsyncPixelTransferManagerEGL::SharedState* shared_state_;
432   scoped_refptr<TransferStateInternal> state_;
433 
434   DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegateEGL);
435 };
436 
AsyncPixelTransferDelegateEGL(AsyncPixelTransferManagerEGL::SharedState * shared_state,GLuint texture_id,const AsyncTexImage2DParams & define_params)437 AsyncPixelTransferDelegateEGL::AsyncPixelTransferDelegateEGL(
438     AsyncPixelTransferManagerEGL::SharedState* shared_state,
439     GLuint texture_id,
440     const AsyncTexImage2DParams& define_params)
441     : shared_state_(shared_state) {
442   // We can't wait on uploads on imagination (it can take 200ms+).
443   // In practice, they are complete when the CPU glTexSubImage2D completes.
444   bool wait_for_uploads = !shared_state_->is_imagination;
445 
446   // Qualcomm runs into texture corruption problems if the same texture is
447   // uploaded to with both async and normal uploads. Synchronize after EGLImage
448   // creation on the main thread as a work-around.
449   bool wait_for_creation = shared_state_->is_qualcomm;
450 
451   // Qualcomm has a race when using image_preserved=FALSE,
452   // which can result in black textures even after the first upload.
453   // Since using FALSE is mainly for performance (to avoid layout changes),
454   // but Qualcomm itself doesn't seem to get any performance benefit,
455   // we just using image_preservedd=TRUE on Qualcomm as a work-around.
456   bool use_image_preserved =
457       shared_state_->is_qualcomm || shared_state_->is_imagination;
458 
459   state_ = new TransferStateInternal(texture_id,
460                                    define_params,
461                                    wait_for_uploads,
462                                    wait_for_creation,
463                                    use_image_preserved);
464 }
465 
~AsyncPixelTransferDelegateEGL()466 AsyncPixelTransferDelegateEGL::~AsyncPixelTransferDelegateEGL() {}
467 
TransferIsInProgress()468 bool AsyncPixelTransferDelegateEGL::TransferIsInProgress() {
469   return state_->TransferIsInProgress();
470 }
471 
WaitForTransferCompletion()472 void AsyncPixelTransferDelegateEGL::WaitForTransferCompletion() {
473   if (state_->TransferIsInProgress()) {
474 #if defined(OS_ANDROID) || defined(OS_LINUX)
475     g_transfer_thread.Pointer()->SetPriority(base::kThreadPriority_Display);
476 #endif
477 
478     state_->WaitForTransferCompletion();
479     DCHECK(!state_->TransferIsInProgress());
480 
481 #if defined(OS_ANDROID) || defined(OS_LINUX)
482     g_transfer_thread.Pointer()->SetPriority(base::kThreadPriority_Background);
483 #endif
484   }
485 }
486 
AsyncTexImage2D(const AsyncTexImage2DParams & tex_params,const AsyncMemoryParams & mem_params,const base::Closure & bind_callback)487 void AsyncPixelTransferDelegateEGL::AsyncTexImage2D(
488     const AsyncTexImage2DParams& tex_params,
489     const AsyncMemoryParams& mem_params,
490     const base::Closure& bind_callback) {
491   if (WorkAroundAsyncTexImage2D(tex_params, mem_params, bind_callback))
492     return;
493 
494   DCHECK(!state_->TransferIsInProgress());
495   DCHECK_EQ(state_->egl_image_, EGL_NO_IMAGE_KHR);
496   DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target);
497   DCHECK_EQ(tex_params.level, 0);
498 
499   // Mark the transfer in progress and save the late bind
500   // callback, so we can notify the client when it is bound.
501   shared_state_->pending_allocations.push_back(AsWeakPtr());
502   state_->bind_callback_ = bind_callback;
503 
504   // Mark the transfer in progress.
505   state_->MarkAsTransferIsInProgress();
506 
507   // Duplicate the shared memory so there is no way we can get
508   // a use-after-free of the raw pixels.
509   transfer_message_loop_proxy()->PostTask(FROM_HERE,
510       base::Bind(
511           &TransferStateInternal::PerformAsyncTexImage2D,
512           state_,
513           tex_params,
514           mem_params,
515           shared_state_->texture_upload_stats));
516 
517   DCHECK(CHECK_GL());
518 }
519 
AsyncTexSubImage2D(const AsyncTexSubImage2DParams & tex_params,const AsyncMemoryParams & mem_params)520 void AsyncPixelTransferDelegateEGL::AsyncTexSubImage2D(
521     const AsyncTexSubImage2DParams& tex_params,
522     const AsyncMemoryParams& mem_params) {
523   TRACE_EVENT2("gpu", "AsyncTexSubImage2D",
524                "width", tex_params.width,
525                "height", tex_params.height);
526   if (WorkAroundAsyncTexSubImage2D(tex_params, mem_params))
527     return;
528   DCHECK(!state_->TransferIsInProgress());
529   DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target);
530   DCHECK_EQ(tex_params.level, 0);
531 
532   // Mark the transfer in progress.
533   state_->MarkAsTransferIsInProgress();
534 
535   // If this wasn't async allocated, we don't have an EGLImage yet.
536   // Create the EGLImage if it hasn't already been created.
537   state_->CreateEglImageOnMainThreadIfNeeded();
538 
539   // Duplicate the shared memory so there are no way we can get
540   // a use-after-free of the raw pixels.
541   transfer_message_loop_proxy()->PostTask(FROM_HERE,
542       base::Bind(
543           &TransferStateInternal::PerformAsyncTexSubImage2D,
544           state_,
545           tex_params,
546           mem_params,
547           shared_state_->texture_upload_stats));
548 
549   DCHECK(CHECK_GL());
550 }
551 
552 namespace {
IsPowerOfTwo(unsigned int x)553 bool IsPowerOfTwo (unsigned int x) {
554   return ((x != 0) && !(x & (x - 1)));
555 }
556 
IsMultipleOfEight(unsigned int x)557 bool IsMultipleOfEight(unsigned int x) {
558   return (x & 7) == 0;
559 }
560 
DimensionsSupportImgFastPath(int width,int height)561 bool DimensionsSupportImgFastPath(int width, int height) {
562   // Multiple of eight, but not a power of two.
563   return IsMultipleOfEight(width) &&
564          IsMultipleOfEight(height) &&
565          !(IsPowerOfTwo(width) &&
566            IsPowerOfTwo(height));
567 }
568 }  // namespace
569 
570 // It is very difficult to stream uploads on Imagination GPUs:
571 // - glTexImage2D defers a swizzle/stall until draw-time
572 // - glTexSubImage2D will sleep for 16ms on a good day, and 100ms
573 //   or longer if OpenGL is in heavy use by another thread.
574 // The one combination that avoids these problems requires:
575 // a.) Allocations/Uploads must occur on different threads/contexts.
576 // b.) Texture size must be non-power-of-two.
577 // When using a+b, uploads will be incorrect/corrupt unless:
578 // c.) Texture size must be a multiple-of-eight.
579 //
580 // To achieve a.) we allocate synchronously on the main thread followed
581 // by uploading on the upload thread. When b/c are not true we fall back
582 // on purely synchronous allocation/upload on the main thread.
583 
WorkAroundAsyncTexImage2D(const AsyncTexImage2DParams & tex_params,const AsyncMemoryParams & mem_params,const base::Closure & bind_callback)584 bool AsyncPixelTransferDelegateEGL::WorkAroundAsyncTexImage2D(
585     const AsyncTexImage2DParams& tex_params,
586     const AsyncMemoryParams& mem_params,
587     const base::Closure& bind_callback) {
588   if (!shared_state_->is_imagination)
589     return false;
590 
591   // On imagination we allocate synchronously all the time, even
592   // if the dimensions support fast uploads. This is for part a.)
593   // above, so allocations occur on a different thread/context as uploads.
594   void* data = mem_params.GetDataAddress();
595   SetGlParametersForEglImageTexture();
596 
597   {
598     TRACE_EVENT0("gpu", "glTexImage2D with data");
599     DoTexImage2D(tex_params, data);
600   }
601 
602   // The allocation has already occured, so mark it as finished
603   // and ready for binding.
604   CHECK(!state_->TransferIsInProgress());
605 
606   // If the dimensions support fast async uploads, create the
607   // EGLImage for future uploads. The late bind should not
608   // be needed since the EGLImage was created from the main thread
609   // texture, but this is required to prevent an imagination driver crash.
610   if (DimensionsSupportImgFastPath(tex_params.width, tex_params.height)) {
611     state_->CreateEglImageOnMainThreadIfNeeded();
612     shared_state_->pending_allocations.push_back(AsWeakPtr());
613     state_->bind_callback_ = bind_callback;
614   }
615 
616   DCHECK(CHECK_GL());
617   return true;
618 }
619 
WorkAroundAsyncTexSubImage2D(const AsyncTexSubImage2DParams & tex_params,const AsyncMemoryParams & mem_params)620 bool AsyncPixelTransferDelegateEGL::WorkAroundAsyncTexSubImage2D(
621     const AsyncTexSubImage2DParams& tex_params,
622     const AsyncMemoryParams& mem_params) {
623   if (!shared_state_->is_imagination)
624     return false;
625 
626   // If the dimensions support fast async uploads, we can use the
627   // normal async upload path for uploads.
628   if (DimensionsSupportImgFastPath(tex_params.width, tex_params.height))
629     return false;
630 
631   // Fall back on a synchronous stub as we don't have a known fast path.
632   // Also, older ICS drivers crash when we do any glTexSubImage2D on the
633   // same thread. To work around this we do glTexImage2D instead. Since
634   // we didn't create an EGLImage for this texture (see above), this is
635   // okay, but it limits this API to full updates for now.
636   DCHECK(!state_->egl_image_);
637   DCHECK_EQ(tex_params.xoffset, 0);
638   DCHECK_EQ(tex_params.yoffset, 0);
639   DCHECK_EQ(state_->define_params_.width, tex_params.width);
640   DCHECK_EQ(state_->define_params_.height, tex_params.height);
641   DCHECK_EQ(state_->define_params_.level, tex_params.level);
642   DCHECK_EQ(state_->define_params_.format, tex_params.format);
643   DCHECK_EQ(state_->define_params_.type, tex_params.type);
644 
645   void* data = mem_params.GetDataAddress();
646   base::TimeTicks begin_time;
647   if (shared_state_->texture_upload_stats.get())
648     begin_time = base::TimeTicks::HighResNow();
649   {
650     TRACE_EVENT0("gpu", "glTexSubImage2D");
651     // Note we use define_params_ instead of tex_params.
652     // The DCHECKs above verify this is always the same.
653     DoTexImage2D(state_->define_params_, data);
654   }
655   if (shared_state_->texture_upload_stats.get()) {
656     shared_state_->texture_upload_stats
657         ->AddUpload(base::TimeTicks::HighResNow() - begin_time);
658   }
659 
660   DCHECK(CHECK_GL());
661   return true;
662 }
663 
SharedState()664 AsyncPixelTransferManagerEGL::SharedState::SharedState()
665     // TODO(reveman): Skip this if --enable-gpu-benchmarking is not present.
666     : texture_upload_stats(new AsyncPixelTransferUploadStats) {
667   const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
668   if (vendor) {
669     is_imagination =
670         std::string(vendor).find("Imagination") != std::string::npos;
671     is_qualcomm = std::string(vendor).find("Qualcomm") != std::string::npos;
672   }
673 }
674 
~SharedState()675 AsyncPixelTransferManagerEGL::SharedState::~SharedState() {}
676 
AsyncPixelTransferManagerEGL()677 AsyncPixelTransferManagerEGL::AsyncPixelTransferManagerEGL() {}
678 
~AsyncPixelTransferManagerEGL()679 AsyncPixelTransferManagerEGL::~AsyncPixelTransferManagerEGL() {}
680 
BindCompletedAsyncTransfers()681 void AsyncPixelTransferManagerEGL::BindCompletedAsyncTransfers() {
682   scoped_ptr<gfx::ScopedTextureBinder> texture_binder;
683 
684   while(!shared_state_.pending_allocations.empty()) {
685     if (!shared_state_.pending_allocations.front().get()) {
686       shared_state_.pending_allocations.pop_front();
687       continue;
688     }
689     AsyncPixelTransferDelegateEGL* delegate =
690         shared_state_.pending_allocations.front().get();
691     // Terminate early, as all transfers finish in order, currently.
692     if (delegate->TransferIsInProgress())
693       break;
694 
695     if (!texture_binder)
696       texture_binder.reset(new gfx::ScopedTextureBinder(GL_TEXTURE_2D, 0));
697 
698     // If the transfer is finished, bind it to the texture
699     // and remove it from pending list.
700     delegate->BindTransfer();
701     shared_state_.pending_allocations.pop_front();
702   }
703 }
704 
AsyncNotifyCompletion(const AsyncMemoryParams & mem_params,AsyncPixelTransferCompletionObserver * observer)705 void AsyncPixelTransferManagerEGL::AsyncNotifyCompletion(
706     const AsyncMemoryParams& mem_params,
707     AsyncPixelTransferCompletionObserver* observer) {
708   // Post a PerformNotifyCompletion task to the upload thread. This task
709   // will run after all async transfers are complete.
710   transfer_message_loop_proxy()->PostTask(
711       FROM_HERE,
712       base::Bind(&PerformNotifyCompletion,
713                  mem_params,
714                  make_scoped_refptr(observer)));
715 }
716 
GetTextureUploadCount()717 uint32 AsyncPixelTransferManagerEGL::GetTextureUploadCount() {
718   return shared_state_.texture_upload_stats->GetStats(NULL);
719 }
720 
GetTotalTextureUploadTime()721 base::TimeDelta AsyncPixelTransferManagerEGL::GetTotalTextureUploadTime() {
722   base::TimeDelta total_texture_upload_time;
723   shared_state_.texture_upload_stats->GetStats(&total_texture_upload_time);
724   return total_texture_upload_time;
725 }
726 
ProcessMorePendingTransfers()727 void AsyncPixelTransferManagerEGL::ProcessMorePendingTransfers() {
728 }
729 
NeedsProcessMorePendingTransfers()730 bool AsyncPixelTransferManagerEGL::NeedsProcessMorePendingTransfers() {
731   return false;
732 }
733 
WaitAllAsyncTexImage2D()734 void AsyncPixelTransferManagerEGL::WaitAllAsyncTexImage2D() {
735   if (shared_state_.pending_allocations.empty())
736     return;
737 
738   AsyncPixelTransferDelegateEGL* delegate =
739       shared_state_.pending_allocations.back().get();
740   if (delegate)
741     delegate->WaitForTransferCompletion();
742 }
743 
744 AsyncPixelTransferDelegate*
CreatePixelTransferDelegateImpl(gles2::TextureRef * ref,const AsyncTexImage2DParams & define_params)745 AsyncPixelTransferManagerEGL::CreatePixelTransferDelegateImpl(
746     gles2::TextureRef* ref,
747     const AsyncTexImage2DParams& define_params) {
748   return new AsyncPixelTransferDelegateEGL(
749       &shared_state_, ref->service_id(), define_params);
750 }
751 
752 }  // namespace gpu
753