blob: dcd870ddd0c730cd05daf4296faa317f7d4f1083 [file] [log] [blame]
// 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_idle.h"
#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/memory/weak_ptr.h"
#include "gpu/command_buffer/service/safe_shared_memory_pool.h"
#include "ui/gl/scoped_binders.h"
namespace gpu {
namespace {
base::LazyInstance<SafeSharedMemoryPool> g_safe_shared_memory_pool =
LAZY_INSTANCE_INITIALIZER;
SafeSharedMemoryPool* safe_shared_memory_pool() {
return g_safe_shared_memory_pool.Pointer();
}
static uint64 g_next_pixel_transfer_state_id = 1;
void PerformNotifyCompletion(
AsyncMemoryParams mem_params,
ScopedSafeSharedMemory* safe_shared_memory,
scoped_refptr<AsyncPixelTransferCompletionObserver> observer) {
TRACE_EVENT0("gpu", "PerformNotifyCompletion");
AsyncMemoryParams safe_mem_params = mem_params;
safe_mem_params.shared_memory = safe_shared_memory->shared_memory();
observer->DidComplete(safe_mem_params);
}
} // namespace
// Class which handles async pixel transfers in a platform
// independent way.
class AsyncPixelTransferDelegateIdle
: public AsyncPixelTransferDelegate,
public base::SupportsWeakPtr<AsyncPixelTransferDelegateIdle> {
public:
AsyncPixelTransferDelegateIdle(
AsyncPixelTransferManagerIdle::SharedState* state,
GLuint texture_id,
const AsyncTexImage2DParams& define_params);
virtual ~AsyncPixelTransferDelegateIdle();
// Implement AsyncPixelTransferDelegate:
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:
void PerformAsyncTexImage2D(
AsyncTexImage2DParams tex_params,
AsyncMemoryParams mem_params,
const base::Closure& bind_callback,
ScopedSafeSharedMemory* safe_shared_memory);
void PerformAsyncTexSubImage2D(
AsyncTexSubImage2DParams tex_params,
AsyncMemoryParams mem_params,
ScopedSafeSharedMemory* safe_shared_memory);
uint64 id_;
GLuint texture_id_;
bool transfer_in_progress_;
AsyncTexImage2DParams define_params_;
// Safe to hold a raw pointer because SharedState is owned by the Manager
// which owns the Delegate.
AsyncPixelTransferManagerIdle::SharedState* shared_state_;
DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegateIdle);
};
AsyncPixelTransferDelegateIdle::AsyncPixelTransferDelegateIdle(
AsyncPixelTransferManagerIdle::SharedState* shared_state,
GLuint texture_id,
const AsyncTexImage2DParams& define_params)
: id_(g_next_pixel_transfer_state_id++),
texture_id_(texture_id),
transfer_in_progress_(false),
define_params_(define_params),
shared_state_(shared_state) {}
AsyncPixelTransferDelegateIdle::~AsyncPixelTransferDelegateIdle() {}
void AsyncPixelTransferDelegateIdle::AsyncTexImage2D(
const AsyncTexImage2DParams& tex_params,
const AsyncMemoryParams& mem_params,
const base::Closure& bind_callback) {
TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("gpu.AsyncTexImage");
DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target);
DCHECK(mem_params.shared_memory);
DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size,
mem_params.shm_size);
shared_state_->tasks.push_back(AsyncPixelTransferManagerIdle::Task(
id_,
base::Bind(
&AsyncPixelTransferDelegateIdle::PerformAsyncTexImage2D,
AsWeakPtr(),
tex_params,
mem_params,
bind_callback,
base::Owned(new ScopedSafeSharedMemory(safe_shared_memory_pool(),
mem_params.shared_memory,
mem_params.shm_size)))));
transfer_in_progress_ = true;
}
void AsyncPixelTransferDelegateIdle::AsyncTexSubImage2D(
const AsyncTexSubImage2DParams& tex_params,
const AsyncMemoryParams& mem_params) {
TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("gpu.AsyncTexImage");
DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target);
DCHECK(mem_params.shared_memory);
DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size,
mem_params.shm_size);
shared_state_->tasks.push_back(AsyncPixelTransferManagerIdle::Task(
id_,
base::Bind(
&AsyncPixelTransferDelegateIdle::PerformAsyncTexSubImage2D,
AsWeakPtr(),
tex_params,
mem_params,
base::Owned(new ScopedSafeSharedMemory(safe_shared_memory_pool(),
mem_params.shared_memory,
mem_params.shm_size)))));
transfer_in_progress_ = true;
}
bool AsyncPixelTransferDelegateIdle::TransferIsInProgress() {
return transfer_in_progress_;
}
void AsyncPixelTransferDelegateIdle::WaitForTransferCompletion() {
for (std::list<AsyncPixelTransferManagerIdle::Task>::iterator iter =
shared_state_->tasks.begin();
iter != shared_state_->tasks.end();
++iter) {
if (iter->transfer_id != id_)
continue;
(*iter).task.Run();
shared_state_->tasks.erase(iter);
break;
}
shared_state_->ProcessNotificationTasks();
}
void AsyncPixelTransferDelegateIdle::PerformAsyncTexImage2D(
AsyncTexImage2DParams tex_params,
AsyncMemoryParams mem_params,
const base::Closure& bind_callback,
ScopedSafeSharedMemory* safe_shared_memory) {
TRACE_EVENT2("gpu", "PerformAsyncTexImage2D",
"width", tex_params.width,
"height", tex_params.height);
void* data = GetAddress(safe_shared_memory, mem_params);
base::TimeTicks begin_time(base::TimeTicks::HighResNow());
gfx::ScopedTextureBinder texture_binder(tex_params.target, texture_id_);
{
TRACE_EVENT0("gpu", "glTexImage2D");
glTexImage2D(
tex_params.target,
tex_params.level,
tex_params.internal_format,
tex_params.width,
tex_params.height,
tex_params.border,
tex_params.format,
tex_params.type,
data);
}
TRACE_EVENT_SYNTHETIC_DELAY_END("gpu.AsyncTexImage");
transfer_in_progress_ = false;
shared_state_->texture_upload_count++;
shared_state_->total_texture_upload_time +=
base::TimeTicks::HighResNow() - begin_time;
// The texture is already fully bound so just call it now.
bind_callback.Run();
}
void AsyncPixelTransferDelegateIdle::PerformAsyncTexSubImage2D(
AsyncTexSubImage2DParams tex_params,
AsyncMemoryParams mem_params,
ScopedSafeSharedMemory* safe_shared_memory) {
TRACE_EVENT2("gpu", "PerformAsyncTexSubImage2D",
"width", tex_params.width,
"height", tex_params.height);
void* data = GetAddress(safe_shared_memory, mem_params);
base::TimeTicks begin_time(base::TimeTicks::HighResNow());
gfx::ScopedTextureBinder texture_binder(tex_params.target, texture_id_);
// If it's a full texture update, use glTexImage2D as it's faster.
// TODO(epenner): Make this configurable (http://crbug.com/259924)
if (tex_params.xoffset == 0 &&
tex_params.yoffset == 0 &&
tex_params.target == define_params_.target &&
tex_params.level == define_params_.level &&
tex_params.width == define_params_.width &&
tex_params.height == define_params_.height) {
TRACE_EVENT0("gpu", "glTexImage2D");
glTexImage2D(
define_params_.target,
define_params_.level,
define_params_.internal_format,
define_params_.width,
define_params_.height,
define_params_.border,
tex_params.format,
tex_params.type,
data);
} else {
TRACE_EVENT0("gpu", "glTexSubImage2D");
glTexSubImage2D(
tex_params.target,
tex_params.level,
tex_params.xoffset,
tex_params.yoffset,
tex_params.width,
tex_params.height,
tex_params.format,
tex_params.type,
data);
}
TRACE_EVENT_SYNTHETIC_DELAY_END("gpu.AsyncTexImage");
transfer_in_progress_ = false;
shared_state_->texture_upload_count++;
shared_state_->total_texture_upload_time +=
base::TimeTicks::HighResNow() - begin_time;
}
AsyncPixelTransferManagerIdle::Task::Task(
uint64 transfer_id, const base::Closure& task)
: transfer_id(transfer_id),
task(task) {
}
AsyncPixelTransferManagerIdle::Task::~Task() {}
AsyncPixelTransferManagerIdle::SharedState::SharedState()
: texture_upload_count(0) {}
AsyncPixelTransferManagerIdle::SharedState::~SharedState() {}
void AsyncPixelTransferManagerIdle::SharedState::ProcessNotificationTasks() {
while (!tasks.empty()) {
// Stop when we reach a pixel transfer task.
if (tasks.front().transfer_id)
return;
tasks.front().task.Run();
tasks.pop_front();
}
}
AsyncPixelTransferManagerIdle::AsyncPixelTransferManagerIdle()
: shared_state_() {
}
AsyncPixelTransferManagerIdle::~AsyncPixelTransferManagerIdle() {}
void AsyncPixelTransferManagerIdle::BindCompletedAsyncTransfers() {
// Everything is already bound.
}
void AsyncPixelTransferManagerIdle::AsyncNotifyCompletion(
const AsyncMemoryParams& mem_params,
AsyncPixelTransferCompletionObserver* observer) {
if (shared_state_.tasks.empty()) {
observer->DidComplete(mem_params);
return;
}
shared_state_.tasks.push_back(
Task(0, // 0 transfer_id for notification tasks.
base::Bind(
&PerformNotifyCompletion,
mem_params,
base::Owned(new ScopedSafeSharedMemory(safe_shared_memory_pool(),
mem_params.shared_memory,
mem_params.shm_size)),
make_scoped_refptr(observer))));
}
uint32 AsyncPixelTransferManagerIdle::GetTextureUploadCount() {
return shared_state_.texture_upload_count;
}
base::TimeDelta AsyncPixelTransferManagerIdle::GetTotalTextureUploadTime() {
return shared_state_.total_texture_upload_time;
}
void AsyncPixelTransferManagerIdle::ProcessMorePendingTransfers() {
if (shared_state_.tasks.empty())
return;
// First task should always be a pixel transfer task.
DCHECK(shared_state_.tasks.front().transfer_id);
shared_state_.tasks.front().task.Run();
shared_state_.tasks.pop_front();
shared_state_.ProcessNotificationTasks();
}
bool AsyncPixelTransferManagerIdle::NeedsProcessMorePendingTransfers() {
return !shared_state_.tasks.empty();
}
AsyncPixelTransferDelegate*
AsyncPixelTransferManagerIdle::CreatePixelTransferDelegateImpl(
gles2::TextureRef* ref,
const AsyncTexImage2DParams& define_params) {
return new AsyncPixelTransferDelegateIdle(&shared_state_,
ref->service_id(),
define_params);
}
} // namespace gpu