diff options
-rw-r--r-- | ash/display/display_info.cc | 3 | ||||
-rw-r--r-- | ash/display/display_manager.cc | 6 | ||||
-rw-r--r-- | ash/display/display_manager_unittest.cc | 47 | ||||
-rw-r--r-- | ash/display/mirror_window_controller.cc | 40 | ||||
-rw-r--r-- | ash/display/mirror_window_controller.h | 17 | ||||
-rw-r--r-- | chromeos/display/output_configurator.cc | 8 | ||||
-rw-r--r-- | chromeos/display/output_configurator_unittest.cc | 12 | ||||
-rw-r--r-- | chromeos/display/real_output_configurator_delegate.cc | 8 | ||||
-rw-r--r-- | content/browser/renderer_host/image_transport_factory.cc | 287 | ||||
-rw-r--r-- | content/common/gpu/client/gl_helper.cc | 35 | ||||
-rw-r--r-- | content/common/gpu/client/gl_helper.h | 13 | ||||
-rw-r--r-- | ui/compositor/compositor.cc | 19 | ||||
-rw-r--r-- | ui/compositor/compositor.h | 37 |
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 |