| // 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 "ui/compositor/compositor.h" |
| |
| #include "base/command_line.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebCompositor.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebFloatPoint.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebRect.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSize.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "third_party/skia/include/images/SkImageEncoder.h" |
| #include "ui/compositor/compositor_observer.h" |
| #include "ui/compositor/compositor_switches.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/compositor/test_web_graphics_context_3d.h" |
| #include "ui/gfx/gl/gl_context.h" |
| #include "ui/gfx/gl/gl_implementation.h" |
| #include "ui/gfx/gl/gl_surface.h" |
| #include "webkit/glue/webthread_impl.h" |
| #include "webkit/gpu/webgraphicscontext3d_in_process_impl.h" |
| |
| namespace { |
| |
| const double kDefaultRefreshRate = 60.0; |
| const double kTestRefreshRate = 100.0; |
| |
| webkit_glue::WebThreadImpl* g_compositor_thread = NULL; |
| |
| bool test_compositor_enabled = false; |
| |
| ui::ContextFactory* g_context_factory = NULL; |
| |
| } // anonymous namespace |
| |
| namespace ui { |
| |
| // static |
| ContextFactory* ContextFactory::GetInstance() { |
| // We leak the shared resources so that we don't race with |
| // the tear down of the gl_bindings. |
| if (!g_context_factory) { |
| DVLOG(1) << "Using DefaultSharedResource"; |
| scoped_ptr<DefaultContextFactory> instance( |
| new DefaultContextFactory()); |
| if (instance->Initialize()) |
| g_context_factory = instance.release(); |
| } |
| return g_context_factory; |
| } |
| |
| // static |
| void ContextFactory::SetInstance(ContextFactory* instance) { |
| g_context_factory = instance; |
| } |
| |
| DefaultContextFactory::DefaultContextFactory() { |
| } |
| |
| DefaultContextFactory::~DefaultContextFactory() { |
| } |
| |
| bool DefaultContextFactory::Initialize() { |
| // The following line of code exists soley to disable IO restrictions |
| // on this thread long enough to perform the GL bindings. |
| // TODO(wjmaclean) Remove this when GL initialisation cleaned up. |
| base::ThreadRestrictions::ScopedAllowIO allow_io; |
| if (!gfx::GLSurface::InitializeOneOff() || |
| gfx::GetGLImplementation() == gfx::kGLImplementationNone) { |
| LOG(ERROR) << "Could not load the GL bindings"; |
| return false; |
| } |
| return true; |
| } |
| |
| WebKit::WebGraphicsContext3D* DefaultContextFactory::CreateContext( |
| Compositor* compositor) { |
| return CreateContextCommon(compositor, false); |
| } |
| |
| WebKit::WebGraphicsContext3D* DefaultContextFactory::CreateOffscreenContext( |
| Compositor* compositor) { |
| return CreateContextCommon(compositor, true); |
| } |
| |
| void DefaultContextFactory::RemoveCompositor(Compositor* compositor) { |
| } |
| |
| WebKit::WebGraphicsContext3D* DefaultContextFactory::CreateContextCommon( |
| Compositor* compositor, |
| bool offscreen) { |
| WebKit::WebGraphicsContext3D::Attributes attrs; |
| attrs.shareResources = true; |
| WebKit::WebGraphicsContext3D* context = |
| offscreen ? |
| webkit::gpu::WebGraphicsContext3DInProcessImpl::CreateForWebView( |
| attrs, false) : |
| webkit::gpu::WebGraphicsContext3DInProcessImpl::CreateForWindow( |
| attrs, compositor->widget(), share_group_.get()); |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| if (!command_line->HasSwitch(switches::kDisableUIVsync)) { |
| context->makeContextCurrent(); |
| gfx::GLContext* gl_context = gfx::GLContext::GetCurrent(); |
| gl_context->SetSwapInterval(1); |
| gl_context->ReleaseCurrent(NULL); |
| } |
| return context; |
| } |
| |
| Texture::Texture(bool flipped, const gfx::Size& size) |
| : texture_id_(0), |
| flipped_(flipped), |
| size_(size) { |
| } |
| |
| Texture::~Texture() { |
| } |
| |
| Compositor::Compositor(CompositorDelegate* delegate, |
| gfx::AcceleratedWidget widget, |
| const gfx::Size& size) |
| : delegate_(delegate), |
| size_(size), |
| root_layer_(NULL), |
| widget_(widget), |
| root_web_layer_(WebKit::WebLayer::create()), |
| swap_posted_(false) { |
| WebKit::WebLayerTreeView::Settings settings; |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| settings.showFPSCounter = |
| command_line->HasSwitch(switches::kUIShowFPSCounter); |
| settings.showPlatformLayerTree = |
| command_line->HasSwitch(switches::kUIShowLayerTree); |
| settings.refreshRate = test_compositor_enabled ? |
| kTestRefreshRate : kDefaultRefreshRate; |
| settings.partialSwapEnabled = |
| command_line->HasSwitch(switches::kUIEnablePartialSwap); |
| settings.perTilePainting = |
| command_line->HasSwitch(switches::kUIEnablePerTilePainting); |
| |
| host_.initialize(this, root_web_layer_, settings); |
| root_web_layer_.setAnchorPoint(WebKit::WebFloatPoint(0.f, 0.f)); |
| WidgetSizeChanged(size_); |
| } |
| |
| Compositor::~Compositor() { |
| // There's a cycle between |root_web_layer_| and |host_|, which results in |
| // leaking and/or crashing. Explicitly set the root layer to NULL so the cycle |
| // is broken. |
| host_.setRootLayer(NULL); |
| if (root_layer_) |
| root_layer_->SetCompositor(NULL); |
| if (!test_compositor_enabled) |
| ContextFactory::GetInstance()->RemoveCompositor(this); |
| } |
| |
| void Compositor::Initialize(bool use_thread) { |
| if (use_thread) |
| g_compositor_thread = new webkit_glue::WebThreadImpl("Browser Compositor"); |
| WebKit::WebCompositor::initialize(g_compositor_thread); |
| } |
| |
| void Compositor::Terminate() { |
| WebKit::WebCompositor::shutdown(); |
| if (g_compositor_thread) { |
| delete g_compositor_thread; |
| g_compositor_thread = NULL; |
| } |
| } |
| |
| void Compositor::ScheduleDraw() { |
| if (g_compositor_thread) { |
| // TODO(nduca): Temporary while compositor calls |
| // compositeImmediately() directly. |
| layout(); |
| host_.composite(); |
| } else { |
| delegate_->ScheduleDraw(); |
| } |
| } |
| |
| void Compositor::SetRootLayer(Layer* root_layer) { |
| if (root_layer_ == root_layer) |
| return; |
| if (root_layer_) |
| root_layer_->SetCompositor(NULL); |
| root_layer_ = root_layer; |
| if (root_layer_ && !root_layer_->GetCompositor()) |
| root_layer_->SetCompositor(this); |
| root_web_layer_.removeAllChildren(); |
| if (root_layer_) |
| root_web_layer_.addChild(root_layer_->web_layer()); |
| } |
| |
| void Compositor::Draw(bool force_clear) { |
| if (!root_layer_) |
| return; |
| |
| // TODO(nduca): Temporary while compositor calls |
| // compositeImmediately() directly. |
| layout(); |
| host_.composite(); |
| if (!g_compositor_thread && !swap_posted_) |
| NotifyEnd(); |
| } |
| |
| void Compositor::ScheduleFullDraw() { |
| host_.setNeedsRedraw(); |
| } |
| |
| bool Compositor::ReadPixels(SkBitmap* bitmap, const gfx::Rect& bounds) { |
| if (bounds.right() > size().width() || bounds.bottom() > size().height()) |
| return false; |
| // Convert to OpenGL coordinates. |
| gfx::Point new_origin(bounds.x(), |
| size().height() - bounds.height() - bounds.y()); |
| |
| bitmap->setConfig(SkBitmap::kARGB_8888_Config, |
| bounds.width(), bounds.height()); |
| bitmap->allocPixels(); |
| SkAutoLockPixels lock_image(*bitmap); |
| unsigned char* pixels = static_cast<unsigned char*>(bitmap->getPixels()); |
| if (host_.compositeAndReadback(pixels, |
| gfx::Rect(new_origin, bounds.size()))) { |
| SwizzleRGBAToBGRAAndFlip(pixels, bounds.size()); |
| return true; |
| } |
| return false; |
| } |
| |
| void Compositor::WidgetSizeChanged(const gfx::Size& size) { |
| if (size.IsEmpty()) |
| return; |
| size_ = size; |
| host_.setViewportSize(size_); |
| root_web_layer_.setBounds(size_); |
| } |
| |
| void Compositor::AddObserver(CompositorObserver* observer) { |
| observer_list_.AddObserver(observer); |
| } |
| |
| void Compositor::RemoveObserver(CompositorObserver* observer) { |
| observer_list_.RemoveObserver(observer); |
| } |
| |
| bool Compositor::HasObserver(CompositorObserver* observer) { |
| return observer_list_.HasObserver(observer); |
| } |
| |
| void Compositor::OnSwapBuffersPosted() { |
| swap_posted_ = true; |
| } |
| |
| void Compositor::OnSwapBuffersComplete() { |
| DCHECK(swap_posted_); |
| swap_posted_ = false; |
| NotifyEnd(); |
| } |
| |
| void Compositor::OnSwapBuffersAborted() { |
| if (swap_posted_) { |
| swap_posted_ = false; |
| NotifyEnd(); |
| } |
| } |
| |
| void Compositor::updateAnimations(double frameBeginTime) { |
| } |
| |
| void Compositor::layout() { |
| if (root_layer_) |
| root_layer_->SendDamagedRects(); |
| } |
| |
| void Compositor::applyScrollAndScale(const WebKit::WebSize& scrollDelta, |
| float scaleFactor) { |
| } |
| |
| WebKit::WebGraphicsContext3D* Compositor::createContext3D() { |
| if (test_compositor_enabled) { |
| ui::TestWebGraphicsContext3D* test_context = |
| new ui::TestWebGraphicsContext3D(); |
| test_context->Initialize(); |
| return test_context; |
| } else { |
| return ContextFactory::GetInstance()->CreateContext(this); |
| } |
| } |
| |
| void Compositor::didRebindGraphicsContext(bool success) { |
| } |
| |
| void Compositor::didCommitAndDrawFrame() { |
| FOR_EACH_OBSERVER(CompositorObserver, |
| observer_list_, |
| OnCompositingStarted(this)); |
| } |
| |
| void Compositor::didCompleteSwapBuffers() { |
| NotifyEnd(); |
| } |
| |
| void Compositor::scheduleComposite() { |
| ScheduleDraw(); |
| } |
| |
| void Compositor::SwizzleRGBAToBGRAAndFlip(unsigned char* pixels, |
| const gfx::Size& image_size) { |
| // Swizzle from RGBA to BGRA |
| size_t bitmap_size = 4 * image_size.width() * image_size.height(); |
| for(size_t i = 0; i < bitmap_size; i += 4) |
| std::swap(pixels[i], pixels[i + 2]); |
| |
| // Vertical flip to transform from GL co-ords |
| size_t row_size = 4 * image_size.width(); |
| scoped_array<unsigned char> tmp_row(new unsigned char[row_size]); |
| for(int row = 0; row < image_size.height() / 2; row++) { |
| memcpy(tmp_row.get(), |
| &pixels[row * row_size], |
| row_size); |
| memcpy(&pixels[row * row_size], |
| &pixels[bitmap_size - (row + 1) * row_size], |
| row_size); |
| memcpy(&pixels[bitmap_size - (row + 1) * row_size], |
| tmp_row.get(), |
| row_size); |
| } |
| } |
| |
| void Compositor::NotifyEnd() { |
| FOR_EACH_OBSERVER(CompositorObserver, |
| observer_list_, |
| OnCompositingEnded(this)); |
| } |
| |
| COMPOSITOR_EXPORT void SetupTestCompositor() { |
| if (!CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kDisableTestCompositor)) { |
| test_compositor_enabled = true; |
| } |
| } |
| |
| COMPOSITOR_EXPORT void DisableTestCompositor() { |
| test_compositor_enabled = false; |
| } |
| |
| COMPOSITOR_EXPORT bool IsTestCompositorEnabled() { |
| return test_compositor_enabled; |
| } |
| |
| } // namespace ui |