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

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: Added memory dup/mmap 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..594306fffe48371d7b81e02341edc50c6151db87
--- /dev/null
+++ b/ui/gl/async_pixel_transfer_delegate_android.cc
@@ -0,0 +1,532 @@
+// 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/process_util.h"
+#include "base/shared_memory.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"
+#include <sys/resource.h>
+
+using base::SharedMemory;
+using base::SharedMemoryHandle;
+
+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__)
+
+// We duplicate shared memory to avoid use-after-free issues. This could also
+// be solved by ref-counting something, or with a destruction callback. There
+// wasn't an obvious hook or ref-counted container, so for now we dup/mmap.
+SharedMemory* DuplicateSharedMemory(SharedMemory* shared_memory, uint32 size) {
+ // Duplicate the handle.
+ SharedMemoryHandle duped_shared_memory_handle;
+ if (!shared_memory->ShareToProcess(
+ base::GetCurrentProcessHandle(),
+ &duped_shared_memory_handle))
+ return NULL;
+ scoped_ptr<SharedMemory> duped_shared_memory(
+ new SharedMemory(duped_shared_memory_handle, false));
+ // Map the shared memory into this process. This validates the size.
+ if (!duped_shared_memory->Map(size))
+ return NULL;
+ return duped_shared_memory.release();
+}
+
+// Gets the address of the data from shared memory.
+void* GetAddress(SharedMemory* shared_memory,
+ uint32 shm_size,
+ uint32 shm_data_offset,
+ uint32 shm_data_size) {
+ // Memory bounds have already been validated, so there
+ // is just DCHECKS here.
+ DCHECK(shared_memory);
+ DCHECK(shared_memory->memory());
+ DCHECK(shm_data_offset + shm_data_size <= shm_size);
greggman 2012/12/06 06:52:00 DCHECK_LE?
epenner 2012/12/08 03:15:04 Done.
+ return static_cast<int8*>(shared_memory->memory()) + shm_data_offset;
+}
+
+} // namespace
+
+
+// Class which holds async pixel transfers state (EGLImage).
+// The EGLImage is accessed by either thread, but the everything
+// else (and the lifetime of this object) is controlled 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_) {
greggman 2012/12/06 06:52:00 nit: As al pointed out. there's 3 spaces of indent
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 as an EGLImage target.
+ 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,
+ SharedMemory*,
greggman 2012/12/06 06:52:00 should this be const?
epenner 2012/12/08 03:15:04 Done.
+ uint32 shm_size,
+ uint32 shm_data_offset,
+ uint32 shm_data_size);
+
+ virtual void AsyncTexSubImage2D(
+ AsyncPixelTransferState*,
+ GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ SharedMemory*,
greggman 2012/12/06 06:52:00 should this be const?
epenner 2012/12/08 03:15:04 Done.
+ uint32 shm_size,
+ uint32 shm_data_offset,
+ uint32 shm_data_size);
+
+ private:
+ void Initialize();
+ void Shutdown();
+ void PerformInitialize();
+ void PerformShutdown();
+
+ struct MemoryParams {
greggman 2012/12/06 06:52:00 nit: type definition come before function declarat
epenner 2012/12/08 03:15:04 Done.
+ SharedMemory* shared_memory; uint32 shm_size;
+ uint32 shm_data_offset; uint32 shm_data_size;
+ };
+ struct AsyncTexImage2DParams {
+ GLenum target; GLint level; GLenum internal_format;
+ GLsizei width; GLsizei height; GLint border; GLenum format; GLenum type;
+ };
+ struct AsyncTexSubImage2DParams {
+ GLenum target; GLint level; GLint xoffset; GLint yoffset;
+ GLsizei width; GLsizei height; GLenum format; GLenum type;
+ };
+ void PerformAsyncTexImage2D(
+ AsyncTransferStateAndroid*,
+ AsyncTexImage2DParams,
+ MemoryParams);
+ void PerformAsyncTexSubImage2D(
+ AsyncTransferStateAndroid*,
+ AsyncTexSubImage2DParams,
+ MemoryParams);
+
+ 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,
+ SharedMemory* shared_memory,
+ uint32 shm_size,
+ uint32 shm_data_offset,
+ uint32 shm_data_size) {
+ AsyncTransferStateAndroid* state =
+ static_cast<AsyncTransferStateAndroid*>(transfer_state);
+ DCHECK(state);
+ DCHECK(shared_memory);
+ DCHECK(state->texture_id_);
+ DCHECK(!state->transfer_in_progress_);
+ 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 affected.
+ if (state->egl_image_) {
+ EGLDisplay display = eglGetCurrentDisplay();
+ eglDestroyImageKHR(display, state->egl_image_);
+ state->egl_image_ = 0;
+ }
+
+ AsyncTexImage2DParams tex_params = {target, level, internal_format,
+ width, height, border, format, type};
+ MemoryParams mem_params = {DuplicateSharedMemory(shared_memory, shm_size),
+ shm_size, shm_data_offset, shm_data_size};
+
+ thread_->message_loop_proxy()->PostTaskAndReply(FROM_HERE,
+ base::Bind(
+ &AsyncPixelTransferDelegateAndroid::PerformAsyncTexImage2D,
+ base::Unretained(this), // The delegate always outlives its tasks.
+ base::Unretained(state), // This is referenced in reply below.
+ tex_params,
+ mem_params),
+ 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,
+ SharedMemory* shared_memory,
+ uint32 shm_size,
+ uint32 shm_data_offset,
+ uint32 shm_data_size) {
+ TRACE_EVENT2("gpu", "AsyncTexSubImage2D",
+ "width", width,
+ "height", height);
+ AsyncTransferStateAndroid* state =
+ static_cast<AsyncTransferStateAndroid*>(transfer_state);
+ DCHECK(state);
+ DCHECK(shared_memory);
+ DCHECK(state->texture_id_);
+ DCHECK(!state->transfer_in_progress_);
+ DCHECK(target == GL_TEXTURE_2D);
greggman 2012/12/06 06:52:00 DCHECK_EQ? You probably need DCHECK_EQ(static_ca
epenner 2012/12/08 03:15:04 Done.
+ DCHECK(level == 0);
greggman 2012/12/06 06:52:00 DCHECK_EQ? or DCHECK(!level)? The linter used to
epenner 2012/12/08 03:15:04 Done.
+
+ 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);
+ }
+
+ AsyncTexSubImage2DParams tex_params = {target, level, xoffset, yoffset,
+ width, height, format, type};
greggman 2012/12/06 06:52:00 nit: indentation is off
epenner 2012/12/08 03:15:04 Done.
+ MemoryParams mem_params = {DuplicateSharedMemory(shared_memory, shm_size),
+ shm_size, shm_data_offset, shm_data_size};
+
+ thread_->message_loop_proxy()->PostTaskAndReply(FROM_HERE,
+ base::Bind(
+ &AsyncPixelTransferDelegateAndroid::PerformAsyncTexSubImage2D,
+ base::Unretained(this), // The delegate always outlives its tasks.
+ base::Unretained(state), // This is referenced in reply below.
+ tex_params,
+ mem_params),
+ base::Bind(
+ &AsyncTransferStateAndroid::TexSubImage2DCompleted,
+ state));
+}
+
+void AsyncPixelTransferDelegateAndroid::PerformInitialize() {
+ DCHECK(!thread_surface_);
+ DCHECK(!thread_context_);
+
+ // Lower the thread priority for uploads.
+ // TODO: What's a good value? Without lowering this, uploads
+ // would often execute immediately and block the GPU thread.
+ setpriority(PRIO_PROCESS, base::PlatformThread::CurrentId(), 15);
+
+ GLShareGroup* share_group = NULL;
+ bool software = false;
+ thread_surface_ = new gfx::PbufferGLSurfaceEGL(software, gfx::Size(1,1));
+ thread_surface_->Initialize();
+ thread_context_ = gfx::GLContext::CreateGLContext(share_group,
+ thread_surface_,
+ gfx::PreferDiscreteGpu);
+ bool is_current = thread_context_->MakeCurrent(thread_surface_);
+
+ 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);
greggman 2012/12/06 11:22:20 btw: If you want these functions I think you need
epenner 2012/12/08 03:15:04 Done.
+ // EGLint flags = EGL_SYNC_FLUSH_COMMANDS_BIT_KHR;
+ // EGLTimeKHR time = EGL_FOREVER_KHR;
+ // eglClientWaitSyncKHR(display, fence, flags, time);
+ glFinish();
+}
+} // namespace
+
+void AsyncPixelTransferDelegateAndroid::PerformAsyncTexImage2D(
+ AsyncTransferStateAndroid* state,
+ AsyncTexImage2DParams t,
greggman 2012/12/06 06:52:00 nit: except for i for index single letter names ar
epenner 2012/12/08 03:15:04 I'll change if you want but I was following the de
+ MemoryParams m) {
+ // This is just to insure it is deleted. Could bind() do this?
+ scoped_ptr<SharedMemory> shared_memory = make_scoped_ptr(m.shared_memory);
+ void* data = GetAddress(m.shared_memory, m.shm_size,
+ m.shm_data_offset, m.shm_data_size);
+
+ // 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(0 == t.level);
greggman 2012/12/06 06:52:00 DCHECK_EQ?
epenner 2012/12/08 03:15:04 Done.
+ DCHECK(EGL_NO_IMAGE_KHR == state->egl_image_);
greggman 2012/12/06 06:52:00 DCHECK_EQ?
epenner 2012/12/08 03:15:04 Done.
+ TRACE_EVENT2("gpu", "performAsyncTexImage2D",
+ "width", t.width,
+ "height", t.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);
+ {
+ TRACE_EVENT0("gpu", "performAsyncTexSubImage2D glTexImage2D");
+ glTexImage2D(GL_TEXTURE_2D,
+ t.level, t.internal_format,
+ t.width, t.height,
+ t.border, t.format, t.type, data);
+ }
+
+ // Create the EGLImage, as texSubImage always 'orphan's a previous EGLImage.
+ 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, t.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);
+ WaitForGlFence();
+
+ // We can delete this thread's texture as the real texture
+ // now contains the data.
+ glDeleteTextures(1, &temp_texture);
+}
+
+void AsyncPixelTransferDelegateAndroid::PerformAsyncTexSubImage2D(
+ AsyncTransferStateAndroid* state,
+ AsyncTexSubImage2DParams t,
+ MemoryParams m) {
+ // This is just to insure it is deleted. Could bind() do this?
+ scoped_ptr<SharedMemory> shared_memory = make_scoped_ptr(m.shared_memory);
+ void* data = GetAddress(m.shared_memory, m.shm_size,
+ m.shm_data_offset, m.shm_data_size);
+
+ // For a texSubImage, the texture must already have been
+ // created on the main thread, along with EGLImageKHR.
+ DCHECK(EGL_NO_IMAGE_KHR != state->egl_image_);
greggman 2012/12/06 06:52:00 DCHECK_EQ?
epenner 2012/12/08 03:15:04 Done.
+ DCHECK(0 == t.level);
greggman 2012/12/06 06:52:00 same
epenner 2012/12/08 03:15:04 Done.
+ TRACE_EVENT2("gpu", "performAsyncTexSubImage2D",
+ "width", t.width,
+ "height", t.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, state->egl_image_);
+ {
+ TRACE_EVENT0("gpu", "performAsyncTexSubImage2D glTexSubImage2D");
+ glTexSubImage2D(GL_TEXTURE_2D,
+ t.level, t.xoffset, t.yoffset,
+ t.width, t.height, t.format, t.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