diff options
author | backer@chromium.org <backer@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-21 21:16:22 +0000 |
---|---|---|
committer | backer@chromium.org <backer@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-21 21:16:22 +0000 |
commit | e6861e5d37e99d90d9355b2c4460ac2df5226253 (patch) | |
tree | 58086229aee8474f3a19684619653ce8ba9e68e6 /gpu | |
parent | 4277e8c5ef126607e54e1e1485392de5d685ffe0 (diff) | |
download | chromium_src-e6861e5d37e99d90d9355b2c4460ac2df5226253.zip chromium_src-e6861e5d37e99d90d9355b2c4460ac2df5226253.tar.gz chromium_src-e6861e5d37e99d90d9355b2c4460ac2df5226253.tar.bz2 |
GPU: Use share groups for cross thread async texture upload.
This is very similar to the existing AsyncPixelTransferDelegateEGL implementation.
It currently uses glFlushes to synchronize GL streams between threads (it works on
the platforms tested).
There is a change to the GPU sandbox to allow calling setpriority. This allows us
to drop the priority of the upload thread.
Behind the flag --enable-share-group-async-texture-upload
BUG=196245
Review URL: https://chromiumcodereview.appspot.com/14516006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@201358 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu')
10 files changed, 759 insertions, 159 deletions
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate.cc b/gpu/command_buffer/service/async_pixel_transfer_delegate.cc index fb63e213..9df9c60 100644 --- a/gpu/command_buffer/service/async_pixel_transfer_delegate.cc +++ b/gpu/command_buffer/service/async_pixel_transfer_delegate.cc @@ -25,6 +25,25 @@ void* GetAddressImpl(base::SharedMemory* shared_memory, } // namespace +AsyncPixelTransferUploadStats::AsyncPixelTransferUploadStats() + : texture_upload_count_(0) {} + +AsyncPixelTransferUploadStats::~AsyncPixelTransferUploadStats() {} + +void AsyncPixelTransferUploadStats::AddUpload(base::TimeDelta transfer_time) { + base::AutoLock scoped_lock(lock_); + texture_upload_count_++; + total_texture_upload_time_ += transfer_time; +} + +int AsyncPixelTransferUploadStats::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_; +} + AsyncPixelTransferState::AsyncPixelTransferState(){} AsyncPixelTransferState::~AsyncPixelTransferState(){} @@ -52,4 +71,4 @@ void* AsyncPixelTransferDelegate::GetAddress( mem_params.shm_data_size); } -}// namespace gpu +} // namespace gpu diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate.h b/gpu/command_buffer/service/async_pixel_transfer_delegate.h index 75f3ad2..b21a63b 100644 --- a/gpu/command_buffer/service/async_pixel_transfer_delegate.h +++ b/gpu/command_buffer/service/async_pixel_transfer_delegate.h @@ -9,6 +9,7 @@ #include "base/callback.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" +#include "base/synchronization/lock.h" #include "base/time.h" #include "gpu/gpu_export.h" #include "ui/gl/gl_bindings.h" @@ -50,6 +51,26 @@ struct AsyncMemoryParams { uint32 shm_data_size; }; +class AsyncPixelTransferUploadStats + : public base::RefCountedThreadSafe<AsyncPixelTransferUploadStats> { + public: + AsyncPixelTransferUploadStats(); + + void AddUpload(base::TimeDelta transfer_time); + int GetStats(base::TimeDelta* total_texture_upload_time); + + private: + friend class base::RefCountedThreadSafe<AsyncPixelTransferUploadStats>; + + ~AsyncPixelTransferUploadStats(); + + int texture_upload_count_; + base::TimeDelta total_texture_upload_time_; + base::Lock lock_; + + DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferUploadStats); +}; + // AsyncPixelTransferState holds the resources required to do async // transfers on one texture. It should stay alive for the lifetime // of the texture to allow multiple transfers. @@ -62,7 +83,6 @@ class GPU_EXPORT AsyncPixelTransferState : virtual bool TransferIsInProgress() = 0; protected: - friend class base::RefCounted<AsyncPixelTransferState>; AsyncPixelTransferState(); private: diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.cc b/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.cc index 6839dda..e8b3ddc 100644 --- a/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.cc +++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.cc @@ -72,6 +72,15 @@ void DoFullTexSubImage2D(const AsyncTexImage2DParams& tex_params, void* data) { tex_params.format, tex_params.type, data); } +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); +} + class TransferThread : public base::Thread { public: TransferThread() : base::Thread(kAsyncTransferThreadName) { @@ -130,8 +139,6 @@ 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. @@ -164,7 +171,7 @@ class TransferStateInternal DCHECK(texture_id_); DCHECK_NE(EGL_NO_IMAGE_KHR, egl_image_); - gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, texture_id_); + glBindTexture(GL_TEXTURE_2D, texture_id_); glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_); bind_callback_.Run(); @@ -232,12 +239,110 @@ class TransferStateInternal } void WaitForTransferCompletion() { + TRACE_EVENT0("gpu", "WaitForTransferCompletion"); + // TODO(backer): Deschedule the channel rather than blocking the main GPU + // thread (crbug.com/240265). transfer_completion_.Wait(); } + void PerformAsyncTexImage2D(AsyncTexImage2DParams tex_params, + AsyncMemoryParams mem_params, + ScopedSafeSharedMemory* safe_shared_memory) { + TRACE_EVENT2("gpu", + "PerformAsyncTexImage", + "width", + tex_params.width, + "height", + tex_params.height); + DCHECK(!thread_texture_id_); + DCHECK_EQ(0, tex_params.level); + DCHECK_EQ(EGL_NO_IMAGE_KHR, egl_image_); + + void* data = + AsyncPixelTransferDelegate::GetAddress(safe_shared_memory, mem_params); + { + TRACE_EVENT0("gpu", "glTexImage2D no data"); + glGenTextures(1, &thread_texture_id_); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 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 (use_image_preserved_) + DoTexImage2D(tex_params, data); + else + DoTexImage2D(tex_params, NULL); + } + + 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 (!use_image_preserved_) + DoFullTexSubImage2D(tex_params, data); + } + + WaitForLastUpload(); + MarkAsCompleted(); + + DCHECK(CHECK_GL()); + } + + void PerformAsyncTexSubImage2D( + AsyncTexSubImage2DParams tex_params, + AsyncMemoryParams mem_params, + ScopedSafeSharedMemory* safe_shared_memory, + scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats) { + TRACE_EVENT2("gpu", + "PerformAsyncTexSubImage2D", + "width", + tex_params.width, + "height", + tex_params.height); + + DCHECK_NE(EGL_NO_IMAGE_KHR, egl_image_); + DCHECK_EQ(0, tex_params.level); + + void* data = + AsyncPixelTransferDelegate::GetAddress(safe_shared_memory, mem_params); + + base::TimeTicks begin_time; + if (texture_upload_stats) + begin_time = base::TimeTicks::HighResNow(); + + if (!thread_texture_id_) { + TRACE_EVENT0("gpu", "glEGLImageTargetTexture2DOES"); + glGenTextures(1, &thread_texture_id_); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, thread_texture_id_); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_); + } else { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, thread_texture_id_); + } + { + TRACE_EVENT0("gpu", "glTexSubImage2D"); + DoTexSubImage2D(tex_params, data); + } + WaitForLastUpload(); + MarkAsCompleted(); + + DCHECK(CHECK_GL()); + if (texture_upload_stats) { + texture_upload_stats->AddUpload(base::TimeTicks::HighResNow() - + begin_time); + } + } + protected: friend class base::RefCountedThreadSafe<TransferStateInternal>; - friend class AsyncPixelTransferDelegateEGL; + friend class gpu::AsyncPixelTransferDelegateEGL; static void DeleteTexture(GLuint id) { glDeleteTextures(1, &id); @@ -282,38 +387,6 @@ class TransferStateInternal 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 { @@ -350,7 +423,7 @@ AsyncPixelTransferDelegateEGL::AsyncPixelTransferDelegateEGL() { 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); + texture_upload_stats_ = make_scoped_refptr(new AsyncPixelTransferUploadStats); } AsyncPixelTransferDelegateEGL::~AsyncPixelTransferDelegateEGL() {} @@ -382,6 +455,8 @@ AsyncPixelTransferState* AsyncPixelTransferDelegateEGL:: } void AsyncPixelTransferDelegateEGL::BindCompletedAsyncTransfers() { + scoped_ptr<gfx::ScopedTextureBinder> texture_binder; + while(!pending_allocations_.empty()) { if (!pending_allocations_.front().get()) { pending_allocations_.pop_front(); @@ -393,6 +468,10 @@ void AsyncPixelTransferDelegateEGL::BindCompletedAsyncTransfers() { // Terminate early, as all transfers finish in order, currently. if (state->TransferIsInProgress()) break; + + if (!texture_binder) + texture_binder.reset(new gfx::ScopedTextureBinder(GL_TEXTURE_2D, 0)); + // If the transfer is finished, bind it to the texture // and remove it from pending list. state->BindTransfer(); @@ -421,7 +500,6 @@ void AsyncPixelTransferDelegateEGL::AsyncNotifyCompletion( void AsyncPixelTransferDelegateEGL::WaitForTransferCompletion( AsyncPixelTransferState* transfer_state) { - TRACE_EVENT0("gpu", "WaitForTransferCompletion"); scoped_refptr<TransferStateInternal> state = static_cast<AsyncTransferStateImpl*>(transfer_state)->internal_.get(); DCHECK(state); @@ -482,7 +560,7 @@ void AsyncPixelTransferDelegateEGL::AsyncTexImage2D( // a use-after-free of the raw pixels. transfer_message_loop_proxy()->PostTask(FROM_HERE, base::Bind( - &AsyncPixelTransferDelegateEGL::PerformAsyncTexImage2D, + &TransferStateInternal::PerformAsyncTexImage2D, state, tex_params, mem_params, @@ -525,7 +603,7 @@ void AsyncPixelTransferDelegateEGL::AsyncTexSubImage2D( // a use-after-free of the raw pixels. transfer_message_loop_proxy()->PostTask(FROM_HERE, base::Bind( - &AsyncPixelTransferDelegateEGL::PerformAsyncTexSubImage2D, + &TransferStateInternal::PerformAsyncTexSubImage2D, state, tex_params, mem_params, @@ -556,17 +634,6 @@ 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, @@ -577,98 +644,6 @@ void AsyncPixelTransferDelegateEGL::PerformNotifyCompletion( 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, mem_params); - { - 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, mem_params); - - 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))); diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.h b/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.h index 39a63cd..67cd971 100644 --- a/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.h +++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.h @@ -11,8 +11,6 @@ namespace gpu { class ScopedSafeSharedMemory; -class TextureUploadStats; -class TransferStateInternal; // Class which handles async pixel transfers using EGLImageKHR and another // upload thread @@ -52,17 +50,6 @@ class AsyncPixelTransferDelegateEGL 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( @@ -78,7 +65,7 @@ class AsyncPixelTransferDelegateEGL typedef std::list<base::WeakPtr<AsyncPixelTransferState> > TransferQueue; TransferQueue pending_allocations_; - scoped_refptr<TextureUploadStats> texture_upload_stats_; + scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats_; bool is_imagination_; bool is_qualcomm_; diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_linux.cc b/gpu/command_buffer/service/async_pixel_transfer_delegate_linux.cc index 76a3e4b..470ac1d 100644 --- a/gpu/command_buffer/service/async_pixel_transfer_delegate_linux.cc +++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_linux.cc @@ -4,15 +4,25 @@ #include "gpu/command_buffer/service/async_pixel_transfer_delegate.h" +#include "base/command_line.h" #include "base/debug/trace_event.h" #include "gpu/command_buffer/service/async_pixel_transfer_delegate_idle.h" +#include "gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.h" #include "gpu/command_buffer/service/async_pixel_transfer_delegate_stub.h" +#include "gpu/command_buffer/service/gpu_switches.h" #include "ui/gl/gl_implementation.h" namespace gpu { AsyncPixelTransferDelegate* AsyncPixelTransferDelegate::Create( gfx::GLContext* context) { + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableShareGroupAsyncTextureUpload)) { + DCHECK(context); + return static_cast<AsyncPixelTransferDelegate*> ( + new AsyncPixelTransferDelegateShareGroup(context)); + } + TRACE_EVENT0("gpu", "AsyncPixelTransferDelegate::Create"); switch (gfx::GetGLImplementation()) { case gfx::kGLImplementationOSMesaGL: diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.cc b/gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.cc new file mode 100644 index 0000000..67d084d --- /dev/null +++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.cc @@ -0,0 +1,523 @@ +// Copyright 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 "gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.h" + +#if defined(OS_ANDROID) || defined(OS_LINUX) +// TODO(epenner): Move thread priorities to base. (crbug.com/170549) +#include <sys/resource.h> +#endif + +#include "base/bind.h" +#include "base/debug/trace_event.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/synchronization/cancellation_flag.h" +#include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "gpu/command_buffer/service/async_pixel_transfer_delegate.h" +#include "gpu/command_buffer/service/safe_shared_memory_pool.h" +#include "ui/gl/gl_bindings.h" +#include "ui/gl/gl_context.h" +#include "ui/gl/gl_surface.h" +#include "ui/gl/gpu_preference.h" +#include "ui/gl/scoped_binders.h" + +namespace gpu { + +namespace { + +const int kDefaultNiceValue = 0; // Default priority. +const int kIdleNiceValue = 10; // Idle priority. + +const char kAsyncTransferThreadName[] = "AsyncTransferThread"; + +// TODO(backer): Factor out common thread scheduling logic from the EGL and +// ShareGroup implementations. http://crbug.com/239889 +class TransferThread : public base::Thread { + public: + TransferThread() + : base::Thread(kAsyncTransferThreadName), + initialized_(false) { + Start(); + } + + virtual ~TransferThread() { + // The only instance of this class was declared leaky. + NOTREACHED(); + } + + virtual void Init() OVERRIDE { +#if defined(OS_ANDROID) || defined(OS_LINUX) + // TODO(epenner): Move thread priorities to base. (crbug.com/170549) + setpriority( + PRIO_PROCESS, base::PlatformThread::CurrentId(), kIdleNiceValue); +#endif + } + + void InitializeOnMainThread(gfx::GLContext* parent_context) { + TRACE_EVENT0("gpu", "TransferThread::InitializeOnMainThread"); + if (initialized_) + return; + + base::WaitableEvent wait_for_init(true, false); + message_loop_proxy()->PostTask( + FROM_HERE, + base::Bind(&TransferThread::InitializeOnTransferThread, + base::Unretained(this), + base::Unretained(parent_context), + &wait_for_init)); + wait_for_init.Wait(); + } + + virtual void CleanUp() OVERRIDE { + surface_ = NULL; + context_ = NULL; + } + + SafeSharedMemoryPool* safe_shared_memory_pool() { + return &safe_shared_memory_pool_; + } + + private: + bool initialized_; + + scoped_refptr<gfx::GLSurface> surface_; + scoped_refptr<gfx::GLContext> context_; + SafeSharedMemoryPool safe_shared_memory_pool_; + + void InitializeOnTransferThread(gfx::GLContext* parent_context, + base::WaitableEvent* caller_wait) { + TRACE_EVENT0("gpu", "InitializeOnTransferThread"); + + if (!parent_context) { + LOG(ERROR) << "No parent context provided."; + caller_wait->Signal(); + return; + } + + surface_ = gfx::GLSurface::CreateOffscreenGLSurface(false, gfx::Size(1, 1)); + if (!surface_) { + LOG(ERROR) << "Unable to create GLSurface"; + caller_wait->Signal(); + return; + } + + // TODO(backer): This is coded for integrated GPUs. For discrete GPUs + // we would probably want to use a PBO texture upload for a true async + // upload (that would hopefully be optimized as a DMA transfer by the + // driver). + context_ = gfx::GLContext::CreateGLContext(parent_context->share_group(), + surface_, + gfx::PreferIntegratedGpu); + if (!context_) { + LOG(ERROR) << "Unable to create GLContext."; + caller_wait->Signal(); + return; + } + + context_->MakeCurrent(surface_); + initialized_ = true; + caller_wait->Signal(); + } + + DISALLOW_COPY_AND_ASSIGN(TransferThread); +}; + +base::LazyInstance<TransferThread>::Leaky + 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(); +} + +// Class which holds async pixel transfers state. +// The texture_id 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) + : texture_id_(texture_id), + transfer_completion_(true, true) { + define_params_ = define_params; + } + + // Implement AsyncPixelTransferState: + bool TransferIsInProgress() { + return !transfer_completion_.IsSignaled(); + } + + void BindTransfer() { + TRACE_EVENT2("gpu", "BindAsyncTransfer", + "width", define_params_.width, + "height", define_params_.height); + DCHECK(texture_id_); + + glBindTexture(GL_TEXTURE_2D, texture_id_); + bind_callback_.Run(); + } + + void MarkAsTransferIsInProgress() { + transfer_completion_.Reset(); + } + + void MarkAsCompleted() { + TRACE_EVENT0("gpu", "MarkAsCompleted"); + glFlush(); + transfer_completion_.Signal(); + } + + void WaitForTransferCompletion() { + TRACE_EVENT0("gpu", "WaitForTransferCompletion"); + // TODO(backer): Deschedule the channel rather than blocking the main GPU + // thread (crbug.com/240265). + transfer_completion_.Wait(); + } + + GLuint texture_id() { return texture_id_; } + + void SetBindCallback(base::Closure bind_callback) { + bind_callback_ = bind_callback; + } + + void PerformAsyncTexImage2D(AsyncTexImage2DParams tex_params, + AsyncMemoryParams mem_params, + ScopedSafeSharedMemory* safe_shared_memory) { + base::AutoLock locked(upload_lock_); + if (cancel_upload_flag_.IsSet()) + return; + + TRACE_EVENT2("gpu", + "PerformAsyncTexImage", + "width", + tex_params.width, + "height", + tex_params.height); + DCHECK_EQ(0, tex_params.level); + + void* data = + AsyncPixelTransferDelegate::GetAddress(safe_shared_memory, mem_params); + + { + TRACE_EVENT0("gpu", "glTexImage2D"); + glBindTexture(GL_TEXTURE_2D, texture_id_); + 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); + glBindTexture(GL_TEXTURE_2D, 0); + } + + MarkAsCompleted(); + } + + void PerformAsyncTexSubImage2D( + AsyncTexSubImage2DParams tex_params, + AsyncMemoryParams mem_params, + ScopedSafeSharedMemory* safe_shared_memory, + scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats) { + base::AutoLock locked(upload_lock_); + if (cancel_upload_flag_.IsSet()) + return; + + TRACE_EVENT2("gpu", + "PerformAsyncTexSubImage2D", + "width", + tex_params.width, + "height", + tex_params.height); + DCHECK_EQ(0, tex_params.level); + + base::TimeTicks begin_time; + if (texture_upload_stats) + begin_time = base::TimeTicks::HighResNow(); + + void* data = + AsyncPixelTransferDelegate::GetAddress(safe_shared_memory, mem_params); + + { + TRACE_EVENT0("gpu", "glTexSubImage2D"); + glBindTexture(GL_TEXTURE_2D, texture_id_); + 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); + glBindTexture(GL_TEXTURE_2D, 0); + } + + MarkAsCompleted(); + + if (texture_upload_stats) { + texture_upload_stats->AddUpload(base::TimeTicks::HighResNow() - + begin_time); + } + } + + base::CancellationFlag* cancel_upload_flag() { return &cancel_upload_flag_; } + base::Lock* upload_lock() { return &upload_lock_; } + + private: + friend class base::RefCountedThreadSafe<TransferStateInternal>; + + virtual ~TransferStateInternal() { + } + + // Used to cancel pending uploads. + base::CancellationFlag cancel_upload_flag_; + base::Lock upload_lock_; + + GLuint texture_id_; + + // Definition params for texture that needs binding. + AsyncTexImage2DParams define_params_; + + // Indicates that an async transfer is in progress. + base::WaitableEvent transfer_completion_; + + // Callback to invoke when AsyncTexImage2D is complete + // and the client can safely use the texture. This occurs + // during BindCompletedAsyncTransfers(). + base::Closure bind_callback_; +}; + +void PerformNotifyCompletion( + AsyncMemoryParams mem_params, + ScopedSafeSharedMemory* safe_shared_memory, + const AsyncPixelTransferDelegate::CompletionCallback& callback) { + TRACE_EVENT0("gpu", "PerformNotifyCompletion"); + AsyncMemoryParams safe_mem_params = mem_params; + safe_mem_params.shared_memory = safe_shared_memory->shared_memory(); + callback.Run(safe_mem_params); +} + +} // namespace + +// ShareGroup 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) + : internal_(new TransferStateInternal(texture_id, define_params)) {} + + virtual ~AsyncTransferStateImpl() { + TRACE_EVENT0("gpu", " ~AsyncTransferStateImpl"); + base::AutoLock locked(*internal_->upload_lock()); + internal_->cancel_upload_flag()->Set(); + } + + virtual bool TransferIsInProgress() OVERRIDE { + return internal_->TransferIsInProgress(); + } + + TransferStateInternal* internal() { return internal_.get(); } + + private: + scoped_refptr<TransferStateInternal> internal_; +}; + +AsyncPixelTransferDelegateShareGroup::AsyncPixelTransferDelegateShareGroup( + gfx::GLContext* context) { + g_transfer_thread.Pointer()->InitializeOnMainThread(context); + + // TODO(reveman): Skip this if --enable-gpu-benchmarking is not present. + texture_upload_stats_ = make_scoped_refptr(new AsyncPixelTransferUploadStats); +} + +AsyncPixelTransferDelegateShareGroup::~AsyncPixelTransferDelegateShareGroup() { +} + +AsyncPixelTransferState* + AsyncPixelTransferDelegateShareGroup:: + CreatePixelTransferState( + GLuint texture_id, + const AsyncTexImage2DParams& define_params) { + return static_cast<AsyncPixelTransferState*>( + new AsyncTransferStateImpl(texture_id, define_params)); +} + +void AsyncPixelTransferDelegateShareGroup::BindCompletedAsyncTransfers() { + scoped_ptr<gfx::ScopedTextureBinder> texture_binder; + + 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(); + // Terminate early, as all transfers finish in order, currently. + if (state->TransferIsInProgress()) + break; + + if (!texture_binder) + texture_binder.reset(new gfx::ScopedTextureBinder(GL_TEXTURE_2D, 0)); + + // Used to set tex info from the gles2 cmd decoder once upload has + // finished (it'll bind the texture and call a callback). + state->BindTransfer(); + + pending_allocations_.pop_front(); + } +} + +void AsyncPixelTransferDelegateShareGroup::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(&PerformNotifyCompletion, + mem_params, + base::Owned( + new ScopedSafeSharedMemory(safe_shared_memory_pool(), + mem_params.shared_memory, + mem_params.shm_size)), + callback)); +} + +void AsyncPixelTransferDelegateShareGroup::WaitForTransferCompletion( + AsyncPixelTransferState* transfer_state) { + scoped_refptr<TransferStateInternal> state = + static_cast<AsyncTransferStateImpl*>(transfer_state)->internal(); + DCHECK(state); + DCHECK(state->texture_id()); + + if (state->TransferIsInProgress()) { +#if defined(OS_ANDROID) || defined(OS_LINUX) + // TODO(epenner): Move thread priorities to base. (crbug.com/170549) + setpriority(PRIO_PROCESS, + g_transfer_thread.Pointer()->thread_id(), + kDefaultNiceValue); +#endif + + state->WaitForTransferCompletion(); + DCHECK(!state->TransferIsInProgress()); + +#if defined(OS_ANDROID) || defined(OS_LINUX) + // TODO(epenner): Move thread priorities to base. (crbug.com/170549) + setpriority(PRIO_PROCESS, + g_transfer_thread.Pointer()->thread_id(), + kIdleNiceValue); +#endif + } +} + +void AsyncPixelTransferDelegateShareGroup::AsyncTexImage2D( + AsyncPixelTransferState* transfer_state, + const AsyncTexImage2DParams& tex_params, + const AsyncMemoryParams& mem_params, + const base::Closure& bind_callback) { + scoped_refptr<TransferStateInternal> state = + static_cast<AsyncTransferStateImpl*>(transfer_state)->internal(); + 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(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->SetBindCallback(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( + &TransferStateInternal::PerformAsyncTexImage2D, + state, + tex_params, + mem_params, + base::Owned(new ScopedSafeSharedMemory(safe_shared_memory_pool(), + mem_params.shared_memory, + mem_params.shm_size)))); +} + +void AsyncPixelTransferDelegateShareGroup::AsyncTexSubImage2D( + AsyncPixelTransferState* transfer_state, + const AsyncTexSubImage2DParams& tex_params, + const AsyncMemoryParams& mem_params) { + TRACE_EVENT2("gpu", "AsyncTexSubImage2D", + "width", tex_params.width, + "height", tex_params.height); + scoped_refptr<TransferStateInternal> state = + static_cast<AsyncTransferStateImpl*>(transfer_state)->internal(); + + 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(); + + // 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( + &TransferStateInternal::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_)); +} + +uint32 AsyncPixelTransferDelegateShareGroup::GetTextureUploadCount() { + DCHECK(texture_upload_stats_); + return texture_upload_stats_->GetStats(NULL); +} + +base::TimeDelta + AsyncPixelTransferDelegateShareGroup::GetTotalTextureUploadTime() { + DCHECK(texture_upload_stats_); + base::TimeDelta total_texture_upload_time; + texture_upload_stats_->GetStats(&total_texture_upload_time); + return total_texture_upload_time; +} + +void AsyncPixelTransferDelegateShareGroup::ProcessMorePendingTransfers() { +} + +bool AsyncPixelTransferDelegateShareGroup::NeedsProcessMorePendingTransfers() { + return false; +} + +} // namespace gpu diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.h b/gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.h new file mode 100644 index 0000000..f0887a9a --- /dev/null +++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.h @@ -0,0 +1,58 @@ +// 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 GPU_COMMAND_BUFFER_SERVICE_ASYNC_PIXEL_TRANSFER_DELEGATE_SHARE_GROUP_H_ +#define GPU_COMMAND_BUFFER_SERVICE_ASYNC_PIXEL_TRANSFER_DELEGATE_SHARE_GROUP_H_ + +#include "gpu/command_buffer/service/async_pixel_transfer_delegate.h" + +#include <list> + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" + +namespace gpu { + +class AsyncPixelTransferDelegateShareGroup : public AsyncPixelTransferDelegate { + public: + explicit AsyncPixelTransferDelegateShareGroup(gfx::GLContext* context); + virtual ~AsyncPixelTransferDelegateShareGroup(); + + // Implement AsyncPixelTransferDelegate: + virtual AsyncPixelTransferState* CreatePixelTransferState( + GLuint texture_id, + const AsyncTexImage2DParams& define_params) OVERRIDE; + virtual void 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 void ProcessMorePendingTransfers() OVERRIDE; + virtual bool NeedsProcessMorePendingTransfers() OVERRIDE; + + private: + typedef std::list<base::WeakPtr<AsyncPixelTransferState> > TransferQueue; + TransferQueue pending_allocations_; + + scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats_; + + DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegateShareGroup); +}; + +} // namespace gpu + +#endif // GPU_COMMAND_BUFFER_SERVICE_ASYNC_PIXEL_TRANSFER_DELEGATE_SHARE_GROUP_H_ diff --git a/gpu/command_buffer/service/gpu_switches.cc b/gpu/command_buffer/service/gpu_switches.cc index 6d699ad..221dd60 100644 --- a/gpu/command_buffer/service/gpu_switches.cc +++ b/gpu/command_buffer/service/gpu_switches.cc @@ -62,6 +62,10 @@ const char kTraceGL[] = "trace-gl"; // Disables the GPU shader on disk cache. const char kDisableGpuShaderDiskCache[] = "disable-gpu-shader-disk-cache"; +// Allows async texture uploads (off main thread) via GL context sharing. +const char kEnableShareGroupAsyncTextureUpload[] = + "enable-share-group-async-texture-upload"; + const char* kGpuSwitches[] = { kCompileShaderAlwaysSucceeds, kDisableGLErrorLimit, @@ -80,6 +84,7 @@ const char* kGpuSwitches[] = { kGpuProgramCacheSizeKb, kTraceGL, kDisableGpuShaderDiskCache, + kEnableShareGroupAsyncTextureUpload, }; const int kNumGpuSwitches = arraysize(kGpuSwitches); diff --git a/gpu/command_buffer/service/gpu_switches.h b/gpu/command_buffer/service/gpu_switches.h index ba6f804..7634c57 100644 --- a/gpu/command_buffer/service/gpu_switches.h +++ b/gpu/command_buffer/service/gpu_switches.h @@ -28,6 +28,7 @@ GPU_EXPORT extern const char kGpuDriverBugWorkarounds[]; GPU_EXPORT extern const char kGpuProgramCacheSizeKb[]; GPU_EXPORT extern const char kTraceGL[]; GPU_EXPORT extern const char kDisableGpuShaderDiskCache[]; +GPU_EXPORT extern const char kEnableShareGroupAsyncTextureUpload[]; GPU_EXPORT extern const char* kGpuSwitches[]; GPU_EXPORT extern const int kNumGpuSwitches; diff --git a/gpu/command_buffer_service.gypi b/gpu/command_buffer_service.gypi index c31e8f2..5df79b6 100644 --- a/gpu/command_buffer_service.gypi +++ b/gpu/command_buffer_service.gypi @@ -32,6 +32,8 @@ 'command_buffer/service/async_pixel_transfer_delegate_idle.h', 'command_buffer/service/async_pixel_transfer_delegate_linux.cc', 'command_buffer/service/async_pixel_transfer_delegate_mac.cc', + 'command_buffer/service/async_pixel_transfer_delegate_share_group.cc', + 'command_buffer/service/async_pixel_transfer_delegate_share_group.h', 'command_buffer/service/async_pixel_transfer_delegate_stub.cc', 'command_buffer/service/async_pixel_transfer_delegate_stub.h', 'command_buffer/service/async_pixel_transfer_delegate_sync.cc', |