// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "gpu/command_buffer/service/async_pixel_transfer_manager_egl.h"

#include <list>
#include <string>

#include "base/bind.h"
#include "base/debug/trace_event.h"
#include "base/debug/trace_event_synthetic_delay.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "gpu/command_buffer/service/async_pixel_transfer_delegate.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface_egl.h"
#include "ui/gl/scoped_binders.h"

namespace gpu {

namespace {

bool CheckErrors(const char* file, int line) {
  EGLint eglerror;
  GLenum glerror;
  bool success = true;
  while ((eglerror = eglGetError()) != EGL_SUCCESS) {
     LOG(ERROR) << "Async transfer EGL error at "
                << file << ":" << line << " " << eglerror;
     success = false;
  }
  while ((glerror = glGetError()) != GL_NO_ERROR) {
     LOG(ERROR) << "Async transfer OpenGL error at "
                << file << ":" << line << " " << glerror;
     success = false;
  }
  return success;
}
#define CHECK_GL() CheckErrors(__FILE__, __LINE__)

const char kAsyncTransferThreadName[] = "AsyncTransferThread";

// Regular glTexImage2D call.
void DoTexImage2D(const AsyncTexImage2DParams& tex_params, void* data) {
  glTexImage2D(
      GL_TEXTURE_2D, tex_params.level, tex_params.internal_format,
      tex_params.width, tex_params.height,
      tex_params.border, tex_params.format, tex_params.type, data);
}

// Regular glTexSubImage2D call.
void DoTexSubImage2D(const AsyncTexSubImage2DParams& tex_params, void* data) {
  glTexSubImage2D(
      GL_TEXTURE_2D, tex_params.level,
      tex_params.xoffset, tex_params.yoffset,
      tex_params.width, tex_params.height,
      tex_params.format, tex_params.type, data);
}

// Full glTexSubImage2D call, from glTexImage2D params.
void DoFullTexSubImage2D(const AsyncTexImage2DParams& tex_params, void* data) {
  glTexSubImage2D(
      GL_TEXTURE_2D, tex_params.level,
      0, 0, tex_params.width, tex_params.height,
      tex_params.format, tex_params.type, data);
}

void SetGlParametersForEglImageTexture() {
  // These params are needed for EGLImage creation to succeed on several
  // Android devices. I couldn't find this requirement in the EGLImage
  // extension spec, but several devices fail without it.
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}

void PerformNotifyCompletion(
    AsyncMemoryParams mem_params,
    scoped_refptr<AsyncPixelTransferCompletionObserver> observer) {
  TRACE_EVENT0("gpu", "PerformNotifyCompletion");
  observer->DidComplete(mem_params);
}

class TransferThread : public base::Thread {
 public:
  TransferThread() : base::Thread(kAsyncTransferThreadName) {
    Start();
#if defined(OS_ANDROID) || defined(OS_LINUX)
    SetPriority(base::kThreadPriority_Background);
#endif
  }
  virtual ~TransferThread() {
    Stop();
  }

  virtual void Init() OVERRIDE {
    gfx::GLShareGroup* share_group = NULL;
    surface_ = new gfx::PbufferGLSurfaceEGL(gfx::Size(1, 1));
    surface_->Initialize();
    context_ = gfx::GLContext::CreateGLContext(
        share_group, surface_.get(), gfx::PreferDiscreteGpu);
    bool is_current = context_->MakeCurrent(surface_.get());
    DCHECK(is_current);
  }

  virtual void CleanUp() OVERRIDE {
    surface_ = NULL;
    context_->ReleaseCurrent(surface_.get());
    context_ = NULL;
  }

 private:
  scoped_refptr<gfx::GLContext> context_;
  scoped_refptr<gfx::GLSurface> surface_;

