0

[Refactor] Refactor ViewTreeHostRootView to use FrameSinkHost.

Refactor ViewTreeHostRootView to use FrameSinkHost. This is an effort
to remove the duplication of functionality of classes trying to submit
their own compositor frames by creating a LayerTreeFrameSink on a host
window.

Also adds unittests coverage for ViewTreeHostRootView.

Bug: 1392895
Test: Ash-HUD still works as expected and covered by unittests.
Change-Id: Iae7be727966fd90eef40246594c3cf060c689dd8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4065036
Commit-Queue: Zoraiz Naeem <zoraiznaeem@chromium.org>
Reviewed-by: Addison Luh <aluh@chromium.org>
Reviewed-by: Mitsuru Oshima <oshima@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1108068}
This commit is contained in:
Zoraiz Naeem
2023-02-22 01:00:48 +00:00
committed by Chromium LUCI CQ
parent 8c7d54720a
commit 8d506e7efa
7 changed files with 776 additions and 552 deletions

@ -546,6 +546,8 @@ component("ash") {
"fast_ink/laser/laser_segment_utils.h",
"fast_ink/view_tree_host_root_view.cc",
"fast_ink/view_tree_host_root_view.h",
"fast_ink/view_tree_host_root_view_frame_factory.cc",
"fast_ink/view_tree_host_root_view_frame_factory.h",
"fast_ink/view_tree_host_widget.cc",
"fast_ink/view_tree_host_widget.h",
"focus_cycler.cc",
@ -2915,6 +2917,7 @@ test("ash_unittests") {
"fast_ink/fast_ink_points_unittest.cc",
"fast_ink/laser/laser_pointer_controller_unittest.cc",
"fast_ink/laser/laser_segment_utils_unittest.cc",
"fast_ink/view_tree_host_root_view_frame_factory_unittest.cc",
"focus_cycler_unittest.cc",
"frame/caption_buttons/frame_caption_button_container_view_unittest.cc",
"frame/caption_buttons/frame_size_button_unittest.cc",

@ -4,375 +4,26 @@
#include "ash/fast_ink/view_tree_host_root_view.h"
#include <GLES2/gl2.h>
#include <memory>
#include "ash/fast_ink/view_tree_host_root_view_frame_factory.h"
#include "ash/frame_sink/frame_sink_host.h"
#include "base/functional/bind.h"
#include "base/task/single_thread_task_runner.h"
#include "base/trace_event/trace_event.h"
#include "cc/paint/display_item_list.h"
#include "cc/trees/layer_tree_frame_sink.h"
#include "cc/trees/layer_tree_frame_sink_client.h"
#include "components/viz/common/frame_timing_details.h"
#include "components/viz/common/gpu/context_provider.h"
#include "components/viz/common/hit_test/hit_test_region_list.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/quads/texture_draw_quad.h"
#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
#include "ui/aura/window_tree_host.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/paint_context.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/gpu_memory_buffer.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/widget/widget.h"
namespace ash {
struct ViewTreeHostRootView::Resource {
Resource() = default;
~Resource() {
gpu::SharedImageInterface* sii = context_provider->SharedImageInterface();
DCHECK(!mailbox.IsZero());
sii->DestroySharedImage(sync_token, mailbox);
}
scoped_refptr<viz::ContextProvider> context_provider;
gpu::Mailbox mailbox;
gpu::SyncToken sync_token;
bool damaged = true;
std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
int group_id = 1;
};
// An object holds a frame shink so that it can outlive the hosting
// widget and its view tree. This is necessary to release in-flight buffers
// (maintained in exported resources).
class ViewTreeHostRootView::LayerTreeViewTreeFrameSinkHolder
: public cc::LayerTreeFrameSinkClient,
public aura::WindowObserver {
public:
LayerTreeViewTreeFrameSinkHolder(
ViewTreeHostRootView* view,
std::unique_ptr<cc::LayerTreeFrameSink> frame_sink)
: view_(view), frame_sink_(std::move(frame_sink)) {
frame_sink_->BindToClient(this);
}
LayerTreeViewTreeFrameSinkHolder(const LayerTreeViewTreeFrameSinkHolder&) =
delete;
LayerTreeViewTreeFrameSinkHolder& operator=(
const LayerTreeViewTreeFrameSinkHolder&) = delete;
~LayerTreeViewTreeFrameSinkHolder() override {
if (frame_sink_)
frame_sink_->DetachFromClient();
if (root_window_)
root_window_->RemoveObserver(this);
}
// Delete frame sink after having reclaimed all exported resources.
// TODO(reveman): Find a better way to handle deletion of in-flight resources.
// https://crbug.com/765763
static void DeleteWhenLastResourceHasBeenReclaimed(
std::unique_ptr<LayerTreeViewTreeFrameSinkHolder> holder) {
if (holder->last_frame_size_in_pixels_.IsEmpty()) {
// Delete sink holder immediately if no frame has been submitted.
DCHECK(holder->exported_resources_.empty());
return;
}
// Submit an empty frame to ensure that pending release callbacks will be
// processed in a finite amount of time.
viz::CompositorFrame frame;
frame.metadata.begin_frame_ack.frame_id =
viz::BeginFrameId(viz::BeginFrameArgs::kManualSourceId,
viz::BeginFrameArgs::kStartingFrameNumber);
frame.metadata.begin_frame_ack.has_damage = true;
frame.metadata.device_scale_factor =
holder->last_frame_device_scale_factor_;
frame.metadata.frame_token = ++holder->next_frame_token_;
auto pass = viz::CompositorRenderPass::Create();
pass->SetNew(viz::CompositorRenderPassId{1},
gfx::Rect(holder->last_frame_size_in_pixels_),
gfx::Rect(holder->last_frame_size_in_pixels_),
gfx::Transform());
frame.render_pass_list.push_back(std::move(pass));
holder->frame_sink_->SubmitCompositorFrame(std::move(frame),
/*hit_test_data_changed=*/true);
// Delete sink holder immediately if not waiting for exported resources to
// be reclaimed.
if (holder->exported_resources_.empty())
return;
// Delete sink holder immediately if native window is already gone.
aura::Window* window = holder->view_->GetWidget()->GetNativeView();
if (!window)
return;
aura::Window* root_window = window->GetRootWindow();
holder->root_window_ = root_window;
holder->view_ = nullptr;
// If we have exported resources to reclaim then extend the lifetime of
// holder by adding it as a root window observer. The holder will delete
// itself when the root window is removed or when all exported resources
// have been reclaimed.
root_window->AddObserver(holder.release());
}
void SubmitCompositorFrame(viz::CompositorFrame frame,
viz::ResourceId resource_id,
std::unique_ptr<Resource> resource) {
exported_resources_[resource_id] = std::move(resource);
last_frame_size_in_pixels_ = frame.size_in_pixels();
last_frame_device_scale_factor_ = frame.metadata.device_scale_factor;
frame.metadata.frame_token = ++next_frame_token_;
frame_sink_->SubmitCompositorFrame(std::move(frame),
/*hit_test_data_changed=*/true);
}
void DamageExportedResources() {
for (auto& entry : exported_resources_)
entry.second->damaged = true;
}
// Overridden from cc::LayerTreeFrameSinkClient:
void SetBeginFrameSource(viz::BeginFrameSource* source) override {}
absl::optional<viz::HitTestRegionList> BuildHitTestData() override {
return {};
}
void ReclaimResources(std::vector<viz::ReturnedResource> resources) override {
if (delete_pending_)
return;
for (auto& entry : resources) {
auto it = exported_resources_.find(entry.id);
DCHECK(it != exported_resources_.end());
std::unique_ptr<Resource> resource = std::move(it->second);
exported_resources_.erase(it);
resource->sync_token = entry.sync_token;
if (view_ && !entry.lost)
view_->ReclaimResource(std::move(resource));
}
if (root_window_ && exported_resources_.empty())
ScheduleDelete();
}
void SetTreeActivationCallback(base::RepeatingClosure callback) override {}
void DidReceiveCompositorFrameAck() override {
if (view_)
view_->DidReceiveCompositorFrameAck();
}
void DidPresentCompositorFrame(
uint32_t frame_token,
const viz::FrameTimingDetails& details) override {
if (view_)
view_->DidPresentCompositorFrame(details.presentation_feedback);
}
void DidLoseLayerTreeFrameSink() override {
exported_resources_.clear();
if (root_window_)
ScheduleDelete();
}
void OnDraw(const gfx::Transform& transform,
const gfx::Rect& viewport,
bool resourceless_software_draw,
bool skip_draw) override {}
void SetMemoryPolicy(const cc::ManagedMemoryPolicy& policy) override {}
void SetExternalTilePriorityConstraints(
const gfx::Rect& viewport_rect,
const gfx::Transform& transform) override {}
// Overridden from aura::WindowObserver:
void OnWindowDestroying(aura::Window* window) override {
root_window_->RemoveObserver(this);
root_window_ = nullptr;
// Make sure frame sink never outlives aura.
frame_sink_->DetachFromClient();
frame_sink_.reset();
ScheduleDelete();
}
private:
void ScheduleDelete() {
if (delete_pending_)
return;
delete_pending_ = true;
base::SingleThreadTaskRunner::GetCurrentDefault()->DeleteSoon(FROM_HERE,
this);
}
ViewTreeHostRootView* view_;
std::unique_ptr<cc::LayerTreeFrameSink> frame_sink_;
base::flat_map<viz::ResourceId, std::unique_ptr<Resource>>
exported_resources_;
viz::FrameTokenGenerator next_frame_token_;
gfx::Size last_frame_size_in_pixels_;
float last_frame_device_scale_factor_ = 1.0f;
aura::Window* root_window_ = nullptr;
bool delete_pending_ = false;
};
ViewTreeHostRootView::ViewTreeHostRootView(views::Widget* widget)
: views::internal::RootView(widget) {}
: views::internal::RootView(widget),
frame_factory_(
std::make_unique<ViewTreeHostRootViewFrameFactory>(widget)) {}
std::unique_ptr<ViewTreeHostRootView::Resource>
ViewTreeHostRootView::ObtainResource() {
auto* window = GetWidget()->GetNativeView();
if (!frame_sink_holder_) {
frame_sink_holder_ = std::make_unique<LayerTreeViewTreeFrameSinkHolder>(
this, window->CreateLayerTreeFrameSink());
}
float dsf = window->GetHost()->device_scale_factor();
display::Display display =
display::Screen::GetScreen()->GetDisplayNearestWindow(window);
gfx::Size new_size = gfx::ScaleToCeiledSize(size(), dsf);
if (display.panel_rotation() == display::Display::ROTATE_90 ||
display.panel_rotation() == display::Display::ROTATE_270) {
new_size.SetSize(new_size.height(), new_size.width());
}
rotate_transform_.MakeIdentity();
switch (display.panel_rotation()) {
case display::Display::ROTATE_0:
break;
case display::Display::ROTATE_90:
rotate_transform_.Translate(new_size.width(), 0);
rotate_transform_.Rotate(90);
break;
case display::Display::ROTATE_180:
rotate_transform_.Translate(new_size.width(), new_size.height());
rotate_transform_.Rotate(180);
break;
case display::Display::ROTATE_270:
rotate_transform_.Translate(0, new_size.height());
rotate_transform_.Rotate(270);
break;
}
if (buffer_size_ != new_size) {
buffer_size_ = new_size;
// Clear All resources.
resource_group_id_++;
returned_resources_.clear();
}
if (!returned_resources_.empty()) {
auto resource = std::move(returned_resources_.back());
returned_resources_.pop_back();
return resource;
}
auto resource = std::make_unique<Resource>();
resource->group_id = resource_group_id_;
gpu::GpuMemoryBufferManager* gmb_manager =
aura::Env::GetInstance()->context_factory()->GetGpuMemoryBufferManager();
resource->gpu_memory_buffer = gmb_manager->CreateGpuMemoryBuffer(
buffer_size_,
SK_B32_SHIFT ? gfx::BufferFormat::RGBA_8888
: gfx::BufferFormat::BGRA_8888,
gfx::BufferUsage::SCANOUT_CPU_READ_WRITE, gpu::kNullSurfaceHandle,
nullptr);
if (!resource->gpu_memory_buffer) {
LOG(ERROR) << "Failed to create GPU memory buffer";
return nullptr;
}
resource->context_provider = aura::Env::GetInstance()
->context_factory()
->SharedMainThreadContextProvider();
if (!resource->context_provider) {
LOG(ERROR) << "Failed to acquire a context provider";
return nullptr;
}
return resource;
}
ViewTreeHostRootView::~ViewTreeHostRootView() {
if (frame_sink_holder_) {
LayerTreeViewTreeFrameSinkHolder::DeleteWhenLastResourceHasBeenReclaimed(
std::move(frame_sink_holder_));
}
}
void ViewTreeHostRootView::Paint() {
if (!GetWidget() || !GetWidget()->GetNativeWindow()) {
SchedulePaintInRect(gfx::Rect());
return;
}
// We make no attempts to recover if it failed to obtain a resource. It is
// expected that this class is either short-lived, or for debugging and
// requiring new instance to be created in lost context situations is
// acceptable and keeps the code simple.
auto resource = ObtainResource();
if (!resource)
return;
DCHECK(pending_paint_);
pending_paint_ = false;
auto display_item_list = base::MakeRefCounted<cc::DisplayItemList>();
float dsf = GetWidget()->GetCompositor()->device_scale_factor();
ui::PaintContext context(display_item_list.get(), dsf, damaged_paint_rect_,
/*pixel_canvas=*/true);
GetWidget()->OnNativeWidgetPaint(context);
display_item_list->Finalize();
if (!resource->gpu_memory_buffer->Map()) {
TRACE_EVENT0("ui", "ViewTreeHostRootView::Paint::Map");
LOG(ERROR) << "Failed to map GPU memory buffer";
return;
}
SkImageInfo info =
SkImageInfo::MakeN32Premul(buffer_size_.width(), buffer_size_.height());
uint8_t* data = static_cast<uint8_t*>(resource->gpu_memory_buffer->memory(0));
int stride = resource->gpu_memory_buffer->stride(0);
std::unique_ptr<SkCanvas> canvas =
SkCanvas::MakeRasterDirect(info, data, stride);
canvas->setMatrix(gfx::TransformToFlattenedSkMatrix(rotate_transform_));
display_item_list->Raster(canvas.get());
{
TRACE_EVENT0("ui", "ViewTreeHostRootView::Paint::Unmap");
// Unmap to flush writes to buffer.
resource->gpu_memory_buffer->Unmap();
}
UpdateSurface(damaged_paint_rect_, std::move(resource));
damaged_paint_rect_ = gfx::Rect();
}
void ViewTreeHostRootView::SchedulePaintInRect(const gfx::Rect& rect) {
damaged_paint_rect_.Union(rect);
if (pending_paint_)
return;
pending_paint_ = true;
if (!pending_compositor_frame_ack_) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&ViewTreeHostRootView::Paint,
weak_ptr_factory_.GetWeakPtr()));
}
}
ViewTreeHostRootView::~ViewTreeHostRootView() = default;
bool ViewTreeHostRootView::GetIsOverlayCandidate() {
return is_overlay_candidate_;
@ -382,150 +33,39 @@ void ViewTreeHostRootView::SetIsOverlayCandidate(bool is_overlay_candidate) {
is_overlay_candidate_ = is_overlay_candidate;
}
void ViewTreeHostRootView::UpdateSurface(const gfx::Rect& damage_rect,
std::unique_ptr<Resource> resource) {
damage_rect_.Union(damage_rect);
pending_resource_ = std::move(resource);
if (!damage_rect.IsEmpty()) {
frame_sink_holder_->DamageExportedResources();
for (auto& returned_resource : returned_resources_)
returned_resource->damaged = true;
}
if (!pending_compositor_frame_ack_)
SubmitCompositorFrame();
void ViewTreeHostRootView::SchedulePaintInRect(const gfx::Rect& damage_rect) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&ViewTreeHostRootView::UpdateSurface,
weak_ptr_factory_.GetWeakPtr(),
/*content_rect=*/gfx::Rect(size()), damage_rect,
/*synchonous_draw=*/false));
}
void ViewTreeHostRootView::SubmitCompositorFrame() {
std::unique_ptr<viz::CompositorFrame>
ViewTreeHostRootView::CreateCompositorFrame(
const viz::BeginFrameAck& begin_frame_ack,
UiResourceManager& resource_manager,
bool auto_update,
const gfx::Size& last_submitted_frame_size,
float last_submitted_frame_dsf) {
TRACE_EVENT1("ui", "ViewTreeHostRootView::SubmitCompositorFrame", "damage",
damage_rect_.ToString());
GetTotalDamage().ToString());
float device_scale_factor =
GetWidget()->GetCompositor()->device_scale_factor();
auto frame = frame_factory_->CreateCompositorFrame(
begin_frame_ack, GetContentRect(), GetTotalDamage(),
is_overlay_candidate_, resource_manager);
// TODO(crbug.com/1131623): Should this be ceil? Why do we choose floor?
gfx::Size size_in_pixel =
gfx::ToFlooredSize(gfx::ConvertSizeToPixels(size(), device_scale_factor));
gfx::Rect output_rect(size_in_pixel);
ResetDamage();
gfx::Rect quad_rect;
quad_rect = gfx::Rect(buffer_size_);
gfx::Rect damage_rect;
// TODO(oshima): Support partial content update.
damage_rect = gfx::ToEnclosingRect(
gfx::ConvertRectToPixels(damage_rect_, device_scale_factor));
damage_rect.Intersect(output_rect);
damage_rect_ = gfx::Rect();
std::unique_ptr<Resource> resource = std::move(pending_resource_);
if (resource->damaged) {
DCHECK(resource->context_provider);
gpu::SharedImageInterface* sii =
resource->context_provider->SharedImageInterface();
if (resource->mailbox.IsZero()) {
DCHECK(!resource->sync_token.HasData());
const uint32_t usage = gpu::SHARED_IMAGE_USAGE_DISPLAY_READ |
gpu::SHARED_IMAGE_USAGE_SCANOUT;
gpu::GpuMemoryBufferManager* gmb_manager =
aura::Env::GetInstance()
->context_factory()
->GetGpuMemoryBufferManager();
resource->mailbox = sii->CreateSharedImage(
resource->gpu_memory_buffer.get(), gmb_manager, gfx::ColorSpace(),
kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage);
} else {
sii->UpdateSharedImage(resource->sync_token, resource->mailbox);
}
resource->sync_token = sii->GenVerifiedSyncToken();
resource->damaged = false;
// A change in the size of the compositor frame means we need to identify a
// new surface to submit the compositor frame to since the surface size is now
// different.
if (last_submitted_frame_size != frame->size_in_pixels() ||
last_submitted_frame_dsf != frame->device_scale_factor()) {
host_window()->AllocateLocalSurfaceId();
}
viz::TransferableResource transferable_resource =
viz::TransferableResource::MakeGpu(
resource->mailbox, GL_LINEAR, GL_TEXTURE_2D, resource->sync_token,
buffer_size_, SK_B32_SHIFT ? viz::RGBA_8888 : viz::BGRA_8888,
is_overlay_candidate_);
transferable_resource.id = id_generator_.GenerateNextId();
gfx::Transform buffer_to_target_transform =
rotate_transform_.GetCheckedInverse();
const viz::CompositorRenderPassId kRenderPassId{1};
auto render_pass = viz::CompositorRenderPass::Create();
render_pass->SetNew(kRenderPassId, output_rect, damage_rect,
buffer_to_target_transform);
viz::SharedQuadState* quad_state =
render_pass->CreateAndAppendSharedQuadState();
quad_state->SetAll(buffer_to_target_transform,
/*quad_layer_rect=*/output_rect,
/*visible_layer_rect=*/output_rect,
/*mask_filter_info=*/gfx::MaskFilterInfo(),
/*clip_rect=*/absl::nullopt, /*are_contents_opaque=*/false,
/*opacity=*/1.f,
/*blend_mode=*/SkBlendMode::kSrcOver,
/*sorting_context_id=*/0);
viz::CompositorFrame frame;
// TODO(eseckler): ViewTreeHostRootView should use BeginFrames and set
// the ack accordingly.
frame.metadata.begin_frame_ack =
viz::BeginFrameAck::CreateManualAckWithDamage();
frame.metadata.device_scale_factor = device_scale_factor;
frame.metadata.frame_token = ++next_frame_token_;
viz::TextureDrawQuad* texture_quad =
render_pass->CreateAndAppendDrawQuad<viz::TextureDrawQuad>();
float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
gfx::RectF uv_crop(quad_rect);
uv_crop.Scale(1.f / buffer_size_.width(), 1.f / buffer_size_.height());
texture_quad->SetNew(
quad_state, quad_rect, quad_rect,
/*needs_blending=*/true, transferable_resource.id,
/*premultiplied_alpha=*/true, uv_crop.origin(), uv_crop.bottom_right(),
SkColors::kTransparent, vertex_opacity,
/*y_flipped=*/false,
/*nearest_neighbor=*/false,
/*secure_output_only=*/false, gfx::ProtectedVideoType::kClear);
texture_quad->set_resource_size_in_pixels(transferable_resource.size);
frame.resource_list.push_back(transferable_resource);
frame.render_pass_list.push_back(std::move(render_pass));
frame_sink_holder_->SubmitCompositorFrame(
std::move(frame), transferable_resource.id, std::move(resource));
}
void ViewTreeHostRootView::SubmitPendingCompositorFrame() {
if (pending_resource_ && !pending_compositor_frame_ack_)
SubmitCompositorFrame();
}
void ViewTreeHostRootView::DidReceiveCompositorFrameAck() {
pending_compositor_frame_ack_ = false;
if (pending_resource_) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&ViewTreeHostRootView::SubmitPendingCompositorFrame,
weak_ptr_factory_.GetWeakPtr()));
}
}
void ViewTreeHostRootView::DidPresentCompositorFrame(
const gfx::PresentationFeedback& feedback) {
if (!presentation_callback_.is_null())
presentation_callback_.Run(feedback);
}
void ViewTreeHostRootView::ReclaimResource(std::unique_ptr<Resource> resource) {
if (resource_group_id_ == resource->group_id)
returned_resources_.push_back(std::move(resource));
return frame;
}
} // namespace ash

