This source file includes following definitions.
- PerformNotifyCompletion
- initialized_
- InitializeOnMainThread
- CleanUp
- InitializeOnTransferThread
- transfer_message_loop_proxy
- task_pending_
- TryRun
- BindAndRun
- Cancel
- TaskIsInProgress
- WaitForTask
- define_params_
- TransferIsInProgress
- BindTransfer
- WaitForTransferCompletion
- CancelUpload
- ScheduleAsyncTexImage2D
- ScheduleAsyncTexSubImage2D
- PerformAsyncTexImage2D
- PerformAsyncTexSubImage2D
- BindTransfer
- state_
- TransferIsInProgress
- WaitForTransferCompletion
- AsyncTexImage2D
- AsyncTexSubImage2D
- BindCompletedAsyncTransfers
- AsyncNotifyCompletion
- GetTextureUploadCount
- GetTotalTextureUploadTime
- ProcessMorePendingTransfers
- NeedsProcessMorePendingTransfers
- WaitAllAsyncTexImage2D
- CreatePixelTransferDelegateImpl
#include "gpu/command_buffer/service/async_pixel_transfer_manager_share_group.h"
#include <list>
#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/memory/weak_ptr.h"
#include "base/synchronization/cancellation_flag.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "base/threading/thread_checker.h"
#include "gpu/command_buffer/service/async_pixel_transfer_delegate.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 char kAsyncTransferThreadName[] = "AsyncTransferThread";
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),
initialized_(false) {
Start();
#if defined(OS_ANDROID) || defined(OS_LINUX)
SetPriority(base::kThreadPriority_Background);
#endif
}
virtual ~TransferThread() {
NOTREACHED();
}
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;
}
private:
bool initialized_;
scoped_refptr<gfx::GLSurface> surface_;
scoped_refptr<gfx::GLContext> context_;
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(gfx::Size(1, 1));
if (!surface_.get()) {
LOG(ERROR) << "Unable to create GLSurface";
caller_wait->Signal();
return;
}
context_ = gfx::GLContext::CreateGLContext(parent_context->share_group(),
surface_.get(),
gfx::PreferIntegratedGpu);
if (!context_.get()) {
LOG(ERROR) << "Unable to create GLContext.";
caller_wait->Signal();
return;
}
context_->MakeCurrent(surface_.get());
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().get();
}
class PendingTask : public base::RefCountedThreadSafe<PendingTask> {
public:
explicit PendingTask(const base::Closure& task)
: task_(task), task_pending_(true, false) {}
bool TryRun() {
DCHECK(checker_.CalledOnValidThread());
if (task_lock_.Try()) {
if (!task_.is_null())
task_.Run();
task_.Reset();
task_lock_.Release();
task_pending_.Signal();
return true;
}
return false;
}
void BindAndRun(GLuint texture_id) {
DCHECK(!checker_.CalledOnValidThread());
base::AutoLock locked(task_lock_);
if (!task_.is_null()) {
glBindTexture(GL_TEXTURE_2D, texture_id);
task_.Run();
task_.Reset();
glBindTexture(GL_TEXTURE_2D, 0);
glFlush();
task_pending_.Signal();
}
}
void Cancel() {
base::AutoLock locked(task_lock_);
task_.Reset();
task_pending_.Signal();
}
bool TaskIsInProgress() {
return !task_pending_.IsSignaled();
}
void WaitForTask() {
task_pending_.Wait();
}
private:
friend class base::RefCountedThreadSafe<PendingTask>;
virtual ~PendingTask() {}
base::ThreadChecker checker_;
base::Lock task_lock_;
base::Closure task_;
base::WaitableEvent task_pending_;
DISALLOW_COPY_AND_ASSIGN(PendingTask);
};
class TransferStateInternal
: public base::RefCountedThreadSafe<TransferStateInternal> {
public:
TransferStateInternal(GLuint texture_id,
const AsyncTexImage2DParams& define_params)
: texture_id_(texture_id), define_params_(define_params) {}
bool TransferIsInProgress() {
return pending_upload_task_.get() &&
pending_upload_task_->TaskIsInProgress();
}
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 WaitForTransferCompletion() {
TRACE_EVENT0("gpu", "WaitForTransferCompletion");
DCHECK(pending_upload_task_.get());
if (!pending_upload_task_->TryRun()) {
pending_upload_task_->WaitForTask();
}
pending_upload_task_ = NULL;
}
void CancelUpload() {
TRACE_EVENT0("gpu", "CancelUpload");
if (pending_upload_task_.get())
pending_upload_task_->Cancel();
pending_upload_task_ = NULL;
}
void ScheduleAsyncTexImage2D(
const AsyncTexImage2DParams tex_params,
const AsyncMemoryParams mem_params,
scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats,
const base::Closure& bind_callback) {
TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("gpu.AsyncTexImage");
pending_upload_task_ = new PendingTask(base::Bind(
&TransferStateInternal::PerformAsyncTexImage2D,
this,
tex_params,
mem_params,
texture_upload_stats));
transfer_message_loop_proxy()->PostTask(
FROM_HERE,
base::Bind(
&PendingTask::BindAndRun, pending_upload_task_, texture_id_));
bind_callback_ = bind_callback;
}
void ScheduleAsyncTexSubImage2D(
AsyncTexSubImage2DParams tex_params,
AsyncMemoryParams mem_params,
scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats) {
TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("gpu.AsyncTexImage");
pending_upload_task_ = new PendingTask(base::Bind(
&TransferStateInternal::PerformAsyncTexSubImage2D,
this,
tex_params,
mem_params,
texture_upload_stats));
transfer_message_loop_proxy()->PostTask(
FROM_HERE,
base::Bind(
&PendingTask::BindAndRun, pending_upload_task_, texture_id_));
}
private:
friend class base::RefCountedThreadSafe<TransferStateInternal>;
virtual ~TransferStateInternal() {
}
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_EQ(0, tex_params.level);
base::TimeTicks begin_time;
if (texture_upload_stats.get())
begin_time = base::TimeTicks::HighResNow();
void* data = mem_params.GetDataAddress();
{
TRACE_EVENT0("gpu", "glTexImage2D");
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);
TRACE_EVENT_SYNTHETIC_DELAY_END("gpu.AsyncTexImage");
}
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_EQ(0, tex_params.level);
base::TimeTicks begin_time;
if (texture_upload_stats.get())
begin_time = base::TimeTicks::HighResNow();
void* data = mem_params.GetDataAddress();
{
TRACE_EVENT0("gpu", "glTexSubImage2D");
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);
TRACE_EVENT_SYNTHETIC_DELAY_END("gpu.AsyncTexImage");
}
if (texture_upload_stats.get()) {
texture_upload_stats->AddUpload(base::TimeTicks::HighResNow() -
begin_time);
}
}
scoped_refptr<PendingTask> pending_upload_task_;
GLuint texture_id_;
AsyncTexImage2DParams define_params_;
base::Closure bind_callback_;
};
}
class AsyncPixelTransferDelegateShareGroup
: public AsyncPixelTransferDelegate,
public base::SupportsWeakPtr<AsyncPixelTransferDelegateShareGroup> {
public:
AsyncPixelTransferDelegateShareGroup(
AsyncPixelTransferManagerShareGroup::SharedState* shared_state,
GLuint texture_id,
const AsyncTexImage2DParams& define_params);
virtual ~AsyncPixelTransferDelegateShareGroup();
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:
AsyncPixelTransferManagerShareGroup::SharedState* shared_state_;
scoped_refptr<TransferStateInternal> state_;
DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegateShareGroup);
};
AsyncPixelTransferDelegateShareGroup::AsyncPixelTransferDelegateShareGroup(
AsyncPixelTransferManagerShareGroup::SharedState* shared_state,
GLuint texture_id,
const AsyncTexImage2DParams& define_params)
: shared_state_(shared_state),
state_(new TransferStateInternal(texture_id, define_params)) {}
AsyncPixelTransferDelegateShareGroup::~AsyncPixelTransferDelegateShareGroup() {
TRACE_EVENT0("gpu", " ~AsyncPixelTransferDelegateShareGroup");
state_->CancelUpload();
}
bool AsyncPixelTransferDelegateShareGroup::TransferIsInProgress() {
return state_->TransferIsInProgress();
}
void AsyncPixelTransferDelegateShareGroup::WaitForTransferCompletion() {
if (state_->TransferIsInProgress()) {
state_->WaitForTransferCompletion();
DCHECK(!state_->TransferIsInProgress());
}
for (AsyncPixelTransferManagerShareGroup::SharedState::TransferQueue::iterator
iter = shared_state_->pending_allocations.begin();
iter != shared_state_->pending_allocations.end();
++iter) {
if (iter->get() != this)
continue;
shared_state_->pending_allocations.erase(iter);
BindTransfer();
break;
}
}
void AsyncPixelTransferDelegateShareGroup::AsyncTexImage2D(
const AsyncTexImage2DParams& tex_params,
const AsyncMemoryParams& mem_params,
const base::Closure& bind_callback) {
DCHECK(!state_->TransferIsInProgress());
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_->ScheduleAsyncTexImage2D(tex_params,
mem_params,
shared_state_->texture_upload_stats,
bind_callback);
}
void AsyncPixelTransferDelegateShareGroup::AsyncTexSubImage2D(
const AsyncTexSubImage2DParams& tex_params,
const AsyncMemoryParams& mem_params) {
TRACE_EVENT2("gpu", "AsyncTexSubImage2D",
"width", tex_params.width,
"height", tex_params.height);
DCHECK(!state_->TransferIsInProgress());
DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target);
DCHECK_EQ(tex_params.level, 0);
state_->ScheduleAsyncTexSubImage2D(
tex_params, mem_params, shared_state_->texture_upload_stats);
}
AsyncPixelTransferManagerShareGroup::SharedState::SharedState()
: texture_upload_stats(new AsyncPixelTransferUploadStats) {}
AsyncPixelTransferManagerShareGroup::SharedState::~SharedState() {}
AsyncPixelTransferManagerShareGroup::AsyncPixelTransferManagerShareGroup(
gfx::GLContext* context) {
g_transfer_thread.Pointer()->InitializeOnMainThread(context);
}
AsyncPixelTransferManagerShareGroup::~AsyncPixelTransferManagerShareGroup() {}
void AsyncPixelTransferManagerShareGroup::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;
}
AsyncPixelTransferDelegateShareGroup* 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 AsyncPixelTransferManagerShareGroup::AsyncNotifyCompletion(
const AsyncMemoryParams& mem_params,
AsyncPixelTransferCompletionObserver* observer) {
transfer_message_loop_proxy()->PostTask(
FROM_HERE,
base::Bind(&PerformNotifyCompletion,
mem_params,
make_scoped_refptr(observer)));
}
uint32 AsyncPixelTransferManagerShareGroup::GetTextureUploadCount() {
return shared_state_.texture_upload_stats->GetStats(NULL);
}
base::TimeDelta
AsyncPixelTransferManagerShareGroup::GetTotalTextureUploadTime() {
base::TimeDelta total_texture_upload_time;
shared_state_.texture_upload_stats->GetStats(&total_texture_upload_time);
return total_texture_upload_time;
}
void AsyncPixelTransferManagerShareGroup::ProcessMorePendingTransfers() {
}
bool AsyncPixelTransferManagerShareGroup::NeedsProcessMorePendingTransfers() {
return false;
}
void AsyncPixelTransferManagerShareGroup::WaitAllAsyncTexImage2D() {
if (shared_state_.pending_allocations.empty())
return;
AsyncPixelTransferDelegateShareGroup* delegate =
shared_state_.pending_allocations.back().get();
if (delegate)
delegate->WaitForTransferCompletion();
}
AsyncPixelTransferDelegate*
AsyncPixelTransferManagerShareGroup::CreatePixelTransferDelegateImpl(
gles2::TextureRef* ref,
const AsyncTexImage2DParams& define_params) {
return new AsyncPixelTransferDelegateShareGroup(
&shared_state_, ref->service_id(), define_params);
}
}