  DISALLOW_COPY_AND_ASSIGN(TransferThread);
};

base::LazyInstance<TransferThread>
    g_transfer_thread = LAZY_INSTANCE_INITIALIZER;

base::MessageLoopProxy* transfer_message_loop_proxy() {
  return g_transfer_thread.Pointer()->message_loop_proxy().get();
}

// Class which holds async pixel transfers state (EGLImage).
// The EGLImage is accessed by either thread, but everything
// else accessed only on the main thread.
class TransferStateInternal
    : public base::RefCountedThreadSafe<TransferStateInternal> {
 public:
  TransferStateInternal(GLuint texture_id,
                        const AsyncTexImage2DParams& define_params,
                        bool wait_for_uploads,
                        bool wait_for_creation,
                        bool use_image_preserved)
      : texture_id_(texture_id),
        thread_texture_id_(0),
        transfer_completion_(true, true),
        egl_image_(EGL_NO_IMAGE_KHR),
        wait_for_uploads_(wait_for_uploads),
        wait_for_creation_(wait_for_creation),
        use_image_preserved_(use_image_preserved) {
    define_params_ = define_params;
  }

  bool TransferIsInProgress() {
    return !transfer_completion_.IsSignaled();
  }

  void BindTransfer() {
    TRACE_EVENT2("gpu", "BindAsyncTransfer glEGLImageTargetTexture2DOES",
                 "width", define_params_.width,
                 "height", define_params_.height);
    DCHECK(texture_id_);
    if (EGL_NO_IMAGE_KHR == egl_image_)
      return;

    glBindTexture(GL_TEXTURE_2D, texture_id_);
    glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_);
    bind_callback_.Run();

    DCHECK(CHECK_GL());
  }

  void CreateEglImage(GLuint texture_id) {
    TRACE_EVENT0("gpu", "eglCreateImageKHR");
    DCHECK(texture_id);
    DCHECK_EQ(egl_image_, EGL_NO_IMAGE_KHR);

    EGLDisplay egl_display = eglGetCurrentDisplay();
    EGLContext egl_context = eglGetCurrentContext();
    EGLenum egl_target = EGL_GL_TEXTURE_2D_KHR;
    EGLClientBuffer egl_buffer =
        reinterpret_cast<EGLClientBuffer>(texture_id);

    EGLint image_preserved = use_image_preserved_ ? EGL_TRUE : EGL_FALSE;
    EGLint egl_attrib_list[] = {
        EGL_GL_TEXTURE_LEVEL_KHR, 0, // mip-level.
        EGL_IMAGE_PRESERVED_KHR, image_preserved,
        EGL_NONE
    };
    egl_image_ = eglCreateImageKHR(
        egl_display,
        egl_context,
        egl_target,
        egl_buffer,
        egl_attrib_list);

    DLOG_IF(ERROR, EGL_NO_IMAGE_KHR == egl_image_)
        << "eglCreateImageKHR failed";
  }

  void CreateEglImageOnUploadThread() {
    CreateEglImage(thread_texture_id_);
  }

  void CreateEglImageOnMainThreadIfNeeded() {
    if (egl_image_ == EGL_NO_IMAGE_KHR) {
      CreateEglImage(texture_id_);
      if (wait_for_creation_) {
        TRACE_EVENT0("gpu", "glFinish creation");
        glFinish();
      }
    }
  }

  void WaitForLastUpload() {
    // This glFinish is just a safe-guard for if uploads have some
    // GPU action that needs to occur. We could use fences and try
    // to do this less often. However, on older drivers fences are
    // not always reliable (eg. Mali-400 just blocks forever).
    if (wait_for_uploads_) {
      TRACE_EVENT0("gpu", "glFinish");
      glFinish();
    }
  }

  void MarkAsTransferIsInProgress() {
    TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("gpu.AsyncTexImage");
    transfer_completion_.Reset();
  }

  void MarkAsCompleted() {
    TRACE_EVENT_SYNTHETIC_DELAY_END("gpu.AsyncTexImage");
    transfer_completion_.Signal();
  }