@ -8,30 +8,34 @@
#include <memory>
#include <vector>
#include "ash/ash_export.h"
#include "ash/frame_sink/frame_sink_host.h"
#include "base/memory/weak_ptr.h"
#include "components/viz/common/quads/compositor_frame_metadata.h"
#include "components/viz/common/resources/resource_id.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "ui/views/widget/root_view.h"
namespace gfx {
struct PresentationFeedback;
}
namespace views {
class Widget;
}
} // namespace views
namespace viz {
class CompositorFrame;
} // namespace viz
namespace gfx {
class Size;
class Rect;
} // namespace gfx
namespace ash {
class ViewTreeHostRootViewFrameFactory;
// ViewTreeHostRootView is a view that submits a compositor frame directly.
// TODO(oshima): Support partial content update & front buffer rendering and
// replace FastInkView.
class ViewTreeHostRootView : public views::internal::RootView {
class ASH_EXPORT ViewTreeHostRootView : public views::internal::RootView,
public ash::FrameSinkHost {
public:
using PresentationCallback =
base::RepeatingCallback<void(const gfx::PresentationFeedback&)>;
explicit ViewTreeHostRootView(views::Widget* widget);
ViewTreeHostRootView(const ViewTreeHostRootView&) = delete;
@ -39,60 +43,25 @@ class ViewTreeHostRootView : public views::internal::RootView {
~ViewTreeHostRootView() override;
// Set presentation callback.
void set_presentation_callback(PresentationCallback callback) {
presentation_callback_ = std::move(callback);
}
void SchedulePaintInRect(const gfx::Rect& rect);
void SchedulePaintInRect(const gfx::Rect& damage_rect);
bool GetIsOverlayCandidate();
void SetIsOverlayCandidate(bool is_overlay_candidate);
// ash::FrameSinkHost:
std::unique_ptr<viz::CompositorFrame> CreateCompositorFrame(
const viz::BeginFrameAck& begin_frame_ack,
UiResourceManager& resource_manager,
bool auto_update,
const gfx::Size& last_submitted_frame_size,
float last_submitted_frame_dsf) override;
private:
struct Resource;
class LayerTreeViewTreeFrameSinkHolder;
std::unique_ptr<Resource> ObtainResource();
void Paint();
// Update content with the |resource| and damage rectangles for surface.
void UpdateSurface(const gfx::Rect& damage_rect,
std::unique_ptr<Resource> resource);
void SubmitCompositorFrame();
void SubmitPendingCompositorFrame();
void ReclaimResource(std::unique_ptr<Resource> resource);
void DidReceiveCompositorFrameAck();
void DidPresentCompositorFrame(const gfx::PresentationFeedback& feedback);
// Constants initialized in constructor.
PresentationCallback presentation_callback_;
// The rotation tranfrom from the panel's original rotation to
// the current logical rotation.
gfx::Transform rotate_transform_;
// GPU Memory buffer size.
gfx::Size buffer_size_;
gfx::Rect damaged_paint_rect_;
bool pending_paint_ = false;
// overlay candidate in submitted frame data.
// True if the content needs to use hardware-overlays.
bool is_overlay_candidate_ = true;
// The resource to be submitted.
std::unique_ptr<Resource> pending_resource_;
std::unique_ptr<ViewTreeHostRootViewFrameFactory> frame_factory_;
int resource_group_id_ = 1;
viz::ResourceIdGenerator id_generator_;
// Total damaged rect in surface.
gfx::Rect damage_rect_;
bool pending_compositor_frame_ack_ = false;
viz::FrameTokenGenerator next_frame_token_;
std::vector<std::unique_ptr<Resource>> returned_resources_;
std::unique_ptr<LayerTreeViewTreeFrameSinkHolder> frame_sink_holder_;
base::WeakPtrFactory<ViewTreeHostRootView> weak_ptr_factory_{this};
};

@ -0,0 +1,329 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/fast_ink/view_tree_host_root_view_frame_factory.h"
#include "ash/frame_sink/frame_sink_host.h"
#include "ash/frame_sink/ui_resource.h"
#include "base/check.h"
#include "base/logging.h"
#include "base/trace_event/trace_event.h"
#include "cc/paint/display_item_list.h"
#include "components/viz/common/gpu/context_provider.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/quads/texture_draw_quad.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "components/viz/common/resources/resource_id.h"
#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "ui/aura/env.h"
#include "ui/aura/window_tree_host.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/paint_context.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/gfx/gpu_memory_buffer.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
constexpr viz::ResourceFormat kResourceFormat =
SK_B32_SHIFT ? viz::RGBA_8888 : viz::BGRA_8888;
constexpr uint32_t kUiSourceId = 1u;
} // namespace
// -----------------------------------------------------------------------------
// ViewTreeHostUiResource:
ViewTreeHostUiResource::ViewTreeHostUiResource() = default;
ViewTreeHostUiResource::~ViewTreeHostUiResource() = default;
// -----------------------------------------------------------------------------
// ViewTreeHostRootViewFrameFactory:
ViewTreeHostRootViewFrameFactory::ViewTreeHostRootViewFrameFactory(
views::Widget* widget)
: widget_(widget) {}
// static
std::unique_ptr<ViewTreeHostUiResource>
ViewTreeHostRootViewFrameFactory::CreateUiResource(const gfx::Size& size,
viz::ResourceFormat format,
UiSourceId ui_source_id,
bool is_overlay_candidate) {
DCHECK(!size.IsEmpty());
DCHECK(ui_source_id > 0);
auto resource = std::make_unique<ViewTreeHostUiResource>();
resource->gpu_memory_buffer =
aura::Env::GetInstance()
->context_factory()
->GetGpuMemoryBufferManager()
->CreateGpuMemoryBuffer(size, viz::BufferFormat(format),
gfx::BufferUsage::SCANOUT_CPU_READ_WRITE,
gpu::kNullSurfaceHandle, nullptr);
if (!resource->gpu_memory_buffer) {
LOG(ERROR) << "Failed to create GPU memory buffer";
return nullptr;
}
resource->context_provider = aura::Env::GetInstance()
->context_factory()
->SharedMainThreadContextProvider();
if (!resource->context_provider) {
LOG(ERROR) << "Failed to acquire a context provider";
return nullptr;
}
gpu::SharedImageInterface* sii =
resource->context_provider->SharedImageInterface();
uint32_t usage = gpu::SHARED_IMAGE_USAGE_DISPLAY_READ;
if (is_overlay_candidate) {
usage |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
}
gpu::GpuMemoryBufferManager* gmb_manager =
aura::Env::GetInstance()->context_factory()->GetGpuMemoryBufferManager();
resource->mailbox = sii->CreateSharedImage(
resource->gpu_memory_buffer.get(), gmb_manager, gfx::ColorSpace(),
kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage);
resource->sync_token = sii->GenVerifiedSyncToken();
resource->damaged = true;
resource->is_overlay_candidate = is_overlay_candidate;
resource->format = format;
resource->ui_source_id = ui_source_id;
resource->resource_size = size;
return resource;
}
std::unique_ptr<viz::CompositorFrame>
ViewTreeHostRootViewFrameFactory::CreateCompositorFrame(
const viz::BeginFrameAck& begin_frame_ack,
const gfx::Rect& content_rect,
const gfx::Rect& total_damage_rect,
bool use_overlays,
UiResourceManager& resource_manager) {
auto* window = widget_->GetNativeView();
float device_scale_factor = window->GetHost()->device_scale_factor();
// We apply the target transform during the buffer update instead of leaving
// it to the display compositor. The benefit is that it significantly reduces
// the hardware overlay requirements. Frames are submitted to the compositor
// with the inverse transform to cancel out the transformation that would
// otherwise be done by the compositor.
gfx::Size buffer_size =
gfx::ScaleToCeiledSize(content_rect.size(), device_scale_factor);
display::Display display =
display::Screen::GetScreen()->GetDisplayNearestWindow(window);
if (display.panel_rotation() == display::Display::ROTATE_90 ||
display.panel_rotation() == display::Display::ROTATE_270) {
buffer_size.Transpose();
}
// The rotation transform from the panel's original rotation to
// the current logical rotation.
gfx::Transform rotation_transform;
switch (display.panel_rotation()) {
case display::Display::ROTATE_0:
break;
case display::Display::ROTATE_90:
rotation_transform.Translate(buffer_size.width(), 0);
rotation_transform.Rotate(90);
break;
case display::Display::ROTATE_180:
rotation_transform.Translate(buffer_size.width(), buffer_size.height());
rotation_transform.Rotate(180);
break;
case display::Display::ROTATE_270:
rotation_transform.Translate(0, buffer_size.height());
rotation_transform.Rotate(270);
break;
}
auto resource =
AcquireUiResource(buffer_size, use_overlays, resource_manager);
if (!resource) {
return nullptr;
}
auto& resource_buffer = resource->gpu_memory_buffer;
DCHECK(resource_buffer);
Paint(total_damage_rect, rotation_transform, resource_buffer.get());
if (resource->damaged) {
DCHECK(resource->context_provider);
gpu::SharedImageInterface* sii =
resource->context_provider->SharedImageInterface();
sii->UpdateSharedImage(resource->sync_token, resource->mailbox);
resource->sync_token = sii->GenVerifiedSyncToken();
resource->damaged = false;
}
viz::ResourceId frame_resource_id =
resource_manager.OfferResource(std::move(resource));
viz::TransferableResource transferable_resource =
resource_manager.PrepareResourceForExport(frame_resource_id);
auto frame = std::make_unique<viz::CompositorFrame>();
frame->metadata.begin_frame_ack = begin_frame_ack;
frame->metadata.begin_frame_ack.has_damage = true;
frame->metadata.device_scale_factor = device_scale_factor;
// TODO(crbug.com/1131623): Should this be ceil? Why do we choose floor?
gfx::Size size_in_pixel = gfx::ToFlooredSize(
gfx::ConvertSizeToPixels(content_rect.size(), device_scale_factor));
gfx::Rect output_rect(size_in_pixel);
gfx::Rect damage_rect;
// TODO(oshima): Support partial content update.
damage_rect = gfx::ToEnclosingRect(
gfx::ConvertRectToPixels(total_damage_rect, device_scale_factor));
// To ensure that the damage_rect is not bigger than the output_rect. We can
// have 1px off errors when converting from dip to pixel values for certain
// device scale factor values what can lead the damage_rect to be bigger than
// output_rect.
damage_rect.Intersect(output_rect);
auto render_pass = viz::CompositorRenderPass::Create(
/*shared_quad_state_list_size=*/1, /*quad_list_size=*/1);
gfx::Transform buffer_to_target_transform =
rotation_transform.GetCheckedInverse();
render_pass->SetNew(viz::CompositorRenderPassId{1}, output_rect, damage_rect,
buffer_to_target_transform);
AppendQuad(*render_pass, transferable_resource, output_rect, buffer_size,
buffer_to_target_transform);
frame->resource_list.push_back(transferable_resource);
frame->render_pass_list.push_back(std::move(render_pass));
return frame;
}
void ViewTreeHostRootViewFrameFactory::Paint(
const gfx::Rect& invalidation_rect,
const gfx::Transform& rotate_transform,
gfx::GpuMemoryBuffer* gpu_buffer) {
auto display_item_list = base::MakeRefCounted<cc::DisplayItemList>();
float dsf = widget_->GetCompositor()->device_scale_factor();
ui::PaintContext context(display_item_list.get(), dsf, invalidation_rect,
/*is_pixel_canvas=*/true);
widget_->OnNativeWidgetPaint(context);
display_item_list->Finalize();
if (!gpu_buffer->Map()) {
TRACE_EVENT0("ui", "ViewTreeHostRootView::Paint::Map");
LOG(ERROR) << "Failed to map GPU memory buffer";
return;
}
SkImageInfo info = SkImageInfo::MakeN32Premul(gpu_buffer->GetSize().width(),
gpu_buffer->GetSize().height());
uint8_t* data = static_cast<uint8_t*>(gpu_buffer->memory(0));
int stride = gpu_buffer->stride(0);
std::unique_ptr<SkCanvas> canvas =
SkCanvas::MakeRasterDirect(info, data, stride);
canvas->setMatrix(gfx::TransformToFlattenedSkMatrix(rotate_transform));
display_item_list->Raster(canvas.get());
TRACE_EVENT0("ui", "ViewTreeHostRootView::Paint::Unmap");
// Unmap to flush writes to buffer.
gpu_buffer->Unmap();
}
void ViewTreeHostRootViewFrameFactory::AppendQuad(
viz::CompositorRenderPass& render_pass,
const viz::TransferableResource& resource,
const gfx::Rect& output_rect,
const gfx::Size& buffer_size,
const gfx::Transform& buffer_to_target_transform) const {
viz::SharedQuadState* quad_state =
render_pass.CreateAndAppendSharedQuadState();
quad_state->SetAll(buffer_to_target_transform,
/*layer_rect=*/output_rect,
/*visible_layer_rect=*/output_rect,
/*filter_info=*/gfx::MaskFilterInfo(),
/*clip=*/absl::nullopt, /*contents_opaque=*/false,
/*opacity_f=*/1.f,
/*blend=*/SkBlendMode::kSrcOver,
/*sorting_context=*/0);
gfx::Rect quad_rect = gfx::Rect(buffer_size);
viz::TextureDrawQuad* texture_quad =
render_pass.CreateAndAppendDrawQuad<viz::TextureDrawQuad>();
static constexpr float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
gfx::RectF uv_crop(quad_rect);
uv_crop.Scale(1.f / buffer_size.width(), 1.f / buffer_size.height());
texture_quad->SetNew(
quad_state, quad_rect, quad_rect,
/*needs_blending=*/true, resource.id,
/*premultiplied=*/true, uv_crop.origin(), uv_crop.bottom_right(),
SkColors::kTransparent, vertex_opacity,
/*flipped=*/false,
/*nearest=*/false,
/*secure_output=*/false, gfx::ProtectedVideoType::kClear);
texture_quad->set_resource_size_in_pixels(resource.size);
}
std::unique_ptr<ViewTreeHostUiResource>
ViewTreeHostRootViewFrameFactory::AcquireUiResource(
const gfx::Size& size,
bool is_overlay_candidate,
UiResourceManager& resource_manager) const {
viz::ResourceId reusable_resource_id =
resource_manager.FindResourceToReuse(size, kResourceFormat, kUiSourceId);
std::unique_ptr<ViewTreeHostUiResource> resource;
if (reusable_resource_id != viz::kInvalidResourceId) {
resource = base::WrapUnique(static_cast<ViewTreeHostUiResource*>(
resource_manager.ReleaseAvailableResource(reusable_resource_id)
.release()));
} else {
resource = CreateUiResource(size, kResourceFormat, kUiSourceId,
is_overlay_candidate);
}
return resource;
}
} // namespace ash

@ -0,0 +1,98 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_FAST_INK_VIEW_TREE_HOST_ROOT_VIEW_FRAME_FACTORY_H_
#define ASH_FAST_INK_VIEW_TREE_HOST_ROOT_VIEW_FRAME_FACTORY_H_
#include <memory>
#include <vector>
#include "ash/ash_export.h"
#include "ash/frame_sink/ui_resource.h"
#include "base/memory/raw_ptr.h"
#include "components/viz/common/quads/compositor_frame.h"
namespace viz {
class CompositorFrame;
} // namespace viz
namespace views {
class Widget;
} // namespace views
namespace gfx {
class GpuMemoryBuffer;
class Size;
} // namespace gfx
namespace ash {
class UiResourceManager;
class ViewTreeHostUiResource : public UiResource {
public:
ViewTreeHostUiResource();
ViewTreeHostUiResource(const ViewTreeHostUiResource&) = delete;
ViewTreeHostUiResource& operator=(const ViewTreeHostUiResource&) = delete;
~ViewTreeHostUiResource() override;
std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
};
class ASH_EXPORT ViewTreeHostRootViewFrameFactory {
public:
explicit ViewTreeHostRootViewFrameFactory(views::Widget* widget);
ViewTreeHostRootViewFrameFactory(const ViewTreeHostRootViewFrameFactory&) =
delete;
ViewTreeHostRootViewFrameFactory& operator=(
const ViewTreeHostRootViewFrameFactory&) = delete;
~ViewTreeHostRootViewFrameFactory() = default;
// Creates a ViewTreeHostUiResource of a given `size` and `format`. We draw
// the textures of view tree host by `widget` into the gpu buffer associated
// with the resource and attach it to a compositor frame by converting it into
// a transferable resource. Note: This method is also used in unittests.
static std::unique_ptr<ViewTreeHostUiResource> CreateUiResource(
const gfx::Size& size,
viz::ResourceFormat format,
UiSourceId ui_source_id,
bool is_overlay_candidate);
// Creates and configures a compositor frame.
std::unique_ptr<viz::CompositorFrame> CreateCompositorFrame(
const viz::BeginFrameAck& begin_frame_ack,
const gfx::Rect& content_rect,
const gfx::Rect& total_damage_rect,
bool use_overlays,
UiResourceManager& resource_manager);
private:
void Paint(const gfx::Rect& invalidation_rect,
const gfx::Transform& rotate_transform,
gfx::GpuMemoryBuffer* gpu_buffer);
// Configures and adds a `TextureDrawQuad` to the `render_pass`.
void AppendQuad(viz::CompositorRenderPass& render_pass,
const viz::TransferableResource& resource,
const gfx::Rect& output_rect,
const gfx::Size& buffer_size,
const gfx::Transform& buffer_to_target_transform) const;
// Get a ViewTreeHostUiResource to paint the texture. We try to reuse any
// existing resources in `resource_manager` before creating a new resource.
std::unique_ptr<ViewTreeHostUiResource> AcquireUiResource(
const gfx::Size& size,
bool is_overlay_candidate,
UiResourceManager& resource_manager) const;
base::raw_ptr<views::Widget> widget_;
};
} // namespace ash
#endif // ASH_FAST_INK_VIEW_TREE_HOST_ROOT_VIEW_FRAME_FACTORY_H_

@ -0,0 +1,282 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/fast_ink/view_tree_host_root_view_frame_factory.h"
#include <memory>
#include <utility>
#include <vector>
#include "ash/frame_sink/ui_resource.h"
#include "ash/frame_sink/ui_resource_manager.h"
#include "ash/test/ash_test_base.h"
#include "base/memory/raw_ptr.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/resources/resource_id.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
constexpr viz::ResourceFormat kTestResourceFormat =
SK_B32_SHIFT ? viz::RGBA_8888 : viz::BGRA_8888;
constexpr UiSourceId kTestSourceId = 1u;
constexpr gfx::Rect kTestContentRect = gfx::Rect(0, 0, 200, 100);
constexpr gfx::Rect kTestTotalDamageRect = gfx::Rect(0, 0, 50, 25);
class ViewTreeHostRootViewFrameFactoryTest : public AshTestBase {
public:
ViewTreeHostRootViewFrameFactoryTest() = default;
ViewTreeHostRootViewFrameFactoryTest(
const ViewTreeHostRootViewFrameFactoryTest&) = delete;
ViewTreeHostRootViewFrameFactoryTest& operator=(
const ViewTreeHostRootViewFrameFactoryTest&) = delete;
~ViewTreeHostRootViewFrameFactoryTest() override = default;
// AshTestBase:
void SetUp() override {
AshTestBase::SetUp();
widget_ = CreateTestWidget(nullptr, kShellWindowId_OverlayContainer,
gfx::Rect(0, 0, 200, 100));
factory_ =
std::make_unique<ViewTreeHostRootViewFrameFactory>(widget_.get());
}
// AshTestBase:
void TearDown() override {
resource_manager_.ClearAvailableResources();
resource_manager_.LostExportedResources();
AshTestBase::TearDown();
}
protected:
std::unique_ptr<ViewTreeHostRootViewFrameFactory> factory_;
UiResourceManager resource_manager_;
std::unique_ptr<views::Widget> widget_;
};
TEST_F(ViewTreeHostRootViewFrameFactoryTest,
CompositorFrameHasCorrectStructure) {
UpdateDisplay("1920x1080");
auto frame = factory_->CreateCompositorFrame(
viz::BeginFrameAck::CreateManualAckWithDamage(), kTestContentRect,
kTestTotalDamageRect,
/*use_overlays=*/true, resource_manager_);
auto primary_display = display::Screen::GetScreen()->GetPrimaryDisplay();
// We should only have the root render pass.
EXPECT_EQ(frame->render_pass_list.size(), 1u);
// Frame size should be the size of content_rect in pixels.
EXPECT_EQ(frame->size_in_pixels(), gfx::Size(200, 100));
// We should have a single resource.
EXPECT_EQ(frame->resource_list.size(), 1u);
EXPECT_EQ(resource_manager_.exported_resources_count(), 1u);
auto& quad_list = frame->render_pass_list.front()->quad_list;
// We should have created a single quad.
EXPECT_EQ(quad_list.size(), 1u);
auto& shared_quad_state_list =
frame->render_pass_list.front()->shared_quad_state_list;
// We should create a single shared_quad_state.
EXPECT_EQ(shared_quad_state_list.size(), 1u);
EXPECT_EQ(frame->device_scale_factor(),
primary_display.device_scale_factor());
}
TEST_F(ViewTreeHostRootViewFrameFactoryTest, HasValidSourceId) {
UpdateDisplay("1920x1080*2");
auto frame = factory_->CreateCompositorFrame(
viz::BeginFrameAck::CreateManualAckWithDamage(), kTestContentRect,
kTestTotalDamageRect,
/*use_overlays=*/true, resource_manager_);
ASSERT_EQ(frame->resource_list.size(), 1u);
viz::ResourceId resource_id = frame->resource_list.back().id;
EXPECT_NE(resource_manager_.PeekExportedResource(resource_id)->ui_source_id,
kInvalidUiSourceId);
}
TEST_F(ViewTreeHostRootViewFrameFactoryTest, FrameDamage) {
UpdateDisplay("1920x1080*2");
auto frame = factory_->CreateCompositorFrame(
viz::BeginFrameAck::CreateManualAckWithDamage(), kTestContentRect,
kTestTotalDamageRect,
/*use_overlays=*/true, resource_manager_);
EXPECT_EQ(frame->render_pass_list.front()->damage_rect,
gfx::Rect(0, 0, 100, 50));
// If total damage is more than content_rect, we crop the damage to
// content_rect.
frame = factory_->CreateCompositorFrame(
viz::BeginFrameAck::CreateManualAckWithDamage(), kTestContentRect,
gfx::Rect(0, 0, 201, 101),
/*use_overlays=*/true, resource_manager_);
EXPECT_EQ(frame->render_pass_list.front()->damage_rect,
gfx::Rect(0, 0, 400, 200));
}
TEST_F(ViewTreeHostRootViewFrameFactoryTest, CorrectQuadConfigured) {
UpdateDisplay("1920x1080*2");
auto frame = factory_->CreateCompositorFrame(
viz::BeginFrameAck::CreateManualAckWithDamage(), kTestContentRect,
kTestTotalDamageRect,
/*use_overlays=*/true, resource_manager_);
auto& quad_list = frame->render_pass_list.front()->quad_list;
ASSERT_EQ(quad_list.size(), 1u);
viz::DrawQuad* quad = quad_list.back();
EXPECT_EQ(quad->material, viz::DrawQuad::Material::kTextureContent);
// Both should be same as content_rect in pixels.
EXPECT_EQ(quad->rect, gfx::Rect(400, 200));
EXPECT_EQ(quad->visible_rect, gfx::Rect(400, 200));
auto* shared_quad_state =
frame->render_pass_list.front()->shared_quad_state_list.front();
EXPECT_EQ(shared_quad_state->quad_layer_rect, gfx::Rect(400, 200));
EXPECT_EQ(shared_quad_state->visible_quad_layer_rect, gfx::Rect(400, 200));
}
class ViewTreeHostRootViewFrameResourceTest
: public ViewTreeHostRootViewFrameFactoryTest,
public ::testing::WithParamInterface<
std::tuple<std::string, gfx::Rect, gfx::Size>> {
public:
ViewTreeHostRootViewFrameResourceTest()
: display_spec_(std::get<0>(GetParam())),
content_rect_(std::get<1>(GetParam())),
expected_resource_size_(std::get<2>(GetParam())) {}
ViewTreeHostRootViewFrameResourceTest(
const ViewTreeHostRootViewFrameResourceTest&) = delete;
ViewTreeHostRootViewFrameResourceTest& operator=(
const ViewTreeHostRootViewFrameResourceTest&) = delete;
~ViewTreeHostRootViewFrameResourceTest() override = default;
protected:
std::string display_spec_;
gfx::Rect content_rect_;
gfx::Size expected_resource_size_;
};
TEST_P(ViewTreeHostRootViewFrameResourceTest, CorrectResourceCreated) {
UpdateDisplay(display_spec_);
auto frame = factory_->CreateCompositorFrame(
viz::BeginFrameAck::CreateManualAckWithDamage(), content_rect_,
content_rect_,
/*use_overlays=*/true, resource_manager_);
auto primary_display = display::Screen::GetScreen()->GetPrimaryDisplay();
ASSERT_EQ(frame->resource_list.size(), 1u);
auto& resource = frame->resource_list.back();
EXPECT_EQ(resource.size, expected_resource_size_);
}
INSTANTIATE_TEST_SUITE_P(
/* no prefix */,
ViewTreeHostRootViewFrameResourceTest,
testing::Values(
std::make_tuple("500x400",
/*content_rect=*/gfx::Rect(200, 100),
/*expected_resource_size=*/gfx::Size(200, 100)),
std::make_tuple("500x400*2",
/*content_rect=*/gfx::Rect(200, 100),
/*expected_resource_size=*/gfx::Size(400, 200)),
// Display is rotated by 90 degrees clockwise.
std::make_tuple("500x400/r",
/*content_rect=*/gfx::Rect(200, 100),
/*expected_resource_size=*/gfx::Size(100, 200)),
// Display is rotated by 180 degrees clockwise.
std::make_tuple("500x400/u",
/*content_rect=*/gfx::Rect(200, 100),
/*expected_resource_size=*/gfx::Size(200, 100)),
// Display is rotated by 270 degrees clockwise.
std::make_tuple("500x400/l",
/*content_rect=*/gfx::Rect(200, 100),
/*expected_resource_size=*/gfx::Size(100, 200)),
// Display is rotated by 90 degrees clockwise and has device scale
// factor of 2.
std::make_tuple("500x400*2/r",
/*content_rect=*/gfx::Rect(200, 100),
/*expected_resource_size=*/gfx::Size(200, 400))));
TEST_F(ViewTreeHostRootViewFrameFactoryTest,
OnlyCreateNewResourcesWhenNecessary) {
// Populate resources in the resource manager.
constexpr gfx::Size kResourceSizes[4] = {
{200, 100}, {200, 100}, {250, 150}, {50, 25}};
for (const auto& size : kResourceSizes) {
resource_manager_.OfferResource(
ViewTreeHostRootViewFrameFactory::CreateUiResource(
size, kTestResourceFormat, kTestSourceId,
/*is_overlay_candidate=*/false));
}
EXPECT_EQ(resource_manager_.available_resources_count(), 4u);
UpdateDisplay("1920x1080");
auto frame = factory_->CreateCompositorFrame(
viz::BeginFrameAck::CreateManualAckWithDamage(), kTestContentRect,
kTestTotalDamageRect,
/*use_overlays=*/true, resource_manager_);
// We reuse one of the matching available resources.
EXPECT_EQ(resource_manager_.available_resources_count(), 3u);
EXPECT_EQ(resource_manager_.exported_resources_count(), 1u);
frame = factory_->CreateCompositorFrame(
viz::BeginFrameAck::CreateManualAckWithDamage(), kTestContentRect,
kTestTotalDamageRect,
/*use_overlays=*/true, resource_manager_);
// We again reuse one of the matching available resources.
EXPECT_EQ(resource_manager_.available_resources_count(), 2u);
EXPECT_EQ(resource_manager_.exported_resources_count(), 2u);
frame = factory_->CreateCompositorFrame(
viz::BeginFrameAck::CreateManualAckWithDamage(), kTestContentRect,
kTestTotalDamageRect,
/*use_overlays=*/true, resource_manager_);
// Now the factory create a new resource since any available resource does not
// match our requirements. The total number of resources in the manager has
// increased by 1.
EXPECT_EQ(resource_manager_.available_resources_count(), 2u);
EXPECT_EQ(resource_manager_.exported_resources_count(), 3u);
frame = factory_->CreateCompositorFrame(
viz::BeginFrameAck::CreateManualAckWithDamage(), gfx::Rect(50, 25),
kTestTotalDamageRect,
/*use_overlays=*/true, resource_manager_);
// We do not create a resource since there is an available resource for the
// new needed size.
EXPECT_EQ(resource_manager_.available_resources_count(), 1u);
EXPECT_EQ(resource_manager_.exported_resources_count(), 4u);
}
} // namespace
} // namespace ash

@ -147,8 +147,11 @@ void HUDDisplayView::Toggle() {
params.bounds = gfx::Rect(kHUDWidth, kHUDHeightWithGraph);
auto* widget = CreateViewTreeHostWidget(std::move(params));
widget->GetLayer()->SetName("HUDDisplayView");
static_cast<ViewTreeHostRootView*>(widget->GetRootView())
->SetIsOverlayCandidate(g_hud_overlay_mode);
ViewTreeHostRootView* root_view =
static_cast<ViewTreeHostRootView*>(widget->GetRootView());
root_view->SetIsOverlayCandidate(g_hud_overlay_mode);
root_view->Init(widget->GetNativeView());
widget->Show();
g_hud_widget = widget;