diff options
Diffstat (limited to 'content/renderer/ggl.cc')
-rw-r--r-- | content/renderer/ggl.cc | 605 |
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 |