  void WaitForTransferCompletion() {
    TRACE_EVENT0("gpu", "WaitForTransferCompletion");
    // TODO(backer): Deschedule the channel rather than blocking the main GPU
    // thread (crbug.com/240265).
    transfer_completion_.Wait();
  }

  void PerformAsyncTexImage2D(
      AsyncTexImage2DParams tex_params,
      AsyncMemoryParams mem_params,
      scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats) {
    TRACE_EVENT2("gpu",
                 "PerformAsyncTexImage",
                 "width",
                 tex_params.width,
                 "height",
                 tex_params.height);
    DCHECK(!thread_texture_id_);
    DCHECK_EQ(0, tex_params.level);
    if (EGL_NO_IMAGE_KHR != egl_image_) {
      MarkAsCompleted();
      return;
    }

    void* data = mem_params.GetDataAddress();

    base::TimeTicks begin_time;
    if (texture_upload_stats.get())
      begin_time = base::TimeTicks::HighResNow();

    {
      TRACE_EVENT0("gpu", "glTexImage2D no data");
      glGenTextures(1, &thread_texture_id_);
      glActiveTexture(GL_TEXTURE0);
      glBindTexture(GL_TEXTURE_2D, thread_texture_id_);

      SetGlParametersForEglImageTexture();

      // If we need to use image_preserved, we pass the data with
      // the allocation. Otherwise we use a NULL allocation to
      // try to avoid any costs associated with creating the EGLImage.
      if (use_image_preserved_)
        DoTexImage2D(tex_params, data);
      else
        DoTexImage2D(tex_params, NULL);
    }

    CreateEglImageOnUploadThread();

    {
      TRACE_EVENT0("gpu", "glTexSubImage2D with data");

      // If we didn't use image_preserved, we haven't uploaded
      // the data yet, so we do this with a full texSubImage.
      if (!use_image_preserved_)
        DoFullTexSubImage2D(tex_params, data);
    }

    WaitForLastUpload();
    MarkAsCompleted();

    DCHECK(CHECK_GL());
    if (texture_upload_stats.get()) {
      texture_upload_stats->AddUpload(base::TimeTicks::HighResNow() -
                                      begin_time);
    }
  }

  void PerformAsyncTexSubImage2D(
      AsyncTexSubImage2DParams tex_params,
      AsyncMemoryParams mem_params,
      scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats) {
    TRACE_EVENT2("gpu",
                 "PerformAsyncTexSubImage2D",
                 "width",
                 tex_params.width,
                 "height",
                 tex_params.height);

    DCHECK_NE(EGL_NO_IMAGE_KHR, egl_image_);
    DCHECK_EQ(0, tex_params.level);

    void* data = mem_params.GetDataAddress();

    base::TimeTicks begin_time;
    if (texture_upload_stats.get())
      begin_time = base::TimeTicks::HighResNow();

    if (!thread_texture_id_) {
      TRACE_EVENT0("gpu", "glEGLImageTargetTexture2DOES");
      glGenTextures(1, &thread_texture_id_);
      glActiveTexture(GL_TEXTURE0);
      glBindTexture(GL_TEXTURE_2D, thread_texture_id_);
      glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_);
    } else {
      glActiveTexture(GL_TEXTURE0);
      glBindTexture(GL_TEXTURE_2D, thread_texture_id_);
    }
    {
      TRACE_EVENT0("gpu", "glTexSubImage2D");
      DoTexSubImage2D(tex_params, data);
    }
    WaitForLastUpload();
    MarkAsCompleted();

    DCHECK(CHECK_GL());
    if (texture_upload_stats.get()) {
      texture_upload_stats->AddUpload(base::TimeTicks::HighResNow() -
                                      begin_time);
    }
  }

 protected:
  friend class base::RefCountedThreadSafe<TransferStateInternal>;
  friend class gpu::AsyncPixelTransferDelegateEGL;

  static void DeleteTexture(GLuint id) {
    glDeleteTextures(1, &id);
  }

