Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(169)

Unified Diff: ui/gl/async_pixel_transfer_delegate_android.cc

Issue 11428140: gpu: Add async pixel transfer interface, stub and tests. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Address feedback. Added asyncTexImage. Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..9d931b6af4efa7b99c71941f02acd3b65cdbdb97
--- /dev/null
+++ b/ui/gl/async_pixel_transfer_delegate_android.cc
@@ -0,0 +1,467 @@
+// 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 {
+
+namespace {
+bool CheckErrors(const char* file, int line) {
+ EGLint eglerror;
+ GLenum glerror;
+ bool success = true;
+ while ((eglerror = eglGetError()) != EGL_SUCCESS) {
+ LOG(ERROR) << "Async transfer eglerror at "
+ << file << ":" << line << " " << eglerror;
+ success = false;
+ }
+ while ((glerror = glGetError()) != GL_NO_ERROR) {
+ LOG(ERROR) << "Async transfer openglerror at "
+ << file << ":" << line << " " << glerror;
+ success = false;
+ }
+ return success;
+}
+#define CHK() CheckErrors(__FILE__, __LINE__)
+} // namespace
+
+// Class which holds async pixel transfers state (EGLImage).
+// The EGLImage is accessed by either thread, but the everything
apatrick_chromium 2012/12/04 20:59:22 nit: fix comment
epenner 2012/12/08 03:15:04 Is this regarding thread-safe ref counting? Let me
+// else (and the lifetime of this object) is access only by the main
+// GPU thread.
+class AsyncTransferStateAndroid : public AsyncPixelTransferState {
+ public:
+ AsyncTransferStateAndroid(GLuint texture_id)
+ : texture_id_(texture_id),
+ needs_bind_(false),
+ transfer_in_progress_(false),
+ egl_image_(0) {}
+
+ virtual ~AsyncTransferStateAndroid() {
+ if (egl_image_) {
apatrick_chromium 2012/12/04 20:59:22 nit: indentation out by 1 space
epenner 2012/12/08 03:15:04 Done.
+ EGLDisplay display = eglGetCurrentDisplay();
+ eglDestroyImageKHR(display, egl_image_);
+ }
+ }
+
+ // implement AsyncPixelTransferState:
+ virtual bool TransferIsInProgress() {
+ return transfer_in_progress_;
+ }
+
+ virtual void BindTexture(GLenum target) {
+ glBindTexture(target, texture_id_);
+ if (needs_bind_)
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_);
+ needs_bind_ = false;
+ }
+
+ // Completion callbacks.
+ void TexImage2DCompleted() {
+ needs_bind_ = true;
+ transfer_in_progress_ = false;
+ }
+ void TexSubImage2DCompleted() {
+ transfer_in_progress_ = false;
+ }
+
+ // The 'real' texture.
+ GLuint texture_id_;
+
+ // Indicates there is a new EGLImage and the 'real'
+ // texture needs to be bound to it.
+ bool needs_bind_;
+
+ // Indicates that an async transfer is in progress.
+ bool transfer_in_progress_;
+
+ // 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_;
+};
+
+// 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 AsyncTexImage2D(
+ AsyncPixelTransferState*,
+ GLenum target,
+ GLint level,
+ GLenum internal_format,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const void* data);
+
+ 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 PerformAsyncTexImage2D(
+ EGLImageKHR* egl_image,
+ std::pair<GLenum, GLenum> formats,
+ gfx::Size size,
+ GLint border,
+ GLenum type,
+ const void* data);
+
+ 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(static_cast<AsyncPixelTransferState*>(
+ new AsyncTransferStateAndroid(texture_id)));
+}
+
+AsyncPixelTransferDelegateAndroid::AsyncPixelTransferDelegateAndroid()
+ : thread_(new base::Thread("GPUAsyncTransferThread"))
+{
+ Initialize();
+}
+
+AsyncPixelTransferDelegateAndroid::~AsyncPixelTransferDelegateAndroid()
+{
+ 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 {
+// Dummy function to measure completion on
+// the upload thread.
+void NoOp() {}
+} // namespace
+
+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 then occur after
+ // all async transfers are complete.
+ thread_->message_loop_proxy()->PostTaskAndReply(FROM_HERE,
+ base::Bind(&NoOp), task);
+}
+
+void AsyncPixelTransferDelegateAndroid::AsyncTexImage2D(
+ AsyncPixelTransferState* transfer_state,
+ GLenum target,
+ GLint level,
+ GLenum internal_format,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const void* data) {
+ AsyncTransferStateAndroid* state =
+ static_cast<AsyncTransferStateAndroid*>(transfer_state);
+ DCHECK(state);
+ DCHECK(state->texture_id_);
+ DCHECK(!state->transfer_in_progress_);
+
+ // TODO: Implement other targets/levels if needed.
+ DCHECK(target == GL_TEXTURE_2D);
+ DCHECK(level == 0);
+
+ state->transfer_in_progress_ = true;
+
+ // Any existing EGLImage is made an 'orphan' by a call to
+ // texImage2D. We can delete the existing one safely since
+ // the client texture is not effected.
apatrick_chromium 2012/12/04 20:59:22 nit: effected -> affected
epenner 2012/12/08 03:15:04 Done.
+ if (state->egl_image_) {
+ EGLDisplay display = eglGetCurrentDisplay();
+ eglDestroyImageKHR(display, state->egl_image_);
+ state->egl_image_ = 0;
+ }
+
+ // Post the upload task. The reply also keeps the texture
+ // state referenced until the reply executes on the main thread.
+ thread_->message_loop_proxy()->PostTaskAndReply(FROM_HERE,
+ base::Bind(
+ &AsyncPixelTransferDelegateAndroid::PerformAsyncTexImage2D,
+ base::Unretained(this),
+ &state->egl_image_,
+ std::pair<GLenum, GLenum>(internal_format, format),
+ gfx::Size(width, height),
+ border, type, data),
+ base::Bind(
+ &AsyncTransferStateAndroid::TexImage2DCompleted,
+ state));
+}
+
+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 =
+ static_cast<AsyncTransferStateAndroid*>(transfer_state);
+ DCHECK(state);
+ DCHECK(state->texture_id_);
+ DCHECK(!state->transfer_in_progress_);
+
+ // TODO: Implement other targets/levels if needed.
+ DCHECK(target == GL_TEXTURE_2D);
+ DCHECK(level == 0);
+
+ state->transfer_in_progress_ = true;
+
+ // 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 =
+ reinterpret_cast<EGLClientBuffer>(state->texture_id_);
+ 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 also keeps the texture
+ // state referenced until the reply executes on the main thread.
+ 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::TexSubImage2DCompleted,
+ state));
+}
+
+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));
+ thread_surface_->Initialize();
apatrick_chromium 2012/12/04 20:59:22 What if this fails?
epenner 2012/12/08 03:15:04 Hmm, is this normally treated as a recoverable err
+ thread_context_ = gfx::GLContext::CreateGLContext(share_group,
apatrick_chromium 2012/12/04 20:59:22 And this.
epenner 2012/12/08 03:15:04 See above.
+ thread_surface_,
+ gfx::PreferDiscreteGpu);
+ bool is_current = thread_context_->MakeCurrent(thread_surface_);
apatrick_chromium 2012/12/04 20:59:22 Ditto.
epenner 2012/12/08 03:15:04 See above.
+ 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;
+}
+
+namespace {
+void WaitForGlFence() {
+ // TODO: Fix bindings (link errors) to enable the code below.
+ // TODO: Should we only sync just before we report completion?
+
+ // Uploads usually finish on the CPU, but just in case add a fence
+ // and guarantee the upload has completed. Flush bit is set to
+ // insure we don't wait forever.
+ // 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);
+ glFinish();
+}
+} // namespace
+
+void AsyncPixelTransferDelegateAndroid::PerformAsyncTexImage2D(
+ EGLImageKHR* egl_image,
+ std::pair<GLenum, GLenum> formats,
+ gfx::Size size,
+ GLint border,
+ GLenum type,
+ const void* data) {
+ // In texImage2D, we do everything on the upload thread.
+ // This is because texImage2D can incur the allocation cost, and
+ // it also 'orphans' any previous EGLImage bound to the texture.
+ DCHECK(egl_image);
+ DCHECK(!(*egl_image));
+ TRACE_EVENT2("gpu", "performAsyncTexImage2D",
+ "width", size.width(),
+ "height", size.height());
+
+ // Create a texture from the image and upload to it.
+ GLenum internal_format = formats.first;
+ GLenum format = formats.second;
+ GLuint temp_texture = 0;
+ glGenTextures(1, &temp_texture);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, temp_texture);
+ {
+ TRACE_EVENT0("gpu", "performAsyncTexSubImage2D glTexImage2D");
+ glTexImage2D(GL_TEXTURE_2D, 0,
+ internal_format,
+ size.width(), size.height(),
+ border, format, type, data);
+ }
+
+ // Create the EGLImage, as texSubImage always 'orphan's a previous EGLImage.
+ GLuint level = 0;
+ EGLDisplay egl_display = eglGetCurrentDisplay();
+ EGLContext egl_context = eglGetCurrentContext();
+ EGLenum egl_target = EGL_GL_TEXTURE_2D_KHR;
+ EGLClientBuffer egl_buffer = (EGLClientBuffer) temp_texture;
+ 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
+ };
+ (*egl_image) = eglCreateImageKHR(
+ egl_display,
+ egl_context,
+ egl_target,
+ egl_buffer,
+ egl_attrib_list);
+
+ WaitForGlFence();
+
+ // We can delete this thread's texture as the real texture
+ // now contains the data.
+ glDeleteTextures(1, &temp_texture);
+}
+
+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 temp_texture = 0;
+ glGenTextures(1, &temp_texture);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, temp_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);
+ }
+
+ WaitForGlFence();
+
+ // We can delete this thread's texture as the real texture
+ // now contains the data.
+ glDeleteTextures(1, &temp_texture);
+}
+
+} // namespace gfx

Powered by Google App Engine
This is Rietveld 408576698