summaryrefslogtreecommitdiffstats
path: root/content/renderer/ggl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'content/renderer/ggl.cc')
-rw-r--r--content/renderer/ggl.cc605
1 files changed, 605 insertions, 0 deletions
diff --git a/content/renderer/ggl.cc b/content/renderer/ggl.cc
new file mode 100644
index 0000000..e73f50d
--- /dev/null
+++ b/content/renderer/ggl.cc
@@ -0,0 +1,605 @@
+// Copyright (c) 2010 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 "content/renderer/ggl.h"
+
+#include "base/lazy_instance.h"
+#include "base/ref_counted.h"
+#include "base/weak_ptr.h"
+#include "chrome/renderer/command_buffer_proxy.h"
+#include "chrome/renderer/gpu_channel_host.h"
+#include "chrome/renderer/gpu_video_service_host.h"
+#include "chrome/renderer/render_widget.h"
+#include "content/renderer/media/gles2_video_decode_context.h"
+#include "ipc/ipc_channel_handle.h"
+
+#if defined(ENABLE_GPU)
+#include "gpu/command_buffer/client/gles2_cmd_helper.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/client/gles2_lib.h"
+#include "gpu/command_buffer/common/constants.h"
+#include "gpu/common/gpu_trace_event.h"
+#include "gpu/GLES2/gles2_command_buffer.h"
+#endif // ENABLE_GPU
+
+namespace ggl {
+
+#if defined(ENABLE_GPU)
+
+namespace {
+
+const int32 kCommandBufferSize = 1024 * 1024;
+// TODO(kbr): make the transfer buffer size configurable via context
+// creation attributes.
+const int32 kTransferBufferSize = 1024 * 1024;
+
+// Singleton used to initialize and terminate the gles2 library.
+class GLES2Initializer {
+ public:
+ GLES2Initializer() {
+ gles2::Initialize();
+ }
+
+ ~GLES2Initializer() {
+ gles2::Terminate();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GLES2Initializer);
+};
+
+static base::LazyInstance<GLES2Initializer> g_gles2_initializer(
+ base::LINKER_INITIALIZED);
+
+} // namespace anonymous
+
+// Manages a GL context.
+class Context : public base::SupportsWeakPtr<Context> {
+ public:
+ Context(GpuChannelHost* channel, Context* parent);
+ ~Context();
+
+ // Initialize a GGL context that can be used in association with a a GPU
+ // channel acquired from a RenderWidget or RenderView.
+ bool Initialize(bool onscreen,
+ int render_view_id,
+ const gfx::Size& size,
+ const char* allowed_extensions,
+ const int32* attrib_list);
+
+#if defined(OS_MACOSX)
+ // Asynchronously resizes an onscreen frame buffer.
+ void ResizeOnscreen(const gfx::Size& size);
+#endif
+
+ // Asynchronously resizes an offscreen frame buffer.
+ void ResizeOffscreen(const gfx::Size& size);
+
+ // Provides a callback that will be invoked when SwapBuffers has completed
+ // service side.
+ void SetSwapBuffersCallback(Callback0::Type* callback) {
+ swap_buffers_callback_.reset(callback);
+ }
+
+ void SetContextLostCallback(Callback0::Type* callback) {
+ context_lost_callback_.reset(callback);
+ }
+
+ // For an offscreen frame buffer context, return the frame buffer ID with
+ // respect to the parent.
+ uint32 parent_texture_id() const {
+ return parent_texture_id_;
+ }
+
+ uint32 CreateParentTexture(const gfx::Size& size) const;
+ void DeleteParentTexture(uint32 texture) const;
+
+ // Destroy all resources associated with the GGL context.
+ void Destroy();
+
+ // Make a GGL context current for the calling thread.
+ static bool MakeCurrent(Context* context);
+
+ // Display all content rendered since last call to SwapBuffers.
+ // TODO(apatrick): support rendering to browser window. This function is
+ // not useful at this point.
+ bool SwapBuffers();
+
+ // Create a hardware accelerated video decoder associated with this context.
+ media::VideoDecodeEngine* CreateVideoDecodeEngine();
+
+ // Create a hardware video decode context associated with this context.
+ media::VideoDecodeContext* CreateVideoDecodeContext(MessageLoop* message_loop,
+ bool hardware_decoder);
+
+ // Get the current error code. Clears context's error code afterwards.
+ Error GetError();
+
+ // Replace the current error code with this.
+ void SetError(Error error);
+
+ bool IsCommandBufferContextLost();
+
+ // TODO(gman): Remove this.
+ void DisableShaderTranslation();
+
+ gpu::gles2::GLES2Implementation* gles2_implementation() const {
+ return gles2_implementation_;
+ }
+
+ CommandBufferProxy* command_buffer() const {
+ return command_buffer_;
+ }
+
+ private:
+ void OnSwapBuffers();
+ void OnContextLost();
+
+ scoped_refptr<GpuChannelHost> channel_;
+ base::WeakPtr<Context> parent_;
+ scoped_ptr<Callback0::Type> swap_buffers_callback_;
+ scoped_ptr<Callback0::Type> context_lost_callback_;
+ uint32 parent_texture_id_;
+ CommandBufferProxy* command_buffer_;
+ gpu::gles2::GLES2CmdHelper* gles2_helper_;
+ int32 transfer_buffer_id_;
+ gpu::gles2::GLES2Implementation* gles2_implementation_;
+ gfx::Size size_;
+ Error last_error_;
+
+ DISALLOW_COPY_AND_ASSIGN(Context);
+};
+
+Context::Context(GpuChannelHost* channel, Context* parent)
+ : channel_(channel),
+ parent_(parent ? parent->AsWeakPtr() : base::WeakPtr<Context>()),
+ parent_texture_id_(0),
+ command_buffer_(NULL),
+ gles2_helper_(NULL),
+ transfer_buffer_id_(0),
+ gles2_implementation_(NULL),
+ last_error_(SUCCESS) {
+ DCHECK(channel);
+}
+
+Context::~Context() {
+ Destroy();
+}
+
+bool Context::Initialize(bool onscreen,
+ int render_view_id,
+ const gfx::Size& size,
+ const char* allowed_extensions,
+ const int32* attrib_list) {
+ DCHECK(size.width() >= 0 && size.height() >= 0);
+
+ if (channel_->state() != GpuChannelHost::kConnected)
+ return false;
+
+ // Ensure the gles2 library is initialized first in a thread safe way.
+ g_gles2_initializer.Get();
+
+ // Allocate a frame buffer ID with respect to the parent.
+ if (parent_.get()) {
+ // Flush any remaining commands in the parent context to make sure the
+ // texture id accounting stays consistent.
+ int32 token = parent_->gles2_helper_->InsertToken();
+ parent_->gles2_helper_->WaitForToken(token);
+ parent_texture_id_ = parent_->gles2_implementation_->MakeTextureId();
+ }
+
+ std::vector<int32> attribs;
+ while (attrib_list) {
+ int32 attrib = *attrib_list++;
+ switch (attrib) {
+ // Known attributes
+ case ggl::GGL_ALPHA_SIZE:
+ case ggl::GGL_BLUE_SIZE:
+ case ggl::GGL_GREEN_SIZE:
+ case ggl::GGL_RED_SIZE:
+ case ggl::GGL_DEPTH_SIZE:
+ case ggl::GGL_STENCIL_SIZE:
+ case ggl::GGL_SAMPLES:
+ case ggl::GGL_SAMPLE_BUFFERS:
+ attribs.push_back(attrib);
+ attribs.push_back(*attrib_list++);
+ break;
+ case ggl::GGL_NONE:
+ attribs.push_back(attrib);
+ attrib_list = NULL;
+ break;
+ default:
+ SetError(ggl::BAD_ATTRIBUTE);
+ attribs.push_back(ggl::GGL_NONE);
+ attrib_list = NULL;
+ break;
+ }
+ }
+
+ // Create a proxy to a command buffer in the GPU process.
+ if (onscreen) {
+ command_buffer_ = channel_->CreateViewCommandBuffer(
+ render_view_id,
+ allowed_extensions,
+ attribs);
+ } else {
+ CommandBufferProxy* parent_command_buffer =
+ parent_.get() ? parent_->command_buffer_ : NULL;
+ command_buffer_ = channel_->CreateOffscreenCommandBuffer(
+ parent_command_buffer,
+ size,
+ allowed_extensions,
+ attribs,
+ parent_texture_id_);
+ }
+ if (!command_buffer_) {
+ Destroy();
+ return false;
+ }
+
+ // Initiaize the command buffer.
+ if (!command_buffer_->Initialize(kCommandBufferSize)) {
+ Destroy();
+ return false;
+ }
+
+ command_buffer_->SetSwapBuffersCallback(
+ NewCallback(this, &Context::OnSwapBuffers));
+
+ command_buffer_->SetChannelErrorCallback(
+ NewCallback(this, &Context::OnContextLost));
+
+ // Create the GLES2 helper, which writes the command buffer protocol.
+ gles2_helper_ = new gpu::gles2::GLES2CmdHelper(command_buffer_);
+ if (!gles2_helper_->Initialize(kCommandBufferSize)) {
+ Destroy();
+ return false;
+ }
+
+ // Create a transfer buffer used to copy resources between the renderer
+ // process and the GPU process.
+ transfer_buffer_id_ =
+ command_buffer_->CreateTransferBuffer(kTransferBufferSize);
+ if (transfer_buffer_id_ < 0) {
+ Destroy();
+ return false;
+ }
+
+ // Map the buffer into the renderer process's address space.
+ gpu::Buffer transfer_buffer =
+ command_buffer_->GetTransferBuffer(transfer_buffer_id_);
+ if (!transfer_buffer.ptr) {
+ Destroy();
+ return false;
+ }
+
+ // Create the object exposing the OpenGL API.
+ gles2_implementation_ = new gpu::gles2::GLES2Implementation(
+ gles2_helper_,
+ transfer_buffer.size,
+ transfer_buffer.ptr,
+ transfer_buffer_id_,
+ false);
+
+ size_ = size;
+
+ return true;
+}
+
+#if defined(OS_MACOSX)
+void Context::ResizeOnscreen(const gfx::Size& size) {
+ DCHECK(size.width() > 0 && size.height() > 0);
+ size_ = size;
+ command_buffer_->SetWindowSize(size);
+}
+#endif
+
+void Context::ResizeOffscreen(const gfx::Size& size) {
+ DCHECK(size.width() > 0 && size.height() > 0);
+ if (size_ != size) {
+ command_buffer_->ResizeOffscreenFrameBuffer(size);
+ size_ = size;
+ }
+}
+
+uint32 Context::CreateParentTexture(const gfx::Size& size) const {
+ // Allocate a texture ID with respect to the parent.
+ if (parent_.get()) {
+ if (!MakeCurrent(parent_.get()))
+ return 0;
+ uint32 texture_id = parent_->gles2_implementation_->MakeTextureId();
+ parent_->gles2_implementation_->BindTexture(GL_TEXTURE_2D, texture_id);
+ parent_->gles2_implementation_->TexParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ parent_->gles2_implementation_->TexParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ parent_->gles2_implementation_->TexParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ parent_->gles2_implementation_->TexParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ parent_->gles2_implementation_->TexImage2D(GL_TEXTURE_2D,
+ 0, // mip level
+ GL_RGBA,
+ size.width(),
+ size.height(),
+ 0, // border
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ NULL);
+ // Make sure that the parent texture's storage is allocated before we let
+ // the caller attempt to use it.
+ int32 token = parent_->gles2_helper_->InsertToken();
+ parent_->gles2_helper_->WaitForToken(token);
+ return texture_id;
+ }
+ return 0;
+}
+
+void Context::DeleteParentTexture(uint32 texture) const {
+ if (parent_.get()) {
+ if (!MakeCurrent(parent_.get()))
+ return;
+ parent_->gles2_implementation_->DeleteTextures(1, &texture);
+ }
+}
+
+void Context::Destroy() {
+ if (parent_.get() && parent_texture_id_ != 0)
+ parent_->gles2_implementation_->FreeTextureId(parent_texture_id_);
+
+ delete gles2_implementation_;
+ gles2_implementation_ = NULL;
+
+ if (command_buffer_ && transfer_buffer_id_ != 0) {
+ command_buffer_->DestroyTransferBuffer(transfer_buffer_id_);
+ transfer_buffer_id_ = 0;
+ }
+
+ delete gles2_helper_;
+ gles2_helper_ = NULL;
+
+ if (channel_ && command_buffer_) {
+ channel_->DestroyCommandBuffer(command_buffer_);
+ command_buffer_ = NULL;
+ }
+
+ channel_ = NULL;
+}
+
+bool Context::MakeCurrent(Context* context) {
+ if (context) {
+ gles2::SetGLContext(context->gles2_implementation_);
+
+ // Don't request latest error status from service. Just use the locally
+ // cached information from the last flush.
+ // TODO(apatrick): I'm not sure if this should actually change the
+ // current context if it fails. For now it gets changed even if it fails
+ // because making GL calls with a NULL context crashes.
+ if (context->command_buffer_->GetLastState().error != gpu::error::kNoError)
+ return false;
+ } else {
+ gles2::SetGLContext(NULL);
+ }
+
+ return true;
+}
+
+bool Context::SwapBuffers() {
+ GPU_TRACE_EVENT0("gpu", "Context::SwapBuffers");
+ // Don't request latest error status from service. Just use the locally cached
+ // information from the last flush.
+ if (command_buffer_->GetLastState().error != gpu::error::kNoError)
+ return false;
+
+ gles2_implementation_->SwapBuffers();
+ return true;
+}
+
+media::VideoDecodeEngine* Context::CreateVideoDecodeEngine() {
+ return channel_->gpu_video_service_host()->CreateVideoDecoder(
+ command_buffer_->route_id());
+}
+
+media::VideoDecodeContext* Context::CreateVideoDecodeContext(
+ MessageLoop* message_loop, bool hardware_decoder) {
+ return new Gles2VideoDecodeContext(message_loop, hardware_decoder, this);
+}
+
+Error Context::GetError() {
+ gpu::CommandBuffer::State state = command_buffer_->GetState();
+ if (state.error == gpu::error::kNoError) {
+ Error old_error = last_error_;
+ last_error_ = SUCCESS;
+ return old_error;
+ } else {
+ // All command buffer errors are unrecoverable. The error is treated as a
+ // lost context: destroy the context and create another one.
+ return CONTEXT_LOST;
+ }
+}
+
+void Context::SetError(Error error) {
+ last_error_ = error;
+}
+
+bool Context::IsCommandBufferContextLost() {
+ gpu::CommandBuffer::State state = command_buffer_->GetLastState();
+ return state.error == gpu::error::kLostContext;
+}
+
+// TODO(gman): Remove This
+void Context::DisableShaderTranslation() {
+ gles2_implementation_->CommandBufferEnableCHROMIUM(
+ PEPPER3D_SKIP_GLSL_TRANSLATION);
+}
+
+void Context::OnSwapBuffers() {
+ if (swap_buffers_callback_.get())
+ swap_buffers_callback_->Run();
+}
+
+void Context::OnContextLost() {
+ if (context_lost_callback_.get())
+ context_lost_callback_->Run();
+}
+
+#endif // ENABLE_GPU
+
+Context* CreateViewContext(GpuChannelHost* channel,
+ int render_view_id,
+ const char* allowed_extensions,
+ const int32* attrib_list) {
+#if defined(ENABLE_GPU)
+ scoped_ptr<Context> context(new Context(channel, NULL));
+ if (!context->Initialize(
+ true, render_view_id, gfx::Size(), allowed_extensions, attrib_list))
+ return NULL;
+
+ return context.release();
+#else
+ return NULL;
+#endif
+}
+
+#if defined(OS_MACOSX)
+void ResizeOnscreenContext(Context* context, const gfx::Size& size) {
+#if defined(ENABLE_GPU)
+ context->ResizeOnscreen(size);
+#endif
+}
+#endif
+
+Context* CreateOffscreenContext(GpuChannelHost* channel,
+ Context* parent,
+ const gfx::Size& size,
+ const char* allowed_extensions,
+ const int32* attrib_list) {
+#if defined(ENABLE_GPU)
+ scoped_ptr<Context> context(new Context(channel, parent));
+ if (!context->Initialize(false, 0, size, allowed_extensions, attrib_list))
+ return NULL;
+
+ return context.release();
+#else
+ return NULL;
+#endif
+}
+
+void ResizeOffscreenContext(Context* context, const gfx::Size& size) {
+#if defined(ENABLE_GPU)
+ context->ResizeOffscreen(size);
+#endif
+}
+
+uint32 GetParentTextureId(Context* context) {
+#if defined(ENABLE_GPU)
+ return context->parent_texture_id();
+#else
+ return 0;
+#endif
+}
+
+uint32 CreateParentTexture(Context* context, const gfx::Size& size) {
+#if defined(ENABLE_GPU)
+ return context->CreateParentTexture(size);
+#else
+ return 0;
+#endif
+}
+
+void DeleteParentTexture(Context* context, uint32 texture) {
+#if defined(ENABLE_GPU)
+ context->DeleteParentTexture(texture);
+#endif
+}
+
+void SetSwapBuffersCallback(Context* context,
+ Callback0::Type* callback) {
+#if defined(ENABLE_GPU)
+ context->SetSwapBuffersCallback(callback);
+#endif
+}
+
+void SetContextLostCallback(Context* context,
+ Callback0::Type* callback) {
+#if defined(ENABLE_GPU)
+ context->SetContextLostCallback(callback);
+#endif
+}
+
+bool MakeCurrent(Context* context) {
+#if defined(ENABLE_GPU)
+ return Context::MakeCurrent(context);
+#else
+ return false;
+#endif
+}
+
+bool SwapBuffers(Context* context) {
+#if defined(ENABLE_GPU)
+ if (!context)
+ return false;
+
+ return context->SwapBuffers();
+#else
+ return false;
+#endif
+}
+
+bool DestroyContext(Context* context) {
+#if defined(ENABLE_GPU)
+ if (!context)
+ return false;
+
+ delete context;
+ return true;
+#else
+ return false;
+#endif
+}
+
+media::VideoDecodeEngine* CreateVideoDecodeEngine(Context* context) {
+ return context->CreateVideoDecodeEngine();
+}
+
+media::VideoDecodeContext* CreateVideoDecodeContext(
+ Context* context, MessageLoop* message_loop, bool hardware_decoder) {
+ return context->CreateVideoDecodeContext(message_loop, hardware_decoder);
+}
+
+Error GetError(Context* context) {
+#if defined(ENABLE_GPU)
+ return context->GetError();
+#else
+ return NOT_INITIALIZED;
+#endif
+}
+
+bool IsCommandBufferContextLost(Context* context) {
+ return context->IsCommandBufferContextLost();
+}
+
+// TODO(gman): Remove This
+void DisableShaderTranslation(Context* context) {
+#if defined(ENABLE_GPU)
+ if (context) {
+ context->DisableShaderTranslation();
+ }
+#endif
+}
+
+gpu::gles2::GLES2Implementation* GetImplementation(Context* context) {
+ if (!context)
+ return NULL;
+
+ return context->gles2_implementation();
+}
+
+CommandBufferProxy* GetCommandBufferProxy(Context* context) {
+ DCHECK(context);
+ return context->command_buffer();
+}
+
+} // namespace ggl