  virtual ~TransferStateInternal() {
    if (egl_image_ != EGL_NO_IMAGE_KHR) {
      EGLDisplay display = eglGetCurrentDisplay();
      eglDestroyImageKHR(display, egl_image_);
    }
    if (thread_texture_id_) {
      transfer_message_loop_proxy()->PostTask(FROM_HERE,
          base::Bind(&DeleteTexture, thread_texture_id_));
    }
  }

  // The 'real' texture.
  GLuint texture_id_;

  // The EGLImage sibling on the upload thread.
  GLuint thread_texture_id_;

  // Definition params for texture that needs binding.
  AsyncTexImage2DParams define_params_;

  // Indicates that an async transfer is in progress.
  base::WaitableEvent transfer_completion_;

  // It would be nice if we could just create a new EGLImage for
  // every upload, but I found that didn't work, so this stores
  // one for the lifetime of the texture.
  EGLImageKHR egl_image_;

  // Callback to invoke when AsyncTexImage2D is complete
  // and the client can safely use the texture. This occurs
  // during BindCompletedAsyncTransfers().
  base::Closure bind_callback_;

  // Customize when we block on fences (these are work-arounds).
  bool wait_for_uploads_;
  bool wait_for_creation_;
  bool use_image_preserved_;
};

}  // namespace

// Class which handles async pixel transfers using EGLImageKHR and another
// upload thread
class AsyncPixelTransferDelegateEGL
    : public AsyncPixelTransferDelegate,
      public base::SupportsWeakPtr<AsyncPixelTransferDelegateEGL> {
 public:
  AsyncPixelTransferDelegateEGL(
      AsyncPixelTransferManagerEGL::SharedState* shared_state,
      GLuint texture_id,
      const AsyncTexImage2DParams& define_params);
  virtual ~AsyncPixelTransferDelegateEGL();

  void BindTransfer() { state_->BindTransfer(); }

  // Implement AsyncPixelTransferDelegate:
  virtual void AsyncTexImage2D(
      const AsyncTexImage2DParams& tex_params,
      const AsyncMemoryParams& mem_params,
      const base::Closure& bind_callback) OVERRIDE;
  virtual void AsyncTexSubImage2D(
      const AsyncTexSubImage2DParams& tex_params,
      const AsyncMemoryParams& mem_params) OVERRIDE;
  virtual bool TransferIsInProgress() OVERRIDE;
  virtual void WaitForTransferCompletion() OVERRIDE;

 private:
  // Returns true if a work-around was used.
  bool WorkAroundAsyncTexImage2D(
      const AsyncTexImage2DParams& tex_params,
      const AsyncMemoryParams& mem_params,
      const base::Closure& bind_callback);
  bool WorkAroundAsyncTexSubImage2D(
      const AsyncTexSubImage2DParams& tex_params,
      const AsyncMemoryParams& mem_params);

  // A raw pointer is safe because the SharedState is owned by the Manager,
  // which owns this Delegate.
  AsyncPixelTransferManagerEGL::SharedState* shared_state_;
  scoped_refptr<TransferStateInternal> state_;

  DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegateEGL);
};

AsyncPixelTransferDelegateEGL::AsyncPixelTransferDelegateEGL(
    AsyncPixelTransferManagerEGL::SharedState* shared_state,
    GLuint texture_id,
    const AsyncTexImage2DParams& define_params)
    : shared_state_(shared_state) {
  // We can't wait on uploads on imagination (it can take 200ms+).
  // In practice, they are complete when the CPU glTexSubImage2D completes.
  bool wait_for_uploads = !shared_state_->is_imagination;

  // Qualcomm runs into texture corruption problems if the same texture is
  // uploaded to with both async and normal uploads. Synchronize after EGLImage
  // creation on the main thread as a work-around.
  bool wait_for_creation = shared_state_->is_qualcomm;

  // Qualcomm has a race when using image_preserved=FALSE,
  // which can result in black textures even after the first upload.
  // Since using FALSE is mainly for performance (to avoid layout changes),
  // but Qualcomm itself doesn't seem to get any performance benefit,
  // we just using image_preservedd=TRUE on Qualcomm as a work-around.
  bool use_image_preserved =
      shared_state_->is_qualcomm || shared_state_->is_imagination;

  state_ = new TransferStateInternal(texture_id,
                                   define_params,
                                   wait_for_uploads,
                                   wait_for_creation,
                                   use_image_preserved);
}

