This source file includes following definitions.
- CheckErrors
- DoTexImage2D
- DoTexSubImage2D
- DoFullTexSubImage2D
- SetGlParametersForEglImageTexture
- PerformNotifyCompletion
- Init
- CleanUp
- transfer_message_loop_proxy
- use_image_preserved_
- TransferIsInProgress
- BindTransfer
- CreateEglImage
- CreateEglImageOnUploadThread
- CreateEglImageOnMainThreadIfNeeded
- WaitForLastUpload
- MarkAsTransferIsInProgress
- MarkAsCompleted
- WaitForTransferCompletion
- PerformAsyncTexImage2D
- PerformAsyncTexSubImage2D
- BindTransfer
- TransferIsInProgress
- WaitForTransferCompletion
- AsyncTexImage2D
- AsyncTexSubImage2D
- IsPowerOfTwo
- IsMultipleOfEight
- DimensionsSupportImgFastPath
- WorkAroundAsyncTexImage2D
- WorkAroundAsyncTexSubImage2D
- BindCompletedAsyncTransfers
- AsyncNotifyCompletion
- GetTextureUploadCount
- GetTotalTextureUploadTime
- ProcessMorePendingTransfers
- NeedsProcessMorePendingTransfers
- WaitAllAsyncTexImage2D
- CreatePixelTransferDelegateImpl
#include "gpu/command_buffer/service/async_pixel_transfer_manager_egl.h"
#include <list>
#include <string>
#include "base/bind.h"
#include "base/debug/trace_event.h"
#include "base/debug/trace_event_synthetic_delay.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 "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";
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);
}
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);
}
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() {
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::kThreadPriority_Background);
#endif
}
virtual ~TransferThread() {
Stop();
}
virtual 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);
}
virtual 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 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,
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() {
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");
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::HighResNow();
{
TRACE_EVENT0("gpu", "glTexImage2D no data");
glGenTextures(1, &thread_texture_id_);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, thread_texture_id_);
SetGlParametersForEglImageTexture();
if (use_image_preserved_)
DoTexImage2D(tex_params, data);
else
DoTexImage2D(tex_params, NULL);
}
CreateEglImageOnUploadThread();
{
TRACE_EVENT0("gpu", "glTexSubImage2D with data");
if (!use_image_preserved_)
DoFullTexSubImage2D(tex_params, data);
}
WaitForLastUpload();
MarkAsCompleted();
DCHECK(CHECK_GL());
if (texture_upload_stats.get()) {
texture_upload_stats->AddUpload(base::TimeTicks::HighResNow() -
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::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.get()) {
texture_upload_stats->AddUpload(base::TimeTicks::HighResNow() -
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_));
}
}
GLuint texture_id_;
GLuint thread_texture_id_;
AsyncTexImage2DParams define_params_;
base::WaitableEvent transfer_completion_;
EGLImageKHR egl_image_;
base::Closure bind_callback_;
bool wait_for_uploads_;
bool wait_for_creation_;
bool use_image_preserved_;
};
}
class AsyncPixelTransferDelegateEGL
: public AsyncPixelTransferDelegate,
public base::SupportsWeakPtr<AsyncPixelTransferDelegateEGL> {
public:
AsyncPixelTransferDelegateEGL(
AsyncPixelTransferManagerEGL::SharedState* shared_state,
GLuint texture_id,
const AsyncTexImage2DParams& define_params);
virtual ~AsyncPixelTransferDelegateEGL();
void BindTransfer() { state_->BindTransfer(); }
virtual void AsyncTexImage2D(
const AsyncTexImage2DParams& tex_params,
const AsyncMemoryParams& mem_params,
const base::Closure& bind_callback) OVERRIDE;
virtual void AsyncTexSubImage2D(
const AsyncTexSubImage2DParams& tex_params,
const AsyncMemoryParams& mem_params) OVERRIDE;
virtual bool TransferIsInProgress() OVERRIDE;
virtual void WaitForTransferCompletion() OVERRIDE;
private:
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);
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) {
bool wait_for_uploads = !shared_state_->is_imagination;
bool wait_for_creation = shared_state_->is_qualcomm;
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::kThreadPriority_Display);
#endif
state_->WaitForTransferCompletion();
DCHECK(!state_->TransferIsInProgress());
#if defined(OS_ANDROID) || defined(OS_LINUX)
g_transfer_thread.Pointer()->SetPriority(base::kThreadPriority_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);
shared_state_->pending_allocations.push_back(AsWeakPtr());
state_->bind_callback_ = bind_callback;
state_->MarkAsTransferIsInProgress();
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);
state_->MarkAsTransferIsInProgress();
state_->CreateEglImageOnMainThreadIfNeeded();
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) {
return IsMultipleOfEight(width) &&
IsMultipleOfEight(height) &&
!(IsPowerOfTwo(width) &&
IsPowerOfTwo(height));
}
}
bool AsyncPixelTransferDelegateEGL::WorkAroundAsyncTexImage2D(
const AsyncTexImage2DParams& tex_params,
const AsyncMemoryParams& mem_params,
const base::Closure& bind_callback) {
if (!shared_state_->is_imagination)
return false;
void* data = mem_params.GetDataAddress();
SetGlParametersForEglImageTexture();
{
TRACE_EVENT0("gpu", "glTexImage2D with data");
DoTexImage2D(tex_params, data);
}
CHECK(!state_->TransferIsInProgress());
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 (DimensionsSupportImgFastPath(tex_params.width, tex_params.height))
return false;
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::HighResNow();
{
TRACE_EVENT0("gpu", "glTexSubImage2D");
DoTexImage2D(state_->define_params_, data);
}
if (shared_state_->texture_upload_stats.get()) {
shared_state_->texture_upload_stats
->AddUpload(base::TimeTicks::HighResNow() - begin_time);
}
DCHECK(CHECK_GL());
return true;
}
AsyncPixelTransferManagerEGL::SharedState::SharedState()
: texture_upload_stats(new AsyncPixelTransferUploadStats) {
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;
}
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();
if (delegate->TransferIsInProgress())
break;
if (!texture_binder)
texture_binder.reset(new gfx::ScopedTextureBinder(GL_TEXTURE_2D, 0));
delegate->BindTransfer();
shared_state_.pending_allocations.pop_front();
}
}
void AsyncPixelTransferManagerEGL::AsyncNotifyCompletion(
const AsyncMemoryParams& mem_params,
AsyncPixelTransferCompletionObserver* observer) {
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);
}
}