// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/renderer_host/compositor_impl_android.h" #include <android/bitmap.h> #include <android/native_window_jni.h> #include "base/bind.h" #include "base/command_line.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "cc/font_atlas.h" #include "cc/input_handler.h" #include "cc/layer.h" #include "cc/layer_tree_host.h" #include "cc/output_surface.h" #include "cc/thread_impl.h" #include "content/browser/gpu/browser_gpu_channel_host_factory.h" #include "content/browser/gpu/gpu_surface_tracker.h" #include "content/browser/renderer_host/image_transport_factory_android.h" #include "content/common/gpu/client/gl_helper.h" #include "content/common/gpu/client/gpu_channel_host.h" #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" #include "content/common/gpu/gpu_process_launch_causes.h" #include "content/public/common/content_switches.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/khronos/GLES2/gl2ext.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h" #include "ui/gfx/android/java_bitmap.h" #include "webkit/glue/webthread_impl.h" #include "webkit/gpu/webgraphicscontext3d_in_process_impl.h" namespace gfx { class JavaBitmap; } namespace { static bool g_initialized = false; static webkit_glue::WebThreadImpl* g_impl_thread = NULL; static bool g_use_direct_gl = false; // Adapts a pure WebGraphicsContext3D into a cc::OutputSurface. class WebGraphicsContextToOutputSurfaceAdapter : public cc::OutputSurface { public: explicit WebGraphicsContextToOutputSurfaceAdapter( WebKit::WebGraphicsContext3D* context) : context3d_(context), client_(0) { } virtual bool BindToClient(cc::OutputSurfaceClient* client) OVERRIDE { DCHECK(client); if (!context3d_->makeContextCurrent()) return false; client_ = client; return true; } virtual const struct Capabilities& Capabilities() const OVERRIDE { return capabilities_; } virtual WebKit::WebGraphicsContext3D* Context3D() const OVERRIDE { return context3d_.get(); } virtual cc::SoftwareOutputDevice* SoftwareDevice() const OVERRIDE { return NULL; } virtual void SendFrameToParentCompositor( const cc::CompositorFrame&) OVERRIDE { } private: scoped_ptr<WebKit::WebGraphicsContext3D> context3d_; struct Capabilities capabilities_; cc::OutputSurfaceClient* client_; }; } // anonymous namespace namespace content { // static Compositor* Compositor::Create(Client* client) { return client ? new CompositorImpl(client) : NULL; } // static void Compositor::Initialize() { DCHECK(!CompositorImpl::IsInitialized()); g_initialized = true; } // static void Compositor::InitializeWithFlags(uint32 flags) { g_use_direct_gl = flags & DIRECT_CONTEXT_ON_DRAW_THREAD; if (flags & ENABLE_COMPOSITOR_THREAD) { TRACE_EVENT_INSTANT0("test_gpu", "ThreadedCompositingInitialization"); g_impl_thread = new webkit_glue::WebThreadImpl("Browser Compositor"); } Compositor::Initialize(); } // static bool CompositorImpl::IsInitialized() { return g_initialized; } // static bool CompositorImpl::IsThreadingEnabled() { return g_impl_thread; } // static bool CompositorImpl::UsesDirectGL() { return g_use_direct_gl; } CompositorImpl::CompositorImpl(Compositor::Client* client) : root_layer_(cc::Layer::create()), window_(NULL), surface_id_(0), client_(client), weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { DCHECK(client); } CompositorImpl::~CompositorImpl() { } void CompositorImpl::Composite() { if (host_.get()) host_->composite(); } void CompositorImpl::SetRootLayer(scoped_refptr<cc::Layer> root_layer) { root_layer_->removeAllChildren(); root_layer_->addChild(root_layer); } void CompositorImpl::SetWindowSurface(ANativeWindow* window) { GpuSurfaceTracker* tracker = GpuSurfaceTracker::Get(); if (window_) { tracker->RemoveSurface(surface_id_); ANativeWindow_release(window_); window_ = NULL; surface_id_ = 0; SetVisible(false); } if (window) { window_ = window; ANativeWindow_acquire(window); surface_id_ = tracker->AddSurfaceForNativeWidget(window); tracker->SetSurfaceHandle( surface_id_, gfx::GLSurfaceHandle(gfx::kDummyPluginWindow, false)); SetVisible(true); } } void CompositorImpl::SetVisible(bool visible) { if (!visible) { host_.reset(); } else if (!host_.get()) { cc::LayerTreeSettings settings; settings.refreshRate = 60.0; scoped_ptr<cc::Thread> impl_thread; if (g_impl_thread) impl_thread = cc::ThreadImpl::createForDifferentThread( g_impl_thread->message_loop()->message_loop_proxy()); host_ = cc::LayerTreeHost::create(this, settings, impl_thread.Pass()); host_->setRootLayer(root_layer_); host_->setVisible(true); host_->setSurfaceReady(); host_->setViewportSize(size_, size_); } } void CompositorImpl::SetWindowBounds(const gfx::Size& size) { if (size_ == size) return; size_ = size; if (host_) host_->setViewportSize(size, size); root_layer_->setBounds(size); } bool CompositorImpl::CompositeAndReadback(void *pixels, const gfx::Rect& rect) { if (host_.get()) return host_->compositeAndReadback(pixels, rect); else return false; } WebKit::WebGLId CompositorImpl::GenerateTexture(gfx::JavaBitmap& bitmap) { unsigned int texture_id = BuildBasicTexture(); WebKit::WebGraphicsContext3D* context = ImageTransportFactoryAndroid::GetInstance()->GetContext3D(); if (texture_id == 0 || context->isContextLost()) return 0; WebKit::WebGLId format = GetGLFormatForBitmap(bitmap); WebKit::WebGLId type = GetGLTypeForBitmap(bitmap); context->texImage2D(GL_TEXTURE_2D, 0, format, bitmap.size().width(), bitmap.size().height(), 0, format, type, bitmap.pixels()); context->shallowFlushCHROMIUM(); DCHECK(context->getError() == GL_NO_ERROR); return texture_id; } WebKit::WebGLId CompositorImpl::GenerateCompressedTexture(gfx::Size& size, int data_size, void* data) { unsigned int texture_id = BuildBasicTexture(); WebKit::WebGraphicsContext3D* context = ImageTransportFactoryAndroid::GetInstance()->GetContext3D(); if (texture_id == 0 || context->isContextLost()) return 0; context->compressedTexImage2D(GL_TEXTURE_2D, 0, GL_ETC1_RGB8_OES, size.width(), size.height(), 0, data_size, data); context->shallowFlushCHROMIUM(); DCHECK(context->getError() == GL_NO_ERROR); return texture_id; } void CompositorImpl::DeleteTexture(WebKit::WebGLId texture_id) { WebKit::WebGraphicsContext3D* context = ImageTransportFactoryAndroid::GetInstance()->GetContext3D(); if (context->isContextLost()) return; context->deleteTexture(texture_id); context->shallowFlushCHROMIUM(); DCHECK(context->getError() == GL_NO_ERROR); } void CompositorImpl::CopyTextureToBitmap(WebKit::WebGLId texture_id, gfx::JavaBitmap& bitmap) { GLHelper* helper = ImageTransportFactoryAndroid::GetInstance()->GetGLHelper(); helper->ReadbackTextureSync(texture_id, bitmap.size(), static_cast<unsigned char*> (bitmap.pixels())); } void CompositorImpl::animate(double monotonicFrameBeginTime) { } void CompositorImpl::layout() { } void CompositorImpl::applyScrollAndScale(gfx::Vector2d scrollDelta, float pageScale) { } scoped_ptr<cc::OutputSurface> CompositorImpl::createOutputSurface() { if (g_use_direct_gl) { WebKit::WebGraphicsContext3D::Attributes attrs; attrs.shareResources = false; attrs.noAutomaticFlushes = true; scoped_ptr<webkit::gpu::WebGraphicsContext3DInProcessImpl> context( webkit::gpu::WebGraphicsContext3DInProcessImpl::CreateForWindow( attrs, window_, NULL)); return scoped_ptr<cc::OutputSurface>( new WebGraphicsContextToOutputSurfaceAdapter(context.release())); } else { DCHECK(window_ && surface_id_); WebKit::WebGraphicsContext3D::Attributes attrs; attrs.shareResources = true; attrs.noAutomaticFlushes = true; GpuChannelHostFactory* factory = BrowserGpuChannelHostFactory::instance(); GURL url("chrome://gpu/Compositor::createContext3D"); scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context( new WebGraphicsContext3DCommandBufferImpl(surface_id_, url, factory, weak_factory_.GetWeakPtr())); if (!context->Initialize( attrs, false, CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE)) { LOG(ERROR) << "Failed to create 3D context for compositor."; return scoped_ptr<cc::OutputSurface>(); } return scoped_ptr<cc::OutputSurface>( new WebGraphicsContextToOutputSurfaceAdapter(context.release())); } } scoped_ptr<cc::InputHandler> CompositorImpl::createInputHandler() { return scoped_ptr<cc::InputHandler>(); } void CompositorImpl::didRecreateOutputSurface(bool success) { } void CompositorImpl::didCommit() { } void CompositorImpl::didCommitAndDrawFrame() { } void CompositorImpl::didCompleteSwapBuffers() { client_->OnSwapBuffersCompleted(); } void CompositorImpl::scheduleComposite() { client_->ScheduleComposite(); } scoped_ptr<cc::FontAtlas> CompositorImpl::createFontAtlas() { return scoped_ptr<cc::FontAtlas>(); } void CompositorImpl::OnViewContextSwapBuffersPosted() { TRACE_EVENT0("compositor", "CompositorImpl::OnViewContextSwapBuffersPosted"); } void CompositorImpl::OnViewContextSwapBuffersComplete() { TRACE_EVENT0("compositor", "CompositorImpl::OnViewContextSwapBuffersComplete"); client_->OnSwapBuffersCompleted(); } void CompositorImpl::OnViewContextSwapBuffersAborted() { TRACE_EVENT0("compositor", "CompositorImpl::OnViewContextSwapBuffersAborted"); client_->OnSwapBuffersCompleted(); } WebKit::WebGLId CompositorImpl::BuildBasicTexture() { WebKit::WebGraphicsContext3D* context = ImageTransportFactoryAndroid::GetInstance()->GetContext3D(); if (context->isContextLost()) return 0; WebKit::WebGLId texture_id = context->createTexture(); context->bindTexture(GL_TEXTURE_2D, texture_id); context->texParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); context->texParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); context->texParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); context->texParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); DCHECK(context->getError() == GL_NO_ERROR); return texture_id; } WebKit::WGC3Denum CompositorImpl::GetGLFormatForBitmap( gfx::JavaBitmap& bitmap) { switch (bitmap.format()) { case ANDROID_BITMAP_FORMAT_A_8: return GL_ALPHA; break; case ANDROID_BITMAP_FORMAT_RGBA_4444: return GL_RGBA; break; case ANDROID_BITMAP_FORMAT_RGBA_8888: return GL_RGBA; break; case ANDROID_BITMAP_FORMAT_RGB_565: default: return GL_RGB; } } WebKit::WGC3Denum CompositorImpl::GetGLTypeForBitmap(gfx::JavaBitmap& bitmap) { switch (bitmap.format()) { case ANDROID_BITMAP_FORMAT_A_8: return GL_UNSIGNED_BYTE; break; case ANDROID_BITMAP_FORMAT_RGBA_4444: return GL_UNSIGNED_SHORT_4_4_4_4; break; case ANDROID_BITMAP_FORMAT_RGBA_8888: return GL_UNSIGNED_BYTE; break; case ANDROID_BITMAP_FORMAT_RGB_565: default: return GL_UNSIGNED_SHORT_5_6_5; } } } // namespace content