0

Implement software mirroring and unified mode using SurfaceLayers

Reflector doesn't work with OOP-D enabled, so use SurfaceLayers to
embed the root surface of the source display onto mirrored and unified
displays.

e.g.
chrome --ash-host-window-bounds=1200x1200*2,1202+0-1200x1200*2
 --ash-enable-unified-desktop --ash-dev-shortcuts --ash-debug-shortcuts
 --user-first-display-as-internal --enable-features=VizDisplayCompositor


Bug: 884805
Change-Id: I809600606e65f970e00396ff8fa0a75819a5ec7b
Reviewed-on: https://chromium-review.googlesource.com/c/1251766
Reviewed-by: Scott Violet <sky@chromium.org>
Reviewed-by: kylechar <kylechar@chromium.org>
Commit-Queue: Sean Gilhuly <sgilhuly@chromium.org>
Cr-Commit-Position: refs/heads/master@{#606481}
This commit is contained in:
Sean Gilhuly
2018-11-08 15:57:27 +00:00
committed by Commit Bot
parent f498307fe7
commit fe2a8c1f25
9 changed files with 287 additions and 22 deletions

@ -18,6 +18,8 @@
#include "ash/window_factory.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/viz/common/features.h"
#include "components/viz/common/surfaces/surface_id.h"
#include "ui/aura/client/capture_client.h"
#include "ui/aura/env.h"
#include "ui/aura/window_delegate.h"
@ -158,6 +160,8 @@ void MirrorWindowController::UpdateWindow(
multi_display_mode_ = GetCurrentMultiDisplayMode();
reflecting_source_id_ = GetCurrentReflectingSourceId();
viz::SurfaceId reflecting_surface_id =
Shell::GetRootWindowForDisplayId(reflecting_source_id_)->GetSurfaceId();
for (const display::ManagedDisplayInfo& display_info : display_info_list) {
std::unique_ptr<RootWindowTransformer> transformer;
@ -220,18 +224,20 @@ void MirrorWindowController::UpdateWindow(
mirror_window->Init(ui::LAYER_SOLID_COLOR);
host->window()->AddChild(mirror_window);
host_info->ash_host->SetRootWindowTransformer(std::move(transformer));
mirror_window->SetBounds(host->window()->bounds());
mirror_window->Show();
// The accelerated widget is created synchronously.
DCHECK_NE(gfx::kNullAcceleratedWidget, host->GetAcceleratedWidget());
if (reflector_) {
reflector_->AddMirroringLayer(mirror_window->layer());
} else if (GetContextFactoryPrivate()) {
reflector_ = GetContextFactoryPrivate()->CreateReflector(
Shell::GetRootWindowForDisplayId(reflecting_source_id_)
->GetHost()
->compositor(),
mirror_window->layer());
if (!base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) {
mirror_window->SetBounds(host->window()->bounds());
mirror_window->Show();
if (reflector_) {
reflector_->AddMirroringLayer(mirror_window->layer());
} else if (GetContextFactoryPrivate()) {
reflector_ = GetContextFactoryPrivate()->CreateReflector(
Shell::GetRootWindowForDisplayId(reflecting_source_id_)
->GetHost()
->compositor(),
mirror_window->layer());
}
}
} else {
AshWindowTreeHost* ash_host =
@ -241,6 +247,22 @@ void MirrorWindowController::UpdateWindow(
ash_host->SetRootWindowTransformer(std::move(transformer));
host->SetBoundsInPixels(display_info.bounds_in_native());
}
if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) {
// |mirror_size| is the size of the mirror source in physical pixels.
// The RootWindowTransformer corrects the scale of the mirrored display
// and the location of input events.
gfx::Size mirror_size =
display_manager->GetDisplayInfo(reflecting_source_id_)
.bounds_in_native()
.size();
aura::Window* mirror_window =
mirroring_host_info_map_[display_info.id()]->mirror_window;
mirror_window->SetBounds(gfx::Rect(mirror_size));
mirror_window->Show();
mirror_window->layer()->SetShowReflectedSurface(reflecting_surface_id,
mirror_size);
}
}
// Deleting WTHs for disconnected displays.

@ -11,6 +11,8 @@
#include "ash/wm/cursor_manager_test_api.h"
#include "base/command_line.h"
#include "base/strings/stringprintf.h"
#include "base/test/scoped_feature_list.h"
#include "components/viz/common/features.h"
#include "ui/aura/env.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/test/test_windows.h"
@ -26,10 +28,12 @@ namespace ash {
namespace {
display::ManagedDisplayInfo CreateDisplayInfo(int64_t id,
const gfx::Rect& bounds) {
const gfx::Rect& bounds,
float scale = 1.f) {
display::ManagedDisplayInfo info(
id, base::StringPrintf("x-%d", static_cast<int>(id)), false);
info.SetBounds(bounds);
info.set_device_scale_factor(scale);
return info;
}
@ -50,24 +54,26 @@ class MirrorOnBootTest : public AshTestBase {
private:
DISALLOW_COPY_AND_ASSIGN(MirrorOnBootTest);
};
}
using MirrorWindowControllerTest = AshTestBase;
class MirrorWindowControllerTestDisableMultiMirroring : public AshTestBase {
class MirrorUsingSurfaceLayersTest : public AshTestBase {
public:
MirrorWindowControllerTestDisableMultiMirroring() = default;
~MirrorWindowControllerTestDisableMultiMirroring() override = default;
MirrorUsingSurfaceLayersTest() = default;
~MirrorUsingSurfaceLayersTest() override = default;
void SetUp() override {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
::switches::kDisableMultiMirroring);
scoped_feature_list_.InitWithFeatures({features::kVizDisplayCompositor},
{});
AshTestBase::SetUp();
}
private:
DISALLOW_COPY_AND_ASSIGN(MirrorWindowControllerTestDisableMultiMirroring);
base::test::ScopedFeatureList scoped_feature_list_;
DISALLOW_COPY_AND_ASSIGN(MirrorUsingSurfaceLayersTest);
};
} // namespace
using MirrorWindowControllerTest = AshTestBase;
// Make sure that the compositor based mirroring can switch
// from/to dock mode.
@ -91,7 +97,7 @@ TEST_F(MirrorWindowControllerTest, DockMode) {
EXPECT_EQ(internal_id, internal_display_id);
display_manager()->SetMirrorMode(display::MirrorMode::kNormal, base::nullopt);
RunAllPendingInMessageLoop();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_EQ(external_id,
@ -123,4 +129,37 @@ TEST_F(MirrorOnBootTest, MirrorOnBoot) {
EXPECT_EQ(1U, test_api.GetHosts().size());
}
// Test that the mirror window matches the size of the host display.
TEST_F(MirrorUsingSurfaceLayersTest, MirrorSize) {
const int64_t primary_id = 1;
const int64_t mirror_id = 2;
// Run the test with and without display scaling.
int scale_factors[] = {1, 2};
for (int scale : scale_factors) {
const display::ManagedDisplayInfo primary_display_info =
CreateDisplayInfo(primary_id, gfx::Rect(0, 0, 400, 400), scale);
const display::ManagedDisplayInfo mirror_display_info =
CreateDisplayInfo(mirror_id, gfx::Rect(400, 0, 600, 600), scale);
std::vector<display::ManagedDisplayInfo> display_info_list = {
primary_display_info, mirror_display_info};
// Start software mirroring.
display_manager()->OnNativeDisplaysChanged(display_info_list);
display_manager()->SetMirrorMode(display::MirrorMode::kNormal,
base::nullopt);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
// Check the size of the mirror window.
const display::Display& primary_display =
display_manager()->GetDisplayForId(primary_id);
aura::Window* root_window = Shell::GetRootWindowForDisplayId(mirror_id);
aura::Window* mirror_window = root_window->children()[0];
EXPECT_EQ(primary_display.GetSizeInPixel(), root_window->bounds().size());
EXPECT_EQ(primary_display.GetSizeInPixel(), mirror_window->bounds().size());
}
}
} // namespace ash

@ -103,6 +103,8 @@ viz_component("service") {
"display_embedder/gl_output_surface.h",
"display_embedder/gl_output_surface_buffer_queue.cc",
"display_embedder/gl_output_surface_buffer_queue.h",
"display_embedder/gl_output_surface_offscreen.cc",
"display_embedder/gl_output_surface_offscreen.h",
"display_embedder/gpu_display_provider.cc",
"display_embedder/gpu_display_provider.h",
"display_embedder/in_process_gpu_memory_buffer_manager.cc",

@ -52,6 +52,7 @@ class GLOutputSurface : public OutputSurface {
protected:
OutputSurfaceClient* client() const { return client_; }
ui::LatencyTracker* latency_tracker() { return &latency_tracker_; }
// Called when a swap completion is signaled from ImageTransportSurface.
virtual void DidReceiveSwapBuffersAck(gfx::SwapResult result);

@ -0,0 +1,120 @@
// Copyright 2018 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 "components/viz/service/display_embedder/gl_output_surface_offscreen.h"
#include <stdint.h>
#include "base/bind.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "components/viz/service/display/output_surface_client.h"
#include "components/viz/service/display/output_surface_frame.h"
#include "gpu/command_buffer/client/context_support.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "ui/gl/gl_utils.h"
namespace viz {
namespace {
constexpr ResourceFormat kFboTextureFormat = RGBA_8888;
} // namespace
GLOutputSurfaceOffscreen::GLOutputSurfaceOffscreen(
scoped_refptr<VizProcessContextProvider> context_provider,
SyntheticBeginFrameSource* synthetic_begin_frame_source)
: GLOutputSurface(context_provider, synthetic_begin_frame_source),
weak_ptr_factory_(this) {}
GLOutputSurfaceOffscreen::~GLOutputSurfaceOffscreen() {}
void GLOutputSurfaceOffscreen::EnsureBackbuffer() {
if (!texture_id_) {
gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
const int max_texture_size =
context_provider_->ContextCapabilities().max_texture_size;
int texture_width = std::min(max_texture_size, size_.width());
int texture_height = std::min(max_texture_size, size_.height());
// TODO(sgilhuly): Draw to a texture backed by a mailbox.
gl->GenTextures(1, &texture_id_);
gl->BindTexture(GL_TEXTURE_2D, texture_id_);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl->TexImage2D(GL_TEXTURE_2D, 0, GLInternalFormat(kFboTextureFormat),
texture_width, texture_height, 0,
GLDataFormat(kFboTextureFormat),
GLDataType(kFboTextureFormat), nullptr);
gl->GenFramebuffers(1, &fbo_);
gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, texture_id_, 0);
}
}
void GLOutputSurfaceOffscreen::DiscardBackbuffer() {
gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
if (texture_id_) {
gl->DeleteTextures(1, &texture_id_);
texture_id_ = 0;
}
if (fbo_) {
gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
gl->DeleteFramebuffers(1, &fbo_);
fbo_ = 0;
}
}
void GLOutputSurfaceOffscreen::BindFramebuffer() {
if (!texture_id_) {
EnsureBackbuffer();
} else {
gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
}
}
void GLOutputSurfaceOffscreen::Reshape(const gfx::Size& size,
float scale_factor,
const gfx::ColorSpace& color_space,
bool alpha,
bool stencil) {
size_ = size;
DiscardBackbuffer();
EnsureBackbuffer();
}
void GLOutputSurfaceOffscreen::SwapBuffers(OutputSurfaceFrame frame) {
gfx::Size surface_size = frame.size;
DCHECK(surface_size == size_);
gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
gpu::SyncToken sync_token;
gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
context_provider_->ContextSupport()->SignalSyncToken(
sync_token,
base::BindOnce(&GLOutputSurfaceOffscreen::OnSwapBuffersComplete,
weak_ptr_factory_.GetWeakPtr(),
std::move(frame.latency_info),
frame.need_presentation_feedback));
}
void GLOutputSurfaceOffscreen::OnSwapBuffersComplete(
std::vector<ui::LatencyInfo> latency_info,
bool need_presentation_feedback) {
latency_tracker()->OnGpuSwapBuffersCompleted(latency_info);
client()->DidReceiveSwapBuffersAck();
if (need_presentation_feedback)
client()->DidReceivePresentationFeedback(gfx::PresentationFeedback());
}
} // namespace viz

@ -0,0 +1,51 @@
// Copyright 2018 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.
#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_OFFSCREEN_H_
#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_OFFSCREEN_H_
#include <memory>
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/service/display_embedder/gl_output_surface.h"
#include "components/viz/service/display_embedder/viz_process_context_provider.h"
#include "ui/latency/latency_tracker.h"
namespace viz {
// An OutputSurface implementation that draws and swaps to an offscreen GL
// framebuffer.
class GLOutputSurfaceOffscreen : public GLOutputSurface {
public:
GLOutputSurfaceOffscreen(
scoped_refptr<VizProcessContextProvider> context_provider,
SyntheticBeginFrameSource* synthetic_begin_frame_source);
~GLOutputSurfaceOffscreen() override;
// OutputSurface implementation.
void EnsureBackbuffer() override;
void DiscardBackbuffer() override;
void BindFramebuffer() override;
void Reshape(const gfx::Size& size,
float scale_factor,
const gfx::ColorSpace& color_space,
bool alpha,
bool stencil) override;
void SwapBuffers(OutputSurfaceFrame frame) override;
private:
void OnSwapBuffersComplete(std::vector<ui::LatencyInfo> latency_info,
bool need_presentation_feedback);
uint32_t fbo_ = 0;
uint32_t texture_id_ = 0;
gfx::Size size_;
base::WeakPtrFactory<GLOutputSurfaceOffscreen> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(GLOutputSurfaceOffscreen);
};
} // namespace viz
#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_OFFSCREEN_H_

@ -15,6 +15,7 @@
#include "components/viz/service/display/display.h"
#include "components/viz/service/display/display_scheduler.h"
#include "components/viz/service/display_embedder/gl_output_surface.h"
#include "components/viz/service/display_embedder/gl_output_surface_offscreen.h"
#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
#include "components/viz/service/display_embedder/skia_output_surface_impl.h"
#include "components/viz/service/display_embedder/software_output_surface.h"
@ -162,7 +163,10 @@ std::unique_ptr<Display> GpuDisplayProvider::CreateDisplay(
}
}
if (context_provider->ContextCapabilities().surfaceless) {
if (surface_handle == gpu::kNullSurfaceHandle) {
output_surface = std::make_unique<GLOutputSurfaceOffscreen>(
std::move(context_provider), synthetic_begin_frame_source);
} else if (context_provider->ContextCapabilities().surfaceless) {
#if defined(USE_OZONE)
output_surface = std::make_unique<GLOutputSurfaceOzone>(
std::move(context_provider), surface_handle,

@ -810,6 +810,27 @@ void Layer::SetOldestAcceptableFallback(const viz::SurfaceId& surface_id) {
mirror->dest()->SetOldestAcceptableFallback(surface_id);
}
void Layer::SetShowReflectedSurface(const viz::SurfaceId& surface_id,
const gfx::Size& frame_size_in_pixels) {
DCHECK(type_ == LAYER_TEXTURED || type_ == LAYER_SOLID_COLOR);
if (!surface_layer_) {
scoped_refptr<cc::SurfaceLayer> new_layer = cc::SurfaceLayer::Create();
SwitchToLayer(new_layer);
surface_layer_ = new_layer;
}
surface_layer_->SetSurfaceId(surface_id,
cc::DeadlinePolicy::UseInfiniteDeadline());
surface_layer_->SetBackgroundColor(SK_ColorBLACK);
// TODO(kylechar): Include UV transform and don't stretch to fill bounds.
surface_layer_->SetStretchContentToFillBounds(true);
// The reflecting surface uses the native size of the display.
frame_size_in_dip_ = frame_size_in_pixels;
RecomputeDrawsContentAndUVRect();
}
const viz::SurfaceId* Layer::GetSurfaceId() const {
if (surface_layer_)
return &surface_layer_->surface_id();

@ -314,6 +314,11 @@ class COMPOSITOR_EXPORT Layer : public LayerAnimationDelegate,
// display compositor, the fallback surface will be used.
void SetOldestAcceptableFallback(const viz::SurfaceId& surface_id);
// Begins mirroring content from a reflected surface, e.g. a software mirrored
// display. |surface_id| should be the root surface for a display.
void SetShowReflectedSurface(const viz::SurfaceId& surface_id,
const gfx::Size& frame_size_in_pixels);
// Returns the primary SurfaceId set by SetShowSurface.
const viz::SurfaceId* GetSurfaceId() const;