blob: 8c9b1bda3ce2e2a7ecd62e715d2be2ed482c9f02 [file] [log] [blame]
// 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