// Copyright (c) 2013 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/aura/reflector_impl.h"
#include "base/bind.h"
#include "base/location.h"
#include "content/browser/aura/browser_compositor_output_surface.h"
#include "content/common/gpu/client/gl_helper.h"
#include "ui/compositor/layer.h"
namespace content {
ReflectorImpl::ReflectorImpl(
ui::Compositor* mirrored_compositor,
ui::Layer* mirroring_layer,
IDMap<BrowserCompositorOutputSurface>* output_surface_map,
int surface_id)
: texture_id_(0),
texture_size_(mirrored_compositor->size()),
output_surface_map_(output_surface_map),
mirrored_compositor_(mirrored_compositor),
mirroring_compositor_(mirroring_layer->GetCompositor()),
mirroring_layer_(mirroring_layer),
impl_message_loop_(ui::Compositor::GetCompositorMessageLoop()),
main_message_loop_(base::MessageLoopProxy::current()),
surface_id_(surface_id) {
CreateSharedTexture();
impl_message_loop_->PostTask(
FROM_HERE,
base::Bind(&ReflectorImpl::InitOnImplThread, this));
}
void ReflectorImpl::InitOnImplThread() {
// Ignore if the reflector was shutdown before
// initialized, or it's already initialized.
if (!output_surface_map_ || gl_helper_.get())
return;
BrowserCompositorOutputSurface* source_surface =
output_surface_map_->Lookup(surface_id_);
// Skip if the source surface isn't ready yet. This will be
// initiailze when the source surface becomes ready.
if (!source_surface)
return;
AttachToOutputSurface(source_surface);
gl_helper_->CopyTextureFullImage(texture_id_, texture_size_);
// The shared texture doesn't have the data, so invokes full redraw
// now.
main_message_loop_->PostTask(
FROM_HERE,
base::Bind(&ReflectorImpl::FullRedrawContentOnMainThread,
scoped_refptr<ReflectorImpl>(this)));
}
void ReflectorImpl::OnSourceSurfaceReady(int surface_id) {
DCHECK_EQ(surface_id_, surface_id);
InitOnImplThread();
}
void ReflectorImpl::Shutdown() {
mirroring_compositor_ = NULL;
mirroring_layer_ = NULL;
shared_texture_ = NULL;
impl_message_loop_->PostTask(
FROM_HERE,
base::Bind(&ReflectorImpl::ShutdownOnImplThread, this));
}
void ReflectorImpl::ShutdownOnImplThread() {
BrowserCompositorOutputSurface* output_surface =
output_surface_map_->Lookup(surface_id_);
if (output_surface)
output_surface->SetReflector(NULL);
output_surface_map_ = NULL;
gl_helper_.reset();
// The instance must be deleted on main thread.
main_message_loop_->PostTask(
FROM_HERE,
base::Bind(&ReflectorImpl::DeleteOnMainThread,
scoped_refptr<ReflectorImpl>(this)));
}
// This must be called on ImplThread, or before the surface is passed to
// ImplThread.
void ReflectorImpl::AttachToOutputSurface(
BrowserCompositorOutputSurface* output_surface) {
gl_helper_.reset(
new GLHelper(output_surface->context_provider()->Context3d(),
output_surface->context_provider()->ContextSupport()));
output_surface->SetReflector(this);
}
void ReflectorImpl::OnMirroringCompositorResized() {
mirroring_compositor_->ScheduleFullRedraw();
}
void ReflectorImpl::OnLostResources() {
shared_texture_ = NULL;
mirroring_layer_->SetShowPaintedContent();
}
void ReflectorImpl::OnReshape(gfx::Size size) {
if (texture_size_ == size)
return;
texture_size_ = size;
DCHECK(texture_id_);
gl_helper_->ResizeTexture(texture_id_, size);
main_message_loop_->PostTask(
FROM_HERE,
base::Bind(&ReflectorImpl::UpdateTextureSizeOnMainThread,
this->AsWeakPtr(),
texture_size_));
}
void ReflectorImpl::OnSwapBuffers() {
DCHECK(texture_id_);
gl_helper_->CopyTextureFullImage(texture_id_, texture_size_);
main_message_loop_->PostTask(
FROM_HERE,
base::Bind(&ReflectorImpl::FullRedrawOnMainThread,
this->AsWeakPtr(),
texture_size_));
}
void ReflectorImpl::OnPostSubBuffer(gfx::Rect rect) {
DCHECK(texture_id_);
gl_helper_->CopyTextureSubImage(texture_id_, rect);
main_message_loop_->PostTask(
FROM_HERE,
base::Bind(&ReflectorImpl::UpdateSubBufferOnMainThread,
this->AsWeakPtr(),
texture_size_,
rect));
}
void ReflectorImpl::CreateSharedTexture() {
texture_id_ =
ImageTransportFactory::GetInstance()->GetGLHelper()->CreateTexture();
shared_texture_ =
ImageTransportFactory::GetInstance()->CreateOwnedTexture(
texture_size_, 1.0f, texture_id_);
mirroring_layer_->SetExternalTexture(shared_texture_.get());
}
ReflectorImpl::~ReflectorImpl() {
// Make sure the reflector is deleted on main thread.
DCHECK_EQ(main_message_loop_.get(),
base::MessageLoopProxy::current().get());
}
void ReflectorImpl::UpdateTextureSizeOnMainThread(gfx::Size size) {
if (!mirroring_layer_)
return;
mirroring_layer_->SetBounds(gfx::Rect(size));
}
void ReflectorImpl::FullRedrawOnMainThread(gfx::Size size) {
if (!mirroring_compositor_)
return;
UpdateTextureSizeOnMainThread(size);
mirroring_compositor_->ScheduleFullRedraw();
}
void ReflectorImpl::UpdateSubBufferOnMainThread(gfx::Size size,
gfx::Rect rect) {
if (!mirroring_compositor_)
return;
UpdateTextureSizeOnMainThread(size);
// Flip the coordinates to compositor's one.
int y = size.height() - rect.y() - rect.height();
gfx::Rect new_rect(rect.x(), y, rect.width(), rect.height());
mirroring_layer_->SchedulePaint(new_rect);
}
void ReflectorImpl::FullRedrawContentOnMainThread() {
mirrored_compositor_->ScheduleFullRedraw();
}
} // namespace content