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:
@ -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;
|
||||
|
||||
|
Reference in New Issue
Block a user