0

[Rounded Display] Specifies rounded-display masks info in

RoundedDisplayHost.

Encodes the information of rounded-display masks in the draw quads by
setting the RoundedDisplayMaskInfo property on TextureDrawQuads.

Bug: 1262970
Test: Covered by unittests
Change-Id: Iee45ec5ad3e03d792e6294ecb8ed7a82a170b75e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4326151
Reviewed-by: Sean Kau <skau@chromium.org>
Commit-Queue: Zoraiz Naeem <zoraiznaeem@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1121981}
This commit is contained in:
Zoraiz Naeem
2023-03-24 22:44:56 +00:00
committed by Chromium LUCI CQ
parent 645d60c90b
commit e03454cee5
3 changed files with 191 additions and 37 deletions

@ -4,12 +4,15 @@
#include "ash/rounded_display/rounded_display_frame_factory.h"
#include <algorithm>
#include <array>
#include <memory>
#include <vector>
#include "ash/frame_sink/ui_resource.h"
#include "ash/frame_sink/ui_resource_manager.h"
#include "ash/rounded_display/rounded_display_gutter.h"
#include "base/check.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "components/viz/common/quads/compositor_frame.h"
@ -30,6 +33,7 @@
#include "ui/display/screen.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/gfx/gpu_memory_buffer.h"
@ -37,6 +41,8 @@
namespace ash {
namespace {
using RoundedCorner = RoundedDisplayGutter::RoundedCorner;
constexpr viz::ResourceFormat kResourceFormat =
SK_B32_SHIFT ? viz::RGBA_8888 : viz::BGRA_8888;
@ -53,6 +59,46 @@ gfx::Transform GetRootRotationTransform(const aura::Window& host_window) {
return root_rotation_transform;
}
viz::TextureDrawQuad::RoundedDisplayMasksInfo MapToRoundedDisplayMasksInfo(
const std::vector<RoundedCorner>& corners) {
DCHECK(corners.size() <= 2) << "Currently, viz can only handle textures that "
"have up to 2 corner masks drawn into them";
if (corners.size() == 1) {
return viz::TextureDrawQuad::RoundedDisplayMasksInfo::
CreateRoundedDisplayMasksInfo(corners.back().radius(), 0,
/*is_horizontally_positioned=*/true);
}
std::array<const RoundedCorner*, 2> sorted_corners = {&corners.at(0),
&corners.at(1)};
std::sort(sorted_corners.begin(), sorted_corners.end(),
[](const RoundedCorner* c1, const RoundedCorner* c2) {
return c1->bounds().origin() < c2->bounds().origin();
});
const RoundedDisplayGutter::RoundedCorner& first_corner =
*sorted_corners.at(0);
const RoundedDisplayGutter::RoundedCorner& second_corner =
*sorted_corners.at(1);
// Corners of a gutter need to be either vertically or horizontally
// aligned.
DCHECK(first_corner.bounds().x() == second_corner.bounds().x() ||
first_corner.bounds().y() == second_corner.bounds().y());
DCHECK(!first_corner.bounds().Intersects(second_corner.bounds()));
bool is_horizontally_positioned =
first_corner.bounds().y() == second_corner.bounds().y();
return viz::TextureDrawQuad::RoundedDisplayMasksInfo::
CreateRoundedDisplayMasksInfo(first_corner.radius(),
second_corner.radius(),
is_horizontally_positioned);
}
} // namespace
// -----------------------------------------------------------------------------
@ -200,8 +246,7 @@ RoundedDisplayFrameFactory::CreateCompositorFrame(
viz::TransferableResource transferable_resource =
resource_manager.PrepareResourceForExport(resource_id);
AppendQuad(transferable_resource, gutter->bounds().size(),
gutter->bounds().size(), buffer_to_target_transform,
AppendQuad(transferable_resource, buffer_to_target_transform, *gutter,
*render_pass);
frame->resource_list.push_back(std::move(transferable_resource));
@ -262,41 +307,49 @@ void RoundedDisplayFrameFactory::Paint(const RoundedDisplayGutter& gutter,
void RoundedDisplayFrameFactory::AppendQuad(
const viz::TransferableResource& resource,
const gfx::Size& gutter_size,
const gfx::Size& buffer_size,
const gfx::Transform& buffer_to_target_transform,
const RoundedDisplayGutter& gutter,
viz::CompositorRenderPass& render_pass_out) const {
gfx::Rect output_rect(gutter_size);
const gfx::Size& gutter_size_in_pixels = gutter.bounds().size();
// Each gutter can be thought of as a single ui::Layer that produces only one
// quad. Therefore the layer_rect and visible_layer_rect is the size of the
// `gutter_size_in_pixels`. (layer is the same size as the texture produced
// and it is all visible)
viz::SharedQuadState* quad_state =
render_pass_out.CreateAndAppendSharedQuadState();
quad_state->SetAll(buffer_to_target_transform,
/*layer_rect=*/output_rect,
/*visible_layer_rect=*/output_rect,
/*layer_rect=*/gfx::Rect(gutter_size_in_pixels),
/*visible_layer_rect=*/gfx::Rect(gutter_size_in_pixels),
/*filter_info=*/gfx::MaskFilterInfo(),
/*clip=*/absl::nullopt, /*contents_opaque=*/false,
/*opacity_f=*/1.f,
/*blend=*/SkBlendMode::kSrcOver,
/*sorting_context=*/0);
gfx::Rect quad_rect(buffer_size);
viz::TextureDrawQuad* texture_quad =
render_pass_out.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, resource.id,
/*premultiplied=*/true, uv_crop.origin(), uv_crop.bottom_right(),
/*background=*/SkColors::kTransparent, vertex_opacity,
/*flipped=*/false,
/*nearest=*/false,
/*secure_output=*/false, gfx::ProtectedVideoType::kClear);
constexpr float kVertexOpacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
// Each frame, we re-render the full texture therefore quad_rect is the size
// of the texture i.e `gutter_size_in_pixels`.
gfx::Rect quad_rect(gutter_size_in_pixels);
texture_quad->SetNew(quad_state, quad_rect, quad_rect,
/*needs_blending=*/true, resource.id,
/*premultiplied=*/true, gfx::RectF(quad_rect).origin(),
gfx::RectF(quad_rect).bottom_right(),
/*background=*/SkColors::kTransparent, kVertexOpacity,
/*flipped=*/false,
/*nearest=*/false,
/*secure_output=*/false,
gfx::ProtectedVideoType::kClear);
texture_quad->set_resource_size_in_pixels(resource.size);
texture_quad->rounded_display_masks_info =
MapToRoundedDisplayMasksInfo(gutter.GetGutterCorners());
}
} // namespace ash

@ -74,9 +74,8 @@ class ASH_EXPORT RoundedDisplayFrameFactory {
private:
// Configures and appends a `TextureDrawQuad` to the `render_pass`.
void AppendQuad(const viz::TransferableResource& resource,
const gfx::Size& gutter_size,
const gfx::Size& buffer_size,
const gfx::Transform& buffer_to_target_transform,
const RoundedDisplayGutter& gutter,
viz::CompositorRenderPass& render_pass_out) const;
// Get a UiResource for the `gutter`. We try to reuse any existing resources

@ -13,7 +13,10 @@
#include "ash/test/ash_test_base.h"
#include "ash/test/ash_test_helper.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/quads/quad_list.h"
#include "components/viz/common/quads/texture_draw_quad.h"
#include "rounded_display_gutter_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/window_tree_host.h"
#include "ui/compositor/layer_type.h"
@ -25,6 +28,9 @@ namespace {
constexpr viz::ResourceFormat kTestResourceFormat =
SK_B32_SHIFT ? viz::RGBA_8888 : viz::BGRA_8888;
constexpr gfx::Size kTestDisplaySize(1920, 1080);
constexpr gfx::RoundedCornersF kTestPanelRadii(10);
using RoundedDisplayMasksInfo = viz::TextureDrawQuad::RoundedDisplayMasksInfo;
class RoundedDisplayFrameFactoryTest : public AshTestBase {
public:
@ -48,9 +54,6 @@ class RoundedDisplayFrameFactoryTest : public AshTestBase {
auto* root_window = ash_test_helper()->GetHost()->window();
root_window->AddChild(host_window_.get());
gutters_ = CreateGutters(kTestDisplaySize, gfx::RoundedCornersF(10),
/*create_vertical_gutters=*/true);
}
// AshTestBase:
@ -73,27 +76,39 @@ class RoundedDisplayFrameFactoryTest : public AshTestBase {
return gutters;
}
std::vector<std::unique_ptr<RoundedDisplayGutter>> CreateGutters(
const gfx::Size& display_size_in_pixels,
const gfx::RoundedCornersF& display_radii,
bool create_vertical_gutters) {
std::vector<std::unique_ptr<RoundedDisplayGutter>> gutters;
// Creates vertical gutters and appends them to `gutters_`.
void AppendVerticalOverlayGutters(const gfx::Size& display_size_in_pixels,
const gfx::RoundedCornersF& panel_radii) {
auto overlay_gutters = gutter_factory_->CreateOverlayGutters(
display_size_in_pixels, display_radii, create_vertical_gutters);
display_size_in_pixels, panel_radii,
/*create_vertical_gutters=*/true);
for (auto& gutter : overlay_gutters) {
gutters.push_back(std::move(gutter));
gutters_.push_back(std::move(gutter));
}
}
// Creates horizontal gutters and appends them to `gutters_`.
void AppendHorizontalOverlayGutters(const gfx::Size& display_size_in_pixels,
const gfx::RoundedCornersF& panel_radii) {
auto overlay_gutters = gutter_factory_->CreateOverlayGutters(
display_size_in_pixels, panel_radii,
/*create_vertical_gutters=*/false);
for (auto& gutter : overlay_gutters) {
gutters_.push_back(std::move(gutter));
}
}
// Creates non overlay gutters and appends them to `gutters_`.
void AppendNonOverlayGutters(const gfx::Size& display_size_in_pixels,
const gfx::RoundedCornersF& panel_radii) {
auto non_overlay_gutters = gutter_factory_->CreateNonOverlayGutters(
display_size_in_pixels, display_radii);
display_size_in_pixels, panel_radii);
for (auto& gutter : non_overlay_gutters) {
gutters.push_back(std::move(gutter));
gutters_.push_back(std::move(gutter));
}
return gutters;
}
protected:
@ -106,6 +121,8 @@ class RoundedDisplayFrameFactoryTest : public AshTestBase {
// TODO(zoraiznaeem): Add more unittest coverage.
TEST_F(RoundedDisplayFrameFactoryTest, CompositorFrameHasCorrectStructure) {
AppendVerticalOverlayGutters(kTestDisplaySize, kTestPanelRadii);
const auto& gutters = GetGutters();
auto frame = frame_factory_->CreateCompositorFrame(
@ -133,7 +150,92 @@ TEST_F(RoundedDisplayFrameFactoryTest, CompositorFrameHasCorrectStructure) {
EXPECT_EQ(shared_quad_state_list.size(), gutters.size());
}
MATCHER_P(IsRoundedDisplayMasksInfoEqual, value, "") {
return arg.is_horizontally_positioned == value.is_horizontally_positioned &&
arg.radii[RoundedDisplayMasksInfo::kOriginRoundedDisplayMaskIndex] ==
value.radii
[RoundedDisplayMasksInfo::kOriginRoundedDisplayMaskIndex] &&
arg.radii[RoundedDisplayMasksInfo::kOtherRoundedDisplayMaskIndex] ==
value
.radii[RoundedDisplayMasksInfo::kOtherRoundedDisplayMaskIndex];
}
TEST_F(RoundedDisplayFrameFactoryTest,
CorrectRoundedDisplayInfo_VerticalGuttersWithTwoCorners) {
const auto panel_radii = gfx::RoundedCornersF(10, 0, 0, 15);
AppendVerticalOverlayGutters(kTestDisplaySize, panel_radii);
// `gutter_factory_` will only create left overlay gutter.
EXPECT_EQ(gutters_.size(), 1u);
auto frame = frame_factory_->CreateCompositorFrame(
viz::BeginFrameAck::CreateManualAckWithDamage(), *host_window_,
resource_manager_, GetGutters());
const viz::QuadList& quad_list = frame->render_pass_list.front()->quad_list;
ASSERT_EQ(quad_list.size(), 1u);
EXPECT_THAT(viz::TextureDrawQuad::MaterialCast(quad_list.ElementAt(0))
->rounded_display_masks_info,
IsRoundedDisplayMasksInfoEqual(
RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(
/*origin_rounded_display_mask_radius=*/10,
/*other_rounded_display_mask_radius=*/15,
/*is_horizontally_positioned=*/false)));
}
TEST_F(RoundedDisplayFrameFactoryTest,
CorrectRoundedDisplayInfo_HorizontalGuttersWithTwoCorners) {
const auto panel_radii = gfx::RoundedCornersF(15, 10, 0, 0);
AppendHorizontalOverlayGutters(kTestDisplaySize, panel_radii);
// `gutter_factory_` will only create upper overlay gutter.
EXPECT_EQ(gutters_.size(), 1u);
auto frame = frame_factory_->CreateCompositorFrame(
viz::BeginFrameAck::CreateManualAckWithDamage(), *host_window_,
resource_manager_, GetGutters());
const viz::QuadList& quad_list = frame->render_pass_list.front()->quad_list;
ASSERT_EQ(quad_list.size(), 1u);
EXPECT_THAT(viz::TextureDrawQuad::MaterialCast(quad_list.ElementAt(0))
->rounded_display_masks_info,
IsRoundedDisplayMasksInfoEqual(
RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(
/*origin_rounded_display_mask_radius=*/15,
/*other_rounded_display_mask_radius=*/10,
/*is_horizontally_positioned=*/true)));
}
TEST_F(RoundedDisplayFrameFactoryTest,
CorrectRoundedDisplayInfo_GuttersWithOneCorner) {
const auto panel_radii = gfx::RoundedCornersF(10, 0, 0, 0);
AppendNonOverlayGutters(kTestDisplaySize, panel_radii);
// `gutter_factory_` will only create upper-left non-overlay gutter.
EXPECT_EQ(gutters_.size(), 1u);
auto frame = frame_factory_->CreateCompositorFrame(
viz::BeginFrameAck::CreateManualAckWithDamage(), *host_window_,
resource_manager_, GetGutters());
const viz::QuadList& quad_list = frame->render_pass_list.front()->quad_list;
ASSERT_EQ(quad_list.size(), 1u);
EXPECT_THAT(viz::TextureDrawQuad::MaterialCast(quad_list.ElementAt(0))
->rounded_display_masks_info,
IsRoundedDisplayMasksInfoEqual(
RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(
/*origin_rounded_display_mask_radius=*/10,
/*other_rounded_display_mask_radius=*/0,
/*is_horizontally_positioned=*/true)));
}
TEST_F(RoundedDisplayFrameFactoryTest, OnlyCreateNewResourcesWhenNecessary) {
AppendVerticalOverlayGutters(kTestDisplaySize, kTestPanelRadii);
AppendNonOverlayGutters(kTestDisplaySize, kTestPanelRadii);
const auto& gutters = GetGutters();
// Populate resources in the resource manager.