AsyncPixelTransferDelegateEGL::~AsyncPixelTransferDelegateEGL() {}

bool AsyncPixelTransferDelegateEGL::TransferIsInProgress() {
  return state_->TransferIsInProgress();
}

void AsyncPixelTransferDelegateEGL::WaitForTransferCompletion() {
  if (state_->TransferIsInProgress()) {
#if defined(OS_ANDROID) || defined(OS_LINUX)
    g_transfer_thread.Pointer()->SetPriority(base::kThreadPriority_Display);
#endif

    state_->WaitForTransferCompletion();
    DCHECK(!state_->TransferIsInProgress());

#if defined(OS_ANDROID) || defined(OS_LINUX)
    g_transfer_thread.Pointer()->SetPriority(base::kThreadPriority_Background);
#endif
  }
}

void AsyncPixelTransferDelegateEGL::AsyncTexImage2D(
    const AsyncTexImage2DParams& tex_params,
    const AsyncMemoryParams& mem_params,
    const base::Closure& bind_callback) {
  if (WorkAroundAsyncTexImage2D(tex_params, mem_params, bind_callback))
    return;

  DCHECK(!state_->TransferIsInProgress());
  DCHECK_EQ(state_->egl_image_, EGL_NO_IMAGE_KHR);
  DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target);
  DCHECK_EQ(tex_params.level, 0);

  // Mark the transfer in progress and save the late bind
  // callback, so we can notify the client when it is bound.
  shared_state_->pending_allocations.push_back(AsWeakPtr());
  state_->bind_callback_ = bind_callback;

  // Mark the transfer in progress.
  state_->MarkAsTransferIsInProgress();

  // Duplicate the shared memory so there is no way we can get
  // a use-after-free of the raw pixels.
  transfer_message_loop_proxy()->PostTask(FROM_HERE,
      base::Bind(
          &TransferStateInternal::PerformAsyncTexImage2D,
          state_,
          tex_params,
          mem_params,
          shared_state_->texture_upload_stats));

  DCHECK(CHECK_GL());
}

void AsyncPixelTransferDelegateEGL::AsyncTexSubImage2D(
    const AsyncTexSubImage2DParams& tex_params,
    const AsyncMemoryParams& mem_params) {
  TRACE_EVENT2("gpu", "AsyncTexSubImage2D",
               "width", tex_params.width,
               "height", tex_params.height);
  if (WorkAroundAsyncTexSubImage2D(tex_params, mem_params))
    return;
  DCHECK(!state_->TransferIsInProgress());
  DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target);
  DCHECK_EQ(tex_params.level, 0);

  // Mark the transfer in progress.
  state_->MarkAsTransferIsInProgress();

  // If this wasn't async allocated, we don't have an EGLImage yet.
  // Create the EGLImage if it hasn't already been created.
  state_->CreateEglImageOnMainThreadIfNeeded();

  // Duplicate the shared memory so there are no way we can get
  // a use-after-free of the raw pixels.
  transfer_message_loop_proxy()->PostTask(FROM_HERE,
      base::Bind(
          &TransferStateInternal::PerformAsyncTexSubImage2D,
          state_,
          tex_params,
          mem_params,
          shared_state_->texture_upload_stats));

  DCHECK(CHECK_GL());
}

