summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ash/display/display_info.cc3
-rw-r--r--ash/display/display_manager.cc6
-rw-r--r--ash/display/display_manager_unittest.cc47
-rw-r--r--ash/display/mirror_window_controller.cc40
-rw-r--r--ash/display/mirror_window_controller.h17
-rw-r--r--chromeos/display/output_configurator.cc8
-rw-r--r--chromeos/display/output_configurator_unittest.cc12
-rw-r--r--chromeos/display/real_output_configurator_delegate.cc8
-rw-r--r--content/browser/renderer_host/image_transport_factory.cc287
-rw-r--r--content/common/gpu/client/gl_helper.cc35
-rw-r--r--content/common/gpu/client/gl_helper.h13
-rw-r--r--ui/compositor/compositor.cc19
-rw-r--r--ui/compositor/compositor.h37
13 files changed, 518 insertions, 14 deletions
diff --git a/ash/display/display_info.cc b/ash/display/display_info.cc
index fa72bdc..1052bf9 100644
--- a/ash/display/display_info.cc
+++ b/ash/display/display_info.cc
@@ -36,7 +36,8 @@ DisplayInfo DisplayInfo::CreateFromSpecWithID(const std::string& spec,
const int kDefaultHostWindowWidth = 1366;
const int kDefaultHostWindowHeight = 768;
- static int64 synthesized_display_id = 1000;
+ // Use larger than max int to catch overflow early.
+ static int64 synthesized_display_id = 2200000000LL;
#if defined(OS_WIN)
gfx::Rect bounds(aura::RootWindowHost::GetNativeScreenSize());
diff --git a/ash/display/display_manager.cc b/ash/display/display_manager.cc
index 28727a2..57441d7 100644
--- a/ash/display/display_manager.cc
+++ b/ash/display/display_manager.cc
@@ -441,7 +441,7 @@ void DisplayManager::UpdateDisplays(
// the root window so that it matches the external display's
// resolution. This is necessary in order for scaling to work while
// mirrored.
- int mirrored_display_id = gfx::Display::kInvalidDisplayID;
+ int64 mirrored_display_id = gfx::Display::kInvalidDisplayID;
if (software_mirroring_enabled_ && new_display_info_list.size() == 2)
mirrored_display_id = new_display_info_list[1].id();
@@ -560,6 +560,9 @@ void DisplayManager::UpdateDisplays(
Shell::GetInstance()->screen()->NotifyDisplayRemoved(displays_.back());
displays_.pop_back();
}
+ // Create or delete the mirror window here to avoid creating two
+ // compositor on one display.
+ mirror_window_updater.reset();
for (std::vector<size_t>::iterator iter = added_display_indices.begin();
iter != added_display_indices.end(); ++iter) {
Shell::GetInstance()->screen()->NotifyDisplayAdded(displays_[*iter]);
@@ -568,7 +571,6 @@ void DisplayManager::UpdateDisplays(
iter != changed_display_indices.end(); ++iter) {
Shell::GetInstance()->screen()->NotifyBoundsChanged(displays_[*iter]);
}
- mirror_window_updater.reset();
display_controller->NotifyDisplayConfigurationChanged();
if (update_mouse_location)
display_controller->EnsurePointerInDisplays();
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index ce0c25d..8b1516b 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -876,15 +876,53 @@ TEST_F(DisplayManagerTest, MAYBE_UpdateMouseCursorAfterRotateZoom) {
#define MAYBE_SoftwareMirroring SoftwareMirroring
#endif
+class TestDisplayObserver : public gfx::DisplayObserver {
+ public:
+ TestDisplayObserver() : changed_(false) {}
+ virtual ~TestDisplayObserver() {}
+
+ // gfx::DisplayObserver overrides:
+ virtual void OnDisplayBoundsChanged(const gfx::Display& display) OVERRIDE {
+ }
+ virtual void OnDisplayAdded(const gfx::Display& new_display) OVERRIDE {
+ // Mirror window should already be delete before restoring
+ // the external dispay.
+ EXPECT_FALSE(test_api.GetRootWindow());
+ changed_ = true;
+ }
+ virtual void OnDisplayRemoved(const gfx::Display& old_display) OVERRIDE {
+ // Mirror window should not be created until the external display
+ // is removed.
+ EXPECT_FALSE(test_api.GetRootWindow());
+ changed_ = true;
+ }
+
+ bool changed_and_reset() {
+ bool changed = changed_;
+ changed_ = false;
+ return changed;
+ }
+
+ private:
+ test::MirrorWindowTestApi test_api;
+ bool changed_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestDisplayObserver);
+};
+
TEST_F(DisplayManagerTest, MAYBE_SoftwareMirroring) {
UpdateDisplay("300x400,400x500");
test::MirrorWindowTestApi test_api;
EXPECT_EQ(NULL, test_api.GetRootWindow());
+ TestDisplayObserver display_observer;
+ Shell::GetScreen()->AddObserver(&display_observer);
+
DisplayManager* display_manager = Shell::GetInstance()->display_manager();
display_manager->SetSoftwareMirroring(true);
display_manager->UpdateDisplays();
+ EXPECT_TRUE(display_observer.changed_and_reset());
EXPECT_EQ(1U, display_manager->GetNumDisplays());
EXPECT_EQ("0,0 300x400",
Shell::GetScreen()->GetPrimaryDisplay().bounds().ToString());
@@ -893,6 +931,7 @@ TEST_F(DisplayManagerTest, MAYBE_SoftwareMirroring) {
EXPECT_TRUE(display_manager->IsMirrored());
display_manager->SetMirrorMode(false);
+ EXPECT_TRUE(display_observer.changed_and_reset());
EXPECT_EQ(NULL, test_api.GetRootWindow());
EXPECT_EQ(2U, display_manager->GetNumDisplays());
EXPECT_FALSE(display_manager->IsMirrored());
@@ -900,27 +939,35 @@ TEST_F(DisplayManagerTest, MAYBE_SoftwareMirroring) {
// Make sure the mirror window has the pixel size of the
// source display.
display_manager->SetMirrorMode(true);
+ EXPECT_TRUE(display_observer.changed_and_reset());
UpdateDisplay("300x400@0.5,400x500");
+ EXPECT_FALSE(display_observer.changed_and_reset());
EXPECT_EQ("300x400", test_api.GetRootWindow()->bounds().size().ToString());
EXPECT_EQ("400x500", GetMirroredDisplay().size().ToString());
UpdateDisplay("310x410*2,400x500");
+ EXPECT_FALSE(display_observer.changed_and_reset());
EXPECT_EQ("310x410", test_api.GetRootWindow()->bounds().size().ToString());
EXPECT_EQ("400x500", GetMirroredDisplay().size().ToString());
UpdateDisplay("320x420/r,400x500");
+ EXPECT_FALSE(display_observer.changed_and_reset());
EXPECT_EQ("320x420", test_api.GetRootWindow()->bounds().size().ToString());
EXPECT_EQ("400x500", GetMirroredDisplay().size().ToString());
UpdateDisplay("330x440/r,400x500");
+ EXPECT_FALSE(display_observer.changed_and_reset());
EXPECT_EQ("330x440", test_api.GetRootWindow()->bounds().size().ToString());
EXPECT_EQ("400x500", GetMirroredDisplay().size().ToString());
// Overscan insets are ignored.
UpdateDisplay("400x600/o,600x800/o");
+ EXPECT_FALSE(display_observer.changed_and_reset());
EXPECT_EQ("400x600", test_api.GetRootWindow()->bounds().size().ToString());
EXPECT_EQ("600x800", GetMirroredDisplay().size().ToString());
+
+ Shell::GetScreen()->RemoveObserver(&display_observer);
}
} // namespace internal
diff --git a/ash/display/mirror_window_controller.cc b/ash/display/mirror_window_controller.cc
index 1b621e7..616cf4b 100644
--- a/ash/display/mirror_window_controller.cc
+++ b/ash/display/mirror_window_controller.cc
@@ -168,6 +168,7 @@ void MirrorWindowController::UpdateWindow(const DisplayInfo& display_info) {
// No need to remove RootWindowObserver because
// the DisplayManager object outlives RootWindow objects.
root_window_->AddRootWindowObserver(display_manager);
+ root_window_->AddRootWindowObserver(this);
// TODO(oshima): TouchHUD is using idkey.
root_window_->SetProperty(internal::kDisplayIdKey, display_info.id());
root_window_->Init();
@@ -179,6 +180,14 @@ void MirrorWindowController::UpdateWindow(const DisplayInfo& display_info) {
root_window_->ShowRootWindow();
// TODO(oshima): Start mirroring.
+ aura::Window* mirror_window = new aura::Window(NULL);
+ mirror_window->Init(ui::LAYER_TEXTURED);
+ root_window_->AddChild(mirror_window);
+ mirror_window->SetBounds(root_window_->bounds());
+ mirror_window->Show();
+ reflector_ = ui::ContextFactory::GetInstance()->
+ CreateReflector(Shell::GetPrimaryRootWindow()->compositor(),
+ mirror_window->layer());
cursor_window_ = new aura::Window(cursor_window_delegate_.get());
cursor_window_->SetTransparent(true);
@@ -213,11 +222,17 @@ void MirrorWindowController::UpdateWindow() {
void MirrorWindowController::Close() {
if (root_window_.get()) {
+ ui::ContextFactory::GetInstance()->RemoveReflector(reflector_);
+ reflector_ = NULL;
root_window_->RemoveRootWindowObserver(
Shell::GetInstance()->display_manager());
NoneCaptureClient* capture_client = static_cast<NoneCaptureClient*>(
aura::client::GetCaptureClient(root_window_.get()));
delete capture_client;
+
+ root_window_->RemoveRootWindowObserver(
+ Shell::GetInstance()->display_manager());
+ root_window_->RemoveRootWindowObserver(this);
root_window_.reset();
cursor_window_ = NULL;
}
@@ -264,5 +279,30 @@ void MirrorWindowController::SetMirroredCursorVisibility(bool visible) {
visible ? cursor_window_->Show() : cursor_window_->Hide();
}
+void MirrorWindowController::OnRootWindowResized(
+ const aura::RootWindow* root,
+ const gfx::Size& old_size) {
+ // Do not use |old_size| as it contains RootWindow's (but not host's) size,
+ // and this parameter wil be removed soon.
+ if (mirror_window_host_size_ == root->GetHostSize())
+ return;
+ mirror_window_host_size_ = root->GetHostSize();
+ reflector_->OnMirroringCompositorResized();
+
+ DisplayManager* display_manager = Shell::GetInstance()->display_manager();
+ const DisplayInfo& mirror_display_info = display_manager->GetDisplayInfo(
+ display_manager->mirrored_display().id());
+ const DisplayInfo& source_display_info = display_manager->GetDisplayInfo(
+ Shell::GetScreen()->GetPrimaryDisplay().id());
+ DCHECK(display_manager->mirrored_display().is_valid());
+ scoped_ptr<aura::RootWindowTransformer> transformer(
+ internal::CreateRootWindowTransformerForMirroredDisplay(
+ source_display_info,
+ mirror_display_info));
+ root_window_->SetRootWindowTransformer(transformer.Pass());
+
+ UpdateCursorLocation();
+}
+
} // namespace internal
} // namespace ash
diff --git a/ash/display/mirror_window_controller.h b/ash/display/mirror_window_controller.h
index 3db86b3..76a8cfb 100644
--- a/ash/display/mirror_window_controller.h
+++ b/ash/display/mirror_window_controller.h
@@ -7,15 +7,22 @@
#include "ash/ash_export.h"
#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+#include "ui/aura/root_window_observer.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/point.h"
+#include "ui/gfx/size.h"
namespace aura {
class RootWindow;
class Window;
}
+namespace ui {
+class Reflector;
+}
+
namespace ash {
namespace test{
class MirrorWindowTestApi;
@@ -28,10 +35,10 @@ class CursorWindowDelegate;
// An object that copies the content of the primary root window to a
// mirror window. This also draws a mouse cursor as the mouse cursor
// is typically drawn by the window system.
-class MirrorWindowController {
+class MirrorWindowController : public aura::RootWindowObserver {
public:
MirrorWindowController();
- ~MirrorWindowController();
+ virtual ~MirrorWindowController();
// Updates the root window's bounds using |display_info|.
// Creates the new root window if one doesn't exist.
@@ -50,6 +57,10 @@ class MirrorWindowController {
void SetMirroredCursor(gfx::NativeCursor cursor);
void SetMirroredCursorVisibility(bool visible);
+ // aura::RootWindowObserver overrides:
+ virtual void OnRootWindowResized(const aura::RootWindow* root,
+ const gfx::Size& old_size) OVERRIDE;
+
private:
friend class test::MirrorWindowTestApi;
@@ -58,6 +69,8 @@ class MirrorWindowController {
scoped_ptr<aura::RootWindow> root_window_;
scoped_ptr<CursorWindowDelegate> cursor_window_delegate_;
gfx::Point hot_point_;
+ gfx::Size mirror_window_host_size_;
+ scoped_refptr<ui::Reflector> reflector_;
DISALLOW_COPY_AND_ASSIGN(MirrorWindowController);
};
diff --git a/chromeos/display/output_configurator.cc b/chromeos/display/output_configurator.cc
index 8214d0c..19b28f5 100644
--- a/chromeos/display/output_configurator.cc
+++ b/chromeos/display/output_configurator.cc
@@ -254,8 +254,14 @@ bool OutputConfigurator::SetDisplayMode(OutputState new_state) {
return false;
VLOG(1) << "SetDisplayMode: state=" << OutputStateToString(new_state);
- if (output_state_ == new_state)
+ if (output_state_ == new_state) {
+ // Cancel software mirroring if the state is moving from
+ // STATE_DUAL_EXTENDED to STATE_DUAL_EXTENDED.
+ if (mirroring_controller_ && new_state == STATE_DUAL_EXTENDED)
+ mirroring_controller_->SetSoftwareMirroring(false);
+ NotifyOnDisplayChanged();
return true;
+ }
delegate_->GrabServer();
std::vector<OutputSnapshot> outputs = delegate_->GetOutputs();
diff --git a/chromeos/display/output_configurator_unittest.cc b/chromeos/display/output_configurator_unittest.cc
index 606180c..6deae54 100644
--- a/chromeos/display/output_configurator_unittest.cc
+++ b/chromeos/display/output_configurator_unittest.cc
@@ -406,6 +406,18 @@ TEST_F(OutputConfiguratorTest, ConnectSecondOutput) {
EXPECT_EQ(STATE_DUAL_EXTENDED, configurator_.output_state());
EXPECT_TRUE(mirroring_controller_.software_mirroring_enabled());
+ // Setting STATE_DUAL_MIRROR should try to reconfigure
+ EXPECT_TRUE(configurator_.SetDisplayMode(STATE_DUAL_EXTENDED));
+ EXPECT_EQ(JoinActions(NULL), delegate_->GetActionsAndClear());
+ EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled());
+
+ // Set back to software mirror mode.
+ EXPECT_TRUE(configurator_.SetDisplayMode(STATE_DUAL_MIRROR));
+ EXPECT_EQ(JoinActions(kGrab, kUngrab, NULL),
+ delegate_->GetActionsAndClear());
+ EXPECT_EQ(STATE_DUAL_EXTENDED, configurator_.output_state());
+ EXPECT_TRUE(mirroring_controller_.software_mirroring_enabled());
+
// Disconnect the second output.
UpdateOutputs(1);
EXPECT_TRUE(test_api_.SendOutputChangeEvents(false));
diff --git a/chromeos/display/real_output_configurator_delegate.cc b/chromeos/display/real_output_configurator_delegate.cc
index 30a8494..a44a41b 100644
--- a/chromeos/display/real_output_configurator_delegate.cc
+++ b/chromeos/display/real_output_configurator_delegate.cc
@@ -123,6 +123,13 @@ RealOutputConfiguratorDelegate::GetOutputs() {
to_populate.output = this_id;
to_populate.has_display_id =
GetDisplayId(this_id, i, &to_populate.display_id);
+ to_populate.is_internal = IsInternalOutput(output_info);
+ // Use the index as a valid display id even if the internal
+ // display doesn't have valid EDID because the index
+ // will never change.
+ if (!to_populate.has_display_id && to_populate.is_internal)
+ to_populate.has_display_id = true;
+
(outputs.empty() ? one_info : two_info) = output_info;
// Now, look up the current CRTC and any related info.
@@ -145,7 +152,6 @@ RealOutputConfiguratorDelegate::GetOutputs() {
}
to_populate.native_mode = GetOutputNativeMode(output_info);
- to_populate.is_internal = IsInternalOutput(output_info);
to_populate.is_aspect_preserving_scaling =
IsOutputAspectPreservingScaling(this_id);
to_populate.touch_device_id = None;
diff --git a/content/browser/renderer_host/image_transport_factory.cc b/content/browser/renderer_host/image_transport_factory.cc
index 3efa0af..412d7c5 100644
--- a/content/browser/renderer_host/image_transport_factory.cc
+++ b/content/browser/renderer_host/image_transport_factory.cc
@@ -34,6 +34,7 @@
#include "ui/compositor/compositor.h"
#include "ui/compositor/compositor_setup.h"
#include "ui/compositor/compositor_switches.h"
+#include "ui/compositor/layer.h"
#include "ui/compositor/test_web_graphics_context_3d.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/size.h"
@@ -267,6 +268,10 @@ class BrowserCompositorOutputSurfaceProxy
surface_map_.AddWithID(surface, surface_id);
}
+ BrowserCompositorOutputSurface* GetOutputSurface(int surface_id) {
+ return surface_map_.Lookup(surface_id);
+ }
+
void RemoveSurface(int surface_id) {
surface_map_.Remove(surface_id);
}
@@ -292,6 +297,182 @@ class BrowserCompositorOutputSurfaceProxy
DISALLOW_COPY_AND_ASSIGN(BrowserCompositorOutputSurfaceProxy);
};
+// A reflector implementation that copies the framebuffer content
+// to the texture, then draw it onto the mirroring compositor.
+class ReflectorImpl : public ImageTransportFactoryObserver,
+ public base::SupportsWeakPtr<ReflectorImpl>,
+ public ui::Reflector {
+ public:
+ ReflectorImpl(ui::Compositor* mirrored_compositor,
+ ui::Layer* mirroring_layer,
+ BrowserCompositorOutputSurfaceProxy* output_surface_proxy,
+ int surface_id)
+ : texture_id_(0),
+ texture_size_(mirrored_compositor->size()),
+ output_surface_proxy_(output_surface_proxy),
+ 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));
+ }
+
+ ui::Compositor* mirrored_compositor() {
+ return mirrored_compositor_;
+ }
+
+ void InitOnImplThread();
+
+ void Shutdown() {
+ mirroring_compositor_ = NULL;
+ mirroring_layer_ = NULL;
+ shared_texture_ = NULL;
+ impl_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&ReflectorImpl::ShutdownOnImplThread,
+ this));
+ }
+
+ void ShutdownOnImplThread();
+
+ // This must be called on ImplThread, or before the surface is passed to
+ // ImplThread.
+ void AttachToOutputSurface(BrowserCompositorOutputSurface* surface);
+
+ // ui::Reflector overrides:
+ virtual void OnMirroringCompositorResized() OVERRIDE {
+ mirroring_compositor_->ScheduleFullRedraw();
+ }
+
+ // ImageTransportFactoryObsever overrides:
+ virtual void OnLostResources() OVERRIDE {
+ shared_texture_ = NULL;
+ mirroring_layer_->SetExternalTexture(NULL);
+ }
+
+ // Called when the output surface's size has changed.
+ // This must be called on ImplThread.
+ void OnReshape(gfx::Size size) {
+ 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_));
+ }
+
+ // Called in |BrowserCompositorOutputSurface::SwapBuffers| to copy
+ // the full screen image to the |texture_id_|. This must be called
+ // on ImplThread.
+ void 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_));
+ }
+
+ // Called in |BrowserCompositorOutputSurface::PostSubBuffer| copy
+ // the sub image given by |rect| to the texture.This must be called
+ // on ImplThread.
+ void 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));
+ }
+
+ // Create a shared texture that will be used to copy the content of
+ // mirrored compositor to the mirroring compositor. This must be
+ // called before the reflector is attached to OutputSurface to avoid
+ // race with ImplThread accessing |texture_id_|.
+ void CreateSharedTexture() {
+ texture_id_ = ImageTransportFactory::GetInstance()->GetGLHelper()->
+ CreateTexture();
+ shared_texture_ = ImageTransportFactory::GetInstance()->
+ CreateOwnedTexture(texture_size_, 1.0f, texture_id_);
+ mirroring_layer_->SetExternalTexture(shared_texture_.get());
+ }
+
+ private:
+ virtual ~ReflectorImpl() {
+ // Make sure the reflector is deleted on main thread.
+ DCHECK_EQ(main_message_loop_.get(),
+ base::MessageLoopProxy::current().get());
+ }
+
+ void UpdateTextureSizeOnMainThread(gfx::Size size) {
+ if (!mirroring_layer_)
+ return;
+ mirroring_layer_->SetBounds(gfx::Rect(size));
+ }
+
+ // Request full redraw on mirroring compositor.
+ void FullRedrawOnMainThread(gfx::Size size) {
+ if (!mirroring_compositor_)
+ return;
+ UpdateTextureSizeOnMainThread(size);
+ mirroring_compositor_->ScheduleFullRedraw();
+ }
+
+ void 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);
+ }
+
+ // Request full redraw on mirrored compositor so that
+ // the full content will be copied to mirroring compositor.
+ void FullRedrawContentOnMainThread() {
+ mirrored_compositor_->ScheduleFullRedraw();
+ }
+
+ static void DeleteOnMainThread(ReflectorImpl* reflector) {
+ // reflector gets deleted when the function returns.
+ }
+
+ // These variables are initialized on MainThread before
+ // the reflector is attached to the output surface. Once
+ // attached, they must be accessed only on ImplThraed unless
+ // the context is lost. When the context is lost, these
+ // will be re-ininitiailzed when the new output-surface
+ // is created on MainThread.
+ int texture_id_;
+ gfx::Size texture_size_;
+
+ // Must be accessed only on ImplThread.
+ scoped_refptr<BrowserCompositorOutputSurfaceProxy> output_surface_proxy_;
+ scoped_ptr<GLHelper> gl_helper_;
+
+ // Must be accessed only on MainThread.
+ ui::Compositor* mirrored_compositor_;
+ ui::Compositor* mirroring_compositor_;
+ ui::Layer* mirroring_layer_;
+ scoped_refptr<ui::Texture> shared_texture_;
+ scoped_refptr<base::MessageLoopProxy> impl_message_loop_;
+ scoped_refptr<base::MessageLoopProxy> main_message_loop_;
+ int surface_id_;
+};
+
// Adapts a WebGraphicsContext3DCommandBufferImpl into a
// cc::OutputSurface that also handles vsync parameter updates
// arriving from the GPU process.
@@ -313,7 +494,7 @@ class BrowserCompositorOutputSurface
CommandLine* command_line = CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kUIMaxFramesPending)) {
std::string string_value = command_line->GetSwitchValueASCII(
- switches::kUIMaxFramesPending);
+ switches::kUIMaxFramesPending);
int int_value;
if (base::StringToInt(string_value, &int_value))
capabilities_.max_frames_pending = int_value;
@@ -352,6 +533,12 @@ class BrowserCompositorOutputSurface
compositor_, timebase, interval));
}
+ virtual void Reshape(gfx::Size size, float scale_factor) OVERRIDE {
+ OutputSurface::Reshape(size, scale_factor);
+ if (reflector_.get())
+ reflector_->OnReshape(size);
+ }
+
virtual void SwapBuffers(const ui::LatencyInfo& latency_info) OVERRIDE {
WebGraphicsContext3DCommandBufferImpl* command_buffer =
static_cast<WebGraphicsContext3DCommandBufferImpl*>(context3d());
@@ -360,6 +547,10 @@ class BrowserCompositorOutputSurface
DCHECK(command_buffer_proxy);
context3d()->shallowFlushCHROMIUM();
command_buffer_proxy->SetLatencyInfo(latency_info);
+
+ if (reflector_.get())
+ reflector_->OnSwapBuffers();
+
OutputSurface::SwapBuffers(latency_info);
}
@@ -372,15 +563,24 @@ class BrowserCompositorOutputSurface
DCHECK(command_buffer_proxy);
context3d()->shallowFlushCHROMIUM();
command_buffer_proxy->SetLatencyInfo(latency_info);
+
+ if (reflector_.get())
+ reflector_->OnPostSubBuffer(rect);
+
OutputSurface::PostSubBuffer(rect, latency_info);
}
+ void SetReflector(ReflectorImpl* reflector) {
+ reflector_ = reflector;
+ }
+
private:
int surface_id_;
scoped_refptr<BrowserCompositorOutputSurfaceProxy> output_surface_proxy_;
scoped_refptr<base::MessageLoopProxy> compositor_message_loop_;
base::WeakPtr<ui::Compositor> compositor_;
+ scoped_refptr<ReflectorImpl> reflector_;
};
void BrowserCompositorOutputSurfaceProxy::OnUpdateVSyncParameters(
@@ -390,6 +590,35 @@ void BrowserCompositorOutputSurfaceProxy::OnUpdateVSyncParameters(
surface->OnUpdateVSyncParameters(timebase, interval);
}
+void ReflectorImpl::AttachToOutputSurface(
+ BrowserCompositorOutputSurface* output_surface) {
+ gl_helper_.reset(new GLHelper(output_surface->context3d()));
+ output_surface->SetReflector(this);
+}
+
+void ReflectorImpl::InitOnImplThread() {
+ AttachToOutputSurface(output_surface_proxy_->GetOutputSurface(surface_id_));
+ 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::ShutdownOnImplThread() {
+ BrowserCompositorOutputSurface* output_surface =
+ output_surface_proxy_->GetOutputSurface(surface_id_);
+ output_surface->SetReflector(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)));
+}
+
class GpuProcessTransportFactory
: public ui::ContextFactory,
public ImageTransportFactory {
@@ -417,12 +646,46 @@ class GpuProcessTransportFactory
WebGraphicsContext3DCommandBufferImpl* context =
CreateContextCommon(data->swap_client->AsWeakPtr(),
data->surface_id);
- return new BrowserCompositorOutputSurface(
- context,
- per_compositor_data_[compositor]->surface_id,
- output_surface_proxy_,
- base::MessageLoopProxy::current(),
- compositor->AsWeakPtr());
+ BrowserCompositorOutputSurface* surface =
+ new BrowserCompositorOutputSurface(
+ context,
+ per_compositor_data_[compositor]->surface_id,
+ output_surface_proxy_,
+ base::MessageLoopProxy::current(),
+ compositor->AsWeakPtr());
+ if (data->reflector.get()) {
+ data->reflector->CreateSharedTexture();
+ data->reflector->AttachToOutputSurface(surface);
+ }
+ return surface;
+ }
+
+ virtual scoped_refptr<ui::Reflector> CreateReflector(
+ ui::Compositor* source,
+ ui::Layer* target) OVERRIDE {
+ PerCompositorData* data = per_compositor_data_[source];
+ DCHECK(data);
+
+ if (data->reflector.get())
+ RemoveObserver(data->reflector.get());
+
+ data->reflector = new ReflectorImpl(
+ source, target, output_surface_proxy_,
+ data->surface_id);
+ AddObserver(data->reflector.get());
+ return data->reflector;
+ }
+
+ virtual void RemoveReflector(
+ scoped_refptr<ui::Reflector> reflector) OVERRIDE {
+ ReflectorImpl* reflector_impl =
+ static_cast<ReflectorImpl*>(reflector.get());
+ PerCompositorData* data =
+ per_compositor_data_[reflector_impl->mirrored_compositor()];
+ DCHECK(data);
+ RemoveObserver(reflector_impl);
+ data->reflector->Shutdown();
+ data->reflector = NULL;
}
virtual void RemoveCompositor(ui::Compositor* compositor) OVERRIDE {
@@ -533,6 +796,7 @@ class GpuProcessTransportFactory
#if defined(OS_WIN)
scoped_ptr<AcceleratedSurface> accelerated_surface;
#endif
+ scoped_refptr<ReflectorImpl> reflector;
};
PerCompositorData* CreatePerCompositorData(ui::Compositor* compositor) {
@@ -743,6 +1007,15 @@ class SoftwareContextFactory : public ui::ContextFactory {
return NULL;
#endif
}
+
+ virtual scoped_refptr<ui::Reflector> CreateReflector(
+ ui::Compositor* source,
+ ui::Layer* target) OVERRIDE {
+ return NULL;
+ }
+ virtual void RemoveReflector(
+ scoped_refptr<ui::Reflector> reflector) OVERRIDE {}
+
virtual void RemoveCompositor(ui::Compositor* compositor) OVERRIDE {}
virtual scoped_refptr<cc::ContextProvider>
diff --git a/content/common/gpu/client/gl_helper.cc b/content/common/gpu/client/gl_helper.cc
index 849998c..2ce57d9 100644
--- a/content/common/gpu/client/gl_helper.cc
+++ b/content/common/gpu/client/gl_helper.cc
@@ -634,6 +634,41 @@ void GLHelper::CopySubBufferDamage(WebKit::WebGLId texture,
}
}
+WebKit::WebGLId GLHelper::CreateTexture() {
+ WebKit::WebGLId texture = context_->createTexture();
+ content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_,
+ texture);
+ context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ return texture;
+}
+
+void GLHelper::ResizeTexture(WebKit::WebGLId texture, const gfx::Size& size) {
+ content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, texture);
+ context_->texImage2D(GL_TEXTURE_2D, 0, GL_RGB,
+ size.width(), size.height(), 0,
+ GL_RGB, GL_UNSIGNED_BYTE, NULL);
+}
+
+void GLHelper::CopyTextureSubImage(WebKit::WebGLId texture,
+ const gfx::Rect& rect) {
+ content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, texture);
+ context_->copyTexSubImage2D(GL_TEXTURE_2D, 0,
+ rect.x(), rect.y(),
+ rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+void GLHelper::CopyTextureFullImage(WebKit::WebGLId texture,
+ const gfx::Size& size) {
+ content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, texture);
+ context_->copyTexImage2D(GL_TEXTURE_2D, 0,
+ GL_RGB,
+ 0, 0,
+ size.width(), size.height(), 0);
+}
+
void GLHelper::CopyTextureToImpl::ReadbackPlane(
TextureFrameBufferPair* source,
media::VideoFrame* target,
diff --git a/content/common/gpu/client/gl_helper.h b/content/common/gpu/client/gl_helper.h
index c312b9d..849ffc2 100644
--- a/content/common/gpu/client/gl_helper.h
+++ b/content/common/gpu/client/gl_helper.h
@@ -262,6 +262,19 @@ class CONTENT_EXPORT GLHelper {
const SkRegion& new_damage,
const SkRegion& old_damage);
+ // Simply creates a texture.
+ WebKit::WebGLId CreateTexture();
+
+ // Resizes the texture's size to |size|.
+ void ResizeTexture(WebKit::WebGLId texture, const gfx::Size& size);
+
+ // Copies the framebuffer data given in |rect| to |texture|.
+ void CopyTextureSubImage(WebKit::WebGLId texture, const gfx::Rect& rect);
+
+ // Copies the all framebuffer data to |texture|. |size| specifies the
+ // size of the framebuffer.
+ void CopyTextureFullImage(WebKit::WebGLId texture, const gfx::Size& size);
+
// A scaler will cache all intermediate textures and programs
// needed to scale from a specified size to a destination size.
// If the source or destination sizes changes, you must create
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index ee996b8..2a2dd03 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -210,6 +210,16 @@ WebKit::WebGraphicsContext3D* DefaultContextFactory::CreateOffscreenContext() {
return CreateContextCommon(NULL, true);
}
+scoped_refptr<Reflector> DefaultContextFactory::CreateReflector(
+ Compositor* mirroed_compositor,
+ Layer* mirroring_layer) {
+ return NULL;
+}
+
+void DefaultContextFactory::RemoveReflector(
+ scoped_refptr<Reflector> reflector) {
+}
+
scoped_refptr<cc::ContextProvider>
DefaultContextFactory::OffscreenContextProviderForMainThread() {
if (!offscreen_contexts_main_thread_.get() ||
@@ -281,6 +291,15 @@ WebKit::WebGraphicsContext3D* TestContextFactory::CreateOffscreenContext() {
return context;
}
+scoped_refptr<Reflector> TestContextFactory::CreateReflector(
+ Compositor* mirrored_compositor,
+ Layer* mirroring_layer) {
+ return new Reflector();
+}
+
+void TestContextFactory::RemoveReflector(scoped_refptr<Reflector> reflector) {
+}
+
scoped_refptr<cc::ContextProvider>
TestContextFactory::OffscreenContextProviderForMainThread() {
if (!offscreen_contexts_main_thread_.get() ||
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h
index ef7ce6b..8e59b7e 100644
--- a/ui/compositor/compositor.h
+++ b/ui/compositor/compositor.h
@@ -42,6 +42,7 @@ class GLSurface;
class GLShareGroup;
class Point;
class Rect;
+class Size;
}
namespace WebKit {
@@ -55,6 +56,8 @@ class CompositorObserver;
class ContextProviderFromContextFactory;
class Layer;
class PostedSwapQueue;
+class Reflector;
+class Texture;
struct LatencyInfo;
// This class abstracts the creation of the 3D context for the compositor. It is
@@ -81,6 +84,14 @@ class COMPOSITOR_EXPORT ContextFactory {
// with all compositors.
virtual WebKit::WebGraphicsContext3D* CreateOffscreenContext() = 0;
+ // Creates a reflector that copies the content of the |mirrored_compositor|
+ // onto |mirroing_layer|.
+ virtual scoped_refptr<Reflector> CreateReflector(
+ Compositor* mirrored_compositor,
+ Layer* mirroring_layer) = 0;
+ // Removes the reflector, which stops the mirroring.
+ virtual void RemoveReflector(scoped_refptr<Reflector> reflector) = 0;
+
virtual scoped_refptr<cc::ContextProvider>
OffscreenContextProviderForMainThread() = 0;
virtual scoped_refptr<cc::ContextProvider>
@@ -100,6 +111,12 @@ class COMPOSITOR_EXPORT DefaultContextFactory : public ContextFactory {
virtual cc::OutputSurface* CreateOutputSurface(
Compositor* compositor) OVERRIDE;
virtual WebKit::WebGraphicsContext3D* CreateOffscreenContext() OVERRIDE;
+
+ virtual scoped_refptr<Reflector> CreateReflector(
+ Compositor* compositor,
+ Layer* layer) OVERRIDE;
+ virtual void RemoveReflector(scoped_refptr<Reflector> reflector) OVERRIDE;
+
virtual scoped_refptr<cc::ContextProvider>
OffscreenContextProviderForMainThread() OVERRIDE;
virtual scoped_refptr<cc::ContextProvider>
@@ -131,6 +148,12 @@ class COMPOSITOR_EXPORT TestContextFactory : public ContextFactory {
virtual cc::OutputSurface* CreateOutputSurface(
Compositor* compositor) OVERRIDE;
virtual WebKit::WebGraphicsContext3D* CreateOffscreenContext() OVERRIDE;
+
+ virtual scoped_refptr<Reflector> CreateReflector(
+ Compositor* mirrored_compositor,
+ Layer* mirroring_layer) OVERRIDE;
+ virtual void RemoveReflector(scoped_refptr<Reflector> reflector) OVERRIDE;
+
virtual scoped_refptr<cc::ContextProvider>
OffscreenContextProviderForMainThread() OVERRIDE;
virtual scoped_refptr<cc::ContextProvider>
@@ -190,6 +213,20 @@ class COMPOSITOR_EXPORT CompositorDelegate {
virtual ~CompositorDelegate() {}
};
+class COMPOSITOR_EXPORT Reflector
+ : public base::RefCountedThreadSafe<Reflector> {
+ public:
+ Reflector() {}
+
+ virtual void OnMirroringCompositorResized() {}
+
+ protected:
+ friend class base::RefCountedThreadSafe<Reflector>;
+ virtual ~Reflector() {}
+
+ DISALLOW_COPY_AND_ASSIGN(Reflector);
+};
+
// This class represents a lock on the compositor, that can be used to prevent
// commits to the compositor tree while we're waiting for an asynchronous
// event. The typical use case is when waiting for a renderer to produce a frame