// 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 "gpu/pgl/pgl.h" #include "build/build_config.h" #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/command_buffer/common/thread_local.h" #include "gpu/pgl/command_buffer_pepper.h" namespace { const int32 kTransferBufferSize = 512 * 1024; class PGLContextImpl { public: PGLContextImpl(NPP npp, NPDevice* device, NPDeviceContext3D* device_context); ~PGLContextImpl(); // Initlaize a PGL context with a transfer buffer of a particular size. PGLBoolean Initialize(int32 transfer_buffer_size); // Destroy all resources associated with the PGL context. void Destroy(); // Make a PGL context current for the calling thread. static PGLBoolean MakeCurrent(PGLContextImpl* pgl_context); // Display all content rendered since last call to SwapBuffers. PGLBoolean SwapBuffers(); // Get the current error code. PGLInt GetError(); private: PGLContextImpl(const PGLContextImpl&); void operator=(const PGLContextImpl&); NPP npp_; NPDevice* device_; NPDeviceContext3D* device_context_; CommandBufferPepper* command_buffer_; gpu::gles2::GLES2CmdHelper* gles2_helper_; int32 transfer_buffer_id_; gpu::gles2::GLES2Implementation* gles2_implementation_; }; gpu::ThreadLocalKey g_pgl_context_key; bool g_pgl_context_key_allocated = false; PGLContextImpl::PGLContextImpl(NPP npp, NPDevice* device, NPDeviceContext3D* device_context) : npp_(npp), device_(device), device_context_(device_context), command_buffer_(NULL), gles2_helper_(NULL), transfer_buffer_id_(0), gles2_implementation_(NULL) { } PGLContextImpl::~PGLContextImpl() { Destroy(); } PGLBoolean PGLContextImpl::Initialize(int32 transfer_buffer_size) { // Create and initialize the objects required to issue GLES2 calls. command_buffer_ = new CommandBufferPepper( npp_, device_, device_context_); gles2_helper_ = new gpu::gles2::GLES2CmdHelper(command_buffer_); gpu::Buffer buffer = command_buffer_->GetRingBuffer(); if (gles2_helper_->Initialize(buffer.size)) { transfer_buffer_id_ = command_buffer_->CreateTransferBuffer(kTransferBufferSize); gpu::Buffer transfer_buffer = command_buffer_->GetTransferBuffer(transfer_buffer_id_); if (transfer_buffer.ptr) { gles2_implementation_ = new gpu::gles2::GLES2Implementation( gles2_helper_, transfer_buffer.size, transfer_buffer.ptr, transfer_buffer_id_, false); return PGL_TRUE; } } // Tear everything down if initialization failed. Destroy(); return PGL_FALSE; } void PGLContextImpl::Destroy() { 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; delete command_buffer_; command_buffer_ = NULL; } PGLBoolean PGLContextImpl::MakeCurrent(PGLContextImpl* pgl_context) { if (!g_pgl_context_key_allocated) return PGL_FALSE; gpu::ThreadLocalSetValue(g_pgl_context_key, pgl_context); if (pgl_context) { gles2::SetGLContext(pgl_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 // becuase making GL calls with a NULL context crashes. #if defined(ENABLE_NEW_NPDEVICE_API) if (pgl_context->command_buffer_->GetCachedError() != gpu::error::kNoError) return PGL_FALSE; #else if (pgl_context->device_context_->error != NPDeviceContext3DError_NoError) return PGL_FALSE; #endif } else { gles2::SetGLContext(NULL); } return PGL_TRUE; } PGLBoolean PGLContextImpl::SwapBuffers() { // Don't request latest error status from service. Just use the locally cached // information from the last flush. #if defined(ENABLE_NEW_NPDEVICE_API) if (command_buffer_->GetCachedError() != gpu::error::kNoError) return PGL_FALSE; #else if (device_context_->error != NPDeviceContext3DError_NoError) return PGL_FALSE; #endif gles2_implementation_->SwapBuffers(); return PGL_TRUE; } PGLInt PGLContextImpl::GetError() { gpu::CommandBuffer::State state = command_buffer_->GetState(); if (state.error == gpu::error::kNoError) { return PGL_SUCCESS; } else { // All command buffer errors are unrecoverable. The error is treated as a // lost context: destroy the context and create another one. return PGL_CONTEXT_LOST; } } } // namespace anonymous extern "C" { PGLBoolean pglInitialize() { if (g_pgl_context_key_allocated) return PGL_TRUE; gles2::Initialize(); g_pgl_context_key = gpu::ThreadLocalAlloc(); g_pgl_context_key_allocated = true; return PGL_TRUE; } PGLBoolean pglTerminate() { if (!g_pgl_context_key_allocated) return PGL_TRUE; gpu::ThreadLocalFree(g_pgl_context_key); g_pgl_context_key_allocated = false; g_pgl_context_key = 0; gles2::Terminate(); return PGL_TRUE; } PGLContext pglCreateContext(NPP npp, NPDevice* device, NPDeviceContext3D* device_context) { if (!g_pgl_context_key_allocated) return NULL; PGLContextImpl* pgl_context = new PGLContextImpl( npp, device, device_context); if (pgl_context->Initialize(kTransferBufferSize)) { return pgl_context; } delete pgl_context; return NULL; } PGLBoolean pglMakeCurrent(PGLContext pgl_context) { return PGLContextImpl::MakeCurrent(static_cast(pgl_context)); } PGLContext pglGetCurrentContext(void) { if (!g_pgl_context_key_allocated) return NULL; return static_cast(gpu::ThreadLocalGetValue(g_pgl_context_key)); } PGLBoolean pglSwapBuffers(void) { PGLContextImpl* context = static_cast( pglGetCurrentContext()); if (!context) return PGL_FALSE; return context->SwapBuffers(); } PGLBoolean pglDestroyContext(PGLContext pgl_context) { if (!g_pgl_context_key_allocated) return PGL_FALSE; if (!pgl_context) return PGL_FALSE; if (pgl_context == pglGetCurrentContext()) pglMakeCurrent(PGL_NO_CONTEXT); delete static_cast(pgl_context); return PGL_TRUE; } PGLInt pglGetError() { if (!g_pgl_context_key) return PGL_NOT_INITIALIZED; PGLContextImpl* context = static_cast( pglGetCurrentContext()); if (!context) return PGL_BAD_CONTEXT; return context->GetError(); } } // extern "C"