namespace {
bool IsPowerOfTwo (unsigned int x) {
  return ((x != 0) && !(x & (x - 1)));
}

bool IsMultipleOfEight(unsigned int x) {
  return (x & 7) == 0;
}

bool DimensionsSupportImgFastPath(int width, int height) {
  // Multiple of eight, but not a power of two.
  return IsMultipleOfEight(width) &&
         IsMultipleOfEight(height) &&
         !(IsPowerOfTwo(width) &&
           IsPowerOfTwo(height));
}
}  // namespace

// It is very difficult to stream uploads on Imagination GPUs:
// - glTexImage2D defers a swizzle/stall until draw-time
// - glTexSubImage2D will sleep for 16ms on a good day, and 100ms
//   or longer if OpenGL is in heavy use by another thread.
// The one combination that avoids these problems requires:
// a.) Allocations/Uploads must occur on different threads/contexts.
// b.) Texture size must be non-power-of-two.
// When using a+b, uploads will be incorrect/corrupt unless:
// c.) Texture size must be a multiple-of-eight.
//
// To achieve a.) we allocate synchronously on the main thread followed
// by uploading on the upload thread. When b/c are not true we fall back
// on purely synchronous allocation/upload on the main thread.

bool AsyncPixelTransferDelegateEGL::WorkAroundAsyncTexImage2D(
    const AsyncTexImage2DParams& tex_params,
    const AsyncMemoryParams& mem_params,
    const base::Closure& bind_callback) {
  if (!shared_state_->is_imagination)
    return false;

  // On imagination we allocate synchronously all the time, even
  // if the dimensions support fast uploads. This is for part a.)
  // above, so allocations occur on a different thread/context as uploads.
  void* data = mem_params.GetDataAddress();
  SetGlParametersForEglImageTexture();

  {
    TRACE_EVENT0("gpu", "glTexImage2D with data");
    DoTexImage2D(tex_params, data);
  }

  // The allocation has already occured, so mark it as finished
  // and ready for binding.
  CHECK(!state_->TransferIsInProgress());

  // If the dimensions support fast async uploads, create the
  // EGLImage for future uploads. The late bind should not
  // be needed since the EGLImage was created from the main thread
  // texture, but this is required to prevent an imagination driver crash.
  if (DimensionsSupportImgFastPath(tex_params.width, tex_params.height)) {
    state_->CreateEglImageOnMainThreadIfNeeded();
    shared_state_->pending_allocations.push_back(AsWeakPtr());
    state_->bind_callback_ = bind_callback;
  }

  DCHECK(CHECK_GL());
  return true;
}

bool AsyncPixelTransferDelegateEGL::WorkAroundAsyncTexSubImage2D(
    const AsyncTexSubImage2DParams& tex_params,
    const AsyncMemoryParams& mem_params) {
  if (!shared_state_->is_imagination)
    return false;

  // If the dimensions support fast async uploads, we can use the
  // normal async upload path for uploads.
  if (DimensionsSupportImgFastPath(tex_params.width, tex_params.height))
    return false;

  // Fall back on a synchronous stub as we don't have a known fast path.
  // Also, older ICS drivers crash when we do any glTexSubImage2D on the
  // same thread. To work around this we do glTexImage2D instead. Since
  // we didn't create an EGLImage for this texture (see above), this is
  // okay, but it limits this API to full updates for now.
  DCHECK(!state_->egl_image_);
  DCHECK_EQ(tex_params.xoffset, 0);
  DCHECK_EQ(tex_params.yoffset, 0);
  DCHECK_EQ(state_->define_params_.width, tex_params.width);
  DCHECK_EQ(state_->define_params_.height, tex_params.height);
  DCHECK_EQ(state_->define_params_.level, tex_params.level);
  DCHECK_EQ(state_->define_params_.format, tex_params.format);
  DCHECK_EQ(state_->define_params_.type, tex_params.type);

  void* data = mem_params.GetDataAddress();
  base::TimeTicks begin_time;
  if (shared_state_->texture_upload_stats.get())
    begin_time = base::TimeTicks::HighResNow();
  {
    TRACE_EVENT0("gpu", "glTexSubImage2D");
    // Note we use define_params_ instead of tex_params.
    // The DCHECKs above verify this is always the same.
    DoTexImage2D(state_->define_params_, data);
  }
  if (shared_state_->texture_upload_stats.get()) {
    shared_state_->texture_upload_stats
        ->AddUpload(base::TimeTicks::HighResNow() - begin_time);
  }

  DCHECK(CHECK_GL());
  return true;
}

