summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsievers@chromium.org <sievers@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-01 23:57:56 +0000
committersievers@chromium.org <sievers@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-01 23:57:56 +0000
commit92dd777782cec8b8add8dcf2d4a3618c3ebbc21a (patch)
treec0b73e8072a880accc293f734ddea14fc4b8995a
parentbd9934815e7974c56e8a205c6e3e08d0dbf27872 (diff)
downloadchromium_src-92dd777782cec8b8add8dcf2d4a3618c3ebbc21a.zip
chromium_src-92dd777782cec8b8add8dcf2d4a3618c3ebbc21a.tar.gz
chromium_src-92dd777782cec8b8add8dcf2d4a3618c3ebbc21a.tar.bz2
GLInProcessContext: support async flushes and dedicated GPU thread
This separates the command buffer client and service portions in GLInProcessContext. It removes the 'big lock', which was used to flush commands instantly and on the calling thread (GL client thread). Instead it now creates a dedicated GPU thread. For Android WebView, it further allows the GPU message loop to be explicitly processed (because a context is only available at given times on a specific thread). BUG=246450,239760,234964 Review URL: https://chromiumcodereview.appspot.com/19522006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@215144 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--android_webview/DEPS2
-rw-r--r--android_webview/android_webview.gyp1
-rw-r--r--android_webview/browser/in_process_view_renderer.cc46
-rw-r--r--android_webview/lib/main/aw_main_delegate.cc3
-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.cc703
-rw-r--r--gpu/command_buffer/service/in_process_command_buffer.h154
-rw-r--r--gpu/command_buffer_service.gypi2
-rw-r--r--webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.cc6
10 files changed, 991 insertions, 332 deletions
diff --git a/android_webview/DEPS b/android_webview/DEPS
index 5f848d2..1ce4c33 100644
--- a/android_webview/DEPS
+++ b/android_webview/DEPS
@@ -8,7 +8,7 @@ include_rules = [
"-android_webview/lib",
"+content/public/common",
- "+gpu/command_buffer/client",
+ "+gpu",
"+jni",
"+net",
"+skia",
diff --git a/android_webview/android_webview.gyp b/android_webview/android_webview.gyp
index ddbd1b8..299dc12 100644
--- a/android_webview/android_webview.gyp
+++ b/android_webview/android_webview.gyp
@@ -83,6 +83,7 @@
'../components/components.gyp:web_contents_delegate_android',
'../content/content.gyp:content',
'../skia/skia.gyp:skia',
+ '../gpu/gpu.gyp:command_buffer_service',
'../gpu/gpu.gyp:gles2_implementation',
'../ui/gl/gl.gyp:gl',
'../webkit/common/gpu/webkit_gpu.gyp:webkit_gpu',
diff --git a/android_webview/browser/in_process_view_renderer.cc b/android_webview/browser/in_process_view_renderer.cc
index f5e1762..d761124 100644
--- a/android_webview/browser/in_process_view_renderer.cc
+++ b/android_webview/browser/in_process_view_renderer.cc
@@ -16,7 +16,9 @@
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "content/public/browser/android/synchronous_compositor.h"
+#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
+#include "gpu/command_buffer/service/in_process_command_buffer.h"
#include "skia/ext/refptr.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
@@ -31,6 +33,7 @@
using base::android::AttachCurrentThread;
using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;
+using content::BrowserThread;
namespace android_webview {
@@ -125,8 +128,44 @@ bool g_is_skia_version_compatible = false;
const int64 kFallbackTickTimeoutInMilliseconds = 500;
+class ScopedAllowGL {
+ public:
+ ScopedAllowGL();
+ ~ScopedAllowGL();
+
+ static bool IsAllowed() {
+ return BrowserThread::CurrentlyOn(BrowserThread::UI) && allow_gl;
+ }
+
+ private:
+ static bool allow_gl;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowGL);
+};
+
+ScopedAllowGL::ScopedAllowGL() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!allow_gl);
+ allow_gl = true;
+}
+
+ScopedAllowGL::~ScopedAllowGL() {
+ allow_gl = false;
+}
+
+bool ScopedAllowGL::allow_gl = false;
+
} // namespace
+// Called from different threads!
+static void ScheduleGpuWork() {
+ if (ScopedAllowGL::IsAllowed()) {
+ gpu::InProcessCommandBuffer::ProcessGpuWorkOnCurrentThread();
+ } else {
+ // TODO: We need to request a callback with a GL context current here.
+ }
+}
+
// static
void BrowserViewRenderer::SetAwDrawSWFunctionTable(
AwDrawSWFunctionTable* table) {
@@ -135,6 +174,9 @@ void BrowserViewRenderer::SetAwDrawSWFunctionTable(
g_sw_draw_functions->is_skia_version_compatible(&SkGraphics::GetVersion);
LOG_IF(WARNING, !g_is_skia_version_compatible)
<< "Skia versions are not compatible, rendering performance will suffer.";
+
+ gpu::InProcessCommandBuffer::SetScheduleCallback(
+ base::Bind(&ScheduleGpuWork));
}
// static
@@ -229,6 +271,8 @@ void InProcessViewRenderer::DrawGL(AwDrawGLInfo* draw_info) {
}
ScopedAppGLStateRestore state_restore(ScopedAppGLStateRestore::MODE_DRAW);
+ gpu::InProcessCommandBuffer::ProcessGpuWorkOnCurrentThread();
+ ScopedAllowGL allow_gl;
if (attached_to_window_ && compositor_ && !hardware_initialized_) {
TRACE_EVENT0("android_webview", "InitializeHwDraw");
@@ -462,6 +506,8 @@ void InProcessViewRenderer::OnDetachedFromWindow() {
ScopedAppGLStateRestore state_restore(
ScopedAppGLStateRestore::MODE_DETACH_FROM_WINDOW);
+ gpu::InProcessCommandBuffer::ProcessGpuWorkOnCurrentThread();
+ ScopedAllowGL allow_gl;
compositor_->ReleaseHwDraw();
hardware_initialized_ = false;
}
diff --git a/android_webview/lib/main/aw_main_delegate.cc b/android_webview/lib/main/aw_main_delegate.cc
index 4c94d1f..0e43adde 100644
--- a/android_webview/lib/main/aw_main_delegate.cc
+++ b/android_webview/lib/main/aw_main_delegate.cc
@@ -22,6 +22,7 @@
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_switches.h"
#include "gpu/command_buffer/client/gl_in_process_context.h"
+#include "gpu/command_buffer/service/in_process_command_buffer.h"
#include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
namespace android_webview {
@@ -45,9 +46,9 @@ AwMainDelegate::~AwMainDelegate() {
bool AwMainDelegate::BasicStartupComplete(int* exit_code) {
content::SetContentClient(&content_client_);
- gpu::GLInProcessContext::EnableVirtualizedContext();
gpu::GLInProcessContext::SetGpuMemoryBufferFactory(
gpu_memory_buffer_factory_.get());
+ gpu::InProcessCommandBuffer::EnableVirtualizedContext();
CommandLine* cl = CommandLine::ForCurrentProcess();
cl->AppendSwitch(switches::kEnableBeginFrameScheduling);
diff --git a/gpu/command_buffer/client/gl_in_process_context.cc b/gpu/command_buffer/client/gl_in_process_context.cc
index 2359a05..b1b5a03 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 9e38b1c..2d0754b 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 0000000..8bf9838
--- /dev/null
+++ b/gpu/command_buffer/service/in_process_command_buffer.cc
@@ -0,0 +1,703 @@
+// 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/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_callback_.Reset();
+ }
+
+ 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 {
+
+void PostCallback(const scoped_refptr<base::MessageLoopProxy>& loop,
+ const base::Closure& callback) {
+ if (!loop->BelongsToCurrentThread()) {
+ loop->PostTask(FROM_HERE, callback);
+ } else {
+ callback.Run();
+ }
+}
+
+void RunOnTargetThread(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(&RunOnTargetThread, base::Passed(&scoped_callback));
+ base::Closure wrapped_callback =
+ base::Bind(&PostCallback, base::MessageLoopProxy::current(),
+ callback_on_client_thread);
+ 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 0000000..489c9e9
--- /dev/null
+++ b/gpu/command_buffer/service/in_process_command_buffer.h
@@ -0,0 +1,154 @@
+// 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.
+
+#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 c47f15f..37d28a5 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/webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.cc b/webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.cc
index 7f5f170..57767ec 100644
--- a/webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.cc
+++ b/webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.cc
@@ -1163,11 +1163,6 @@ void WebGraphicsContext3DInProcessCommandBufferImpl::signalSyncPoint(
// Take ownership of the callback.
context_->SignalSyncPoint(
sync_point, base::Bind(&OnSignalSyncPoint, base::Owned(callback)));
- // Make sure we have something to flush, or shallowFlushCHROMIUM()
- // doesn't do anything.
- gl_->helper()->Noop(1);
- // This fill force PumpCommands() to run which is what triggers the signal.
- shallowFlushCHROMIUM();
}
void WebGraphicsContext3DInProcessCommandBufferImpl::signalQuery(
@@ -1182,7 +1177,6 @@ void WebGraphicsContext3DInProcessCommandBufferImpl::loseContextCHROMIUM(
WGC3Denum current, WGC3Denum other) {
gl_->LoseContextCHROMIUM(current, other);
gl_->ShallowFlushCHROMIUM();
- DCHECK(isContextLost());
}
DELEGATE_TO_GL_9(asyncTexImage2DCHROMIUM, AsyncTexImage2DCHROMIUM,