summaryrefslogtreecommitdiff
path: root/gpu
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2013-08-01 12:44:22 +0100
committerBen Murdoch <benm@google.com>2013-08-01 13:12:08 +0100
commit28390f6bb8dc6eb59bac1e0576f95a7740a9bd61 (patch)
tree02dad0e26c6660746a487ef814fffb404e16c12a /gpu
parent226ac25887216e7df5a5dbd091cae3894b3497d2 (diff)
downloadchromium_org-28390f6bb8dc6eb59bac1e0576f95a7740a9bd61.tar.gz
Cherry pick of https://codereview.chromium.org/19522006
Stability fixes for Chromium WebView. Bug:9915533 Change-Id: If5730b3f0c79b1ddd56bce3a2903531632ea0bc6
Diffstat (limited to 'gpu')
-rw-r--r--gpu/command_buffer/client/gl_in_process_context.cc402
-rw-r--r--gpu/command_buffer/client/gl_in_process_context.h4
-rw-r--r--gpu/command_buffer/service/in_process_command_buffer.cc700
-rw-r--r--gpu/command_buffer/service/in_process_command_buffer.h154
-rw-r--r--gpu/command_buffer_service.gypi2
-rw-r--r--gpu/command_buffer_service.target.darwin-arm.mk1
-rw-r--r--gpu/command_buffer_service.target.darwin-mips.mk1
-rw-r--r--gpu/command_buffer_service.target.darwin-x86.mk1
-rw-r--r--gpu/command_buffer_service.target.linux-arm.mk1
-rw-r--r--gpu/command_buffer_service.target.linux-mips.mk1
-rw-r--r--gpu/command_buffer_service.target.linux-x86.mk1
11 files changed, 944 insertions, 324 deletions
diff --git a/gpu/command_buffer/client/gl_in_process_context.cc b/gpu/command_buffer/client/gl_in_process_context.cc
index 2359a05887..b1b5a03a68 100644
--- a/gpu/command_buffer/client/gl_in_process_context.cc
+++ b/gpu/command_buffer/client/gl_in_process_context.cc
@@ -4,6 +4,7 @@
#include "gpu/command_buffer/client/gl_in_process_context.h"
+#include <set>
#include <utility>
#include <vector>
@@ -16,36 +17,24 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
-#include "base/callback.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
-#include "base/synchronization/lock.h"
#include "gpu/command_buffer/client/gles2_implementation.h"
#include "gpu/command_buffer/client/gpu_memory_buffer.h"
#include "gpu/command_buffer/client/gpu_memory_buffer_factory.h"
#include "gpu/command_buffer/client/image_factory.h"
#include "gpu/command_buffer/client/transfer_buffer.h"
+#include "gpu/command_buffer/common/command_buffer.h"
#include "gpu/command_buffer/common/constants.h"
-#include "gpu/command_buffer/common/id_allocator.h"
-#include "gpu/command_buffer/service/command_buffer_service.h"
-#include "gpu/command_buffer/service/context_group.h"
-#include "gpu/command_buffer/service/gl_context_virtual.h"
-#include "gpu/command_buffer/service/gpu_scheduler.h"
-#include "gpu/command_buffer/service/image_manager.h"
-#include "gpu/command_buffer/service/transfer_buffer_manager.h"
+#include "gpu/command_buffer/service/in_process_command_buffer.h"
#include "ui/gfx/size.h"
-#include "ui/gl/gl_context.h"
#include "ui/gl/gl_image.h"
-#include "ui/gl/gl_share_group.h"
-#include "ui/gl/gl_surface.h"
namespace gpu {
-using gles2::ImageManager;
-
namespace {
const int32 kCommandBufferSize = 1024 * 1024;
@@ -55,56 +44,18 @@ const size_t kStartTransferBufferSize = 4 * 1024 * 1024;
const size_t kMinTransferBufferSize = 1 * 256 * 1024;
const size_t kMaxTransferBufferSize = 16 * 1024 * 1024;
-// In the normal command buffer implementation, all commands are passed over IPC
-// to the gpu process where they are fed to the GLES2Decoder from a single
-// thread. In layout tests, any thread could call this function. GLES2Decoder,
-// and in particular the GL implementations behind it, are not generally
-// threadsafe, so we guard entry points with a mutex.
-static base::LazyInstance<base::Lock> g_decoder_lock =
- LAZY_INSTANCE_INITIALIZER;
-
-class GLInProcessContextImpl;
-
-static base::LazyInstance<
- std::set<GLInProcessContextImpl*> >
- g_all_shared_contexts = LAZY_INSTANCE_INITIALIZER;
-
-static bool g_use_virtualized_gl_context = false;
-
static GpuMemoryBufferFactory* g_gpu_memory_buffer_factory = NULL;
-// Also calls DetachFromThreadHack on all GLES2Decoders before the lock is
-// released to maintain the invariant that all decoders are unbound while the
-// lock is not held. This is to workaround DumpRenderTree using WGC3DIPCBI with
-// shared resources on different threads.
-// Remove this as part of crbug.com/234964.
-class AutoLockAndDecoderDetachThread {
- public:
- AutoLockAndDecoderDetachThread(
- base::Lock& lock,
- const std::set<GLInProcessContextImpl*>& contexts);
- ~AutoLockAndDecoderDetachThread();
-
- private:
- base::AutoLock auto_lock_;
- const std::set<GLInProcessContextImpl*>& contexts_;
-};
-
-size_t SharedContextCount() {
- AutoLockAndDecoderDetachThread lock(g_decoder_lock.Get(),
- g_all_shared_contexts.Get());
- return g_all_shared_contexts.Get().size();
-}
-
class GLInProcessContextImpl
: public GLInProcessContext,
public gles2::ImageFactory,
public base::SupportsWeakPtr<GLInProcessContextImpl> {
public:
- explicit GLInProcessContextImpl(bool share_resources);
+ explicit GLInProcessContextImpl();
virtual ~GLInProcessContextImpl();
bool Initialize(bool is_offscreen,
+ bool share_resources,
gfx::AcceleratedWidget window,
const gfx::Size& size,
const char* allowed_extensions,
@@ -125,67 +76,39 @@ class GLInProcessContextImpl
unsigned* image_id) OVERRIDE;
virtual void DeleteGpuMemoryBuffer(unsigned image_id) OVERRIDE;
- // Other methods:
- gles2::GLES2Decoder* GetDecoder();
- bool GetBufferChanged(int32 transfer_buffer_id);
- void PumpCommands();
- void OnResizeView(gfx::Size size, float scale_factor);
- void OnContextLost();
-
private:
void Destroy();
- bool IsCommandBufferContextLost();
void PollQueryCallbacks();
void CallQueryCallback(size_t index);
- bool MakeCurrent();
+ void OnContextLost(const base::Closure& callback);
+ void OnSignalSyncPoint(const base::Closure& callback);
- gles2::ImageManager* GetImageManager();
-
- base::Closure context_lost_callback_;
- scoped_ptr<TransferBufferManagerInterface> transfer_buffer_manager_;
- scoped_ptr<CommandBuffer> command_buffer_;
- scoped_ptr<GpuScheduler> gpu_scheduler_;
- scoped_ptr<gles2::GLES2Decoder> decoder_;
- scoped_refptr<gfx::GLContext> context_;
- scoped_refptr<gfx::GLSurface> surface_;
scoped_ptr<gles2::GLES2CmdHelper> gles2_helper_;
scoped_ptr<TransferBuffer> transfer_buffer_;
scoped_ptr<gles2::GLES2Implementation> gles2_implementation_;
- bool share_resources_;
- bool context_lost_;
+ scoped_ptr<InProcessCommandBuffer> command_buffer_;
typedef std::pair<unsigned, base::Closure> QueryCallback;
std::vector<QueryCallback> query_callbacks_;
- std::vector<base::Closure> signal_sync_point_callbacks_;
+ unsigned int share_group_id_;
+ bool context_lost_;
DISALLOW_COPY_AND_ASSIGN(GLInProcessContextImpl);
};
-AutoLockAndDecoderDetachThread::AutoLockAndDecoderDetachThread(
- base::Lock& lock,
- const std::set<GLInProcessContextImpl*>& contexts)
- : auto_lock_(lock),
- contexts_(contexts) {
-}
-
-void DetachThread(GLInProcessContextImpl* context) {
- if (context->GetDecoder())
- context->GetDecoder()->DetachFromThreadHack();
-}
+base::LazyInstance<base::Lock> g_all_shared_contexts_lock =
+ LAZY_INSTANCE_INITIALIZER;
+base::LazyInstance<std::set<GLInProcessContextImpl*> > g_all_shared_contexts =
+ LAZY_INSTANCE_INITIALIZER;
-AutoLockAndDecoderDetachThread::~AutoLockAndDecoderDetachThread() {
- std::for_each(contexts_.begin(),
- contexts_.end(),
- &DetachThread);
+size_t SharedContextCount() {
+ base::AutoLock lock(g_all_shared_contexts_lock.Get());
+ return g_all_shared_contexts.Get().size();
}
scoped_ptr<GpuMemoryBuffer> GLInProcessContextImpl::CreateGpuMemoryBuffer(
int width, int height, GLenum internalformat, unsigned int* image_id) {
- // We're taking the lock here because we're accessing the ContextGroup's
- // shared IdManager.
- AutoLockAndDecoderDetachThread lock(g_decoder_lock.Get(),
- g_all_shared_contexts.Get());
scoped_ptr<GpuMemoryBuffer> buffer(
g_gpu_memory_buffer_factory->CreateGpuMemoryBuffer(width,
height,
@@ -193,111 +116,58 @@ scoped_ptr<GpuMemoryBuffer> GLInProcessContextImpl::CreateGpuMemoryBuffer(
if (!buffer)
return scoped_ptr<GpuMemoryBuffer>();
- scoped_refptr<gfx::GLImage> gl_image =
- gfx::GLImage::CreateGLImageForGpuMemoryBuffer(buffer->GetNativeBuffer(),
- gfx::Size(width, height));
- *image_id = decoder_->GetContextGroup()
- ->GetIdAllocator(gles2::id_namespaces::kImages)->AllocateID();
- GetImageManager()->AddImage(gl_image.get(), *image_id);
+ *image_id = command_buffer_->CreateImageForGpuMemoryBuffer(
+ buffer->GetNativeBuffer(), gfx::Size(width, height));
return buffer.Pass();
}
void GLInProcessContextImpl::DeleteGpuMemoryBuffer(unsigned int image_id) {
- // We're taking the lock here because we're accessing the ContextGroup's
- // shared ImageManager.
- AutoLockAndDecoderDetachThread lock(g_decoder_lock.Get(),
- g_all_shared_contexts.Get());
- GetImageManager()->RemoveImage(image_id);
- decoder_->GetContextGroup()->GetIdAllocator(gles2::id_namespaces::kImages)
- ->FreeID(image_id);
+ command_buffer_->RemoveImage(image_id);
}
-GLInProcessContextImpl::GLInProcessContextImpl(bool share_resources)
- : share_resources_(share_resources),
- context_lost_(false) {
-}
+GLInProcessContextImpl::GLInProcessContextImpl()
+ : share_group_id_(0), context_lost_(false) {}
GLInProcessContextImpl::~GLInProcessContextImpl() {
- Destroy();
-}
-
-bool GLInProcessContextImpl::MakeCurrent() {
- if (decoder_->MakeCurrent())
- return true;
- DLOG(ERROR) << "Context lost because MakeCurrent failed.";
- command_buffer_->SetContextLostReason(decoder_->GetContextLostReason());
- command_buffer_->SetParseError(gpu::error::kLostContext);
- return false;
-}
-
-void GLInProcessContextImpl::PumpCommands() {
{
- AutoLockAndDecoderDetachThread lock(g_decoder_lock.Get(),
- g_all_shared_contexts.Get());
- if (!MakeCurrent())
- return;
- gpu_scheduler_->PutChanged();
- CommandBuffer::State state = command_buffer_->GetState();
- DCHECK((!error::IsError(state.error) && !context_lost_) ||
- (error::IsError(state.error) && context_lost_));
+ base::AutoLock lock(g_all_shared_contexts_lock.Get());
+ g_all_shared_contexts.Get().erase(this);
}
-
- if (!context_lost_ && signal_sync_point_callbacks_.size()) {
- for (size_t n = 0; n < signal_sync_point_callbacks_.size(); n++) {
- base::MessageLoop::current()->PostTask(FROM_HERE,
- signal_sync_point_callbacks_[n]);
- }
- }
- signal_sync_point_callbacks_.clear();
-}
-
-bool GLInProcessContextImpl::GetBufferChanged(int32 transfer_buffer_id) {
- return gpu_scheduler_->SetGetBuffer(transfer_buffer_id);
+ Destroy();
}
void GLInProcessContextImpl::SignalSyncPoint(unsigned sync_point,
const base::Closure& callback) {
DCHECK(!callback.is_null());
- signal_sync_point_callbacks_.push_back(callback);
-}
-
-bool GLInProcessContextImpl::IsCommandBufferContextLost() {
- if (context_lost_ || !command_buffer_) {
- return true;
- }
- CommandBuffer::State state = command_buffer_->GetState();
- return error::IsError(state.error);
-}
-
-gles2::GLES2Decoder* GLInProcessContextImpl::GetDecoder() {
- return decoder_.get();
-}
-
-void GLInProcessContextImpl::OnResizeView(gfx::Size size, float scale_factor) {
- DCHECK(!surface_->IsOffscreen());
- surface_->Resize(size);
+ base::Closure wrapped_callback = base::Bind(
+ &GLInProcessContextImpl::OnSignalSyncPoint, AsWeakPtr(), callback);
+ command_buffer_->SignalSyncPoint(sync_point, wrapped_callback);
}
gles2::GLES2Implementation* GLInProcessContextImpl::GetImplementation() {
return gles2_implementation_.get();
}
-gles2::ImageManager* GLInProcessContextImpl::GetImageManager() {
- return decoder_->GetContextGroup()->image_manager();
+void GLInProcessContextImpl::OnContextLost(const base::Closure& callback) {
+ context_lost_ = true;
+ callback.Run();
+}
+
+void GLInProcessContextImpl::OnSignalSyncPoint(const base::Closure& callback) {
+ // TODO: Should it always trigger callbacks?
+ if (!context_lost_)
+ callback.Run();
}
bool GLInProcessContextImpl::Initialize(
bool is_offscreen,
+ bool share_resources,
gfx::AcceleratedWidget window,
const gfx::Size& size,
const char* allowed_extensions,
const int32* attrib_list,
gfx::GpuPreference gpu_preference,
const base::Closure& context_lost_callback) {
- // Use one share group for all contexts.
- CR_DEFINE_STATIC_LOCAL(scoped_refptr<gfx::GLShareGroup>, share_group,
- (new gfx::GLShareGroup));
-
DCHECK(size.width() >= 0 && size.height() >= 0);
std::vector<int32> attribs;
@@ -327,128 +197,50 @@ bool GLInProcessContextImpl::Initialize(
}
}
- {
- TransferBufferManager* manager = new TransferBufferManager();
- transfer_buffer_manager_.reset(manager);
- manager->Initialize();
- }
-
- scoped_ptr<CommandBufferService> command_buffer(
- new CommandBufferService(transfer_buffer_manager_.get()));
- command_buffer->SetPutOffsetChangeCallback(base::Bind(
- &GLInProcessContextImpl::PumpCommands, base::Unretained(this)));
- command_buffer->SetGetBufferChangeCallback(base::Bind(
- &GLInProcessContextImpl::GetBufferChanged, base::Unretained(this)));
- command_buffer->SetParseErrorCallback(base::Bind(
- &GLInProcessContextImpl::OnContextLost, base::Unretained(this)));
-
- command_buffer_ = command_buffer.Pass();
- if (!command_buffer_->Initialize()) {
- LOG(ERROR) << "Could not initialize command buffer.";
- Destroy();
- return false;
- }
-
- GLInProcessContextImpl* context_group = NULL;
-
- {
- AutoLockAndDecoderDetachThread lock(g_decoder_lock.Get(),
- g_all_shared_contexts.Get());
- if (share_resources_ && !g_all_shared_contexts.Get().empty()) {
- for (std::set<GLInProcessContextImpl*>::iterator it =
- g_all_shared_contexts.Get().begin();
- it != g_all_shared_contexts.Get().end();
- ++it) {
- if (!(*it)->IsCommandBufferContextLost()) {
- context_group = *it;
- break;
- }
- }
- if (!context_group)
- share_group = new gfx::GLShareGroup;
- }
-
- // TODO(gman): This needs to be true if this is Pepper.
- bool bind_generates_resource = false;
- decoder_.reset(gles2::GLES2Decoder::Create(
- context_group ? context_group->decoder_->GetContextGroup()
- : new gles2::ContextGroup(
- NULL, NULL, NULL, NULL, bind_generates_resource)));
-
- gpu_scheduler_.reset(new GpuScheduler(command_buffer_.get(),
- decoder_.get(),
- decoder_.get()));
-
- decoder_->set_engine(gpu_scheduler_.get());
-
- if (is_offscreen)
- surface_ = gfx::GLSurface::CreateOffscreenGLSurface(size);
- else
- surface_ = gfx::GLSurface::CreateViewGLSurface(window);
-
- if (!surface_.get()) {
- LOG(ERROR) << "Could not create GLSurface.";
- Destroy();
- return false;
- }
-
- if (g_use_virtualized_gl_context) {
- context_ = share_group->GetSharedContext();
- if (!context_.get()) {
- context_ = gfx::GLContext::CreateGLContext(
- share_group.get(), surface_.get(), gpu_preference);
- share_group->SetSharedContext(context_.get());
- }
-
- context_ = new GLContextVirtual(
- share_group.get(), context_.get(), decoder_->AsWeakPtr());
- if (context_->Initialize(surface_.get(), gpu_preference)) {
- VLOG(1) << "Created virtual GL context.";
- } else {
- context_ = NULL;
+ base::Closure wrapped_callback =
+ base::Bind(&GLInProcessContextImpl::OnContextLost,
+ AsWeakPtr(),
+ context_lost_callback);
+ command_buffer_.reset(new InProcessCommandBuffer());
+
+ scoped_ptr<base::AutoLock> scoped_shared_context_lock;
+ scoped_refptr<gles2::ShareGroup> share_group;
+ if (share_resources) {
+ scoped_shared_context_lock.reset(
+ new base::AutoLock(g_all_shared_contexts_lock.Get()));
+ for (std::set<GLInProcessContextImpl*>::const_iterator it =
+ g_all_shared_contexts.Get().begin();
+ it != g_all_shared_contexts.Get().end();
+ it++) {
+ const GLInProcessContextImpl* context = *it;
+ if (!context->context_lost_) {
+ share_group = context->gles2_implementation_->share_group();
+ DCHECK(share_group);
+ share_group_id_ = context->share_group_id_;
+ break;
}
- } else {
- context_ = gfx::GLContext::CreateGLContext(share_group.get(),
- surface_.get(),
- gpu_preference);
+ share_group_id_ = std::max(share_group_id_, context->share_group_id_);
}
-
- if (!context_.get()) {
- LOG(ERROR) << "Could not create GLContext.";
- Destroy();
- return false;
- }
-
- if (!context_->MakeCurrent(surface_.get())) {
- LOG(ERROR) << "Could not make context current.";
- Destroy();
- return false;
- }
-
- gles2::DisallowedFeatures disallowed_features;
- disallowed_features.swap_buffer_complete_callback = true;
- disallowed_features.gpu_memory_manager = true;
- if (!decoder_->Initialize(surface_,
- context_,
- is_offscreen,
+ if (!share_group && !++share_group_id_)
+ ++share_group_id_;
+ }
+ if (!command_buffer_->Initialize(is_offscreen,
+ share_resources,
+ window,
size,
- disallowed_features,
allowed_extensions,
- attribs)) {
- LOG(ERROR) << "Could not initialize decoder.";
- Destroy();
- return false;
- }
-
- if (!is_offscreen) {
- decoder_->SetResizeCallback(base::Bind(
- &GLInProcessContextImpl::OnResizeView, base::Unretained(this)));
- }
+ attribs,
+ gpu_preference,
+ wrapped_callback,
+ share_group_id_)) {
+ LOG(INFO) << "Failed to initialize InProcessCommmandBuffer";
+ return false;
}
// Create the GLES2 helper, which writes the command buffer protocol.
gles2_helper_.reset(new gles2::GLES2CmdHelper(command_buffer_.get()));
if (!gles2_helper_->Initialize(kCommandBufferSize)) {
+ LOG(INFO) << "Failed to initialize GLES2CmdHelper";
Destroy();
return false;
}
@@ -459,11 +251,16 @@ bool GLInProcessContextImpl::Initialize(
// Create the object exposing the OpenGL API.
gles2_implementation_.reset(new gles2::GLES2Implementation(
gles2_helper_.get(),
- context_group ? context_group->GetImplementation()->share_group() : NULL,
+ share_group,
transfer_buffer_.get(),
false,
this));
+ if (share_resources) {
+ g_all_shared_contexts.Get().insert(this);
+ scoped_shared_context_lock.reset();
+ }
+
if (!gles2_implementation_->Initialize(
kStartTransferBufferSize,
kMinTransferBufferSize,
@@ -471,13 +268,6 @@ bool GLInProcessContextImpl::Initialize(
return false;
}
- if (share_resources_) {
- AutoLockAndDecoderDetachThread lock(g_decoder_lock.Get(),
- g_all_shared_contexts.Get());
- g_all_shared_contexts.Pointer()->insert(this);
- }
-
- context_lost_callback_ = context_lost_callback;
return true;
}
@@ -486,8 +276,6 @@ void GLInProcessContextImpl::Destroy() {
CallQueryCallback(0);
}
- bool context_lost = IsCommandBufferContextLost();
-
if (gles2_implementation_) {
// First flush the context to ensure that any pending frees of resources
// are completed. Otherwise, if this context is part of a share group,
@@ -502,28 +290,6 @@ void GLInProcessContextImpl::Destroy() {
transfer_buffer_.reset();
gles2_helper_.reset();
command_buffer_.reset();
-
- AutoLockAndDecoderDetachThread lock(g_decoder_lock.Get(),
- g_all_shared_contexts.Get());
- if (decoder_) {
- decoder_->Destroy(!context_lost);
- }
-
- g_all_shared_contexts.Pointer()->erase(this);
-}
-
-void GLInProcessContextImpl::OnContextLost() {
- if (!context_lost_callback_.is_null())
- context_lost_callback_.Run();
-
- context_lost_ = true;
- if (share_resources_) {
- for (std::set<GLInProcessContextImpl*>::iterator it =
- g_all_shared_contexts.Get().begin();
- it != g_all_shared_contexts.Get().end();
- ++it)
- (*it)->context_lost_ = true;
- }
}
void GLInProcessContextImpl::CallQueryCallback(size_t index) {
@@ -534,6 +300,7 @@ void GLInProcessContextImpl::CallQueryCallback(size_t index) {
query_callback.second.Run();
}
+// TODO(sievers): Move this to the service side
void GLInProcessContextImpl::PollQueryCallbacks() {
for (size_t i = 0; i < query_callbacks_.size();) {
unsigned query = query_callbacks_[i].first;
@@ -582,9 +349,10 @@ GLInProcessContext* GLInProcessContext::CreateContext(
gfx::GpuPreference gpu_preference,
const base::Closure& callback) {
scoped_ptr<GLInProcessContextImpl> context(
- new GLInProcessContextImpl(share_resources));
+ new GLInProcessContextImpl());
if (!context->Initialize(
is_offscreen,
+ share_resources,
window,
size,
allowed_extensions,
@@ -603,10 +371,4 @@ void GLInProcessContext::SetGpuMemoryBufferFactory(
g_gpu_memory_buffer_factory = factory;
}
-// static
-void GLInProcessContext::EnableVirtualizedContext() {
- DCHECK_EQ(0u, SharedContextCount());
- g_use_virtualized_gl_context = true;
-}
-
} // namespace gpu
diff --git a/gpu/command_buffer/client/gl_in_process_context.h b/gpu/command_buffer/client/gl_in_process_context.h
index 9e38b1c84b..2d0754b227 100644
--- a/gpu/command_buffer/client/gl_in_process_context.h
+++ b/gpu/command_buffer/client/gl_in_process_context.h
@@ -30,10 +30,6 @@ class GLES2_IMPL_EXPORT GLInProcessContext {
// Must be called before any GLInProcessContext instances are created.
static void SetGpuMemoryBufferFactory(GpuMemoryBufferFactory* factory);
- // Must be called before any GLInProcessContext instances are created.
- // Default value is false.
- static void EnableVirtualizedContext();
-
// GLInProcessContext configuration attributes. These are the same as used by
// EGL. Attributes are matched using a closest fit algorithm.
enum Attribute {
diff --git a/gpu/command_buffer/service/in_process_command_buffer.cc b/gpu/command_buffer/service/in_process_command_buffer.cc
new file mode 100644
index 0000000000..e8219a1819
--- /dev/null
+++ b/gpu/command_buffer/service/in_process_command_buffer.cc
@@ -0,0 +1,700 @@
+// Copyright (c) 2012 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/in_process_command_buffer.h"
+
+#include <queue>
+#include <utility>
+
+#include <GLES2/gl2.h>
+#ifndef GL_GLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES 1
+#endif
+#include <GLES2/gl2ext.h>
+#include <GLES2/gl2extchromium.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/threading/thread.h"
+#include "gpu/command_buffer/common/id_allocator.h"
+#include "gpu/command_buffer/service/command_buffer_service.h"
+#include "gpu/command_buffer/service/context_group.h"
+#include "gpu/command_buffer/service/gl_context_virtual.h"
+#include "gpu/command_buffer/service/gpu_scheduler.h"
+#include "gpu/command_buffer/service/image_manager.h"
+#include "gpu/command_buffer/service/transfer_buffer_manager.h"
+#include "ui/gfx/size.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/gl_image.h"
+#include "ui/gl/gl_share_group.h"
+#include "ui/gl/gl_surface.h"
+
+namespace gpu {
+
+namespace {
+
+static base::LazyInstance<std::set<InProcessCommandBuffer*> >
+ g_all_shared_contexts = LAZY_INSTANCE_INITIALIZER;
+
+static bool g_use_virtualized_gl_context = false;
+static bool g_uses_explicit_scheduling = false;
+
+template <typename T>
+static void RunTaskWithResult(base::Callback<T(void)> task,
+ T* result,
+ base::WaitableEvent* completion) {
+ *result = task.Run();
+ completion->Signal();
+}
+
+class GpuInProcessThread
+ : public base::Thread,
+ public base::RefCountedThreadSafe<GpuInProcessThread> {
+ public:
+ GpuInProcessThread();
+
+ private:
+ friend class base::RefCountedThreadSafe<GpuInProcessThread>;
+ virtual ~GpuInProcessThread();
+
+ DISALLOW_COPY_AND_ASSIGN(GpuInProcessThread);
+};
+
+GpuInProcessThread::GpuInProcessThread() : base::Thread("GpuThread") {
+ Start();
+}
+
+GpuInProcessThread::~GpuInProcessThread() {
+ Stop();
+}
+
+// Used with explicit scheduling when there is no dedicated GPU thread.
+class GpuCommandQueue {
+ public:
+ GpuCommandQueue();
+ ~GpuCommandQueue();
+
+ void QueueTask(const base::Closure& task);
+ void RunTasks();
+ void SetScheduleCallback(const base::Closure& callback);
+
+ private:
+ base::Lock tasks_lock_;
+ std::queue<base::Closure> tasks_;
+ base::Closure schedule_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpuCommandQueue);
+};
+
+GpuCommandQueue::GpuCommandQueue() {}
+
+GpuCommandQueue::~GpuCommandQueue() {
+ base::AutoLock lock(tasks_lock_);
+ DCHECK(tasks_.empty());
+}
+
+void GpuCommandQueue::QueueTask(const base::Closure& task) {
+ {
+ base::AutoLock lock(tasks_lock_);
+ tasks_.push(task);
+ }
+
+ DCHECK(!schedule_callback_.is_null());
+ schedule_callback_.Run();
+}
+
+void GpuCommandQueue::RunTasks() {
+ size_t num_tasks;
+ {
+ base::AutoLock lock(tasks_lock_);
+ num_tasks = tasks_.size();
+ }
+
+ while (num_tasks) {
+ base::Closure task;
+ {
+ base::AutoLock lock(tasks_lock_);
+ task = tasks_.front();
+ tasks_.pop();
+ num_tasks = tasks_.size();
+ }
+
+ task.Run();
+ }
+}
+
+void GpuCommandQueue::SetScheduleCallback(const base::Closure& callback) {
+ DCHECK(schedule_callback_.is_null());
+ schedule_callback_ = callback;
+}
+
+static base::LazyInstance<GpuCommandQueue> g_gpu_queue =
+ LAZY_INSTANCE_INITIALIZER;
+
+class SchedulerClientBase : public InProcessCommandBuffer::SchedulerClient {
+ public:
+ explicit SchedulerClientBase(bool need_thread);
+ virtual ~SchedulerClientBase();
+
+ static bool HasClients();
+
+ protected:
+ scoped_refptr<GpuInProcessThread> thread_;
+
+ private:
+ static base::LazyInstance<std::set<SchedulerClientBase*> > all_clients_;
+ static base::LazyInstance<base::Lock> all_clients_lock_;
+};
+
+base::LazyInstance<std::set<SchedulerClientBase*> >
+ SchedulerClientBase::all_clients_ = LAZY_INSTANCE_INITIALIZER;
+base::LazyInstance<base::Lock> SchedulerClientBase::all_clients_lock_ =
+ LAZY_INSTANCE_INITIALIZER;
+
+SchedulerClientBase::SchedulerClientBase(bool need_thread) {
+ base::AutoLock(all_clients_lock_.Get());
+ if (need_thread) {
+ if (!all_clients_.Get().empty()) {
+ SchedulerClientBase* other = *all_clients_.Get().begin();
+ thread_ = other->thread_;
+ DCHECK(thread_.get());
+ } else {
+ thread_ = new GpuInProcessThread;
+ }
+ }
+ all_clients_.Get().insert(this);
+}
+
+SchedulerClientBase::~SchedulerClientBase() {
+ base::AutoLock(all_clients_lock_.Get());
+ all_clients_.Get().erase(this);
+}
+
+bool SchedulerClientBase::HasClients() {
+ base::AutoLock(all_clients_lock_.Get());
+ return !all_clients_.Get().empty();
+}
+
+// A client that talks to the GPU thread
+class ThreadClient : public SchedulerClientBase {
+ public:
+ ThreadClient();
+ virtual void QueueTask(const base::Closure& task) OVERRIDE;
+};
+
+ThreadClient::ThreadClient() : SchedulerClientBase(true) {
+ DCHECK(thread_.get());
+}
+
+void ThreadClient::QueueTask(const base::Closure& task) {
+ thread_->message_loop()->PostTask(FROM_HERE, task);
+}
+
+// A client that talks to the GpuCommandQueue
+class QueueClient : public SchedulerClientBase {
+ public:
+ QueueClient();
+ virtual void QueueTask(const base::Closure& task) OVERRIDE;
+};
+
+QueueClient::QueueClient() : SchedulerClientBase(false) {
+ DCHECK(!thread_.get());
+}
+
+void QueueClient::QueueTask(const base::Closure& task) {
+ g_gpu_queue.Get().QueueTask(task);
+}
+
+static scoped_ptr<InProcessCommandBuffer::SchedulerClient>
+CreateSchedulerClient() {
+ scoped_ptr<InProcessCommandBuffer::SchedulerClient> client;
+ if (g_uses_explicit_scheduling)
+ client.reset(new QueueClient);
+ else
+ client.reset(new ThreadClient);
+
+ return client.Pass();
+}
+
+class ScopedEvent {
+ public:
+ ScopedEvent(base::WaitableEvent* event) : event_(event) {}
+ ~ScopedEvent() { event_->Signal(); }
+
+ private:
+ base::WaitableEvent* event_;
+};
+
+} // anonyous namespace
+
+InProcessCommandBuffer::InProcessCommandBuffer()
+ : context_lost_(false),
+ share_group_id_(0),
+ last_put_offset_(-1),
+ flush_event_(false, false),
+ queue_(CreateSchedulerClient()) {}
+
+InProcessCommandBuffer::~InProcessCommandBuffer() {
+ Destroy();
+}
+
+bool InProcessCommandBuffer::IsContextLost() {
+ if (context_lost_ || !command_buffer_) {
+ return true;
+ }
+ CommandBuffer::State state = GetState();
+ return error::IsError(state.error);
+}
+
+void InProcessCommandBuffer::OnResizeView(gfx::Size size, float scale_factor) {
+ DCHECK(!surface_->IsOffscreen());
+ surface_->Resize(size);
+}
+
+bool InProcessCommandBuffer::MakeCurrent() {
+ command_buffer_lock_.AssertAcquired();
+
+ if (!context_lost_ && decoder_->MakeCurrent())
+ return true;
+ DLOG(ERROR) << "Context lost because MakeCurrent failed.";
+ command_buffer_->SetContextLostReason(decoder_->GetContextLostReason());
+ command_buffer_->SetParseError(gpu::error::kLostContext);
+ return false;
+}
+
+void InProcessCommandBuffer::PumpCommands() {
+ ScopedEvent handle_flush(&flush_event_);
+ command_buffer_lock_.AssertAcquired();
+
+ if (!MakeCurrent())
+ return;
+
+ gpu_scheduler_->PutChanged();
+ CommandBuffer::State state = command_buffer_->GetState();
+ DCHECK((!error::IsError(state.error) && !context_lost_) ||
+ (error::IsError(state.error) && context_lost_));
+}
+
+bool InProcessCommandBuffer::GetBufferChanged(int32 transfer_buffer_id) {
+ command_buffer_lock_.AssertAcquired();
+ command_buffer_->SetGetBuffer(transfer_buffer_id);
+ return true;
+}
+
+bool InProcessCommandBuffer::Initialize(
+ bool is_offscreen,
+ bool share_resources,
+ gfx::AcceleratedWidget window,
+ const gfx::Size& size,
+ const char* allowed_extensions,
+ const std::vector<int32>& attribs,
+ gfx::GpuPreference gpu_preference,
+ const base::Closure& context_lost_callback,
+ unsigned int share_group_id) {
+
+ share_resources_ = share_resources;
+ context_lost_callback_ = WrapCallback(context_lost_callback);
+ share_group_id_ = share_group_id;
+
+ base::WaitableEvent completion(true, false);
+ bool result;
+ base::Callback<bool(void)> init_task =
+ base::Bind(&InProcessCommandBuffer::InitializeOnGpuThread,
+ base::Unretained(this),
+ is_offscreen,
+ window,
+ size,
+ allowed_extensions,
+ attribs,
+ gpu_preference);
+ QueueTask(
+ base::Bind(&RunTaskWithResult<bool>, init_task, &result, &completion));
+ completion.Wait();
+ return result;
+}
+
+bool InProcessCommandBuffer::InitializeOnGpuThread(
+ bool is_offscreen,
+ gfx::AcceleratedWidget window,
+ const gfx::Size& size,
+ const char* allowed_extensions,
+ const std::vector<int32>& attribs,
+ gfx::GpuPreference gpu_preference) {
+ // Use one share group for all contexts.
+ CR_DEFINE_STATIC_LOCAL(scoped_refptr<gfx::GLShareGroup>, share_group,
+ (new gfx::GLShareGroup));
+
+ DCHECK(size.width() >= 0 && size.height() >= 0);
+
+ TransferBufferManager* manager = new TransferBufferManager();
+ transfer_buffer_manager_.reset(manager);
+ manager->Initialize();
+
+ scoped_ptr<CommandBufferService> command_buffer(
+ new CommandBufferService(transfer_buffer_manager_.get()));
+ command_buffer->SetPutOffsetChangeCallback(base::Bind(
+ &InProcessCommandBuffer::PumpCommands, base::Unretained(this)));
+ command_buffer->SetParseErrorCallback(base::Bind(
+ &InProcessCommandBuffer::OnContextLost, base::Unretained(this)));
+
+ if (!command_buffer->Initialize()) {
+ LOG(ERROR) << "Could not initialize command buffer.";
+ DestroyOnGpuThread();
+ return false;
+ }
+
+ InProcessCommandBuffer* context_group = NULL;
+
+ if (share_resources_ && !g_all_shared_contexts.Get().empty()) {
+ DCHECK(share_group_id_);
+ for (std::set<InProcessCommandBuffer*>::iterator it =
+ g_all_shared_contexts.Get().begin();
+ it != g_all_shared_contexts.Get().end();
+ ++it) {
+ if ((*it)->share_group_id_ == share_group_id_) {
+ context_group = *it;
+ DCHECK(context_group->share_resources_);
+ context_lost_ = context_group->IsContextLost();
+ break;
+ }
+ }
+ if (!context_group)
+ share_group = new gfx::GLShareGroup;
+ }
+
+ bool bind_generates_resource = false;
+ decoder_.reset(gles2::GLES2Decoder::Create(
+ context_group ? context_group->decoder_->GetContextGroup()
+ : new gles2::ContextGroup(
+ NULL, NULL, NULL, NULL, bind_generates_resource)));
+
+ gpu_scheduler_.reset(
+ new GpuScheduler(command_buffer.get(), decoder_.get(), decoder_.get()));
+ command_buffer->SetGetBufferChangeCallback(base::Bind(
+ &GpuScheduler::SetGetBuffer, base::Unretained(gpu_scheduler_.get())));
+ command_buffer_ = command_buffer.Pass();
+
+ decoder_->set_engine(gpu_scheduler_.get());
+
+ if (is_offscreen)
+ surface_ = gfx::GLSurface::CreateOffscreenGLSurface(size);
+ else
+ surface_ = gfx::GLSurface::CreateViewGLSurface(window);
+
+ if (!surface_.get()) {
+ LOG(ERROR) << "Could not create GLSurface.";
+ DestroyOnGpuThread();
+ return false;
+ }
+
+ if (g_use_virtualized_gl_context) {
+ context_ = share_group->GetSharedContext();
+ if (!context_.get()) {
+ context_ = gfx::GLContext::CreateGLContext(
+ share_group.get(), surface_.get(), gpu_preference);
+ share_group->SetSharedContext(context_.get());
+ }
+
+ context_ = new GLContextVirtual(
+ share_group.get(), context_.get(), decoder_->AsWeakPtr());
+ if (context_->Initialize(surface_.get(), gpu_preference)) {
+ VLOG(1) << "Created virtual GL context.";
+ } else {
+ context_ = NULL;
+ }
+ } else {
+ context_ = gfx::GLContext::CreateGLContext(
+ share_group.get(), surface_.get(), gpu_preference);
+ }
+
+ if (!context_.get()) {
+ LOG(ERROR) << "Could not create GLContext.";
+ DestroyOnGpuThread();
+ return false;
+ }
+
+ if (!context_->MakeCurrent(surface_.get())) {
+ LOG(ERROR) << "Could not make context current.";
+ DestroyOnGpuThread();
+ return false;
+ }
+
+ gles2::DisallowedFeatures disallowed_features;
+ disallowed_features.swap_buffer_complete_callback = true;
+ disallowed_features.gpu_memory_manager = true;
+ if (!decoder_->Initialize(surface_,
+ context_,
+ is_offscreen,
+ size,
+ disallowed_features,
+ allowed_extensions,
+ attribs)) {
+ LOG(ERROR) << "Could not initialize decoder.";
+ DestroyOnGpuThread();
+ return false;
+ }
+
+ if (!is_offscreen) {
+ decoder_->SetResizeCallback(base::Bind(
+ &InProcessCommandBuffer::OnResizeView, base::Unretained(this)));
+ }
+
+ if (share_resources_) {
+ g_all_shared_contexts.Pointer()->insert(this);
+ }
+
+ return true;
+}
+
+void InProcessCommandBuffer::Destroy() {
+ base::WaitableEvent completion(true, false);
+ bool result;
+ base::Callback<bool(void)> destroy_task = base::Bind(
+ &InProcessCommandBuffer::DestroyOnGpuThread, base::Unretained(this));
+ QueueTask(
+ base::Bind(&RunTaskWithResult<bool>, destroy_task, &result, &completion));
+ completion.Wait();
+}
+
+bool InProcessCommandBuffer::DestroyOnGpuThread() {
+ command_buffer_.reset();
+ // Clean up GL resources if possible.
+ bool have_context = context_ && context_->MakeCurrent(surface_);
+ if (decoder_) {
+ decoder_->Destroy(have_context);
+ decoder_.reset();
+ }
+ context_ = NULL;
+ surface_ = NULL;
+
+ g_all_shared_contexts.Pointer()->erase(this);
+ return true;
+}
+
+unsigned int InProcessCommandBuffer::CreateImageForGpuMemoryBuffer(
+ gfx::GpuMemoryBufferHandle buffer,
+ gfx::Size size) {
+ unsigned int image_id;
+ {
+ // TODO: ID allocation should go through CommandBuffer
+ base::AutoLock lock(command_buffer_lock_);
+ gles2::ContextGroup* group = decoder_->GetContextGroup();
+ image_id =
+ group->GetIdAllocator(gles2::id_namespaces::kImages)->AllocateID();
+ }
+ base::Closure image_task =
+ base::Bind(&InProcessCommandBuffer::CreateImageOnGpuThread,
+ base::Unretained(this), buffer, size, image_id);
+ QueueTask(image_task);
+ return image_id;
+}
+
+void InProcessCommandBuffer::CreateImageOnGpuThread(
+ gfx::GpuMemoryBufferHandle buffer,
+ gfx::Size size,
+ unsigned int image_id) {
+ scoped_refptr<gfx::GLImage> gl_image =
+ gfx::GLImage::CreateGLImageForGpuMemoryBuffer(buffer, size);
+ decoder_->GetContextGroup()->image_manager()->AddImage(gl_image, image_id);
+}
+
+void InProcessCommandBuffer::RemoveImage(unsigned int image_id) {
+ {
+ // TODO: ID allocation should go through CommandBuffer
+ base::AutoLock lock(command_buffer_lock_);
+ gles2::ContextGroup* group = decoder_->GetContextGroup();
+ group->GetIdAllocator(gles2::id_namespaces::kImages)->FreeID(image_id);
+ }
+ base::Closure image_manager_task =
+ base::Bind(&InProcessCommandBuffer::RemoveImageOnGpuThread,
+ base::Unretained(this),
+ image_id);
+ QueueTask(image_manager_task);
+}
+
+void InProcessCommandBuffer::RemoveImageOnGpuThread(unsigned int image_id) {
+ decoder_->GetContextGroup()->image_manager()->RemoveImage(image_id);
+}
+
+void InProcessCommandBuffer::OnContextLost() {
+ if (!context_lost_callback_.is_null())
+ context_lost_callback_.Run();
+
+ context_lost_ = true;
+ if (share_resources_) {
+ for (std::set<InProcessCommandBuffer*>::iterator it =
+ g_all_shared_contexts.Get().begin();
+ it != g_all_shared_contexts.Get().end();
+ ++it) {
+ (*it)->context_lost_ = true;
+ }
+ }
+}
+
+CommandBuffer::State InProcessCommandBuffer::GetStateFast() {
+ base::AutoLock lock(command_buffer_lock_);
+ return last_state_ = command_buffer_->GetState();
+}
+
+CommandBuffer::State InProcessCommandBuffer::GetState() {
+ return GetStateFast();
+}
+
+CommandBuffer::State InProcessCommandBuffer::GetLastState() {
+ return last_state_;
+}
+
+int32 InProcessCommandBuffer::GetLastToken() { return last_state_.token; }
+
+void InProcessCommandBuffer::FlushOnGpuThread(int32 put_offset) {
+ base::AutoLock lock(command_buffer_lock_);
+ command_buffer_->Flush(put_offset);
+}
+
+void InProcessCommandBuffer::Flush(int32 put_offset) {
+ if (last_state_.error != gpu::error::kNoError)
+ return;
+
+ if (last_put_offset_ == put_offset)
+ return;
+
+ last_put_offset_ = put_offset;
+ base::Closure task = base::Bind(&InProcessCommandBuffer::FlushOnGpuThread,
+ base::Unretained(this),
+ put_offset);
+ QueueTask(task);
+}
+
+CommandBuffer::State InProcessCommandBuffer::FlushSync(int32 put_offset,
+ int32 last_known_get) {
+ if (put_offset == last_known_get || last_state_.error != gpu::error::kNoError)
+ return last_state_;
+
+ Flush(put_offset);
+ GetStateFast();
+ while (last_known_get == last_state_.get_offset &&
+ last_state_.error == gpu::error::kNoError) {
+ flush_event_.Wait();
+ GetStateFast();
+ }
+
+ return last_state_;
+}
+
+void InProcessCommandBuffer::SetGetBuffer(int32 shm_id) {
+ if (last_state_.error != gpu::error::kNoError)
+ return;
+
+ {
+ base::AutoLock lock(command_buffer_lock_);
+ command_buffer_->SetGetBuffer(shm_id);
+ last_put_offset_ = 0;
+ }
+ GetStateFast();
+}
+
+gpu::Buffer InProcessCommandBuffer::CreateTransferBuffer(size_t size,
+ int32* id) {
+ base::AutoLock lock(command_buffer_lock_);
+ return command_buffer_->CreateTransferBuffer(size, id);
+}
+
+void InProcessCommandBuffer::DestroyTransferBuffer(int32 id) {
+ base::Closure task = base::Bind(&CommandBuffer::DestroyTransferBuffer,
+ base::Unretained(command_buffer_.get()),
+ id);
+
+ QueueTask(task);
+}
+
+gpu::Buffer InProcessCommandBuffer::GetTransferBuffer(int32 id) {
+ NOTREACHED();
+ return gpu::Buffer();
+}
+
+uint32 InProcessCommandBuffer::InsertSyncPoint() {
+ NOTREACHED();
+ return 0;
+}
+void InProcessCommandBuffer::SignalSyncPoint(unsigned sync_point,
+ const base::Closure& callback) {
+ QueueTask(WrapCallback(callback));
+}
+
+gpu::error::Error InProcessCommandBuffer::GetLastError() {
+ return last_state_.error;
+}
+
+bool InProcessCommandBuffer::Initialize() {
+ NOTREACHED();
+ return false;
+}
+
+void InProcessCommandBuffer::SetGetOffset(int32 get_offset) { NOTREACHED(); }
+
+void InProcessCommandBuffer::SetToken(int32 token) { NOTREACHED(); }
+
+void InProcessCommandBuffer::SetParseError(gpu::error::Error error) {
+ NOTREACHED();
+}
+
+void InProcessCommandBuffer::SetContextLostReason(
+ gpu::error::ContextLostReason reason) {
+ NOTREACHED();
+}
+
+namespace {
+
+static void PostCallback(const scoped_refptr<base::MessageLoopProxy>& loop,
+ const base::Closure& callback) {
+ if (loop != base::MessageLoopProxy::current())
+ loop->PostTask(FROM_HERE, callback);
+ else
+ callback.Run();
+}
+
+static void RunCallback(scoped_ptr<base::Closure> callback) {
+ DCHECK(callback.get());
+ callback->Run();
+}
+
+} // anonymous namespace
+
+base::Closure InProcessCommandBuffer::WrapCallback(
+ const base::Closure& callback) {
+ // Make sure the callback gets deleted on the target thread by passing
+ // ownership.
+ scoped_ptr<base::Closure> scoped_callback(new base::Closure(callback));
+ base::Closure callback_on_client_thread =
+ base::Bind(&RunCallback, base::Passed(&scoped_callback));
+ base::Closure wrapped_callback =
+ base::Bind(&PostCallback, base::MessageLoopProxy::current(),
+ callback);
+ return wrapped_callback;
+}
+
+// static
+void InProcessCommandBuffer::EnableVirtualizedContext() {
+ g_use_virtualized_gl_context = true;
+}
+
+// static
+void InProcessCommandBuffer::SetScheduleCallback(
+ const base::Closure& callback) {
+ DCHECK(!g_uses_explicit_scheduling);
+ DCHECK(!SchedulerClientBase::HasClients());
+ g_uses_explicit_scheduling = true;
+ g_gpu_queue.Get().SetScheduleCallback(callback);
+}
+
+// static
+void InProcessCommandBuffer::ProcessGpuWorkOnCurrentThread() {
+ g_gpu_queue.Get().RunTasks();
+}
+
+} // namespace gpu
diff --git a/gpu/command_buffer/service/in_process_command_buffer.h b/gpu/command_buffer/service/in_process_command_buffer.h
new file mode 100644
index 0000000000..88d437dace
--- /dev/null
+++ b/gpu/command_buffer/service/in_process_command_buffer.h
@@ -0,0 +1,154 @@
+// Copyright (c) 2012 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_IN_PROCESS_COMMAND_BUFFER_H_
+#define GPU_COMMAND_BUFFER_SERVICE_IN_PROCESS_COMMAND_BUFFER_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "gpu/command_buffer/common/command_buffer.h"
+#include "gpu/gpu_export.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gl/gpu_preference.h"
+
+namespace gfx {
+class GLContext;
+class GLImage;
+class GLSurface;
+class Size;
+}
+
+namespace gpu {
+
+namespace gles2 {
+class GLES2Decoder;
+}
+
+class GpuScheduler;
+class TransferBufferManagerInterface;
+
+// This class provides a thread-safe interface to the global GPU service (for
+// example GPU thread) when being run in single process mode.
+// However, the behavior for accessing one context (i.e. one instance of this
+// class) from different client threads is undefined.
+class GPU_EXPORT InProcessCommandBuffer : public CommandBuffer {
+ public:
+ InProcessCommandBuffer();
+ virtual ~InProcessCommandBuffer();
+
+ // Used to override the GPU thread with explicit scheduling.
+ // (By default an internal GPU thread will be spawned to handle all GL work
+ // and the two functions are unused.)
+ // The callback will be called from different client threads. After the
+ // callback is issued, the client is expected to eventually call
+ // ProcessGpuWorkOnCurrentThread(). The latter cannot be called from different
+ // threads.
+ // The callback needs to be set before any context is created.
+ static void SetScheduleCallback(const base::Closure& callback);
+ static void ProcessGpuWorkOnCurrentThread();
+
+ static void EnableVirtualizedContext();
+
+ bool Initialize(bool is_offscreen,
+ bool share_resources,
+ gfx::AcceleratedWidget window,
+ const gfx::Size& size,
+ const char* allowed_extensions,
+ const std::vector<int32>& attribs,
+ gfx::GpuPreference gpu_preference,
+ const base::Closure& context_lost_callback,
+ unsigned int share_group_id);
+ void Destroy();
+ void SignalSyncPoint(unsigned sync_point,
+ const base::Closure& callback);
+ unsigned int CreateImageForGpuMemoryBuffer(
+ gfx::GpuMemoryBufferHandle buffer,
+ gfx::Size size);
+ void RemoveImage(unsigned int image_id);
+
+ // CommandBuffer implementation:
+ virtual bool Initialize() OVERRIDE;
+ virtual State GetState() OVERRIDE;
+ virtual State GetLastState() OVERRIDE;
+ virtual int32 GetLastToken() OVERRIDE;
+ virtual void Flush(int32 put_offset) OVERRIDE;
+ virtual State FlushSync(int32 put_offset, int32 last_known_get) OVERRIDE;
+ virtual void SetGetBuffer(int32 shm_id) OVERRIDE;
+ virtual void SetGetOffset(int32 get_offset) OVERRIDE;
+ virtual gpu::Buffer CreateTransferBuffer(size_t size, int32* id) OVERRIDE;
+ virtual void DestroyTransferBuffer(int32 id) OVERRIDE;
+ virtual gpu::Buffer GetTransferBuffer(int32 id) OVERRIDE;
+ virtual void SetToken(int32 token) OVERRIDE;
+ virtual void SetParseError(gpu::error::Error error) OVERRIDE;
+ virtual void SetContextLostReason(
+ gpu::error::ContextLostReason reason) OVERRIDE;
+ virtual uint32 InsertSyncPoint() OVERRIDE;
+ virtual gpu::error::Error GetLastError() OVERRIDE;
+
+ // The serializer interface to the GPU service (i.e. thread).
+ class SchedulerClient {
+ public:
+ virtual ~SchedulerClient() {}
+ virtual void QueueTask(const base::Closure& task) = 0;
+ };
+
+ private:
+ bool InitializeOnGpuThread(bool is_offscreen,
+ gfx::AcceleratedWidget window,
+ const gfx::Size& size,
+ const char* allowed_extensions,
+ const std::vector<int32>& attribs,
+ gfx::GpuPreference gpu_preference);
+ bool DestroyOnGpuThread();
+ void FlushOnGpuThread(int32 put_offset);
+ void CreateImageOnGpuThread(gfx::GpuMemoryBufferHandle buffer,
+ gfx::Size size,
+ unsigned int image_id);
+ void RemoveImageOnGpuThread(unsigned int image_id);
+ bool MakeCurrent();
+ bool IsContextLost();
+ base::Closure WrapCallback(const base::Closure& callback);
+ State GetStateFast();
+ void QueueTask(const base::Closure& task) { queue_->QueueTask(task); }
+
+ // Callbacks:
+ void OnContextLost();
+ void OnResizeView(gfx::Size size, float scale_factor);
+ bool GetBufferChanged(int32 transfer_buffer_id);
+ void PumpCommands();
+
+ // Members accessed on the gpu thread (possibly with the exception of
+ // creation):
+ bool context_lost_;
+ bool share_resources_;
+ scoped_ptr<TransferBufferManagerInterface> transfer_buffer_manager_;
+ scoped_ptr<GpuScheduler> gpu_scheduler_;
+ scoped_ptr<gles2::GLES2Decoder> decoder_;
+ scoped_refptr<gfx::GLContext> context_;
+ scoped_refptr<gfx::GLSurface> surface_;
+ base::Closure context_lost_callback_;
+ unsigned int share_group_id_;
+
+ // Members accessed on the client thread:
+ State last_state_;
+ int32 last_put_offset_;
+
+ // Accessed on both threads:
+ scoped_ptr<CommandBuffer> command_buffer_;
+ base::Lock command_buffer_lock_;
+ base::WaitableEvent flush_event_;
+ scoped_ptr<SchedulerClient> queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(InProcessCommandBuffer);
+};
+
+} // namespace gpu
+
+#endif // GPU_COMMAND_BUFFER_SERVICE_IN_PROCESS_COMMAND_BUFFER_H_
diff --git a/gpu/command_buffer_service.gypi b/gpu/command_buffer_service.gypi
index c47f15f86c..37d28a5b4c 100644
--- a/gpu/command_buffer_service.gypi
+++ b/gpu/command_buffer_service.gypi
@@ -86,6 +86,8 @@
'command_buffer/service/id_manager.cc',
'command_buffer/service/image_manager.cc',
'command_buffer/service/image_manager.h',
+ 'command_buffer/service/in_process_command_buffer.cc',
+ 'command_buffer/service/in_process_command_buffer.h',
'command_buffer/service/logger.cc',
'command_buffer/service/logger.h',
'command_buffer/service/mailbox_manager.cc',
diff --git a/gpu/command_buffer_service.target.darwin-arm.mk b/gpu/command_buffer_service.target.darwin-arm.mk
index 8c6fdca212..dc7f90e20f 100644
--- a/gpu/command_buffer_service.target.darwin-arm.mk
+++ b/gpu/command_buffer_service.target.darwin-arm.mk
@@ -53,6 +53,7 @@ LOCAL_SRC_FILES := \
gpu/command_buffer/service/gpu_tracer.cc \
gpu/command_buffer/service/id_manager.cc \
gpu/command_buffer/service/image_manager.cc \
+ gpu/command_buffer/service/in_process_command_buffer.cc \
gpu/command_buffer/service/logger.cc \
gpu/command_buffer/service/mailbox_manager.cc \
gpu/command_buffer/service/memory_program_cache.cc \
diff --git a/gpu/command_buffer_service.target.darwin-mips.mk b/gpu/command_buffer_service.target.darwin-mips.mk
index f324f40bb5..8ab0fbf9a7 100644
--- a/gpu/command_buffer_service.target.darwin-mips.mk
+++ b/gpu/command_buffer_service.target.darwin-mips.mk
@@ -53,6 +53,7 @@ LOCAL_SRC_FILES := \
gpu/command_buffer/service/gpu_tracer.cc \
gpu/command_buffer/service/id_manager.cc \
gpu/command_buffer/service/image_manager.cc \
+ gpu/command_buffer/service/in_process_command_buffer.cc \
gpu/command_buffer/service/logger.cc \
gpu/command_buffer/service/mailbox_manager.cc \
gpu/command_buffer/service/memory_program_cache.cc \
diff --git a/gpu/command_buffer_service.target.darwin-x86.mk b/gpu/command_buffer_service.target.darwin-x86.mk
index 4cef07bcea..6c7b408aec 100644
--- a/gpu/command_buffer_service.target.darwin-x86.mk
+++ b/gpu/command_buffer_service.target.darwin-x86.mk
@@ -53,6 +53,7 @@ LOCAL_SRC_FILES := \
gpu/command_buffer/service/gpu_tracer.cc \
gpu/command_buffer/service/id_manager.cc \
gpu/command_buffer/service/image_manager.cc \
+ gpu/command_buffer/service/in_process_command_buffer.cc \
gpu/command_buffer/service/logger.cc \
gpu/command_buffer/service/mailbox_manager.cc \
gpu/command_buffer/service/memory_program_cache.cc \
diff --git a/gpu/command_buffer_service.target.linux-arm.mk b/gpu/command_buffer_service.target.linux-arm.mk
index 8c6fdca212..dc7f90e20f 100644
--- a/gpu/command_buffer_service.target.linux-arm.mk
+++ b/gpu/command_buffer_service.target.linux-arm.mk
@@ -53,6 +53,7 @@ LOCAL_SRC_FILES := \
gpu/command_buffer/service/gpu_tracer.cc \
gpu/command_buffer/service/id_manager.cc \
gpu/command_buffer/service/image_manager.cc \
+ gpu/command_buffer/service/in_process_command_buffer.cc \
gpu/command_buffer/service/logger.cc \
gpu/command_buffer/service/mailbox_manager.cc \
gpu/command_buffer/service/memory_program_cache.cc \
diff --git a/gpu/command_buffer_service.target.linux-mips.mk b/gpu/command_buffer_service.target.linux-mips.mk
index f324f40bb5..8ab0fbf9a7 100644
--- a/gpu/command_buffer_service.target.linux-mips.mk
+++ b/gpu/command_buffer_service.target.linux-mips.mk
@@ -53,6 +53,7 @@ LOCAL_SRC_FILES := \
gpu/command_buffer/service/gpu_tracer.cc \
gpu/command_buffer/service/id_manager.cc \
gpu/command_buffer/service/image_manager.cc \
+ gpu/command_buffer/service/in_process_command_buffer.cc \
gpu/command_buffer/service/logger.cc \
gpu/command_buffer/service/mailbox_manager.cc \
gpu/command_buffer/service/memory_program_cache.cc \
diff --git a/gpu/command_buffer_service.target.linux-x86.mk b/gpu/command_buffer_service.target.linux-x86.mk
index 4cef07bcea..6c7b408aec 100644
--- a/gpu/command_buffer_service.target.linux-x86.mk
+++ b/gpu/command_buffer_service.target.linux-x86.mk
@@ -53,6 +53,7 @@ LOCAL_SRC_FILES := \
gpu/command_buffer/service/gpu_tracer.cc \
gpu/command_buffer/service/id_manager.cc \
gpu/command_buffer/service/image_manager.cc \
+ gpu/command_buffer/service/in_process_command_buffer.cc \
gpu/command_buffer/service/logger.cc \
gpu/command_buffer/service/mailbox_manager.cc \
gpu/command_buffer/service/memory_program_cache.cc \