Chromium Code Reviews| Index: ui/gl/async_pixel_transfer_delegate_android.cc |
| diff --git a/ui/gl/async_pixel_transfer_delegate_android.cc b/ui/gl/async_pixel_transfer_delegate_android.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..0be2d51d3f217f6c5ab21f05a564ee3d3395c0c0 |
| --- /dev/null |
| +++ b/ui/gl/async_pixel_transfer_delegate_android.cc |
| @@ -0,0 +1,292 @@ |
| +// Copyright (c) 2012 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 "ui/gl/async_pixel_transfer_delegate_android.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/cancelable_callback.h" |
| +#include "base/debug/trace_event.h" |
| +#include "base/lazy_instance.h" |
| +#include "base/logging.h" |
| +#include "base/memory/scoped_ptr.h" |
| +#include "base/threading/thread.h" |
| +#include "build/build_config.h" |
| +#include "third_party/angle/include/EGL/egl.h" |
| +#include "third_party/angle/include/EGL/eglext.h" |
| +#include "ui/gfx/point.h" |
| +#include "ui/gfx/size.h" |
| +#include "ui/gl/async_pixel_transfer_delegate.h" |
| +#include "ui/gl/egl_util.h" |
| +#include "ui/gl/gl_bindings.h" |
| +#include "ui/gl/gl_context.h" |
| +#include "ui/gl/gl_surface_egl.h" |
| + |
| +namespace gfx { |
| + |
| + |
|
apatrick
2012/12/03 21:23:59
nit: delete blank line.
epennerAtGoogle
2012/12/04 19:56:48
Done.
|
| +bool check_errors(const char* file, int line) { |
|
apatrick
2012/12/03 21:23:59
nit: CheckErrors
epennerAtGoogle
2012/12/04 19:56:48
Done.
|
| + EGLint eglerror; |
| + GLenum glerror; |
| + bool success = true; |
| + while ((eglerror = eglGetError()) != EGL_SUCCESS) { |
| + LOG(ERROR) << "ESP eglerror " << file << ":" << line << " " << eglerror; |
| + success = false; |
| + } |
| + while ((glerror = glGetError()) != GL_NO_ERROR) { |
| + LOG(ERROR) << "ESP openglerror " << file << ":" << line << " " << glerror; |
| + success = false; |
| + } |
| + return success; |
| +} |
| +#define CHK() check_errors(__FILE__, __LINE__) |
| + |
| + |
| +class AsyncTransferStateAndroid : public AsyncPixelTransferState { |
| + public: |
| + AsyncTransferStateAndroid(GLuint texture_id) |
| + : texture_id_(texture_id) |
| + , egl_image_(0) {} |
|
apatrick
2012/12/03 21:23:59
nit: comma to line above.
epennerAtGoogle
2012/12/04 19:56:48
Done.
|
| + virtual ~AsyncTransferStateAndroid() { |
| + if (egl_image_) { |
| + EGLDisplay display = eglGetCurrentDisplay(); |
| + eglDestroyImageKHR(display, egl_image_); |
| + } |
| + } |
| + void no_op() {} |
|
apatrick
2012/12/03 21:23:59
I think you sent with the free function in the ano
epennerAtGoogle
2012/12/03 22:48:45
I use this one too, solely such that a reply holds
greggman
2012/12/05 02:23:42
nit: functions are CamelCase
epenner
2012/12/08 03:15:03
Done.
|
| + GLuint texture_id_; |
| + EGLImageKHR egl_image_; |
| +}; |
| + |
| + |
| +// Class which handles async pixel transfers on Android (using |
| +// EGLImageKHR and another upload thread) |
| +class AsyncPixelTransferDelegateAndroid : public AsyncPixelTransferDelegate { |
| + public: |
| + AsyncPixelTransferDelegateAndroid(); |
| + virtual ~AsyncPixelTransferDelegateAndroid(); |
| + |
| + // implement AsyncPixelTransferDelegate: |
| + virtual scoped_refptr<AsyncPixelTransferState> |
| + CreatePixelTransferState(GLuint); |
| + virtual void AsyncNotifyCompletion( |
| + const base::Closure& task); |
| + virtual void AsyncTexSubImage2D( |
| + AsyncPixelTransferState*, |
| + GLenum target, |
| + GLint level, |
| + GLint xoffset, |
| + GLint yoffset, |
| + GLsizei width, |
| + GLsizei height, |
| + GLenum format, |
| + GLenum type, |
| + const void* data); |
| + |
| + private: |
| + void Initialize(); |
| + void Shutdown(); |
| + void PerformInitialize(); |
| + void PerformShutdown(); |
| + |
| + void PerformAsyncTexSubImage2D( |
| + EGLImageKHR egl_image, |
| + gfx::Point offset, |
| + gfx::Size size, |
| + GLenum format, |
| + GLenum type, |
| + const void * data); |
| + |
| + scoped_ptr<base::Thread> thread_; |
| + scoped_refptr<gfx::GLContext> thread_context_; |
| + scoped_refptr<gfx::GLSurface> thread_surface_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegateAndroid); |
| +}; |
| + |
| +// Lazy instance creation. |
| +base::LazyInstance<AsyncPixelTransferDelegateAndroid> |
| + g_async_pixel_transfer_delegate_ = LAZY_INSTANCE_INITIALIZER; |
| + |
| +AsyncPixelTransferDelegate* AsyncPixelTransferDelegate::Get() { |
| + return g_async_pixel_transfer_delegate_.Pointer(); |
| +} |
| + |
| +scoped_refptr<AsyncPixelTransferState> |
| + AsyncPixelTransferDelegateAndroid:: |
| + CreatePixelTransferState(GLuint texture_id) { |
| + return make_scoped_refptr((AsyncPixelTransferState*) |
|
apatrick
2012/12/03 21:23:59
nit: no c-style casts.
epennerAtGoogle
2012/12/04 19:56:48
Done.
|
| + new AsyncTransferStateAndroid(texture_id)); |
| +} |
| + |
| +AsyncPixelTransferDelegateAndroid::AsyncPixelTransferDelegateAndroid() |
| + : thread_(new base::Thread("GPUAsyncTransferThread")) |
| +{ |
|
greggman
2012/12/05 02:23:42
nit: { wants to be on previous line
epenner
2012/12/08 03:15:03
Done.
|
| + Initialize(); |
| +} |
| + |
| +AsyncPixelTransferDelegateAndroid::~AsyncPixelTransferDelegateAndroid() |
| +{ |
|
greggman
2012/12/05 02:23:42
nit: { wants to be on previous line
epenner
2012/12/08 03:15:03
Done.
|
| + Shutdown(); |
| +} |
| + |
| +void AsyncPixelTransferDelegateAndroid::Initialize() { |
| + // Start the thread and initialize on the thread. |
| + thread_->Start(); |
| + thread_->message_loop()->PostTask(FROM_HERE, base::Bind( |
| + &AsyncPixelTransferDelegateAndroid::PerformInitialize, |
| + base::Unretained(this))); |
| +} |
| + |
| +void AsyncPixelTransferDelegateAndroid::Shutdown() { |
| + // Shutdown and wait for the thread to finish. |
| + thread_->message_loop()->PostTask(FROM_HERE, base::Bind( |
| + &AsyncPixelTransferDelegateAndroid::PerformShutdown, |
| + base::Unretained(this))); |
| + thread_->Stop(); |
| +} |
| + |
| +namespace { |
| + void no_op() {} |
|
apatrick
2012/12/03 21:23:59
nit: no indentation. NoOp.
epennerAtGoogle
2012/12/04 19:56:48
Done.
|
| +} |
| + |
| +void AsyncPixelTransferDelegateAndroid::AsyncNotifyCompletion( |
| + const base::Closure& task) { |
| + // Post a no-op task to the upload thread followed |
| + // by a reply to the callback. The reply will occur after |
| + // all async transfers are complete. |
| + thread_->message_loop_proxy()->PostTaskAndReply(FROM_HERE, |
| + base::Bind(&no_op), task); |
| +} |
| + |
| + |
| +void AsyncPixelTransferDelegateAndroid::AsyncTexSubImage2D( |
| + AsyncPixelTransferState* transfer_state, |
| + GLenum target, |
| + GLint level, |
| + GLint xoffset, |
| + GLint yoffset, |
| + GLsizei width, |
| + GLsizei height, |
| + GLenum format, |
| + GLenum type, |
| + const void* data) { |
| + TRACE_EVENT2("gpu", "AsyncTexSubImage2D", |
| + "width", width, |
| + "height", height); |
| + AsyncTransferStateAndroid* state =(AsyncTransferStateAndroid*)transfer_state; |
|
apatrick
2012/12/03 21:23:59
nit: no c-style casts.
epennerAtGoogle
2012/12/04 19:56:48
Done.
|
| + DCHECK(state); |
| + DCHECK(state->texture_id_); |
| + |
| + // TODO: Implement other targets/levels if needed. |
| + DCHECK(target == GL_TEXTURE_2D); |
| + DCHECK(level == 0); |
| + |
| + // Create the EGLImage if it hasn't already been created. |
| + if (!state->egl_image_) { |
| + EGLDisplay egl_display = eglGetCurrentDisplay(); |
| + EGLContext egl_context = eglGetCurrentContext(); |
| + EGLenum egl_target = EGL_GL_TEXTURE_2D_KHR; |
| + EGLClientBuffer egl_buffer = (EGLClientBuffer) state->texture_id_; |
|
apatrick
2012/12/03 21:23:59
nit: c-style casts.
epennerAtGoogle
2012/12/04 19:56:48
Done.
|
| + EGLint egl_attrib_list[] = { |
| + EGL_GL_TEXTURE_LEVEL_KHR, level, // mip-map level to reference. |
| + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, // preserve the data in the texture. |
| + EGL_NONE |
| + }; |
| + state->egl_image_ = eglCreateImageKHR( |
| + egl_display, |
| + egl_context, |
| + egl_target, |
| + egl_buffer, |
| + egl_attrib_list); |
| + } |
| + |
| + // Post the upload task. The reply is a no-op but keeps |
| + // the texture state referenced, so the EGLImage is not destroyed |
| + // before it is used on the upload thead. |
| + thread_->message_loop_proxy()->PostTaskAndReply(FROM_HERE, |
| + base::Bind( |
| + &AsyncPixelTransferDelegateAndroid::PerformAsyncTexSubImage2D, |
| + base::Unretained(this), |
| + state->egl_image_, |
| + gfx::Point(xoffset, yoffset), |
| + gfx::Size(width, height), |
| + format, type, data), |
| + base::Bind( |
| + &AsyncTransferStateAndroid::no_op, |
| + state)); |
| + CHK(); |
| +} |
| + |
| + |
| +void AsyncPixelTransferDelegateAndroid::PerformInitialize() { |
| + DCHECK(!thread_surface_); |
| + DCHECK(!thread_context_); |
| + GLShareGroup* share_group = NULL; |
| + bool software = false; |
| + thread_surface_ = new gfx::PbufferGLSurfaceEGL(software, gfx::Size(1,1)); |
|
apatrick
2012/12/03 21:23:59
Neither GLSurface or GLContext implementations are
epennerAtGoogle
2012/12/03 22:48:45
I see! I think since this is self-contained and p
epennerAtGoogle
2012/12/04 00:47:14
Actually after looking at this it appears some of
|
| + thread_surface_->Initialize(); |
| + thread_context_ = gfx::GLContext::CreateGLContext(share_group, |
| + thread_surface_, |
| + gfx::PreferDiscreteGpu); |
| + bool is_current = thread_context_->MakeCurrent(thread_surface_); |
|
apatrick
2012/12/03 21:23:59
This will modify the current context pointer, shar
epennerAtGoogle
2012/12/03 22:48:45
Very good to know, thanks!
|
| + DCHECK(thread_surface_); |
| + DCHECK(thread_context_); |
| + DCHECK(is_current); |
| +} |
| + |
| +void AsyncPixelTransferDelegateAndroid::PerformShutdown() { |
| + DCHECK(thread_surface_); |
| + DCHECK(thread_context_); |
| + thread_surface_ = NULL; |
| + thread_context_->ReleaseCurrent(thread_surface_); |
| + thread_context_ = NULL; |
| +} |
| + |
| +void AsyncPixelTransferDelegateAndroid::PerformAsyncTexSubImage2D( |
| + EGLImageKHR egl_image, |
| + gfx::Point offset, |
| + gfx::Size size, |
| + GLenum format, |
| + GLenum type, |
| + const void* data) { |
| + // For a texSubImage, the texture must already have been |
| + // created on the main thread, along with EGLImageKHR. |
| + DCHECK(egl_image); |
| + TRACE_EVENT2("gpu", "performAsyncTexSubImage2D", |
| + "width", size.width(), |
| + "height", size.height()); |
| + |
| + // Create a texture from the image and upload to it. |
| + GLuint texture = 0; |
| + glGenTextures(1, &texture); |
| + glActiveTexture(GL_TEXTURE0); |
| + glBindTexture(GL_TEXTURE_2D, texture); |
| + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image); |
| + { |
| + TRACE_EVENT0("gpu", "performAsyncTexSubImage2D glTexSubImage2D"); |
| + glTexSubImage2D(GL_TEXTURE_2D, 0, |
| + offset.x(), offset.y(), |
| + size.width(), size.height(), |
| + format, type, data); |
| + } |
| + |
| + // Uploads usually finish on the GPU, but just in case add a fence |
| + // and guarantee the upload has completed. |
| + // EGLSyncKHR fence = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, NULL); |
| + // EGLint flags = EGL_SYNC_FLUSH_COMMANDS_BIT_KHR; |
| + // EGLTimeKHR time = EGL_FOREVER_KHR; |
| + // eglClientWaitSyncKHR(display, fence, flags, time); |
| + |
| + // TODO: Fix bindings (link errors) to enable the above instead of glFinish. |
| + glFinish(); |
| + |
| + // We can delete this thread's texture as the real texture |
| + // now contains the data. |
| + glDeleteTextures(1, &texture); |
| + CHK(); |
| +} |
| + |
| + |
| + |
| +} // namespace gfx |