| // 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_manager_egl.h" |
| |
| #include <list> |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/lazy_instance.h" |
| #include "base/logging.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/threading/thread.h" |
| #include "base/trace_event/trace_event.h" |
| #include "base/trace_event/trace_event_synthetic_delay.h" |
| #include "gpu/command_buffer/service/async_pixel_transfer_delegate.h" |
| #include "ui/gl/gl_context.h" |
| #include "ui/gl/gl_surface_egl.h" |
| #include "ui/gl/scoped_binders.h" |
| |
| namespace gpu { |
| |
| 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); |
| } |
| |
| 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); |
| } |
| |
| void PerformNotifyCompletion( |
| AsyncMemoryParams mem_params, |
| scoped_refptr<AsyncPixelTransferCompletionObserver> observer) { |
| TRACE_EVENT0("gpu", "PerformNotifyCompletion"); |
| observer->DidComplete(mem_params); |
| } |
| |
| class TransferThread : public base::Thread { |
| public: |
| TransferThread() : base::Thread(kAsyncTransferThreadName) { |
| Start(); |
| #if defined(OS_ANDROID) || defined(OS_LINUX) |
| SetPriority(base::ThreadPriority::BACKGROUND); |
| #endif |
| } |
| ~TransferThread() override { Stop(); } |
| |
| void Init() override { |
| gfx::GLShareGroup* share_group = NULL; |
| surface_ = new gfx::PbufferGLSurfaceEGL(gfx::Size(1, 1)); |
| surface_->Initialize(); |
| context_ = gfx::GLContext::CreateGLContext( |
| share_group, surface_.get(), gfx::PreferDiscreteGpu); |
| bool is_current = context_->MakeCurrent(surface_.get()); |
| DCHECK(is_current); |
| } |
| |
| void CleanUp() override { |
| surface_ = NULL; |
| context_->ReleaseCurrent(surface_.get()); |
| context_ = NULL; |
| } |
| |
| private: |
| scoped_refptr<gfx::GLContext> context_; |
| scoped_refptr<gfx::GLSurface> surface_; |
| |
| 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().get(); |
| } |
| |
| // 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_); |
| if (EGL_NO_IMAGE_KHR == egl_image_) |
| return; |
| |
| 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); |
| |
| DLOG_IF(ERROR, EGL_NO_IMAGE_KHR == egl_image_) |
| << "eglCreateImageKHR failed"; |
| } |
| |
| 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() { |
| TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("gpu.AsyncTexImage"); |
| transfer_completion_.Reset(); |
| } |
| |
| void MarkAsCompleted() { |
| TRACE_EVENT_SYNTHETIC_DELAY_END("gpu.AsyncTexImage"); |
| 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(); |
| } |
| |
| void PerformAsyncTexImage2D( |
| AsyncTexImage2DParams tex_params, |
| AsyncMemoryParams mem_params, |
| scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats) { |
| TRACE_EVENT2("gpu", |
| "PerformAsyncTexImage", |
| "width", |
| tex_params.width, |
| "height", |
| tex_params.height); |
| DCHECK(!thread_texture_id_); |
| DCHECK_EQ(0, tex_params.level); |
| if (EGL_NO_IMAGE_KHR != egl_image_) { |
| MarkAsCompleted(); |
| return; |
| } |
| |
| void* data = mem_params.GetDataAddress(); |
| |
| base::TimeTicks begin_time; |
| if (texture_upload_stats.get()) |
| begin_time = base::TimeTicks::Now(); |
| |
| { |
| 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()); |
| if (texture_upload_stats.get()) { |
| texture_upload_stats->AddUpload(base::TimeTicks::Now() - begin_time); |
| } |
| } |
| |
| void PerformAsyncTexSubImage2D( |
| AsyncTexSubImage2DParams tex_params, |
| AsyncMemoryParams mem_params, |
| 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 = mem_params.GetDataAddress(); |
| |
| base::TimeTicks begin_time; |
| if (texture_upload_stats.get()) |
| begin_time = base::TimeTicks::Now(); |
| |
| 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.get()) { |
| texture_upload_stats->AddUpload(base::TimeTicks::Now() - begin_time); |
| } |
| } |
| |
| protected: |
| friend class base::RefCountedThreadSafe<TransferStateInternal>; |
| friend class gpu::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_; |
| }; |
| |
| } // namespace |
| |
| // Class which handles async pixel transfers using EGLImageKHR and another |
| // upload thread |
| class AsyncPixelTransferDelegateEGL |
| : public AsyncPixelTransferDelegate, |
| public base::SupportsWeakPtr<AsyncPixelTransferDelegateEGL> { |
| public: |
| AsyncPixelTransferDelegateEGL( |
| AsyncPixelTransferManagerEGL::SharedState* shared_state, |
| GLuint texture_id, |
| const AsyncTexImage2DParams& define_params); |
| ~AsyncPixelTransferDelegateEGL() override; |
| |
| void BindTransfer() { state_->BindTransfer(); } |
| |
| // Implement AsyncPixelTransferDelegate: |
| void AsyncTexImage2D(const AsyncTexImage2DParams& tex_params, |
| const AsyncMemoryParams& mem_params, |
| const base::Closure& bind_callback) override; |
| void AsyncTexSubImage2D(const AsyncTexSubImage2DParams& tex_params, |
| const AsyncMemoryParams& mem_params) override; |
| bool TransferIsInProgress() override; |
| void WaitForTransferCompletion() override; |
| |
| private: |
| // Returns true if a work-around was used. |
| bool WorkAroundAsyncTexImage2D( |
| const AsyncTexImage2DParams& tex_params, |
| const AsyncMemoryParams& mem_params, |
| const base::Closure& bind_callback); |
| bool WorkAroundAsyncTexSubImage2D( |
| const AsyncTexSubImage2DParams& tex_params, |
| const AsyncMemoryParams& mem_params); |
| |
| // A raw pointer is safe because the SharedState is owned by the Manager, |
| // which owns this Delegate. |
| AsyncPixelTransferManagerEGL::SharedState* shared_state_; |
| scoped_refptr<TransferStateInternal> state_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegateEGL); |
| }; |
| |
| AsyncPixelTransferDelegateEGL::AsyncPixelTransferDelegateEGL( |
| AsyncPixelTransferManagerEGL::SharedState* shared_state, |
| GLuint texture_id, |
| const AsyncTexImage2DParams& define_params) |
| : shared_state_(shared_state) { |
| // 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 = !shared_state_->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 = shared_state_->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 = |
| shared_state_->is_qualcomm || shared_state_->is_imagination; |
| |
| state_ = new TransferStateInternal(texture_id, |
| define_params, |
| wait_for_uploads, |
| wait_for_creation, |
| use_image_preserved); |
| } |
| |
| AsyncPixelTransferDelegateEGL::~AsyncPixelTransferDelegateEGL() {} |
| |
| bool AsyncPixelTransferDelegateEGL::TransferIsInProgress() { |
| return state_->TransferIsInProgress(); |
| } |
| |
| void AsyncPixelTransferDelegateEGL::WaitForTransferCompletion() { |
| if (state_->TransferIsInProgress()) { |
| #if defined(OS_ANDROID) || defined(OS_LINUX) |
| g_transfer_thread.Pointer()->SetPriority(base::ThreadPriority::BACKGROUND); |
| #endif |
| |
| state_->WaitForTransferCompletion(); |
| DCHECK(!state_->TransferIsInProgress()); |
| |
| #if defined(OS_ANDROID) || defined(OS_LINUX) |
| g_transfer_thread.Pointer()->SetPriority(base::ThreadPriority::BACKGROUND); |
| #endif |
| } |
| } |
| |
| void AsyncPixelTransferDelegateEGL::AsyncTexImage2D( |
| const AsyncTexImage2DParams& tex_params, |
| const AsyncMemoryParams& mem_params, |
| const base::Closure& bind_callback) { |
| if (WorkAroundAsyncTexImage2D(tex_params, mem_params, bind_callback)) |
| return; |
| |
| 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. |
| shared_state_->pending_allocations.push_back(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( |
| &TransferStateInternal::PerformAsyncTexImage2D, |
| state_, |
| tex_params, |
| mem_params, |
| shared_state_->texture_upload_stats)); |
| |
| DCHECK(CHECK_GL()); |
| } |
| |
| void AsyncPixelTransferDelegateEGL::AsyncTexSubImage2D( |
| const AsyncTexSubImage2DParams& tex_params, |
| const AsyncMemoryParams& mem_params) { |
| TRACE_EVENT2("gpu", "AsyncTexSubImage2D", |
| "width", tex_params.width, |
| "height", tex_params.height); |
| if (WorkAroundAsyncTexSubImage2D(tex_params, mem_params)) |
| return; |
| 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. |
| 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( |
| &TransferStateInternal::PerformAsyncTexSubImage2D, |
| state_, |
| tex_params, |
| mem_params, |
| shared_state_->texture_upload_stats)); |
| |
| DCHECK(CHECK_GL()); |
| } |
| |
| 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( |
| const AsyncTexImage2DParams& tex_params, |
| const AsyncMemoryParams& mem_params, |
| const base::Closure& bind_callback) { |
| if (!shared_state_->is_imagination) |
| return false; |
| |
| // 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 = mem_params.GetDataAddress(); |
| 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(); |
| shared_state_->pending_allocations.push_back(AsWeakPtr()); |
| state_->bind_callback_ = bind_callback; |
| } |
| |
| DCHECK(CHECK_GL()); |
| return true; |
| } |
| |
| bool AsyncPixelTransferDelegateEGL::WorkAroundAsyncTexSubImage2D( |
| const AsyncTexSubImage2DParams& tex_params, |
| const AsyncMemoryParams& mem_params) { |
| if (!shared_state_->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; |
| |
| // 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 = mem_params.GetDataAddress(); |
| base::TimeTicks begin_time; |
| if (shared_state_->texture_upload_stats.get()) |
| begin_time = base::TimeTicks::Now(); |
| { |
| 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 (shared_state_->texture_upload_stats.get()) { |
| shared_state_->texture_upload_stats |
| ->AddUpload(base::TimeTicks::Now() - begin_time); |
| } |
| |
| DCHECK(CHECK_GL()); |
| return true; |
| } |
| |
| AsyncPixelTransferManagerEGL::SharedState::SharedState() |
| // TODO(reveman): Skip this if --enable-gpu-benchmarking is not present. |
| : texture_upload_stats(new AsyncPixelTransferUploadStats) { |
| const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); |
| if (vendor) { |
| is_imagination = |
| std::string(vendor).find("Imagination") != std::string::npos; |
| is_qualcomm = std::string(vendor).find("Qualcomm") != std::string::npos; |
| } |
| } |
| |
| AsyncPixelTransferManagerEGL::SharedState::~SharedState() {} |
| |
| AsyncPixelTransferManagerEGL::AsyncPixelTransferManagerEGL() {} |
| |
| AsyncPixelTransferManagerEGL::~AsyncPixelTransferManagerEGL() {} |
| |
| void AsyncPixelTransferManagerEGL::BindCompletedAsyncTransfers() { |
| scoped_ptr<gfx::ScopedTextureBinder> texture_binder; |
| |
| while(!shared_state_.pending_allocations.empty()) { |
| if (!shared_state_.pending_allocations.front().get()) { |
| shared_state_.pending_allocations.pop_front(); |
| continue; |
| } |
| AsyncPixelTransferDelegateEGL* delegate = |
| shared_state_.pending_allocations.front().get(); |
| // Terminate early, as all transfers finish in order, currently. |
| if (delegate->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. |
| delegate->BindTransfer(); |
| shared_state_.pending_allocations.pop_front(); |
| } |
| } |
| |
| void AsyncPixelTransferManagerEGL::AsyncNotifyCompletion( |
| const AsyncMemoryParams& mem_params, |
| AsyncPixelTransferCompletionObserver* observer) { |
| // 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, |
| make_scoped_refptr(observer))); |
| } |
| |
| uint32 AsyncPixelTransferManagerEGL::GetTextureUploadCount() { |
| return shared_state_.texture_upload_stats->GetStats(NULL); |
| } |
| |
| base::TimeDelta AsyncPixelTransferManagerEGL::GetTotalTextureUploadTime() { |
| base::TimeDelta total_texture_upload_time; |
| shared_state_.texture_upload_stats->GetStats(&total_texture_upload_time); |
| return total_texture_upload_time; |
| } |
| |
| void AsyncPixelTransferManagerEGL::ProcessMorePendingTransfers() { |
| } |
| |
| bool AsyncPixelTransferManagerEGL::NeedsProcessMorePendingTransfers() { |
| return false; |
| } |
| |
| void AsyncPixelTransferManagerEGL::WaitAllAsyncTexImage2D() { |
| if (shared_state_.pending_allocations.empty()) |
| return; |
| |
| AsyncPixelTransferDelegateEGL* delegate = |
| shared_state_.pending_allocations.back().get(); |
| if (delegate) |
| delegate->WaitForTransferCompletion(); |
| } |
| |
| AsyncPixelTransferDelegate* |
| AsyncPixelTransferManagerEGL::CreatePixelTransferDelegateImpl( |
| gles2::TextureRef* ref, |
| const AsyncTexImage2DParams& define_params) { |
| return new AsyncPixelTransferDelegateEGL( |
| &shared_state_, ref->service_id(), define_params); |
| } |
| |
| } // namespace gpu |