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 |