AsyncPixelTransferManagerEGL::SharedState::SharedState()
    // TODO(reveman): Skip this if --enable-gpu-benchmarking is not present.
    : texture_upload_stats(new AsyncPixelTransferUploadStats) {
  std::string vendor;
  vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
  is_imagination = vendor.find("Imagination") != std::string::npos;
  is_qualcomm = vendor.find("Qualcomm") != std::string::npos;
}

AsyncPixelTransferManagerEGL::SharedState::~SharedState() {}

AsyncPixelTransferManagerEGL::AsyncPixelTransferManagerEGL() {}

AsyncPixelTransferManagerEGL::~AsyncPixelTransferManagerEGL() {}

void AsyncPixelTransferManagerEGL::BindCompletedAsyncTransfers() {
  scoped_ptr<gfx::ScopedTextureBinder> texture_binder;

  while(!shared_state_.pending_allocations.empty()) {
    if (!shared_state_.pending_allocations.front().get()) {
      shared_state_.pending_allocations.pop_front();
      continue;
    }
    AsyncPixelTransferDelegateEGL* delegate =
        shared_state_.pending_allocations.front().get();
    // Terminate early, as all transfers finish in order, currently.
    if (delegate->TransferIsInProgress())
      break;

    if (!texture_binder)
      texture_binder.reset(new gfx::ScopedTextureBinder(GL_TEXTURE_2D, 0));

    // If the transfer is finished, bind it to the texture
    // and remove it from pending list.
    delegate->BindTransfer();
    shared_state_.pending_allocations.pop_front();
  }
}

void AsyncPixelTransferManagerEGL::AsyncNotifyCompletion(
    const AsyncMemoryParams& mem_params,
    AsyncPixelTransferCompletionObserver* observer) {
  // Post a PerformNotifyCompletion task to the upload thread. This task
  // will run after all async transfers are complete.
  transfer_message_loop_proxy()->PostTask(
      FROM_HERE,
      base::Bind(&PerformNotifyCompletion,
                 mem_params,
                 make_scoped_refptr(observer)));
}

uint32 AsyncPixelTransferManagerEGL::GetTextureUploadCount() {
  return shared_state_.texture_upload_stats->GetStats(NULL);
}

base::TimeDelta AsyncPixelTransferManagerEGL::GetTotalTextureUploadTime() {
  base::TimeDelta total_texture_upload_time;
  shared_state_.texture_upload_stats->GetStats(&total_texture_upload_time);
  return total_texture_upload_time;
}

void AsyncPixelTransferManagerEGL::ProcessMorePendingTransfers() {
}

bool AsyncPixelTransferManagerEGL::NeedsProcessMorePendingTransfers() {
  return false;
}

void AsyncPixelTransferManagerEGL::WaitAllAsyncTexImage2D() {
  if (shared_state_.pending_allocations.empty())
    return;

  AsyncPixelTransferDelegateEGL* delegate =
      shared_state_.pending_allocations.back().get();
  if (delegate)
    delegate->WaitForTransferCompletion();
}

AsyncPixelTransferDelegate*
AsyncPixelTransferManagerEGL::CreatePixelTransferDelegateImpl(
    gles2::TextureRef* ref,
    const AsyncTexImage2DParams& define_params) {
  return new AsyncPixelTransferDelegateEGL(
      &shared_state_, ref->service_id(), define_params);
}

}  // namespace gpu
