diff options
19 files changed, 1314 insertions, 1114 deletions
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h b/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h index e75cf1b..202c398 100644 --- a/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h +++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h @@ -30,7 +30,7 @@ class MockAsyncPixelTransferDelegate : public gfx::AsyncPixelTransferDelegate { virtual ~MockAsyncPixelTransferDelegate(); // Implement AsyncPixelTransferDelegate. - MOCK_METHOD2(CreateRawPixelTransferState, + MOCK_METHOD2(CreatePixelTransferState, gfx::AsyncPixelTransferState*( GLuint service_id, const AsyncTexImage2DParams& define_params)); MOCK_METHOD0(BindCompletedAsyncTransfers, bool()); diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index b50437a..10fec42 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -2546,8 +2546,8 @@ bool GLES2DecoderImpl::Initialize( context_->SetSafeToForceGpuSwitch(); // Create a delegate to perform async pixel transfers. - async_pixel_transfer_delegate_ = - gfx::AsyncPixelTransferDelegate::Create(context.get()); + async_pixel_transfer_delegate_.reset( + gfx::AsyncPixelTransferDelegate::Create(context.get())); return true; } @@ -10278,9 +10278,10 @@ error::Error GLES2DecoderImpl::HandleAsyncTexImage2DCHROMIUM( // is set up lazily when the transfer completes. DCHECK(!texture->GetAsyncTransferState()); texture->SetAsyncTransferState( - async_pixel_transfer_delegate_-> - CreatePixelTransferState(texture->service_id(), - tex_params)); + make_scoped_ptr( + async_pixel_transfer_delegate_->CreatePixelTransferState( + texture->service_id(), + tex_params))); texture->SetImmutable(true); async_pixel_transfer_delegate_->AsyncTexImage2D( @@ -10374,9 +10375,10 @@ error::Error GLES2DecoderImpl::HandleAsyncTexSubImage2DCHROMIUM( // Set up the async state if needed, and make the texture // immutable so the async state stays valid. texture->SetAsyncTransferState( - async_pixel_transfer_delegate_-> - CreatePixelTransferState(texture->service_id(), - define_params)); + make_scoped_ptr( + async_pixel_transfer_delegate_->CreatePixelTransferState( + texture->service_id(), + define_params))); texture->SetImmutable(true); } diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc index 2c0b782..2b11977 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc @@ -8134,7 +8134,6 @@ TEST_F(GLES2DecoderManualInitTest, AsyncPixelTransfers) { Texture* texture = GetTexture(client_texture_id_); // Set a mock Async delegate - // Async state is returned as a scoped_ptr, but we keep a raw copy. StrictMock<gfx::MockAsyncPixelTransferDelegate>* delegate = new StrictMock<gfx::MockAsyncPixelTransferDelegate>; decoder_->SetAsyncPixelTransferDelegate(delegate); @@ -8158,7 +8157,7 @@ TEST_F(GLES2DecoderManualInitTest, AsyncPixelTransfers) { // AsyncTexImage2D { // Create transfer state since it doesn't exist. - EXPECT_CALL(*delegate, CreateRawPixelTransferState(kServiceTextureId, _)) + EXPECT_CALL(*delegate, CreatePixelTransferState(kServiceTextureId, _)) .WillOnce(Return( state = new StrictMock<gfx::MockAsyncPixelTransferState>)) .RetiresOnSaturation(); @@ -8208,7 +8207,7 @@ TEST_F(GLES2DecoderManualInitTest, AsyncPixelTransfers) { texture->SetImmutable(false); { // Create transfer state since it doesn't exist. - EXPECT_CALL(*delegate, CreateRawPixelTransferState(kServiceTextureId, _)) + EXPECT_CALL(*delegate, CreatePixelTransferState(kServiceTextureId, _)) .WillOnce(Return( state = new StrictMock<gfx::MockAsyncPixelTransferState>)) .RetiresOnSaturation(); @@ -8262,7 +8261,7 @@ TEST_F(GLES2DecoderManualInitTest, AsyncPixelTransfers) { texture->SetAsyncTransferState(scoped_ptr<gfx::AsyncPixelTransferState>()); texture->SetImmutable(false); // Create transfer state since it doesn't exist. - EXPECT_CALL(*delegate, CreateRawPixelTransferState(kServiceTextureId, _)) + EXPECT_CALL(*delegate, CreatePixelTransferState(kServiceTextureId, _)) .WillOnce(Return( state = new StrictMock<gfx::MockAsyncPixelTransferState>)) .RetiresOnSaturation(); diff --git a/ui/gl/async_pixel_transfer_delegate.cc b/ui/gl/async_pixel_transfer_delegate.cc new file mode 100644 index 0000000..9d4c4a2 --- /dev/null +++ b/ui/gl/async_pixel_transfer_delegate.cc @@ -0,0 +1,17 @@ +// Copyright (c) 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 "ui/gl/async_pixel_transfer_delegate.h" + +namespace gfx { + +AsyncPixelTransferState::AsyncPixelTransferState() {} + +AsyncPixelTransferState::~AsyncPixelTransferState() {} + +AsyncPixelTransferDelegate::AsyncPixelTransferDelegate() {} + +AsyncPixelTransferDelegate::~AsyncPixelTransferDelegate() {} + +} // namespace gfx diff --git a/ui/gl/async_pixel_transfer_delegate.h b/ui/gl/async_pixel_transfer_delegate.h index 3f9f434..e5a0e2e 100644 --- a/ui/gl/async_pixel_transfer_delegate.h +++ b/ui/gl/async_pixel_transfer_delegate.h @@ -1,16 +1,15 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 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. -#ifndef UI_GL_ASYNC_TASK_DELEGATE_H_ -#define UI_GL_ASYNC_TASK_DELEGATE_H_ +#ifndef UI_GL_ASYNC_PIXEL_TRANSFER_DELEGATE_H_ +#define UI_GL_ASYNC_PIXEL_TRANSFER_DELEGATE_H_ #include "base/basictypes.h" -#include "base/bind.h" +#include "base/callback.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/time.h" -#include "build/build_config.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_export.h" @@ -55,14 +54,14 @@ struct AsyncMemoryParams { class GL_EXPORT AsyncPixelTransferState : public base::SupportsWeakPtr<AsyncPixelTransferState> { public: - virtual ~AsyncPixelTransferState() {} + virtual ~AsyncPixelTransferState(); // Returns true if there is a transfer in progress. virtual bool TransferIsInProgress() = 0; protected: friend class base::RefCounted<AsyncPixelTransferState>; - AsyncPixelTransferState() {} + AsyncPixelTransferState(); private: DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferState); @@ -72,17 +71,13 @@ class GL_EXPORT AsyncPixelTransferDelegate { public: typedef base::Callback<void(const AsyncMemoryParams&)> CompletionCallback; - static scoped_ptr<AsyncPixelTransferDelegate> - Create(gfx::GLContext* context); - virtual ~AsyncPixelTransferDelegate() {} + static AsyncPixelTransferDelegate* Create(gfx::GLContext* context); - // This wouldn't work with MOCK_METHOD, so it routes through - // a virtual protected version which returns a raw pointer. - scoped_ptr<AsyncPixelTransferState> - CreatePixelTransferState(GLuint tex_id, - const AsyncTexImage2DParams& define_params) { - return make_scoped_ptr(CreateRawPixelTransferState(tex_id, define_params)); - } + virtual ~AsyncPixelTransferDelegate(); + + virtual AsyncPixelTransferState* CreatePixelTransferState( + GLuint texture_id, + const AsyncTexImage2DParams& define_params) = 0; // Returns true iff a texture was bound to texture-unit zero. virtual bool BindCompletedAsyncTransfers() = 0; @@ -123,11 +118,7 @@ class GL_EXPORT AsyncPixelTransferDelegate { virtual bool NeedsProcessMorePendingTransfers() = 0; protected: - AsyncPixelTransferDelegate() {} - // For testing, as returning scoped_ptr wouldn't work with MOCK_METHOD. - virtual AsyncPixelTransferState* - CreateRawPixelTransferState(GLuint tex_id, - const AsyncTexImage2DParams& define_params) = 0; + AsyncPixelTransferDelegate(); private: DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegate); @@ -135,5 +126,5 @@ class GL_EXPORT AsyncPixelTransferDelegate { } // namespace gfx -#endif // UI_GL_ASYNC_TASK_DELEGATE_H_ +#endif // UI_GL_ASYNC_PIXEL_TRANSFER_DELEGATE_H_ diff --git a/ui/gl/async_pixel_transfer_delegate_android.cc b/ui/gl/async_pixel_transfer_delegate_android.cc index 2414472..f29fdf6 100644 --- a/ui/gl/async_pixel_transfer_delegate_android.cc +++ b/ui/gl/async_pixel_transfer_delegate_android.cc @@ -1,906 +1,43 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 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 "ui/gl/async_pixel_transfer_delegate_android.h" - -#include <list> -#include <string> +#include "ui/gl/async_pixel_transfer_delegate.h" -#include "base/bind.h" #include "base/debug/trace_event.h" -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/memory/shared_memory.h" -#include "base/process_util.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/thread.h" -#include "build/build_config.h" -#include "ui/gl/async_pixel_transfer_delegate.h" +#include "ui/gl/async_pixel_transfer_delegate_egl.h" #include "ui/gl/async_pixel_transfer_delegate_stub.h" -#include "ui/gl/egl_util.h" -#include "ui/gl/gl_bindings.h" +#include "ui/gl/async_pixel_transfer_delegate_sync.h" #include "ui/gl/gl_context.h" -#include "ui/gl/gl_surface_egl.h" -#include "ui/gl/safe_shared_memory_pool.h" - -// TODO(epenner): Move thread priorities to base. (crbug.com/170549) -#include <sys/resource.h> - -using base::SharedMemory; -using base::SharedMemoryHandle; +#include "ui/gl/gl_implementation.h" namespace gfx { -namespace { - -class TextureUploadStats - : public base::RefCountedThreadSafe<TextureUploadStats> { - public: - TextureUploadStats() : texture_upload_count_(0) {} - - void AddUpload(base::TimeDelta transfer_time) { - base::AutoLock scoped_lock(lock_); - texture_upload_count_++; - total_texture_upload_time_ += transfer_time; - } - - int GetStats(base::TimeDelta* total_texture_upload_time) { - base::AutoLock scoped_lock(lock_); - if (total_texture_upload_time) - *total_texture_upload_time = total_texture_upload_time_; - return texture_upload_count_; - } - - private: - friend class RefCountedThreadSafe<TextureUploadStats>; - - ~TextureUploadStats() {} - - int texture_upload_count_; - base::TimeDelta total_texture_upload_time_; - base::Lock lock_; - - DISALLOW_COPY_AND_ASSIGN(TextureUploadStats); -}; - -const char kAsyncTransferThreadName[] = "AsyncTransferThread"; - -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__) - -// 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); -} - -// Gets the address of the data from shared memory. -void* GetAddress(SharedMemory* shared_memory, uint32 shm_data_offset) { - // Memory bounds have already been validated, so there - // are just DCHECKS here. - CHECK(shared_memory); - CHECK(shared_memory->memory()); - return static_cast<int8*>(shared_memory->memory()) + shm_data_offset; -} - -class TransferThread : public base::Thread { - public: - TransferThread() : base::Thread(kAsyncTransferThreadName) { - Start(); - } - virtual ~TransferThread() { - Stop(); - } - - virtual void Init() OVERRIDE { - GLShareGroup* share_group = NULL; - bool software = false; - surface_ = new gfx::PbufferGLSurfaceEGL(software, gfx::Size(1,1)); - surface_->Initialize(); - context_ = gfx::GLContext::CreateGLContext(share_group, - surface_, - gfx::PreferDiscreteGpu); - bool is_current = context_->MakeCurrent(surface_); - DCHECK(is_current); - - // TODO(epenner): Move thread priorities to base. (crbug.com/170549) - int nice_value = 10; // Idle priority. - setpriority(PRIO_PROCESS, base::PlatformThread::CurrentId(), nice_value); - } - - virtual void CleanUp() OVERRIDE { - surface_ = NULL; - context_->ReleaseCurrent(surface_); - context_ = NULL; - } - - SafeSharedMemoryPool* safe_shared_memory_pool() { - return &safe_shared_memory_pool_; - } - - private: - scoped_refptr<gfx::GLContext> context_; - scoped_refptr<gfx::GLSurface> surface_; - - SafeSharedMemoryPool safe_shared_memory_pool_; - - 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(); -} - -SafeSharedMemoryPool* safe_shared_memory_pool() { - return g_transfer_thread.Pointer()->safe_shared_memory_pool(); -} - - -} // namespace - -// 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: - explicit 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; - } - - // Implement AsyncPixelTransferState: - bool TransferIsInProgress() { - return !transfer_completion_.IsSignaled(); - } - - void BindTransfer() { - TRACE_EVENT2("gpu", "BindAsyncTransfer glEGLImageTargetTexture2DOES", - "width", define_params_.width, - "height", define_params_.height); - DCHECK(texture_id_); - DCHECK_NE(EGL_NO_IMAGE_KHR, egl_image_); - - // We can only change the active texture and unit 0, - // as that is all that will be restored. - 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); - - DCHECK_NE(EGL_NO_IMAGE_KHR, egl_image_); - } - - 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() { - transfer_completion_.Reset(); - } - - void MarkAsCompleted() { - transfer_completion_.Signal(); - } - - void WaitForTransferCompletion() { - transfer_completion_.Wait(); - } - - protected: - friend class base::RefCountedThreadSafe<TransferStateInternal>; - friend class AsyncPixelTransferDelegateAndroid; - - 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_; -}; - -// Android needs thread-safe ref-counting, so this just wraps -// an internal thread-safe ref-counted state object. -class AsyncTransferStateAndroid : public AsyncPixelTransferState { - public: - explicit AsyncTransferStateAndroid( - GLuint texture_id, - const AsyncTexImage2DParams& define_params, - bool wait_for_uploads, - bool wait_for_creation, - bool use_image_preserved) - : internal_(new TransferStateInternal(texture_id, - define_params, - wait_for_uploads, - wait_for_creation, - use_image_preserved)) { - } - virtual ~AsyncTransferStateAndroid() {} - virtual bool TransferIsInProgress() OVERRIDE { - return internal_->TransferIsInProgress(); - } - void BindTransfer() { - internal_->BindTransfer(); - } - scoped_refptr<TransferStateInternal> internal_; -}; - -// Class which handles async pixel transfers on Android (using -// EGLImageKHR and another upload thread) -class AsyncPixelTransferDelegateAndroid - : public AsyncPixelTransferDelegate, - public base::SupportsWeakPtr<AsyncPixelTransferDelegateAndroid> { - public: - AsyncPixelTransferDelegateAndroid(); - virtual ~AsyncPixelTransferDelegateAndroid(); - - // implement AsyncPixelTransferDelegate: - virtual bool BindCompletedAsyncTransfers() OVERRIDE; - virtual void AsyncNotifyCompletion( - const AsyncMemoryParams& mem_params, - const CompletionCallback& callback) OVERRIDE; - virtual void AsyncTexImage2D( - AsyncPixelTransferState* state, - const AsyncTexImage2DParams& tex_params, - const AsyncMemoryParams& mem_params, - const base::Closure& bind_callback) OVERRIDE; - virtual void AsyncTexSubImage2D( - AsyncPixelTransferState* state, - const AsyncTexSubImage2DParams& tex_params, - const AsyncMemoryParams& mem_params) OVERRIDE; - virtual void WaitForTransferCompletion( - AsyncPixelTransferState* state) OVERRIDE; - virtual uint32 GetTextureUploadCount() OVERRIDE; - virtual base::TimeDelta GetTotalTextureUploadTime() OVERRIDE; - virtual bool ProcessMorePendingTransfers() OVERRIDE; - virtual bool NeedsProcessMorePendingTransfers() OVERRIDE; - - private: - // implement AsyncPixelTransferDelegate: - virtual AsyncPixelTransferState* - CreateRawPixelTransferState(GLuint texture_id, - const AsyncTexImage2DParams& define_params) OVERRIDE; - - static void PerformNotifyCompletion( - AsyncMemoryParams mem_params, - ScopedSafeSharedMemory* safe_shared_memory, - const CompletionCallback& callback); - static void PerformAsyncTexImage2D( - TransferStateInternal* state, - AsyncTexImage2DParams tex_params, - AsyncMemoryParams mem_params, - ScopedSafeSharedMemory* safe_shared_memory); - static void PerformAsyncTexSubImage2D( - TransferStateInternal* state, - AsyncTexSubImage2DParams tex_params, - AsyncMemoryParams mem_params, - ScopedSafeSharedMemory* safe_shared_memory, - scoped_refptr<TextureUploadStats> texture_upload_stats); - - // Returns true if a work-around was used. - bool WorkAroundAsyncTexImage2D( - AsyncPixelTransferState* state, - const AsyncTexImage2DParams& tex_params, - const AsyncMemoryParams& mem_params, - const base::Closure& bind_callback); - bool WorkAroundAsyncTexSubImage2D( - AsyncPixelTransferState* state, - const AsyncTexSubImage2DParams& tex_params, - const AsyncMemoryParams& mem_params); - - typedef std::list<base::WeakPtr<AsyncPixelTransferState> > TransferQueue; - TransferQueue pending_allocations_; - - scoped_refptr<TextureUploadStats> texture_upload_stats_; - bool is_imagination_; - bool is_qualcomm_; - - DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegateAndroid); -}; - // We only used threaded uploads when we can: // - Create EGLImages out of OpenGL textures (EGL_KHR_gl_texture_2D_image) // - Bind EGLImages to OpenGL textures (GL_OES_EGL_image) // - Use fences (to test for upload completion). -scoped_ptr<AsyncPixelTransferDelegate> - AsyncPixelTransferDelegate::Create(gfx::GLContext* context) { - DCHECK(context); - if (context->HasExtension("EGL_KHR_fence_sync") && - context->HasExtension("EGL_KHR_image") && - context->HasExtension("EGL_KHR_image_base") && - context->HasExtension("EGL_KHR_gl_texture_2D_image") && - context->HasExtension("GL_OES_EGL_image")) { - return make_scoped_ptr( - static_cast<AsyncPixelTransferDelegate*>( - new AsyncPixelTransferDelegateAndroid())); - } else { - LOG(INFO) << "Async pixel transfers not supported"; - return make_scoped_ptr( - static_cast<AsyncPixelTransferDelegate*>( - new AsyncPixelTransferDelegateStub())); - } -} - -AsyncPixelTransferDelegateAndroid::AsyncPixelTransferDelegateAndroid() { - 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; - // TODO(reveman): Skip this if --enable-gpu-benchmarking is not present. - texture_upload_stats_ = make_scoped_refptr(new TextureUploadStats); -} - -AsyncPixelTransferDelegateAndroid::~AsyncPixelTransferDelegateAndroid() { -} - -AsyncPixelTransferState* - AsyncPixelTransferDelegateAndroid:: - CreateRawPixelTransferState(GLuint texture_id, - const AsyncTexImage2DParams& define_params) { - // 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 = !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 = 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 = is_qualcomm_ || is_imagination_; - - return new AsyncTransferStateAndroid(texture_id, - define_params, - wait_for_uploads, - wait_for_creation, - use_image_preserved); -} - -bool AsyncPixelTransferDelegateAndroid::BindCompletedAsyncTransfers() { - bool texture_dirty = false; - while(!pending_allocations_.empty()) { - if (!pending_allocations_.front().get()) { - pending_allocations_.pop_front(); - continue; - } - scoped_refptr<TransferStateInternal> state = - static_cast<AsyncTransferStateAndroid*> - (pending_allocations_.front().get())->internal_.get(); - // Terminate early, as all transfers finish in order, currently. - if (state->TransferIsInProgress()) - break; - // If the transfer is finished, bind it to the texture - // and remove it from pending list. - texture_dirty = true; - state->BindTransfer(); - pending_allocations_.pop_front(); - } - return texture_dirty; -} - -void AsyncPixelTransferDelegateAndroid::AsyncNotifyCompletion( - const AsyncMemoryParams& mem_params, - const CompletionCallback& callback) { - DCHECK(mem_params.shared_memory); - DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size, - mem_params.shm_size); - // 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(&AsyncPixelTransferDelegateAndroid::PerformNotifyCompletion, - mem_params, - base::Owned( - new ScopedSafeSharedMemory(safe_shared_memory_pool(), - mem_params.shared_memory, - mem_params.shm_size)), - callback)); -} - -void AsyncPixelTransferDelegateAndroid::WaitForTransferCompletion( - AsyncPixelTransferState* transfer_state) { - TRACE_EVENT0("gpu", "WaitForTransferCompletion"); - scoped_refptr<TransferStateInternal> state = - static_cast<AsyncTransferStateAndroid*>(transfer_state)->internal_.get(); - DCHECK(state); - DCHECK(state->texture_id_); - - if (state->TransferIsInProgress()) { - // TODO(epenner): Move thread priorities to base. (crbug.com/170549) - int default_nice_value = 0; // Default priority. - int idle_nice_value = 10; // Idle priority. - setpriority(PRIO_PROCESS, - g_transfer_thread.Pointer()->thread_id(), - default_nice_value); - - state->WaitForTransferCompletion(); - DCHECK(!state->TransferIsInProgress()); - - // TODO(epenner): Move thread priorities to base. (crbug.com/170549) - setpriority(PRIO_PROCESS, - g_transfer_thread.Pointer()->thread_id(), - idle_nice_value); - } -} - -void AsyncPixelTransferDelegateAndroid::AsyncTexImage2D( - AsyncPixelTransferState* transfer_state, - const AsyncTexImage2DParams& tex_params, - const AsyncMemoryParams& mem_params, - const base::Closure& bind_callback) { - if (WorkAroundAsyncTexImage2D(transfer_state, tex_params, - mem_params, bind_callback)) - return; - - scoped_refptr<TransferStateInternal> state = - static_cast<AsyncTransferStateAndroid*>(transfer_state)->internal_.get(); - DCHECK(mem_params.shared_memory); - DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size, - mem_params.shm_size); - DCHECK(state); - DCHECK(state->texture_id_); - 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. - pending_allocations_.push_back(transfer_state->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( - &AsyncPixelTransferDelegateAndroid::PerformAsyncTexImage2D, - state, - tex_params, - mem_params, - base::Owned(new ScopedSafeSharedMemory(safe_shared_memory_pool(), - mem_params.shared_memory, - mem_params.shm_size)))); - - - DCHECK(CHECK_GL()); -} - -void AsyncPixelTransferDelegateAndroid::AsyncTexSubImage2D( - AsyncPixelTransferState* transfer_state, - const AsyncTexSubImage2DParams& tex_params, - const AsyncMemoryParams& mem_params) { - TRACE_EVENT2("gpu", "AsyncTexSubImage2D", - "width", tex_params.width, - "height", tex_params.height); - if (WorkAroundAsyncTexSubImage2D(transfer_state, tex_params, mem_params)) - return; - scoped_refptr<TransferStateInternal> state = - static_cast<AsyncTransferStateAndroid*>(transfer_state)->internal_.get(); - - DCHECK(state->texture_id_); - DCHECK(!state->TransferIsInProgress()); - DCHECK(mem_params.shared_memory); - DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size, - mem_params.shm_size); - 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( - &AsyncPixelTransferDelegateAndroid::PerformAsyncTexSubImage2D, - state, - tex_params, - mem_params, - base::Owned(new ScopedSafeSharedMemory(safe_shared_memory_pool(), - mem_params.shared_memory, - mem_params.shm_size)), - texture_upload_stats_)); - - DCHECK(CHECK_GL()); -} - -uint32 AsyncPixelTransferDelegateAndroid::GetTextureUploadCount() { - CHECK(texture_upload_stats_); - return texture_upload_stats_->GetStats(NULL); -} - -base::TimeDelta AsyncPixelTransferDelegateAndroid::GetTotalTextureUploadTime() { - CHECK(texture_upload_stats_); - base::TimeDelta total_texture_upload_time; - texture_upload_stats_->GetStats(&total_texture_upload_time); - return total_texture_upload_time; -} - -bool AsyncPixelTransferDelegateAndroid::ProcessMorePendingTransfers() { - return false; -} - -bool AsyncPixelTransferDelegateAndroid::NeedsProcessMorePendingTransfers() { - return false; -} - - -namespace { -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); -} -} // namespace - -void AsyncPixelTransferDelegateAndroid::PerformNotifyCompletion( - AsyncMemoryParams mem_params, - ScopedSafeSharedMemory* safe_shared_memory, - const CompletionCallback& callback) { - TRACE_EVENT0("gpu", "PerformNotifyCompletion"); - gfx::AsyncMemoryParams safe_mem_params = mem_params; - safe_mem_params.shared_memory = safe_shared_memory->shared_memory(); - callback.Run(safe_mem_params); -} - -void AsyncPixelTransferDelegateAndroid::PerformAsyncTexImage2D( - TransferStateInternal* state, - AsyncTexImage2DParams tex_params, - AsyncMemoryParams mem_params, - ScopedSafeSharedMemory* safe_shared_memory) { - TRACE_EVENT2("gpu", "PerformAsyncTexImage", - "width", tex_params.width, - "height", tex_params.height); - DCHECK(state); - DCHECK(!state->thread_texture_id_); - DCHECK_EQ(0, tex_params.level); - DCHECK_EQ(EGL_NO_IMAGE_KHR, state->egl_image_); - - void* data = GetAddress(safe_shared_memory->shared_memory(), - mem_params.shm_data_offset); - { - TRACE_EVENT0("gpu", "glTexImage2D no data"); - glGenTextures(1, &state->thread_texture_id_); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, state->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 (state->use_image_preserved_) - DoTexImage2D(tex_params, data); - else - DoTexImage2D(tex_params, NULL); - } - - state->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 (!state->use_image_preserved_) - DoFullTexSubImage2D(tex_params, data); - } - - state->WaitForLastUpload(); - state->MarkAsCompleted(); - - DCHECK(CHECK_GL()); -} - -void AsyncPixelTransferDelegateAndroid::PerformAsyncTexSubImage2D( - TransferStateInternal* state, - AsyncTexSubImage2DParams tex_params, - AsyncMemoryParams mem_params, - ScopedSafeSharedMemory* safe_shared_memory, - scoped_refptr<TextureUploadStats> texture_upload_stats) { - TRACE_EVENT2("gpu", "PerformAsyncTexSubImage2D", - "width", tex_params.width, - "height", tex_params.height); - - DCHECK(state); - DCHECK_NE(EGL_NO_IMAGE_KHR, state->egl_image_); - DCHECK_EQ(0, tex_params.level); - - void* data = GetAddress(safe_shared_memory->shared_memory(), - mem_params.shm_data_offset); - - base::TimeTicks begin_time; - if (texture_upload_stats) - begin_time = base::TimeTicks::HighResNow(); - - if (!state->thread_texture_id_) { - TRACE_EVENT0("gpu", "glEGLImageTargetTexture2DOES"); - glGenTextures(1, &state->thread_texture_id_); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, state->thread_texture_id_); - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, state->egl_image_); - } else { - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, state->thread_texture_id_); - } - { - TRACE_EVENT0("gpu", "glTexSubImage2D"); - DoTexSubImage2D(tex_params, data); - } - state->WaitForLastUpload(); - state->MarkAsCompleted(); - - DCHECK(CHECK_GL()); - if (texture_upload_stats) { - texture_upload_stats->AddUpload( - base::TimeTicks::HighResNow() - begin_time); - } -} - -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 AsyncPixelTransferDelegateAndroid::WorkAroundAsyncTexImage2D( - AsyncPixelTransferState* transfer_state, - const AsyncTexImage2DParams& tex_params, - const AsyncMemoryParams& mem_params, - const base::Closure& bind_callback) { - if (!is_imagination_) - return false; - scoped_refptr<TransferStateInternal> state = - static_cast<AsyncTransferStateAndroid*>(transfer_state)->internal_.get(); - - // 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 = GetAddress(mem_params.shared_memory, - mem_params.shm_data_offset); - 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(); - pending_allocations_.push_back(transfer_state->AsWeakPtr()); - state->bind_callback_ = bind_callback; - } - - DCHECK(CHECK_GL()); - return true; -} - -bool AsyncPixelTransferDelegateAndroid::WorkAroundAsyncTexSubImage2D( - AsyncPixelTransferState* transfer_state, - const AsyncTexSubImage2DParams& tex_params, - const AsyncMemoryParams& mem_params) { - if (!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; - - scoped_refptr<TransferStateInternal> state = - static_cast<AsyncTransferStateAndroid*>(transfer_state)->internal_.get(); - - // 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 = GetAddress(mem_params.shared_memory, - mem_params.shm_data_offset); - base::TimeTicks begin_time; - if (texture_upload_stats_) - 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 (texture_upload_stats_) { - texture_upload_stats_->AddUpload( - base::TimeTicks::HighResNow() - begin_time); +AsyncPixelTransferDelegate* AsyncPixelTransferDelegate::Create( + gfx::GLContext* context) { + TRACE_EVENT0("gpu", "AsyncPixelTransferDelegate::Create"); + switch (GetGLImplementation()) { + case kGLImplementationEGLGLES2: + DCHECK(context); + if (context->HasExtension("EGL_KHR_fence_sync") && + context->HasExtension("EGL_KHR_image") && + context->HasExtension("EGL_KHR_image_base") && + context->HasExtension("EGL_KHR_gl_texture_2D_image") && + context->HasExtension("GL_OES_EGL_image")) { + return new AsyncPixelTransferDelegateEGL; + } + LOG(INFO) << "Async pixel transfers not supported"; + return new AsyncPixelTransferDelegateSync; + case kGLImplementationMockGL: + return new AsyncPixelTransferDelegateStub; + default: + NOTREACHED(); + return NULL; } - - DCHECK(CHECK_GL()); - return true; } } // namespace gfx diff --git a/ui/gl/async_pixel_transfer_delegate_android.h b/ui/gl/async_pixel_transfer_delegate_android.h deleted file mode 100644 index 4534b40..0000000 --- a/ui/gl/async_pixel_transfer_delegate_android.h +++ /dev/null @@ -1,11 +0,0 @@ -// 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. - -#ifndef UI_GL_ASYNC_TASK_DELEGATE_ANDROID_H_ -#define UI_GL_ASYNC_TASK_DELEGATE_ANDROID_H_ - -namespace gfx { -} // namespace gfx - -#endif // UI_GL_ASYNC_TASK_DELEGATE_ANDROID_H_ diff --git a/ui/gl/async_pixel_transfer_delegate_egl.cc b/ui/gl/async_pixel_transfer_delegate_egl.cc new file mode 100644 index 0000000..d2878a2 --- /dev/null +++ b/ui/gl/async_pixel_transfer_delegate_egl.cc @@ -0,0 +1,813 @@ +// Copyright (c) 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 "ui/gl/async_pixel_transfer_delegate_egl.h" + +#include <string> + +#include "base/bind.h" +#include "base/debug/trace_event.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/memory/shared_memory.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "ui/gl/gl_context.h" +#include "ui/gl/gl_surface_egl.h" +#include "ui/gl/safe_shared_memory_pool.h" + +#if defined(OS_ANDROID) +// TODO(epenner): Move thread priorities to base. (crbug.com/170549) +#include <sys/resource.h> +#endif + +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 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); +} + +// Gets the address of the data from shared memory. +void* GetAddress(SharedMemory* shared_memory, uint32 shm_data_offset) { + // Memory bounds have already been validated, so there + // are just DCHECKS here. + CHECK(shared_memory); + CHECK(shared_memory->memory()); + return static_cast<int8*>(shared_memory->memory()) + shm_data_offset; +} + +class TransferThread : public base::Thread { + public: + TransferThread() : base::Thread(kAsyncTransferThreadName) { + Start(); + } + virtual ~TransferThread() { + Stop(); + } + + virtual void Init() OVERRIDE { + GLShareGroup* share_group = NULL; + bool software = false; + surface_ = new gfx::PbufferGLSurfaceEGL(software, gfx::Size(1,1)); + surface_->Initialize(); + context_ = gfx::GLContext::CreateGLContext(share_group, + surface_, + gfx::PreferDiscreteGpu); + bool is_current = context_->MakeCurrent(surface_); + DCHECK(is_current); + +#if defined(OS_ANDROID) + // TODO(epenner): Move thread priorities to base. (crbug.com/170549) + int nice_value = 10; // Idle priority. + setpriority(PRIO_PROCESS, base::PlatformThread::CurrentId(), nice_value); +#endif + + } + + virtual void CleanUp() OVERRIDE { + surface_ = NULL; + context_->ReleaseCurrent(surface_); + context_ = NULL; + } + + SafeSharedMemoryPool* safe_shared_memory_pool() { + return &safe_shared_memory_pool_; + } + + private: + scoped_refptr<gfx::GLContext> context_; + scoped_refptr<gfx::GLSurface> surface_; + + SafeSharedMemoryPool safe_shared_memory_pool_; + + 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(); +} + +SafeSharedMemoryPool* safe_shared_memory_pool() { + return g_transfer_thread.Pointer()->safe_shared_memory_pool(); +} + +} // namespace + +// 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_); + DCHECK_NE(EGL_NO_IMAGE_KHR, egl_image_); + + // We can only change the active texture and unit 0, + // as that is all that will be restored. + 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); + + DCHECK_NE(EGL_NO_IMAGE_KHR, egl_image_); + } + + 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() { + transfer_completion_.Reset(); + } + + void MarkAsCompleted() { + transfer_completion_.Signal(); + } + + void WaitForTransferCompletion() { + transfer_completion_.Wait(); + } + + protected: + friend class base::RefCountedThreadSafe<TransferStateInternal>; + friend class 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_; +}; + +class TextureUploadStats + : public base::RefCountedThreadSafe<TextureUploadStats> { + public: + TextureUploadStats() : texture_upload_count_(0) {} + + void AddUpload(base::TimeDelta transfer_time) { + base::AutoLock scoped_lock(lock_); + texture_upload_count_++; + total_texture_upload_time_ += transfer_time; + } + + int GetStats(base::TimeDelta* total_texture_upload_time) { + base::AutoLock scoped_lock(lock_); + if (total_texture_upload_time) + *total_texture_upload_time = total_texture_upload_time_; + return texture_upload_count_; + } + + private: + friend class base::RefCountedThreadSafe<TextureUploadStats>; + + ~TextureUploadStats() {} + + int texture_upload_count_; + base::TimeDelta total_texture_upload_time_; + base::Lock lock_; + + DISALLOW_COPY_AND_ASSIGN(TextureUploadStats); +}; + +namespace { + +// EGL needs thread-safe ref-counting, so this just wraps +// an internal thread-safe ref-counted state object. +class AsyncTransferStateImpl : public AsyncPixelTransferState { + public: + AsyncTransferStateImpl(GLuint texture_id, + const AsyncTexImage2DParams& define_params, + bool wait_for_uploads, + bool wait_for_creation, + bool use_image_preserved) + : internal_(new TransferStateInternal(texture_id, + define_params, + wait_for_uploads, + wait_for_creation, + use_image_preserved)) { + } + virtual ~AsyncTransferStateImpl() {} + + virtual bool TransferIsInProgress() OVERRIDE { + return internal_->TransferIsInProgress(); + } + + void BindTransfer() { + internal_->BindTransfer(); + } + + scoped_refptr<TransferStateInternal> internal_; +}; + +} // namespace + +AsyncPixelTransferDelegateEGL::AsyncPixelTransferDelegateEGL() { + 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; + // TODO(reveman): Skip this if --enable-gpu-benchmarking is not present. + texture_upload_stats_ = make_scoped_refptr(new TextureUploadStats); +} + +AsyncPixelTransferDelegateEGL::~AsyncPixelTransferDelegateEGL() {} + +AsyncPixelTransferState* AsyncPixelTransferDelegateEGL:: + CreatePixelTransferState(GLuint texture_id, + const AsyncTexImage2DParams& define_params) { + // 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 = !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 = 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 = is_qualcomm_ || is_imagination_; + + return new AsyncTransferStateImpl(texture_id, + define_params, + wait_for_uploads, + wait_for_creation, + use_image_preserved); +} + +bool AsyncPixelTransferDelegateEGL::BindCompletedAsyncTransfers() { + bool texture_dirty = false; + while(!pending_allocations_.empty()) { + if (!pending_allocations_.front().get()) { + pending_allocations_.pop_front(); + continue; + } + scoped_refptr<TransferStateInternal> state = + static_cast<AsyncTransferStateImpl*> + (pending_allocations_.front().get())->internal_.get(); + // Terminate early, as all transfers finish in order, currently. + if (state->TransferIsInProgress()) + break; + // If the transfer is finished, bind it to the texture + // and remove it from pending list. + texture_dirty = true; + state->BindTransfer(); + pending_allocations_.pop_front(); + } + return texture_dirty; +} + +void AsyncPixelTransferDelegateEGL::AsyncNotifyCompletion( + const AsyncMemoryParams& mem_params, + const CompletionCallback& callback) { + DCHECK(mem_params.shared_memory); + DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size, + mem_params.shm_size); + // 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(&AsyncPixelTransferDelegateEGL::PerformNotifyCompletion, + mem_params, + base::Owned( + new ScopedSafeSharedMemory(safe_shared_memory_pool(), + mem_params.shared_memory, + mem_params.shm_size)), + callback)); +} + +void AsyncPixelTransferDelegateEGL::WaitForTransferCompletion( + AsyncPixelTransferState* transfer_state) { + TRACE_EVENT0("gpu", "WaitForTransferCompletion"); + scoped_refptr<TransferStateInternal> state = + static_cast<AsyncTransferStateImpl*>(transfer_state)->internal_.get(); + DCHECK(state); + DCHECK(state->texture_id_); + + if (state->TransferIsInProgress()) { +#if defined(OS_ANDROID) + // TODO(epenner): Move thread priorities to base. (crbug.com/170549) + int default_nice_value = 0; // Default priority. + int idle_nice_value = 10; // Idle priority. + setpriority(PRIO_PROCESS, + g_transfer_thread.Pointer()->thread_id(), + default_nice_value); +#endif + + state->WaitForTransferCompletion(); + DCHECK(!state->TransferIsInProgress()); + +#if defined(OS_ANDROID) + // TODO(epenner): Move thread priorities to base. (crbug.com/170549) + setpriority(PRIO_PROCESS, + g_transfer_thread.Pointer()->thread_id(), + idle_nice_value); +#endif + } +} + +void AsyncPixelTransferDelegateEGL::AsyncTexImage2D( + AsyncPixelTransferState* transfer_state, + const AsyncTexImage2DParams& tex_params, + const AsyncMemoryParams& mem_params, + const base::Closure& bind_callback) { + if (WorkAroundAsyncTexImage2D(transfer_state, tex_params, + mem_params, bind_callback)) + return; + + scoped_refptr<TransferStateInternal> state = + static_cast<AsyncTransferStateImpl*>(transfer_state)->internal_.get(); + DCHECK(mem_params.shared_memory); + DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size, + mem_params.shm_size); + DCHECK(state); + DCHECK(state->texture_id_); + 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. + pending_allocations_.push_back(transfer_state->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( + &AsyncPixelTransferDelegateEGL::PerformAsyncTexImage2D, + state, + tex_params, + mem_params, + base::Owned(new ScopedSafeSharedMemory(safe_shared_memory_pool(), + mem_params.shared_memory, + mem_params.shm_size)))); + + + DCHECK(CHECK_GL()); +} + +void AsyncPixelTransferDelegateEGL::AsyncTexSubImage2D( + AsyncPixelTransferState* transfer_state, + const AsyncTexSubImage2DParams& tex_params, + const AsyncMemoryParams& mem_params) { + TRACE_EVENT2("gpu", "AsyncTexSubImage2D", + "width", tex_params.width, + "height", tex_params.height); + if (WorkAroundAsyncTexSubImage2D(transfer_state, tex_params, mem_params)) + return; + scoped_refptr<TransferStateInternal> state = + static_cast<AsyncTransferStateImpl*>(transfer_state)->internal_.get(); + + DCHECK(state->texture_id_); + DCHECK(!state->TransferIsInProgress()); + DCHECK(mem_params.shared_memory); + DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size, + mem_params.shm_size); + 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( + &AsyncPixelTransferDelegateEGL::PerformAsyncTexSubImage2D, + state, + tex_params, + mem_params, + base::Owned(new ScopedSafeSharedMemory(safe_shared_memory_pool(), + mem_params.shared_memory, + mem_params.shm_size)), + texture_upload_stats_)); + + DCHECK(CHECK_GL()); +} + +uint32 AsyncPixelTransferDelegateEGL::GetTextureUploadCount() { + CHECK(texture_upload_stats_); + return texture_upload_stats_->GetStats(NULL); +} + +base::TimeDelta AsyncPixelTransferDelegateEGL::GetTotalTextureUploadTime() { + CHECK(texture_upload_stats_); + base::TimeDelta total_texture_upload_time; + texture_upload_stats_->GetStats(&total_texture_upload_time); + return total_texture_upload_time; +} + +bool AsyncPixelTransferDelegateEGL::ProcessMorePendingTransfers() { + return false; +} + +bool AsyncPixelTransferDelegateEGL::NeedsProcessMorePendingTransfers() { + return false; +} + +namespace { +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); +} +} // namespace + +void AsyncPixelTransferDelegateEGL::PerformNotifyCompletion( + AsyncMemoryParams mem_params, + ScopedSafeSharedMemory* safe_shared_memory, + const CompletionCallback& callback) { + TRACE_EVENT0("gpu", "PerformNotifyCompletion"); + gfx::AsyncMemoryParams safe_mem_params = mem_params; + safe_mem_params.shared_memory = safe_shared_memory->shared_memory(); + callback.Run(safe_mem_params); +} + +void AsyncPixelTransferDelegateEGL::PerformAsyncTexImage2D( + TransferStateInternal* state, + AsyncTexImage2DParams tex_params, + AsyncMemoryParams mem_params, + ScopedSafeSharedMemory* safe_shared_memory) { + TRACE_EVENT2("gpu", "PerformAsyncTexImage", + "width", tex_params.width, + "height", tex_params.height); + DCHECK(state); + DCHECK(!state->thread_texture_id_); + DCHECK_EQ(0, tex_params.level); + DCHECK_EQ(EGL_NO_IMAGE_KHR, state->egl_image_); + + void* data = GetAddress(safe_shared_memory->shared_memory(), + mem_params.shm_data_offset); + { + TRACE_EVENT0("gpu", "glTexImage2D no data"); + glGenTextures(1, &state->thread_texture_id_); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, state->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 (state->use_image_preserved_) + DoTexImage2D(tex_params, data); + else + DoTexImage2D(tex_params, NULL); + } + + state->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 (!state->use_image_preserved_) + DoFullTexSubImage2D(tex_params, data); + } + + state->WaitForLastUpload(); + state->MarkAsCompleted(); + + DCHECK(CHECK_GL()); +} + +void AsyncPixelTransferDelegateEGL::PerformAsyncTexSubImage2D( + TransferStateInternal* state, + AsyncTexSubImage2DParams tex_params, + AsyncMemoryParams mem_params, + ScopedSafeSharedMemory* safe_shared_memory, + scoped_refptr<TextureUploadStats> texture_upload_stats) { + TRACE_EVENT2("gpu", "PerformAsyncTexSubImage2D", + "width", tex_params.width, + "height", tex_params.height); + + DCHECK(state); + DCHECK_NE(EGL_NO_IMAGE_KHR, state->egl_image_); + DCHECK_EQ(0, tex_params.level); + + void* data = GetAddress(safe_shared_memory->shared_memory(), + mem_params.shm_data_offset); + + base::TimeTicks begin_time; + if (texture_upload_stats) + begin_time = base::TimeTicks::HighResNow(); + + if (!state->thread_texture_id_) { + TRACE_EVENT0("gpu", "glEGLImageTargetTexture2DOES"); + glGenTextures(1, &state->thread_texture_id_); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, state->thread_texture_id_); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, state->egl_image_); + } else { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, state->thread_texture_id_); + } + { + TRACE_EVENT0("gpu", "glTexSubImage2D"); + DoTexSubImage2D(tex_params, data); + } + state->WaitForLastUpload(); + state->MarkAsCompleted(); + + DCHECK(CHECK_GL()); + if (texture_upload_stats) { + texture_upload_stats->AddUpload( + base::TimeTicks::HighResNow() - begin_time); + } +} + +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( + AsyncPixelTransferState* transfer_state, + const AsyncTexImage2DParams& tex_params, + const AsyncMemoryParams& mem_params, + const base::Closure& bind_callback) { + if (!is_imagination_) + return false; + scoped_refptr<TransferStateInternal> state = + static_cast<AsyncTransferStateImpl*>(transfer_state)->internal_.get(); + + // 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 = GetAddress(mem_params.shared_memory, + mem_params.shm_data_offset); + 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(); + pending_allocations_.push_back(transfer_state->AsWeakPtr()); + state->bind_callback_ = bind_callback; + } + + DCHECK(CHECK_GL()); + return true; +} + +bool AsyncPixelTransferDelegateEGL::WorkAroundAsyncTexSubImage2D( + AsyncPixelTransferState* transfer_state, + const AsyncTexSubImage2DParams& tex_params, + const AsyncMemoryParams& mem_params) { + if (!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; + + scoped_refptr<TransferStateInternal> state = + static_cast<AsyncTransferStateImpl*>(transfer_state)->internal_.get(); + + // 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 = GetAddress(mem_params.shared_memory, + mem_params.shm_data_offset); + base::TimeTicks begin_time; + if (texture_upload_stats_) + 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 (texture_upload_stats_) { + texture_upload_stats_->AddUpload( + base::TimeTicks::HighResNow() - begin_time); + } + + DCHECK(CHECK_GL()); + return true; +} + +} // namespace gfx diff --git a/ui/gl/async_pixel_transfer_delegate_egl.h b/ui/gl/async_pixel_transfer_delegate_egl.h new file mode 100644 index 0000000..f9b3d10 --- /dev/null +++ b/ui/gl/async_pixel_transfer_delegate_egl.h @@ -0,0 +1,90 @@ +// Copyright (c) 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. + +#ifndef UI_GL_ASYNC_PIXEL_TRANSFER_DELEGATE_EGL_H_ +#define UI_GL_ASYNC_PIXEL_TRANSFER_DELEGATE_EGL_H_ + +#include <list> + +#include "ui/gl/async_pixel_transfer_delegate.h" + +namespace gfx { +class ScopedSafeSharedMemory; +class TextureUploadStats; +class TransferStateInternal; + +// Class which handles async pixel transfers using EGLImageKHR and another +// upload thread +class AsyncPixelTransferDelegateEGL + : public AsyncPixelTransferDelegate, + public base::SupportsWeakPtr<AsyncPixelTransferDelegateEGL> { + public: + AsyncPixelTransferDelegateEGL(); + virtual ~AsyncPixelTransferDelegateEGL(); + + // Implement AsyncPixelTransferDelegate: + virtual AsyncPixelTransferState* CreatePixelTransferState( + GLuint texture_id, + const AsyncTexImage2DParams& define_params) OVERRIDE; + virtual bool BindCompletedAsyncTransfers() OVERRIDE; + virtual void AsyncNotifyCompletion( + const AsyncMemoryParams& mem_params, + const CompletionCallback& callback) OVERRIDE; + virtual void AsyncTexImage2D( + AsyncPixelTransferState* state, + const AsyncTexImage2DParams& tex_params, + const AsyncMemoryParams& mem_params, + const base::Closure& bind_callback) OVERRIDE; + virtual void AsyncTexSubImage2D( + AsyncPixelTransferState* state, + const AsyncTexSubImage2DParams& tex_params, + const AsyncMemoryParams& mem_params) OVERRIDE; + virtual void WaitForTransferCompletion( + AsyncPixelTransferState* state) OVERRIDE; + virtual uint32 GetTextureUploadCount() OVERRIDE; + virtual base::TimeDelta GetTotalTextureUploadTime() OVERRIDE; + virtual bool ProcessMorePendingTransfers() OVERRIDE; + virtual bool NeedsProcessMorePendingTransfers() OVERRIDE; + + private: + static void PerformNotifyCompletion( + AsyncMemoryParams mem_params, + ScopedSafeSharedMemory* safe_shared_memory, + const CompletionCallback& callback); + static void PerformAsyncTexImage2D( + TransferStateInternal* state, + AsyncTexImage2DParams tex_params, + AsyncMemoryParams mem_params, + ScopedSafeSharedMemory* safe_shared_memory); + static void PerformAsyncTexSubImage2D( + TransferStateInternal* state, + AsyncTexSubImage2DParams tex_params, + AsyncMemoryParams mem_params, + ScopedSafeSharedMemory* safe_shared_memory, + scoped_refptr<TextureUploadStats> texture_upload_stats); + + // Returns true if a work-around was used. + bool WorkAroundAsyncTexImage2D( + AsyncPixelTransferState* state, + const AsyncTexImage2DParams& tex_params, + const AsyncMemoryParams& mem_params, + const base::Closure& bind_callback); + bool WorkAroundAsyncTexSubImage2D( + AsyncPixelTransferState* state, + const AsyncTexSubImage2DParams& tex_params, + const AsyncMemoryParams& mem_params); + + typedef std::list<base::WeakPtr<AsyncPixelTransferState> > TransferQueue; + TransferQueue pending_allocations_; + + scoped_refptr<TextureUploadStats> texture_upload_stats_; + bool is_imagination_; + bool is_qualcomm_; + + DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegateEGL); +}; + +} // namespace gfx + +#endif // UI_GL_ASYNC_PIXEL_TRANSFER_DELEGATE_EGL_H_ diff --git a/ui/gl/async_pixel_transfer_delegate_idle.cc b/ui/gl/async_pixel_transfer_delegate_idle.cc index a47da8f..9b95ac6 100644 --- a/ui/gl/async_pixel_transfer_delegate_idle.cc +++ b/ui/gl/async_pixel_transfer_delegate_idle.cc @@ -8,14 +8,7 @@ #include "base/debug/trace_event.h" #include "base/lazy_instance.h" #include "base/memory/shared_memory.h" -#include "base/message_loop.h" -#include "base/process_util.h" -#include "build/build_config.h" -#include "ui/gl/gl_bindings.h" -#include "ui/gl/gl_context.h" -#include "ui/gl/gl_surface.h" #include "ui/gl/safe_shared_memory_pool.h" -#include "ui/gl/scoped_make_current.h" using base::SharedMemory; using base::SharedMemoryHandle; @@ -42,34 +35,18 @@ SafeSharedMemoryPool* safe_shared_memory_pool() { static uint64 g_next_pixel_transfer_state_id = 1; -} // namespace - -#if !defined(OS_ANDROID) -scoped_ptr<AsyncPixelTransferDelegate> - AsyncPixelTransferDelegate::Create(gfx::GLContext* context) { - return AsyncPixelTransferDelegateIdle::Create(context); -} -#endif - -scoped_ptr<AsyncPixelTransferDelegate> - AsyncPixelTransferDelegateIdle::Create(gfx::GLContext* context) { - return make_scoped_ptr( - static_cast<AsyncPixelTransferDelegate*>( - new AsyncPixelTransferDelegateIdle())); -} - -class AsyncPixelTransferStateIdle : public AsyncPixelTransferState { +class AsyncPixelTransferStateImpl : public AsyncPixelTransferState { public: typedef base::Callback<void(GLuint)> TransferCallback; - explicit AsyncPixelTransferStateIdle(GLuint texture_id) + explicit AsyncPixelTransferStateImpl(GLuint texture_id) : id_(g_next_pixel_transfer_state_id++), texture_id_(texture_id), transfer_in_progress_(false) { } - virtual ~AsyncPixelTransferStateIdle() {} + virtual ~AsyncPixelTransferStateImpl() {} - // Overridden from gfx::AsyncPixelTransferState: + // Implement AsyncPixelTransferState: virtual bool TransferIsInProgress() OVERRIDE { return transfer_in_progress_; } @@ -93,6 +70,8 @@ class AsyncPixelTransferStateIdle : public AsyncPixelTransferState { bool transfer_in_progress_; }; +} // namespace + AsyncPixelTransferDelegateIdle::Task::Task( uint64 transfer_id, const base::Closure& task) : transfer_id(transfer_id), @@ -105,14 +84,12 @@ AsyncPixelTransferDelegateIdle::AsyncPixelTransferDelegateIdle() : texture_upload_count_(0) { } -AsyncPixelTransferDelegateIdle::~AsyncPixelTransferDelegateIdle() { -} +AsyncPixelTransferDelegateIdle::~AsyncPixelTransferDelegateIdle() {} -AsyncPixelTransferState* - AsyncPixelTransferDelegateIdle::CreateRawPixelTransferState( - GLuint texture_id, - const AsyncTexImage2DParams& define_params) { - return new AsyncPixelTransferStateIdle(texture_id); +AsyncPixelTransferState* AsyncPixelTransferDelegateIdle:: + CreatePixelTransferState(GLuint texture_id, + const AsyncTexImage2DParams& define_params) { + return new AsyncPixelTransferStateImpl(texture_id); } bool AsyncPixelTransferDelegateIdle::BindCompletedAsyncTransfers() { @@ -146,8 +123,8 @@ void AsyncPixelTransferDelegateIdle::AsyncTexImage2D( const AsyncTexImage2DParams& tex_params, const AsyncMemoryParams& mem_params, const base::Closure& bind_callback) { - AsyncPixelTransferStateIdle* state = - static_cast<AsyncPixelTransferStateIdle*>(transfer_state); + AsyncPixelTransferStateImpl* state = + static_cast<AsyncPixelTransferStateImpl*>(transfer_state); DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target); DCHECK(mem_params.shared_memory); DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size, @@ -157,7 +134,7 @@ void AsyncPixelTransferDelegateIdle::AsyncTexImage2D( tasks_.push_back( Task(state->id(), base::Bind( - &AsyncPixelTransferStateIdle::PerformTransfer, + &AsyncPixelTransferStateImpl::PerformTransfer, base::AsWeakPtr(state), base::Bind( &AsyncPixelTransferDelegateIdle::PerformAsyncTexImage2D, @@ -177,8 +154,8 @@ void AsyncPixelTransferDelegateIdle::AsyncTexSubImage2D( AsyncPixelTransferState* transfer_state, const AsyncTexSubImage2DParams& tex_params, const AsyncMemoryParams& mem_params) { - AsyncPixelTransferStateIdle* state = - static_cast<AsyncPixelTransferStateIdle*>(transfer_state); + AsyncPixelTransferStateImpl* state = + static_cast<AsyncPixelTransferStateImpl*>(transfer_state); DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target); DCHECK(mem_params.shared_memory); DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size, @@ -188,7 +165,7 @@ void AsyncPixelTransferDelegateIdle::AsyncTexSubImage2D( tasks_.push_back( Task(state->id(), base::Bind( - &AsyncPixelTransferStateIdle::PerformTransfer, + &AsyncPixelTransferStateImpl::PerformTransfer, base::AsWeakPtr(state), base::Bind( &AsyncPixelTransferDelegateIdle::PerformAsyncTexSubImage2D, @@ -205,8 +182,8 @@ void AsyncPixelTransferDelegateIdle::AsyncTexSubImage2D( void AsyncPixelTransferDelegateIdle::WaitForTransferCompletion( AsyncPixelTransferState* transfer_state) { - AsyncPixelTransferStateIdle* state = - static_cast<AsyncPixelTransferStateIdle*>(transfer_state); + AsyncPixelTransferStateImpl* state = + static_cast<AsyncPixelTransferStateImpl*>(transfer_state); DCHECK(state); for (std::list<Task>::iterator iter = tasks_.begin(); diff --git a/ui/gl/async_pixel_transfer_delegate_idle.h b/ui/gl/async_pixel_transfer_delegate_idle.h index f0a8fc8..96c2363 100644 --- a/ui/gl/async_pixel_transfer_delegate_idle.h +++ b/ui/gl/async_pixel_transfer_delegate_idle.h @@ -2,18 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GL_ASYNC_TASK_DELEGATE_IDLE_H_ -#define UI_GL_ASYNC_TASK_DELEGATE_IDLE_H_ +#ifndef UI_GL_ASYNC_PIXEL_TRANSFER_DELEGATE_IDLE_H_ +#define UI_GL_ASYNC_PIXEL_TRANSFER_DELEGATE_IDLE_H_ #include <list> -#include <queue> -#include "base/callback.h" #include "ui/gl/async_pixel_transfer_delegate.h" namespace gfx { -class GLSurface; -class TransferStateInternalIdle; class ScopedSafeSharedMemory; // Class which handles async pixel transfers in a platform @@ -21,13 +17,13 @@ class ScopedSafeSharedMemory; class AsyncPixelTransferDelegateIdle : public AsyncPixelTransferDelegate, public base::SupportsWeakPtr<AsyncPixelTransferDelegateIdle> { public: - static scoped_ptr<AsyncPixelTransferDelegate> - Create(gfx::GLContext* context); - AsyncPixelTransferDelegateIdle(); virtual ~AsyncPixelTransferDelegateIdle(); // implement AsyncPixelTransferDelegate: + virtual AsyncPixelTransferState* CreatePixelTransferState( + GLuint texture_id, + const AsyncTexImage2DParams& define_params) OVERRIDE; virtual bool BindCompletedAsyncTransfers() OVERRIDE; virtual void AsyncNotifyCompletion( const AsyncMemoryParams& mem_params, @@ -61,12 +57,6 @@ class AsyncPixelTransferDelegateIdle : public AsyncPixelTransferDelegate, void ProcessNotificationTasks(); - // implement AsyncPixelTransferDelegate: - virtual AsyncPixelTransferState* - CreateRawPixelTransferState( - GLuint texture_id, - const AsyncTexImage2DParams& define_params) OVERRIDE; - void PerformNotifyCompletion( AsyncMemoryParams mem_params, ScopedSafeSharedMemory* safe_shared_memory, @@ -93,5 +83,4 @@ class AsyncPixelTransferDelegateIdle : public AsyncPixelTransferDelegate, } // namespace gfx -#endif // UI_GL_ASYNC_TASK_DELEGATE_IDLE_H_ - +#endif // UI_GL_ASYNC_PIXEL_TRANSFER_DELEGATE_IDLE_H_ diff --git a/ui/gl/async_pixel_transfer_delegate_linux.cc b/ui/gl/async_pixel_transfer_delegate_linux.cc new file mode 100644 index 0000000..6eb8021 --- /dev/null +++ b/ui/gl/async_pixel_transfer_delegate_linux.cc @@ -0,0 +1,30 @@ +// Copyright (c) 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 "ui/gl/async_pixel_transfer_delegate.h" + +#include "base/debug/trace_event.h" +#include "ui/gl/async_pixel_transfer_delegate_idle.h" +#include "ui/gl/async_pixel_transfer_delegate_stub.h" +#include "ui/gl/gl_implementation.h" + +namespace gfx { + +AsyncPixelTransferDelegate* AsyncPixelTransferDelegate::Create( + gfx::GLContext* context) { + TRACE_EVENT0("gpu", "AsyncPixelTransferDelegate::Create"); + switch (GetGLImplementation()) { + case kGLImplementationOSMesaGL: + case kGLImplementationDesktopGL: + case kGLImplementationEGLGLES2: + return new AsyncPixelTransferDelegateIdle; + case kGLImplementationMockGL: + return new AsyncPixelTransferDelegateStub; + default: + NOTREACHED(); + return NULL; + } +} + +} // namespace gfx diff --git a/ui/gl/async_pixel_transfer_delegate_mac.cc b/ui/gl/async_pixel_transfer_delegate_mac.cc new file mode 100644 index 0000000..779c517 --- /dev/null +++ b/ui/gl/async_pixel_transfer_delegate_mac.cc @@ -0,0 +1,30 @@ +// Copyright (c) 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 "ui/gl/async_pixel_transfer_delegate.h" + +#include "base/debug/trace_event.h" +#include "ui/gl/async_pixel_transfer_delegate_idle.h" +#include "ui/gl/async_pixel_transfer_delegate_stub.h" +#include "ui/gl/gl_implementation.h" + +namespace gfx { + +AsyncPixelTransferDelegate* AsyncPixelTransferDelegate::Create( + gfx::GLContext* context) { + TRACE_EVENT0("gpu", "AsyncPixelTransferDelegate::Create"); + switch (GetGLImplementation()) { + case kGLImplementationOSMesaGL: + case kGLImplementationDesktopGL: + case kGLImplementationAppleGL: + return new AsyncPixelTransferDelegateIdle; + case kGLImplementationMockGL: + return new AsyncPixelTransferDelegateStub; + default: + NOTREACHED(); + return NULL; + } +} + +} // namespace gfx diff --git a/ui/gl/async_pixel_transfer_delegate_stub.cc b/ui/gl/async_pixel_transfer_delegate_stub.cc index b085b1e..79b697c 100644 --- a/ui/gl/async_pixel_transfer_delegate_stub.cc +++ b/ui/gl/async_pixel_transfer_delegate_stub.cc @@ -1,66 +1,37 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 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 "ui/gl/async_pixel_transfer_delegate_stub.h" -#include "base/memory/shared_memory.h" -#include "build/build_config.h" -#include "ui/gl/gl_bindings.h" - -using base::SharedMemory; -using base::SharedMemoryHandle; +namespace gfx { namespace { -// 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 - // are just DCHECKS here. - DCHECK(shared_memory); - DCHECK(shared_memory->memory()); - DCHECK_LE(shm_data_offset + shm_data_size, shm_size); - return static_cast<int8*>(shared_memory->memory()) + shm_data_offset; -} -} // namespace -namespace gfx { +class AsyncPixelTransferStateImpl : public AsyncPixelTransferState { + public: + AsyncPixelTransferStateImpl() {} + virtual ~AsyncPixelTransferStateImpl() {} -scoped_ptr<AsyncPixelTransferDelegate> - AsyncPixelTransferDelegateStub::Create(gfx::GLContext* context) { - return make_scoped_ptr( - static_cast<AsyncPixelTransferDelegate*>( - new AsyncPixelTransferDelegateStub())); -} + // Implement AsyncPixelTransferState: + virtual bool TransferIsInProgress() OVERRIDE { + return false; + } +}; -AsyncTransferStateStub::AsyncTransferStateStub(GLuint texture_id) { -} +} // namespace -AsyncTransferStateStub::~AsyncTransferStateStub() { -} +AsyncPixelTransferDelegateStub::AsyncPixelTransferDelegateStub() {} -bool AsyncTransferStateStub::TransferIsInProgress() { - return false; -} - -AsyncPixelTransferDelegateStub::AsyncPixelTransferDelegateStub() - : texture_upload_count_(0) { -} - -AsyncPixelTransferDelegateStub::~AsyncPixelTransferDelegateStub() { -} +AsyncPixelTransferDelegateStub::~AsyncPixelTransferDelegateStub() {} -AsyncPixelTransferState* - AsyncPixelTransferDelegateStub::CreateRawPixelTransferState( - GLuint texture_id, - const AsyncTexImage2DParams& define_params) { - return new AsyncTransferStateStub(texture_id); +AsyncPixelTransferState* AsyncPixelTransferDelegateStub:: + CreatePixelTransferState(GLuint texture_id, + const AsyncTexImage2DParams& define_params) { + return new AsyncPixelTransferStateImpl; } bool AsyncPixelTransferDelegateStub::BindCompletedAsyncTransfers() { - // Everything is already bound. return false; } @@ -75,24 +46,6 @@ void AsyncPixelTransferDelegateStub::AsyncTexImage2D( const AsyncTexImage2DParams& tex_params, const AsyncMemoryParams& mem_params, const base::Closure& bind_callback) { - // Save the define params to return later during deferred - // binding of the transfer texture. - DCHECK(transfer_state); - void* data = GetAddress(mem_params.shared_memory, - mem_params.shm_size, - mem_params.shm_data_offset, - mem_params.shm_data_size); - glTexImage2D( - tex_params.target, - tex_params.level, - tex_params.internal_format, - tex_params.width, - tex_params.height, - tex_params.border, - tex_params.format, - tex_params.type, - data); - // The texture is already fully bound so just call it now. bind_callback.Run(); } @@ -100,37 +53,18 @@ void AsyncPixelTransferDelegateStub::AsyncTexSubImage2D( AsyncPixelTransferState* transfer_state, const AsyncTexSubImage2DParams& tex_params, const AsyncMemoryParams& mem_params) { - void* data = GetAddress(mem_params.shared_memory, - mem_params.shm_size, - mem_params.shm_data_offset, - mem_params.shm_data_size); - DCHECK(transfer_state); - base::TimeTicks begin_time(base::TimeTicks::HighResNow()); - glTexSubImage2D( - tex_params.target, - tex_params.level, - tex_params.xoffset, - tex_params.yoffset, - tex_params.width, - tex_params.height, - tex_params.format, - tex_params.type, - data); - texture_upload_count_++; - total_texture_upload_time_ += base::TimeTicks::HighResNow() - begin_time; } void AsyncPixelTransferDelegateStub::WaitForTransferCompletion( AsyncPixelTransferState* state) { - // Already done. } uint32 AsyncPixelTransferDelegateStub::GetTextureUploadCount() { - return texture_upload_count_; + return 0; } base::TimeDelta AsyncPixelTransferDelegateStub::GetTotalTextureUploadTime() { - return total_texture_upload_time_; + return base::TimeDelta(); } bool AsyncPixelTransferDelegateStub::ProcessMorePendingTransfers() { diff --git a/ui/gl/async_pixel_transfer_delegate_stub.h b/ui/gl/async_pixel_transfer_delegate_stub.h index 40548b1..2a73d2d 100644 --- a/ui/gl/async_pixel_transfer_delegate_stub.h +++ b/ui/gl/async_pixel_transfer_delegate_stub.h @@ -1,38 +1,23 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 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. -#ifndef UI_GL_ASYNC_TASK_DELEGATE_STUB_H_ -#define UI_GL_ASYNC_TASK_DELEGATE_STUB_H_ +#ifndef UI_GL_ASYNC_PIXEL_TRANSFER_DELEGATE_STUB_H_ +#define UI_GL_ASYNC_PIXEL_TRANSFER_DELEGATE_STUB_H_ #include "ui/gl/async_pixel_transfer_delegate.h" namespace gfx { -class AsyncTransferStateStub : public AsyncPixelTransferState { - public: - // implement AsyncPixelTransferState: - virtual bool TransferIsInProgress() OVERRIDE; - - private: - friend class AsyncPixelTransferDelegateStub; - - explicit AsyncTransferStateStub(GLuint texture_id); - virtual ~AsyncTransferStateStub(); - DISALLOW_COPY_AND_ASSIGN(AsyncTransferStateStub); -}; - -// Class which handles async pixel transfers (as a fallback). -// This class just does the uploads synchronously. class AsyncPixelTransferDelegateStub : public AsyncPixelTransferDelegate { public: - static scoped_ptr<AsyncPixelTransferDelegate> - Create(gfx::GLContext* context); - AsyncPixelTransferDelegateStub(); virtual ~AsyncPixelTransferDelegateStub(); - // implement AsyncPixelTransferDelegate: + // Implement AsyncPixelTransferDelegate: + virtual AsyncPixelTransferState* CreatePixelTransferState( + GLuint texture_id, + const AsyncTexImage2DParams& define_params) OVERRIDE; virtual bool BindCompletedAsyncTransfers() OVERRIDE; virtual void AsyncNotifyCompletion( const AsyncMemoryParams& mem_params, @@ -52,19 +37,11 @@ class AsyncPixelTransferDelegateStub : public AsyncPixelTransferDelegate { virtual base::TimeDelta GetTotalTextureUploadTime() OVERRIDE; virtual bool ProcessMorePendingTransfers() OVERRIDE; virtual bool NeedsProcessMorePendingTransfers() OVERRIDE; - private: - // implement AsyncPixelTransferDelegate: - virtual AsyncPixelTransferState* - CreateRawPixelTransferState(GLuint texture_id, - const AsyncTexImage2DParams& define_params) OVERRIDE; - - int texture_upload_count_; - base::TimeDelta total_texture_upload_time_; + private: DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegateStub); }; } // namespace gfx -#endif // UI_GL_ASYNC_TASK_DELEGATE_ANDROID_H_ - +#endif // UI_GL_ASYNC_PIXEL_TRANSFER_DELEGATE_STUB_H_ diff --git a/ui/gl/async_pixel_transfer_delegate_sync.cc b/ui/gl/async_pixel_transfer_delegate_sync.cc new file mode 100644 index 0000000..e948957 --- /dev/null +++ b/ui/gl/async_pixel_transfer_delegate_sync.cc @@ -0,0 +1,137 @@ +// 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_sync.h" + +#include "base/memory/shared_memory.h" + +using base::SharedMemory; +using base::SharedMemoryHandle; + +namespace gfx { + +namespace { + +// 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 + // are just DCHECKS here. + DCHECK(shared_memory); + DCHECK(shared_memory->memory()); + DCHECK_LE(shm_data_offset + shm_data_size, shm_size); + return static_cast<int8*>(shared_memory->memory()) + shm_data_offset; +} + +class AsyncPixelTransferStateImpl : public AsyncPixelTransferState { + public: + AsyncPixelTransferStateImpl() {} + virtual ~AsyncPixelTransferStateImpl() {} + + // Implement AsyncPixelTransferState: + virtual bool TransferIsInProgress() OVERRIDE { + return false; + } +}; + +} // namespace + +AsyncPixelTransferDelegateSync::AsyncPixelTransferDelegateSync() + : texture_upload_count_(0) { +} + +AsyncPixelTransferDelegateSync::~AsyncPixelTransferDelegateSync() {} + +AsyncPixelTransferState* AsyncPixelTransferDelegateSync:: + CreatePixelTransferState(GLuint texture_id, + const AsyncTexImage2DParams& define_params) { + return new AsyncPixelTransferStateImpl; +} + +bool AsyncPixelTransferDelegateSync::BindCompletedAsyncTransfers() { + // Everything is already bound. + return false; +} + +void AsyncPixelTransferDelegateSync::AsyncNotifyCompletion( + const AsyncMemoryParams& mem_params, + const CompletionCallback& callback) { + callback.Run(mem_params); +} + +void AsyncPixelTransferDelegateSync::AsyncTexImage2D( + AsyncPixelTransferState* transfer_state, + const AsyncTexImage2DParams& tex_params, + const AsyncMemoryParams& mem_params, + const base::Closure& bind_callback) { + // Save the define params to return later during deferred + // binding of the transfer texture. + DCHECK(transfer_state); + void* data = GetAddress(mem_params.shared_memory, + mem_params.shm_size, + mem_params.shm_data_offset, + mem_params.shm_data_size); + glTexImage2D( + tex_params.target, + tex_params.level, + tex_params.internal_format, + tex_params.width, + tex_params.height, + tex_params.border, + tex_params.format, + tex_params.type, + data); + // The texture is already fully bound so just call it now. + bind_callback.Run(); +} + +void AsyncPixelTransferDelegateSync::AsyncTexSubImage2D( + AsyncPixelTransferState* transfer_state, + const AsyncTexSubImage2DParams& tex_params, + const AsyncMemoryParams& mem_params) { + void* data = GetAddress(mem_params.shared_memory, + mem_params.shm_size, + mem_params.shm_data_offset, + mem_params.shm_data_size); + DCHECK(transfer_state); + base::TimeTicks begin_time(base::TimeTicks::HighResNow()); + glTexSubImage2D( + tex_params.target, + tex_params.level, + tex_params.xoffset, + tex_params.yoffset, + tex_params.width, + tex_params.height, + tex_params.format, + tex_params.type, + data); + texture_upload_count_++; + total_texture_upload_time_ += base::TimeTicks::HighResNow() - begin_time; +} + +void AsyncPixelTransferDelegateSync::WaitForTransferCompletion( + AsyncPixelTransferState* state) { + // Already done. +} + +uint32 AsyncPixelTransferDelegateSync::GetTextureUploadCount() { + return texture_upload_count_; +} + +base::TimeDelta AsyncPixelTransferDelegateSync::GetTotalTextureUploadTime() { + return total_texture_upload_time_; +} + +bool AsyncPixelTransferDelegateSync::ProcessMorePendingTransfers() { + return false; +} + +bool AsyncPixelTransferDelegateSync::NeedsProcessMorePendingTransfers() { + return false; +} + +} // namespace gfx + diff --git a/ui/gl/async_pixel_transfer_delegate_sync.h b/ui/gl/async_pixel_transfer_delegate_sync.h new file mode 100644 index 0000000..ba60042 --- /dev/null +++ b/ui/gl/async_pixel_transfer_delegate_sync.h @@ -0,0 +1,51 @@ +// Copyright (c) 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. + +#ifndef UI_GL_ASYNC_PIXEL_TRANSFER_DELEGATE_SYNC_H_ +#define UI_GL_ASYNC_PIXEL_TRANSFER_DELEGATE_SYNC_H_ + +#include "ui/gl/async_pixel_transfer_delegate.h" + +namespace gfx { + +// Class which handles async pixel transfers synchronously. +class AsyncPixelTransferDelegateSync : public AsyncPixelTransferDelegate { + public: + AsyncPixelTransferDelegateSync(); + virtual ~AsyncPixelTransferDelegateSync(); + + // Implement AsyncPixelTransferDelegate: + virtual AsyncPixelTransferState* CreatePixelTransferState( + GLuint texture_id, + const AsyncTexImage2DParams& define_params) OVERRIDE; + virtual bool BindCompletedAsyncTransfers() OVERRIDE; + virtual void AsyncNotifyCompletion( + const AsyncMemoryParams& mem_params, + const CompletionCallback& callback) OVERRIDE; + virtual void AsyncTexImage2D( + AsyncPixelTransferState* state, + const AsyncTexImage2DParams& tex_params, + const AsyncMemoryParams& mem_params, + const base::Closure& bind_callback) OVERRIDE; + virtual void AsyncTexSubImage2D( + AsyncPixelTransferState* transfer_state, + const AsyncTexSubImage2DParams& tex_params, + const AsyncMemoryParams& mem_params) OVERRIDE; + virtual void WaitForTransferCompletion( + AsyncPixelTransferState* state) OVERRIDE; + virtual uint32 GetTextureUploadCount() OVERRIDE; + virtual base::TimeDelta GetTotalTextureUploadTime() OVERRIDE; + virtual bool ProcessMorePendingTransfers() OVERRIDE; + virtual bool NeedsProcessMorePendingTransfers() OVERRIDE; + + private: + int texture_upload_count_; + base::TimeDelta total_texture_upload_time_; + + DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegateSync); +}; + +} // namespace gfx + +#endif // UI_GL_ASYNC_PIXEL_TRANSFER_DELEGATE_SYNC_H_ diff --git a/ui/gl/async_pixel_transfer_delegate_win.cc b/ui/gl/async_pixel_transfer_delegate_win.cc new file mode 100644 index 0000000..6eb8021 --- /dev/null +++ b/ui/gl/async_pixel_transfer_delegate_win.cc @@ -0,0 +1,30 @@ +// Copyright (c) 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 "ui/gl/async_pixel_transfer_delegate.h" + +#include "base/debug/trace_event.h" +#include "ui/gl/async_pixel_transfer_delegate_idle.h" +#include "ui/gl/async_pixel_transfer_delegate_stub.h" +#include "ui/gl/gl_implementation.h" + +namespace gfx { + +AsyncPixelTransferDelegate* AsyncPixelTransferDelegate::Create( + gfx::GLContext* context) { + TRACE_EVENT0("gpu", "AsyncPixelTransferDelegate::Create"); + switch (GetGLImplementation()) { + case kGLImplementationOSMesaGL: + case kGLImplementationDesktopGL: + case kGLImplementationEGLGLES2: + return new AsyncPixelTransferDelegateIdle; + case kGLImplementationMockGL: + return new AsyncPixelTransferDelegateStub; + default: + NOTREACHED(); + return NULL; + } +} + +} // namespace gfx diff --git a/ui/gl/gl.gyp b/ui/gl/gl.gyp index a6bf398..b1527cb 100644 --- a/ui/gl/gl.gyp +++ b/ui/gl/gl.gyp @@ -48,11 +48,18 @@ 'android/surface_texture_bridge.h', 'android/surface_texture_listener.cc', 'android/surface_texture_listener.h', + 'async_pixel_transfer_delegate.cc', 'async_pixel_transfer_delegate.h', + 'async_pixel_transfer_delegate_android.cc', 'async_pixel_transfer_delegate_idle.cc', 'async_pixel_transfer_delegate_idle.h', + 'async_pixel_transfer_delegate_linux.cc', + 'async_pixel_transfer_delegate_mac.cc', 'async_pixel_transfer_delegate_stub.cc', 'async_pixel_transfer_delegate_stub.h', + 'async_pixel_transfer_delegate_sync.cc', + 'async_pixel_transfer_delegate_sync.h', + 'async_pixel_transfer_delegate_win.cc', 'gl_bindings.h', 'gl_bindings_skia_in_process.cc', 'gl_bindings_skia_in_process.h', @@ -183,6 +190,8 @@ 'conditions': [ ['OS != "mac"', { 'sources': [ + 'async_pixel_transfer_delegate_egl.cc', + 'async_pixel_transfer_delegate_egl.h', 'egl_util.cc', 'egl_util.h', 'gl_context_egl.cc', @@ -265,8 +274,6 @@ 'gl_jni_headers', ], 'sources': [ - 'async_pixel_transfer_delegate_android.cc', - 'async_pixel_transfer_delegate_android.h', 'gl_image_egl.cc', 'gl_image_egl.h', ], |