| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stddef.h> |
| |
| #include <string> |
| #include <unordered_map> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/containers/cxx20_erase_vector.h" |
| #include "base/containers/flat_map.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/test/gtest_util.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/time/time.h" |
| #include "base/time/time_override.h" |
| #include "base/unguessable_token.h" |
| #include "build/build_config.h" |
| #include "cc/base/math_util.h" |
| #include "cc/test/fake_output_surface_client.h" |
| #include "cc/test/resource_provider_test_utils.h" |
| #include "components/viz/client/client_resource_provider.h" |
| #include "components/viz/common/buildflags.h" |
| #include "components/viz/common/features.h" |
| #include "components/viz/common/frame_sinks/copy_output_request.h" |
| #include "components/viz/common/quads/aggregated_render_pass_draw_quad.h" |
| #include "components/viz/common/quads/compositor_render_pass.h" |
| #include "components/viz/common/quads/compositor_render_pass_draw_quad.h" |
| #include "components/viz/common/quads/solid_color_draw_quad.h" |
| #include "components/viz/common/quads/texture_draw_quad.h" |
| #include "components/viz/common/quads/video_hole_draw_quad.h" |
| #include "components/viz/common/resources/resource_id.h" |
| #include "components/viz/common/resources/transferable_resource.h" |
| #include "components/viz/service/display/display_resource_provider_skia.h" |
| #include "components/viz/service/display/output_surface.h" |
| #include "components/viz/service/display/output_surface_client.h" |
| #include "components/viz/service/display/output_surface_frame.h" |
| #include "components/viz/service/display/overlay_candidate.h" |
| #include "components/viz/service/display/overlay_candidate_factory.h" |
| #include "components/viz/service/display/overlay_candidate_temporal_tracker.h" |
| #include "components/viz/service/display/overlay_processor_using_strategy.h" |
| #include "components/viz/service/display/overlay_proposed_candidate.h" |
| #include "components/viz/service/display/overlay_strategy_fullscreen.h" |
| #include "components/viz/service/display/overlay_strategy_single_on_top.h" |
| #include "components/viz/service/display/overlay_strategy_underlay.h" |
| #include "components/viz/test/fake_skia_output_surface.h" |
| #include "components/viz/test/test_context_provider.h" |
| #include "components/viz/test/test_gles2_interface.h" |
| #include "gpu/config/gpu_finch_features.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gfx/geometry/point.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/rect_conversions.h" |
| #include "ui/gfx/geometry/rect_f.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/geometry/test/geometry_util.h" |
| #include "ui/latency/latency_info.h" |
| |
| #if BUILDFLAG(IS_OZONE) |
| #include "components/viz/service/display/overlay_processor_delegated.h" |
| #include "ui/base/ui_base_features.h" |
| #endif |
| |
| #if BUILDFLAG(ENABLE_CAST_OVERLAY_STRATEGY) |
| #include "components/viz/service/display/overlay_strategy_underlay_cast.h" |
| #endif |
| |
| #if BUILDFLAG(IS_APPLE) |
| #include "components/viz/service/display/ca_layer_overlay.h" |
| #endif |
| |
| using testing::_; |
| using testing::Mock; |
| |
| namespace viz { |
| namespace { |
| |
| using RoundedDisplayMasksInfo = TextureDrawQuad::RoundedDisplayMasksInfo; |
| |
| const gfx::Size kDisplaySize(256, 256); |
| const gfx::Rect kOverlayRect(0, 0, 256, 256); |
| const gfx::Rect kOverlayTopLeftRect(0, 0, 128, 128); |
| const gfx::Rect kOverlayTopRightRect(128, 0, 128, 128); |
| const gfx::Rect kOverlayBottomRightRect(128, 128, 128, 128); |
| const gfx::Rect kOverlayClipRect(0, 0, 128, 128); |
| const gfx::PointF kUVTopLeft(0.1f, 0.2f); |
| const gfx::PointF kUVBottomRight(1.0f, 1.0f); |
| const gfx::BufferFormat kDefaultBufferFormat = gfx::BufferFormat::RGBA_8888; |
| constexpr OverlayCandidateFactory::OverlayContext kTestOverlayContext = {}; |
| |
| class TimeTicksOverride { |
| public: |
| static base::TimeTicks Now() { return now_ticks_; } |
| static base::TimeTicks now_ticks_; |
| }; |
| |
| // static |
| base::TimeTicks TimeTicksOverride::now_ticks_ = base::TimeTicks::Now(); |
| |
| class TestOverlayProcessor : public OverlayProcessorUsingStrategy { |
| public: |
| using PrimaryPlane = OverlayProcessorInterface::OutputSurfaceOverlayPlane; |
| TestOverlayProcessor() { |
| // By default the prioritization thresholding features are disabled for all |
| // tests. |
| prioritization_config_.changing_threshold = false; |
| prioritization_config_.damage_rate_threshold = false; |
| } |
| ~TestOverlayProcessor() override = default; |
| |
| bool IsOverlaySupported() const override { return true; } |
| bool NeedsSurfaceDamageRectList() const override { return false; } |
| void CheckOverlaySupportImpl(const PrimaryPlane* primary_plane, |
| OverlayCandidateList* surfaces) override {} |
| size_t GetStrategyCount() const { return strategies_.size(); } |
| }; |
| |
| class FullscreenOverlayProcessor : public TestOverlayProcessor { |
| public: |
| FullscreenOverlayProcessor() { |
| strategies_.push_back(std::make_unique<OverlayStrategyFullscreen>(this)); |
| } |
| bool NeedsSurfaceDamageRectList() const override { return true; } |
| void CheckOverlaySupportImpl(const PrimaryPlane* primary_plane, |
| OverlayCandidateList* surfaces) override { |
| surfaces->back().overlay_handled = true; |
| } |
| }; |
| |
| // Gets the minimum scaling amount used by either dimension for the src relative |
| // to the dst. |
| float GetMinScaleFactor(const OverlayCandidate& candidate) { |
| if (candidate.resource_size_in_pixels.IsEmpty() || |
| candidate.uv_rect.IsEmpty()) { |
| return 1.0f; |
| } |
| return std::min(candidate.display_rect.width() / |
| (candidate.uv_rect.width() * |
| candidate.resource_size_in_pixels.width()), |
| candidate.display_rect.height() / |
| (candidate.uv_rect.height() * |
| candidate.resource_size_in_pixels.height())); |
| } |
| |
| class DefaultOverlayProcessor : public TestOverlayProcessor { |
| public: |
| DefaultOverlayProcessor() : expected_rects_(1, gfx::RectF(kOverlayRect)) {} |
| |
| bool NeedsSurfaceDamageRectList() const override { return true; } |
| void CheckOverlaySupportImpl(const PrimaryPlane* primary_plane, |
| OverlayCandidateList* surfaces) override { |
| // We have one overlay surface to test. The output surface as primary plane |
| // is optional, depending on whether this ran |
| // through the full renderer and picked up the output surface, or not. |
| ASSERT_EQ(1U, surfaces->size()); |
| OverlayCandidate& candidate = surfaces->back(); |
| const float kAbsoluteError = 0.01f; |
| // If we are testing fallback for protected overlay scaling, then check that |
| // first. |
| if (!scaling_sequence_.empty()) { |
| float scaling = GetMinScaleFactor(candidate); |
| EXPECT_LE(std::abs(scaling - scaling_sequence_.front().first), |
| kAbsoluteError); |
| candidate.overlay_handled = scaling_sequence_.front().second; |
| scaling_sequence_.erase(scaling_sequence_.begin()); |
| return; |
| } |
| |
| for (const auto& r : expected_rects_) { |
| SCOPED_TRACE(r.ToString()); |
| |
| if (std::abs(r.x() - candidate.display_rect.x()) <= kAbsoluteError && |
| std::abs(r.y() - candidate.display_rect.y()) <= kAbsoluteError && |
| std::abs(r.width() - candidate.display_rect.width()) <= |
| kAbsoluteError && |
| std::abs(r.height() - candidate.display_rect.height()) <= |
| kAbsoluteError) { |
| EXPECT_RECTF_EQ(BoundingRect(kUVTopLeft, kUVBottomRight), |
| candidate.uv_rect); |
| if (candidate.clip_rect) { |
| EXPECT_EQ(kOverlayClipRect, candidate.clip_rect); |
| } |
| candidate.overlay_handled = true; |
| return; |
| } |
| } |
| // We should find one rect in expected_rects_that matches candidate. |
| EXPECT_TRUE(false); |
| } |
| |
| void AddExpectedRect(const gfx::RectF& rect) { |
| expected_rects_.push_back(rect); |
| } |
| |
| void AddScalingSequence(float scaling, bool uses_overlay) { |
| scaling_sequence_.emplace_back(scaling, uses_overlay); |
| } |
| |
| private: |
| std::vector<gfx::RectF> expected_rects_; |
| std::vector<std::pair<float, bool>> scaling_sequence_; |
| }; |
| |
| class SingleOverlayProcessor : public DefaultOverlayProcessor { |
| public: |
| SingleOverlayProcessor() : DefaultOverlayProcessor() { |
| strategies_.push_back(std::make_unique<OverlayStrategySingleOnTop>(this)); |
| strategies_.push_back(std::make_unique<OverlayStrategyUnderlay>(this)); |
| } |
| }; |
| |
| class MultiOverlayProcessorBase : public TestOverlayProcessor { |
| public: |
| MultiOverlayProcessorBase() { |
| // Don't wait for hardware support in these tests. |
| max_overlays_considered_ = max_overlays_config_; |
| } |
| |
| void ProcessForOverlays( |
| DisplayResourceProvider* resource_provider, |
| AggregatedRenderPassList* render_passes, |
| const SkM44& output_color_matrix, |
| const FilterOperationsMap& render_pass_filters, |
| const FilterOperationsMap& render_pass_backdrop_filters, |
| SurfaceDamageRectList surface_damage_rect_list, |
| OutputSurfaceOverlayPlane* output_surface_plane, |
| CandidateList* overlay_candidates, |
| gfx::Rect* damage_rect, |
| std::vector<gfx::Rect>* content_bounds) override { |
| // Clear the combination cache every frame so results are more predictable |
| // in these tests. |
| ClearOverlayCombinationCache(); |
| // Parameters unchanged. |
| OverlayProcessorUsingStrategy::ProcessForOverlays( |
| resource_provider, render_passes, output_color_matrix, |
| render_pass_filters, render_pass_backdrop_filters, |
| surface_damage_rect_list, output_surface_plane, overlay_candidates, |
| damage_rect, content_bounds); |
| } |
| |
| void CheckOverlaySupportImpl(const PrimaryPlane* primary_plane, |
| OverlayCandidateList* surfaces) override { |
| EXPECT_EQ(expected_rects_.size(), surfaces->size()); |
| |
| for (size_t i = 0; i < surfaces->size(); ++i) { |
| OverlayCandidate& candidate = (*surfaces)[i]; |
| EXPECT_EQ(gfx::ToEnclosedRect(candidate.display_rect), |
| expected_rects_[i]); |
| candidate.overlay_handled = responses_[i]; |
| } |
| } |
| |
| // Sort required overlay candidates first, followed by candidates with |
| // rounded_display masks and then just by input order. |
| void SortProposedOverlayCandidates( |
| std::vector<OverlayProposedCandidate>* proposed_candidates) override { |
| // We want the power gains to be assigned for the OverlayCombinationCache. |
| size_t order = proposed_candidates->size(); |
| for (auto& proposed_candidate : *proposed_candidates) { |
| proposed_candidate.relative_power_gain = order--; |
| } |
| std::stable_sort( |
| proposed_candidates->begin(), proposed_candidates->end(), |
| [](const auto& a, const auto& b) { |
| if (a.candidate.requires_overlay != b.candidate.requires_overlay) { |
| return a.candidate.requires_overlay > b.candidate.requires_overlay; |
| } |
| |
| if (a.candidate.has_rounded_display_masks != |
| b.candidate.has_rounded_display_masks) { |
| return a.candidate.has_rounded_display_masks > |
| b.candidate.has_rounded_display_masks; |
| } |
| |
| return a.relative_power_gain > b.relative_power_gain; |
| }); |
| } |
| |
| void AddExpectedRect(const gfx::Rect& rect, bool response) { |
| expected_rects_.push_back(rect); |
| responses_.push_back(response); |
| } |
| |
| void ClearExpectedRects() { |
| expected_rects_.clear(); |
| responses_.clear(); |
| } |
| |
| // Overrides the maximum number of candidates that the OverlayProcessor can |
| // consider each frame. |
| void SetMaximumOverlaysConsidered(int overlays_considered) { |
| max_overlays_considered_ = overlays_considered; |
| } |
| |
| private: |
| std::vector<gfx::Rect> expected_rects_; |
| std::vector<bool> responses_; |
| }; |
| |
| class MultiOverlayProcessor : public MultiOverlayProcessorBase { |
| public: |
| MultiOverlayProcessor() { |
| strategies_.push_back(std::make_unique<OverlayStrategyFullscreen>(this)); |
| strategies_.push_back(std::make_unique<OverlayStrategySingleOnTop>(this)); |
| strategies_.push_back(std::make_unique<OverlayStrategyUnderlay>(this)); |
| } |
| }; |
| |
| class MultiSingleOnTopProcessor : public MultiOverlayProcessorBase { |
| public: |
| MultiSingleOnTopProcessor() { |
| strategies_.push_back(std::make_unique<OverlayStrategySingleOnTop>(this)); |
| } |
| }; |
| |
| class SizeSortedMultiSingleOnTopProcessor : public MultiOverlayProcessorBase { |
| public: |
| SizeSortedMultiSingleOnTopProcessor() { |
| strategies_.push_back(std::make_unique<OverlayStrategySingleOnTop>(this)); |
| } |
| |
| // Sort candidates only by their display_rect area. |
| void SortProposedOverlayCandidates( |
| std::vector<OverlayProposedCandidate>* proposed_candidates) override { |
| // We want the power gains to be assigned for the OverlayCombinationCache. |
| for (auto& proposed_candidate : *proposed_candidates) { |
| proposed_candidate.relative_power_gain = |
| proposed_candidate.candidate.display_rect.size().GetArea(); |
| } |
| std::sort(proposed_candidates->begin(), proposed_candidates->end(), |
| [](const auto& a, const auto& b) { |
| return a.relative_power_gain > b.relative_power_gain; |
| }); |
| } |
| |
| void CheckOverlaySupportImpl(const PrimaryPlane* primary_plane, |
| OverlayCandidateList* surfaces) override { |
| for (auto& candidate : *surfaces) { |
| candidate.overlay_handled = true; |
| } |
| } |
| }; |
| |
| class MultiUnderlayProcessor : public MultiOverlayProcessorBase { |
| public: |
| MultiUnderlayProcessor() { |
| strategies_.push_back(std::make_unique<OverlayStrategyUnderlay>(this)); |
| } |
| }; |
| |
| class SizeSortedMultiOverlayProcessorBase : public MultiOverlayProcessorBase { |
| public: |
| // Sort candidates only by their display_rect area. |
| void SortProposedOverlayCandidates( |
| std::vector<OverlayProposedCandidate>* proposed_candidates) override { |
| // We want the power gains to be assigned for the OverlayCombinationCache. |
| for (auto& proposed_candidate : *proposed_candidates) { |
| proposed_candidate.relative_power_gain = |
| proposed_candidate.candidate.display_rect.size().GetArea(); |
| } |
| std::sort(proposed_candidates->begin(), proposed_candidates->end(), |
| [](const auto& a, const auto& b) { |
| return a.relative_power_gain > b.relative_power_gain; |
| }); |
| } |
| }; |
| |
| class SizeSortedMultiOverlayProcessor |
| : public SizeSortedMultiOverlayProcessorBase { |
| public: |
| SizeSortedMultiOverlayProcessor() { |
| strategies_.push_back(std::make_unique<OverlayStrategyFullscreen>(this)); |
| strategies_.push_back(std::make_unique<OverlayStrategySingleOnTop>(this)); |
| } |
| }; |
| |
| class SizeSortedMultiUnderlayProcessor |
| : public SizeSortedMultiOverlayProcessorBase { |
| public: |
| SizeSortedMultiUnderlayProcessor() { |
| strategies_.push_back(std::make_unique<OverlayStrategyUnderlay>(this)); |
| } |
| }; |
| |
| class TypeAndSizeSortedMultiOverlayProcessor |
| : public MultiOverlayProcessorBase { |
| public: |
| TypeAndSizeSortedMultiOverlayProcessor() { |
| strategies_.push_back(std::make_unique<OverlayStrategySingleOnTop>(this)); |
| } |
| |
| void SortProposedOverlayCandidates( |
| std::vector<OverlayProposedCandidate>* proposed_candidates) override { |
| // We want the power gains to be assigned for the OverlayCombinationCache. |
| for (auto& proposed_candidate : *proposed_candidates) { |
| proposed_candidate.relative_power_gain = |
| proposed_candidate.candidate.display_rect.size().GetArea(); |
| } |
| std::sort( |
| proposed_candidates->begin(), proposed_candidates->end(), |
| [](const auto& a, const auto& b) { |
| if (a.candidate.requires_overlay || b.candidate.requires_overlay) { |
| return a.candidate.requires_overlay && |
| !b.candidate.requires_overlay; |
| } |
| |
| if (a.candidate.has_rounded_display_masks || |
| b.candidate.has_rounded_display_masks) { |
| return a.candidate.has_rounded_display_masks && |
| !b.candidate.has_rounded_display_masks; |
| } |
| |
| return a.relative_power_gain > b.relative_power_gain; |
| }); |
| } |
| }; |
| |
| // This processor only allows only candidates with rounded-display masks after |
| // sorting them. |
| class AllowCandidateWithMasksSortedMultiOverlayProcessor |
| : public MultiOverlayProcessorBase { |
| public: |
| AllowCandidateWithMasksSortedMultiOverlayProcessor() { |
| strategies_.push_back(std::make_unique<OverlayStrategySingleOnTop>(this)); |
| } |
| |
| void SortProposedOverlayCandidates( |
| std::vector<OverlayProposedCandidate>* proposed_candidates) override { |
| // After sort we should only be left with candidates with rounded-display |
| // masks. |
| base::EraseIf(*proposed_candidates, [](OverlayProposedCandidate& cand) { |
| return !cand.candidate.has_rounded_display_masks; |
| }); |
| |
| // We want the power gains to be assigned for the OverlayCombinationCache. |
| for (auto& proposed_candidate : *proposed_candidates) { |
| proposed_candidate.relative_power_gain = |
| proposed_candidate.candidate.display_rect.size().GetArea(); |
| } |
| } |
| }; |
| |
| class SingleOnTopOverlayProcessor : public DefaultOverlayProcessor { |
| public: |
| SingleOnTopOverlayProcessor() : DefaultOverlayProcessor() { |
| strategies_.push_back(std::make_unique<OverlayStrategySingleOnTop>(this)); |
| // To reduce the complexity of this test for the prioritization feature the |
| // |damage_rate_threshold| is set to zero to make all opaque power positive. |
| tracker_config_.damage_rate_threshold = 0.f; |
| } |
| }; |
| |
| class UnderlayOverlayProcessor : public DefaultOverlayProcessor { |
| public: |
| UnderlayOverlayProcessor() : DefaultOverlayProcessor() { |
| strategies_.push_back(std::make_unique<OverlayStrategyUnderlay>(this)); |
| prioritization_config_.changing_threshold = false; |
| prioritization_config_.damage_rate_threshold = false; |
| prioritization_config_.power_gain_sort = false; |
| } |
| }; |
| |
| class TransitionOverlayProcessor : public DefaultOverlayProcessor { |
| public: |
| TransitionOverlayProcessor() { |
| strategies_.push_back(std::make_unique<OverlayStrategySingleOnTop>(this)); |
| strategies_.push_back(std::make_unique<OverlayStrategyUnderlay>(this)); |
| prioritization_config_.changing_threshold = false; |
| prioritization_config_.damage_rate_threshold = false; |
| prioritization_config_.power_gain_sort = false; |
| } |
| }; |
| |
| class TransparentUnderlayOverlayProcessor : public DefaultOverlayProcessor { |
| public: |
| TransparentUnderlayOverlayProcessor() : DefaultOverlayProcessor() { |
| strategies_.push_back(std::make_unique<OverlayStrategyUnderlay>( |
| this, OverlayStrategyUnderlay::OpaqueMode::AllowTransparentCandidates)); |
| } |
| }; |
| |
| #if BUILDFLAG(ENABLE_CAST_OVERLAY_STRATEGY) |
| class UnderlayCastOverlayProcessor : public DefaultOverlayProcessor { |
| public: |
| UnderlayCastOverlayProcessor() : DefaultOverlayProcessor() { |
| strategies_.push_back(std::make_unique<OverlayStrategyUnderlayCast>(this)); |
| } |
| }; |
| #endif |
| |
| class ChangeThresholdOnTopOverlayProcessor : public DefaultOverlayProcessor { |
| public: |
| ChangeThresholdOnTopOverlayProcessor() { |
| strategies_.push_back(std::make_unique<OverlayStrategySingleOnTop>(this)); |
| prioritization_config_.damage_rate_threshold = false; |
| prioritization_config_.changing_threshold = true; |
| } |
| |
| // To keep this test consistent we need to expose the config for how long it |
| // takes for the system to threshold a unchanging candidate. |
| const OverlayCandidateTemporalTracker::Config& TrackerConfigAccessor() { |
| return tracker_config_; |
| } |
| }; |
| |
| class FullThresholdUnderlayOverlayProcessor : public DefaultOverlayProcessor { |
| public: |
| FullThresholdUnderlayOverlayProcessor() { |
| strategies_.push_back(std::make_unique<OverlayStrategyUnderlay>(this)); |
| // Disable this feature as it is tested in |
| // |ChangeThresholdOnTopOverlayProcessor|. |
| prioritization_config_.changing_threshold = false; |
| prioritization_config_.damage_rate_threshold = true; |
| } |
| }; |
| |
| std::unique_ptr<AggregatedRenderPass> CreateRenderPass() { |
| AggregatedRenderPassId render_pass_id{1}; |
| gfx::Rect output_rect(0, 0, 256, 256); |
| |
| auto pass = std::make_unique<AggregatedRenderPass>(); |
| pass->SetNew(render_pass_id, output_rect, output_rect, gfx::Transform()); |
| |
| SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState(); |
| shared_state->opacity = 1.f; |
| return pass; |
| } |
| |
| static ResourceId CreateResourceInLayerTree( |
| ClientResourceProvider* child_resource_provider, |
| const gfx::Size& size, |
| bool is_overlay_candidate, |
| SharedImageFormat format) { |
| auto resource = TransferableResource::MakeGpu( |
| gpu::Mailbox::GenerateForSharedImage(), GL_TEXTURE_2D, gpu::SyncToken(), |
| size, format, is_overlay_candidate); |
| |
| ResourceId resource_id = |
| child_resource_provider->ImportResource(resource, base::DoNothing()); |
| |
| return resource_id; |
| } |
| |
| ResourceId CreateResource(DisplayResourceProvider* parent_resource_provider, |
| ClientResourceProvider* child_resource_provider, |
| ContextProvider* child_context_provider, |
| const gfx::Size& size, |
| bool is_overlay_candidate, |
| SharedImageFormat format, |
| SurfaceId test_surface_id = SurfaceId()) { |
| ResourceId resource_id = CreateResourceInLayerTree( |
| child_resource_provider, size, is_overlay_candidate, format); |
| |
| int child_id = |
| parent_resource_provider->CreateChild(base::DoNothing(), test_surface_id); |
| |
| // Transfer resource to the parent. |
| std::vector<ResourceId> resource_ids_to_transfer; |
| resource_ids_to_transfer.push_back(resource_id); |
| std::vector<TransferableResource> list; |
| child_resource_provider->PrepareSendToParent(resource_ids_to_transfer, &list, |
| child_context_provider); |
| parent_resource_provider->ReceiveFromChild(child_id, list); |
| |
| // Delete it in the child so it won't be leaked, and will be released once |
| // returned from the parent. |
| child_resource_provider->RemoveImportedResource(resource_id); |
| |
| // In DisplayResourceProvider's namespace, use the mapped resource id. |
| std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = |
| parent_resource_provider->GetChildToParentMap(child_id); |
| return resource_map[list[0].id]; |
| } |
| |
| ResourceId CreateResource(DisplayResourceProvider* parent_resource_provider, |
| ClientResourceProvider* child_resource_provider, |
| ContextProvider* child_context_provider, |
| const gfx::Size& size, |
| bool is_overlay_candidate) { |
| return CreateResource(parent_resource_provider, child_resource_provider, |
| child_context_provider, size, is_overlay_candidate, |
| SinglePlaneFormat::kRGBA_8888); |
| } |
| |
| SolidColorDrawQuad* CreateSolidColorQuadAt( |
| const SharedQuadState* shared_quad_state, |
| SkColor4f color, |
| AggregatedRenderPass* render_pass, |
| const gfx::Rect& rect) { |
| SolidColorDrawQuad* quad = |
| render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); |
| quad->SetNew(shared_quad_state, rect, rect, color, false); |
| return quad; |
| } |
| |
| TextureDrawQuad* CreateCandidateQuadAt( |
| DisplayResourceProvider* parent_resource_provider, |
| ClientResourceProvider* child_resource_provider, |
| ContextProvider* child_context_provider, |
| const SharedQuadState* shared_quad_state, |
| AggregatedRenderPass* render_pass, |
| const gfx::Rect& rect, |
| gfx::ProtectedVideoType protected_video_type, |
| SharedImageFormat format, |
| const gfx::Size& resource_size_in_pixels, |
| SurfaceId test_surface_id = SurfaceId()) { |
| bool needs_blending = false; |
| bool premultiplied_alpha = false; |
| bool flipped = false; |
| bool nearest_neighbor = false; |
| float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f}; |
| bool is_overlay_candidate = true; |
| ResourceId resource_id = CreateResource( |
| parent_resource_provider, child_resource_provider, child_context_provider, |
| resource_size_in_pixels, is_overlay_candidate, format, test_surface_id); |
| |
| auto* overlay_quad = render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>(); |
| overlay_quad->SetNew(shared_quad_state, rect, rect, needs_blending, |
| resource_id, premultiplied_alpha, kUVTopLeft, |
| kUVBottomRight, SkColors::kTransparent, vertex_opacity, |
| flipped, nearest_neighbor, /*secure_output_only=*/false, |
| protected_video_type); |
| overlay_quad->set_resource_size_in_pixels(resource_size_in_pixels); |
| |
| return overlay_quad; |
| } |
| |
| TextureDrawQuad* CreateCandidateQuadAt( |
| DisplayResourceProvider* parent_resource_provider, |
| ClientResourceProvider* child_resource_provider, |
| ContextProvider* child_context_provider, |
| const SharedQuadState* shared_quad_state, |
| AggregatedRenderPass* render_pass, |
| const gfx::Rect& rect, |
| gfx::ProtectedVideoType protected_video_type, |
| SharedImageFormat format, |
| SurfaceId test_surface_id = SurfaceId()) { |
| return CreateCandidateQuadAt( |
| parent_resource_provider, child_resource_provider, child_context_provider, |
| shared_quad_state, render_pass, rect, protected_video_type, format, |
| rect.size(), test_surface_id); |
| } |
| |
| TextureDrawQuad* CreateCandidateQuadAt( |
| DisplayResourceProvider* parent_resource_provider, |
| ClientResourceProvider* child_resource_provider, |
| ContextProvider* child_context_provider, |
| const SharedQuadState* shared_quad_state, |
| AggregatedRenderPass* render_pass, |
| const gfx::Rect& rect, |
| SurfaceId test_surface_id = SurfaceId()) { |
| return CreateCandidateQuadAt( |
| parent_resource_provider, child_resource_provider, child_context_provider, |
| shared_quad_state, render_pass, rect, gfx::ProtectedVideoType::kClear, |
| SinglePlaneFormat::kRGBA_8888, test_surface_id); |
| } |
| |
| #if BUILDFLAG(ENABLE_CAST_OVERLAY_STRATEGY) |
| std::unique_ptr<AggregatedRenderPass> CreateRenderPassWithTransform( |
| const gfx::Transform& transform) { |
| AggregatedRenderPassId render_pass_id{1}; |
| gfx::Rect output_rect(0, 0, 256, 256); |
| |
| auto pass = std::make_unique<AggregatedRenderPass>(); |
| pass->SetNew(render_pass_id, output_rect, output_rect, gfx::Transform()); |
| |
| SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState(); |
| shared_state->opacity = 1.f; |
| shared_state->quad_to_target_transform = transform; |
| return pass; |
| } |
| |
| // For Cast we use VideoHoleDrawQuad, and that's what overlay_processor_ |
| // expects. |
| VideoHoleDrawQuad* CreateVideoHoleDrawQuadAt( |
| const SharedQuadState* shared_quad_state, |
| AggregatedRenderPass* render_pass, |
| const gfx::Rect& rect) { |
| base::UnguessableToken overlay_plane_id = base::UnguessableToken::Create(); |
| auto* overlay_quad = |
| render_pass->CreateAndAppendDrawQuad<VideoHoleDrawQuad>(); |
| overlay_quad->SetNew(shared_quad_state, rect, rect, overlay_plane_id); |
| return overlay_quad; |
| } |
| #endif |
| |
| TextureDrawQuad* CreateTransparentCandidateQuadAt( |
| DisplayResourceProvider* parent_resource_provider, |
| ClientResourceProvider* child_resource_provider, |
| ContextProvider* child_context_provider, |
| const SharedQuadState* shared_quad_state, |
| AggregatedRenderPass* render_pass, |
| const gfx::Rect& rect) { |
| bool needs_blending = true; |
| bool premultiplied_alpha = false; |
| bool flipped = false; |
| bool nearest_neighbor = false; |
| float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f}; |
| gfx::Size resource_size_in_pixels = rect.size(); |
| bool is_overlay_candidate = true; |
| ResourceId resource_id = CreateResource( |
| parent_resource_provider, child_resource_provider, child_context_provider, |
| resource_size_in_pixels, is_overlay_candidate); |
| |
| auto* overlay_quad = render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>(); |
| overlay_quad->SetNew(shared_quad_state, rect, rect, needs_blending, |
| resource_id, premultiplied_alpha, kUVTopLeft, |
| kUVBottomRight, SkColors::kTransparent, vertex_opacity, |
| flipped, nearest_neighbor, /*secure_output_only=*/false, |
| gfx::ProtectedVideoType::kClear); |
| overlay_quad->set_resource_size_in_pixels(resource_size_in_pixels); |
| |
| return overlay_quad; |
| } |
| |
| TextureDrawQuad* CreateFullscreenCandidateQuad( |
| DisplayResourceProvider* parent_resource_provider, |
| ClientResourceProvider* child_resource_provider, |
| ContextProvider* child_context_provider, |
| const SharedQuadState* shared_quad_state, |
| AggregatedRenderPass* render_pass) { |
| return CreateCandidateQuadAt( |
| parent_resource_provider, child_resource_provider, child_context_provider, |
| shared_quad_state, render_pass, render_pass->output_rect); |
| } |
| |
| TextureDrawQuad* CreateQuadWithRoundedDisplayMasksAt( |
| DisplayResourceProvider* parent_resource_provider, |
| ClientResourceProvider* child_resource_provider, |
| ContextProvider* child_context_provider, |
| const SharedQuadState* shared_quad_state, |
| AggregatedRenderPass* render_pass, |
| bool is_overlay_candidate, |
| const gfx::Rect& rect, |
| const RoundedDisplayMasksInfo& rounded_display_masks_info) { |
| bool needs_blending = true; |
| bool premultiplied_alpha = true; |
| bool flipped = false; |
| bool nearest_neighbor = false; |
| float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f}; |
| gfx::Size resource_size_in_pixels = rect.size(); |
| ResourceId resource_id = CreateResource( |
| parent_resource_provider, child_resource_provider, child_context_provider, |
| resource_size_in_pixels, is_overlay_candidate); |
| |
| auto* overlay_quad = render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>(); |
| overlay_quad->SetNew( |
| shared_quad_state, rect, rect, needs_blending, resource_id, |
| premultiplied_alpha, kUVTopLeft, kUVBottomRight, SkColors::kTransparent, |
| vertex_opacity, flipped, nearest_neighbor, |
| /*secure_output=*/false, gfx::ProtectedVideoType::kClear); |
| overlay_quad->rounded_display_masks_info = rounded_display_masks_info; |
| |
| return overlay_quad; |
| } |
| |
| TextureDrawQuad* CreateFullscreenQuadWithRoundedDisplayMasks( |
| DisplayResourceProvider* parent_resource_provider, |
| ClientResourceProvider* child_resource_provider, |
| ContextProvider* child_context_provider, |
| const SharedQuadState* shared_quad_state, |
| AggregatedRenderPass* render_pass, |
| bool is_overlay_candidate, |
| const RoundedDisplayMasksInfo& rounded_display_masks_info) { |
| return CreateQuadWithRoundedDisplayMasksAt( |
| parent_resource_provider, child_resource_provider, child_context_provider, |
| shared_quad_state, render_pass, is_overlay_candidate, |
| render_pass->output_rect, rounded_display_masks_info); |
| } |
| |
| void CreateOpaqueQuadAt(DisplayResourceProvider* resource_provider, |
| const SharedQuadState* shared_quad_state, |
| AggregatedRenderPass* render_pass, |
| const gfx::Rect& rect) { |
| auto* color_quad = render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); |
| color_quad->SetNew(shared_quad_state, rect, rect, SkColors::kBlack, false); |
| } |
| |
| void CreateOpaqueQuadAt(DisplayResourceProvider* resource_provider, |
| const SharedQuadState* shared_quad_state, |
| AggregatedRenderPass* render_pass, |
| const gfx::Rect& rect, |
| SkColor4f color) { |
| DCHECK(color.isOpaque()); |
| auto* color_quad = render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); |
| color_quad->SetNew(shared_quad_state, rect, rect, color, false); |
| } |
| |
| void CreateFullscreenOpaqueQuad(DisplayResourceProvider* resource_provider, |
| const SharedQuadState* shared_quad_state, |
| AggregatedRenderPass* render_pass) { |
| CreateOpaqueQuadAt(resource_provider, shared_quad_state, render_pass, |
| render_pass->output_rect); |
| } |
| |
| static void CompareRenderPassLists( |
| const AggregatedRenderPassList& expected_list, |
| const AggregatedRenderPassList& actual_list) { |
| EXPECT_EQ(expected_list.size(), actual_list.size()); |
| for (size_t i = 0; i < actual_list.size(); ++i) { |
| SCOPED_TRACE(i); |
| |
| AggregatedRenderPass* expected = expected_list[i].get(); |
| AggregatedRenderPass* actual = actual_list[i].get(); |
| |
| EXPECT_EQ(expected->id, actual->id); |
| EXPECT_EQ(expected->output_rect, actual->output_rect); |
| EXPECT_EQ(expected->transform_to_root_target, |
| actual->transform_to_root_target); |
| EXPECT_EQ(expected->damage_rect, actual->damage_rect); |
| EXPECT_EQ(expected->has_transparent_background, |
| actual->has_transparent_background); |
| |
| EXPECT_EQ(expected->shared_quad_state_list.size(), |
| actual->shared_quad_state_list.size()); |
| EXPECT_EQ(expected->quad_list.size(), actual->quad_list.size()); |
| |
| for (auto exp_iter = expected->quad_list.cbegin(), |
| act_iter = actual->quad_list.cbegin(); |
| exp_iter != expected->quad_list.cend(); ++exp_iter, ++act_iter) { |
| EXPECT_EQ(exp_iter->rect.ToString(), act_iter->rect.ToString()); |
| EXPECT_EQ(exp_iter->shared_quad_state->quad_layer_rect.ToString(), |
| act_iter->shared_quad_state->quad_layer_rect.ToString()); |
| } |
| } |
| } |
| |
| SkM44 GetIdentityColorMatrix() { |
| return SkM44(); |
| } |
| |
| SkM44 GetNonIdentityColorMatrix() { |
| SkM44 matrix = GetIdentityColorMatrix(); |
| matrix.setRC(1, 1, 0.5f); |
| matrix.setRC(2, 2, 0.5f); |
| return matrix; |
| } |
| |
| template <typename OverlayProcessorType> |
| class OverlayTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| output_surface_ = FakeSkiaOutputSurface::Create3d(); |
| output_surface_->BindToClient(&output_surface_client_); |
| |
| resource_provider_ = std::make_unique<DisplayResourceProviderSkia>(); |
| lock_set_for_external_use_.emplace(resource_provider_.get(), |
| output_surface_.get()); |
| |
| child_provider_ = TestContextProvider::Create(); |
| child_provider_->BindToCurrentSequence(); |
| child_resource_provider_ = std::make_unique<ClientResourceProvider>(); |
| |
| overlay_processor_ = std::make_unique<OverlayProcessorType>(); |
| } |
| |
| void TearDown() override { |
| overlay_processor_ = nullptr; |
| child_resource_provider_->ShutdownAndReleaseAllResources(); |
| child_resource_provider_ = nullptr; |
| child_provider_ = nullptr; |
| lock_set_for_external_use_.reset(); |
| resource_provider_ = nullptr; |
| output_surface_ = nullptr; |
| } |
| |
| void AddExpectedRectToOverlayProcessor(const gfx::RectF& rect) { |
| overlay_processor_->AddExpectedRect(rect); |
| } |
| |
| void AddScalingSequenceToOverlayProcessor(float scaling, bool uses_overlay) { |
| overlay_processor_->AddScalingSequence(scaling, uses_overlay); |
| } |
| |
| std::unique_ptr<SkiaOutputSurface> output_surface_; |
| cc::FakeOutputSurfaceClient output_surface_client_; |
| std::unique_ptr<DisplayResourceProviderSkia> resource_provider_; |
| absl::optional<DisplayResourceProviderSkia::LockSetForExternalUse> |
| lock_set_for_external_use_; |
| scoped_refptr<TestContextProvider> child_provider_; |
| std::unique_ptr<ClientResourceProvider> child_resource_provider_; |
| std::unique_ptr<OverlayProcessorType> overlay_processor_; |
| gfx::Rect damage_rect_; |
| std::vector<gfx::Rect> content_bounds_; |
| }; |
| |
| template <typename OverlayProcessorType> |
| class UseMultipleOverlaysTest : public OverlayTest<OverlayProcessorType> { |
| public: |
| UseMultipleOverlaysTest() { |
| // To use more than one overlay, we need to enable some features. |
| const std::vector<base::test::FeatureRefAndParams> featureAndParamsList = { |
| {features::kUseMultipleOverlays, {{features::kMaxOverlaysParam, "4"}}}}; |
| scoped_features.InitWithFeaturesAndParameters(featureAndParamsList, {}); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_features; |
| }; |
| |
| using FullscreenOverlayTest = OverlayTest<FullscreenOverlayProcessor>; |
| using SingleOverlayOnTopTest = OverlayTest<SingleOnTopOverlayProcessor>; |
| using ChangeSingleOnTopTest = OverlayTest<ChangeThresholdOnTopOverlayProcessor>; |
| using FullThresholdTest = OverlayTest<FullThresholdUnderlayOverlayProcessor>; |
| using TransitionOverlayTypeTest = OverlayTest<TransitionOverlayProcessor>; |
| |
| using UnderlayTest = OverlayTest<UnderlayOverlayProcessor>; |
| using TransparentUnderlayTest = |
| OverlayTest<TransparentUnderlayOverlayProcessor>; |
| #if BUILDFLAG(ENABLE_CAST_OVERLAY_STRATEGY) |
| using UnderlayCastTest = OverlayTest<UnderlayCastOverlayProcessor>; |
| #endif |
| using MultiOverlayTest = UseMultipleOverlaysTest<MultiOverlayProcessor>; |
| using MultiUnderlayTest = UseMultipleOverlaysTest<MultiUnderlayProcessor>; |
| using MultiSingleOnTopOverlayTest = |
| UseMultipleOverlaysTest<MultiSingleOnTopProcessor>; |
| using SizeSortedMultiSingeOnTopOverlayTest = |
| UseMultipleOverlaysTest<SizeSortedMultiSingleOnTopProcessor>; |
| |
| using SizeSortedMultiUnderlayOverlayTest = |
| UseMultipleOverlaysTest<SizeSortedMultiUnderlayProcessor>; |
| using SizeSortedMultiOverlayTest = |
| UseMultipleOverlaysTest<SizeSortedMultiOverlayProcessor>; |
| using TypeAndSizeSortedMultiOverlayTest = |
| UseMultipleOverlaysTest<TypeAndSizeSortedMultiOverlayProcessor>; |
| using AllowCandidateWithMasksSortedMultiOverlayTest = |
| UseMultipleOverlaysTest<AllowCandidateWithMasksSortedMultiOverlayProcessor>; |
| |
| TEST(OverlayTest, OverlaysProcessorHasStrategy) { |
| auto overlay_processor = std::make_unique<TestOverlayProcessor>(); |
| EXPECT_GE(2U, overlay_processor->GetStrategyCount()); |
| } |
| |
| #if !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_WIN) |
| TEST_F(FullscreenOverlayTest, SuccessfulOverlay) { |
| auto pass = CreateRenderPass(); |
| gfx::Rect output_rect = pass->output_rect; |
| TextureDrawQuad* original_quad = CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| ResourceId original_resource_id = original_quad->resource_id(); |
| |
| // Add something behind it. |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| ASSERT_EQ(1U, candidate_list.size()); |
| |
| // Check that all the quads are gone. |
| EXPECT_EQ(0U, main_pass->quad_list.size()); |
| // Check that we have only one overlay. |
| EXPECT_EQ(1U, candidate_list.size()); |
| // Check that the right resource id got extracted. |
| EXPECT_EQ(original_resource_id, candidate_list.front().resource_id); |
| gfx::Rect overlay_damage_rect = |
| overlay_processor_->GetAndResetOverlayDamage(); |
| EXPECT_EQ(output_rect, overlay_damage_rect); |
| } |
| |
| TEST_F(FullscreenOverlayTest, FailIfFullscreenQuadHasRoundedDisplayMasks) { |
| gfx::Rect rect = kOverlayRect; |
| |
| auto pass = CreateRenderPass(); |
| |
| // Create a full-screen quad. |
| CreateQuadWithRoundedDisplayMasksAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, rect, |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(10, 0)); |
| |
| // Add something behind it. |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(0U, candidate_list.size()); |
| |
| // Check that the 2 quads are not gone. |
| EXPECT_EQ(2U, main_pass->quad_list.size()); |
| } |
| |
| TEST_F(FullscreenOverlayTest, FailOnOutputColorMatrix) { |
| auto pass = CreateRenderPass(); |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Add something behind it. |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| // This is passing a non-identity color matrix which will result in disabling |
| // overlays since color matrices are not supported yet. |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetNonIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(0U, candidate_list.size()); |
| |
| // Check that the 2 quads are not gone. |
| EXPECT_EQ(2U, main_pass->quad_list.size()); |
| } |
| |
| TEST_F(FullscreenOverlayTest, AlphaFail) { |
| auto pass = CreateRenderPass(); |
| CreateTransparentCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| pass->output_rect); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| // Check that all the quads are gone. |
| EXPECT_EQ(1U, main_pass->quad_list.size()); |
| // Check that we have only one overlay. |
| EXPECT_EQ(0U, candidate_list.size()); |
| } |
| |
| TEST_F(FullscreenOverlayTest, SuccessfulResourceSizeInPixels) { |
| auto pass = CreateRenderPass(); |
| TextureDrawQuad* original_quad = CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| original_quad->set_resource_size_in_pixels(gfx::Size(64, 64)); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| |
| // Check that the quad is gone. |
| EXPECT_EQ(0U, main_pass->quad_list.size()); |
| } |
| |
| TEST_F(FullscreenOverlayTest, OnTopFail) { |
| auto pass = CreateRenderPass(); |
| |
| // Add something in front of it. |
| CreateOpaqueQuadAt(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayTopLeftRect); |
| |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(0U, candidate_list.size()); |
| |
| // Check that the 2 quads are not gone. |
| EXPECT_EQ(2U, main_pass->quad_list.size()); |
| } |
| |
| TEST_F(FullscreenOverlayTest, NotCoveringFullscreenFail) { |
| auto pass = CreateRenderPass(); |
| gfx::Rect inset_rect = pass->output_rect; |
| inset_rect.Inset(gfx::Insets::VH(1, 0)); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| inset_rect); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(0U, candidate_list.size()); |
| |
| // Check that the quad is not gone. |
| EXPECT_EQ(1U, main_pass->quad_list.size()); |
| } |
| |
| TEST_F(FullscreenOverlayTest, RemoveFullscreenQuadFromQuadList) { |
| auto pass = CreateRenderPass(); |
| |
| // Add something in front of it that is fully transparent. |
| pass->shared_quad_state_list.back()->opacity = 0.0f; |
| CreateOpaqueQuadAt(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayTopLeftRect); |
| |
| SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState(); |
| shared_state->opacity = 1.f; |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| |
| // Check that the fullscreen quad is gone. |
| for (const DrawQuad* quad : main_pass->quad_list) { |
| EXPECT_NE(main_pass->output_rect, quad->rect); |
| } |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, SuccessfulOverlay) { |
| auto pass = CreateRenderPass(); |
| TextureDrawQuad* original_quad = CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| ResourceId original_resource_id = original_quad->resource_id(); |
| |
| // Add something behind it. |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| |
| // Check that the quad is gone. |
| EXPECT_EQ(2U, main_pass->quad_list.size()); |
| const auto& quad_list = main_pass->quad_list; |
| for (auto it = quad_list.BackToFrontBegin(); it != quad_list.BackToFrontEnd(); |
| ++it) { |
| EXPECT_NE(DrawQuad::Material::kTextureContent, it->material); |
| } |
| |
| // Check that the right resource id got extracted. |
| EXPECT_EQ(original_resource_id, candidate_list.back().resource_id); |
| } |
| |
| TEST_F(MultiSingleOnTopOverlayTest, |
| SuccessfulOverlay_OccludedByOverlayRoundedDisplayMaskCandidate) { |
| auto pass = CreateRenderPass(); |
| |
| // Add a quad with rounded-display masks. |
| CreateFullscreenQuadWithRoundedDisplayMasks( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(10, 0)); |
| overlay_processor_->AddExpectedRect(kOverlayRect, /*response=*/true); |
| |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| overlay_processor_->AddExpectedRect(kOverlayRect, /*response=*/true); |
| |
| // Add something behind it. |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| // Since the rounded-display mask quad is an overlay candidate, it will not |
| // occlude the other potential single-on-top candidate. Both the |
| // rounded-display mask quad and other single-on-top candidate quad will get |
| // promoted. |
| ASSERT_EQ(2U, candidate_list.size()); |
| |
| // Check that the two quads are gone. |
| EXPECT_EQ(1U, main_pass->quad_list.size()); |
| } |
| |
| TEST_F(MultiSingleOnTopOverlayTest, RoundedDisplayMaskCandidateNotDrawnOnTop) { |
| auto pass = CreateRenderPass(); |
| |
| const auto kSmallCandidateRect = gfx::Rect(16, 0, 16, 16); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kSmallCandidateRect); |
| overlay_processor_->AddExpectedRect(kSmallCandidateRect, /*response=*/true); |
| |
| // Add a quad with rounded-display masks that is an overlay candidate. It will |
| // not be promoted since it is not drawn on top. |
| CreateQuadWithRoundedDisplayMasksAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, gfx::Rect(0, 0, 16, 100), |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(10, 0)); |
| |
| const auto kMediumCandidateRect = gfx::Rect(16, 16, 32, 32); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kMediumCandidateRect); |
| overlay_processor_->AddExpectedRect(kMediumCandidateRect, /*response=*/true); |
| |
| // This quad is occluded by the quad with mask candidate, so will not be |
| // promoted. |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| gfx::Rect(0, 16, 16, 16)); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| // The first two candidates are promoted as they are not occluded. |
| ASSERT_EQ(2U, candidate_list.size()); |
| |
| // Candidate with mask candidate should be composited since it is not drawn |
| // on top. |
| for (const auto& candidate : candidate_list) { |
| EXPECT_FALSE(candidate.has_rounded_display_masks); |
| } |
| |
| EXPECT_EQ(2U, main_pass->quad_list.size()); |
| } |
| |
| using DeathMultiSingleOnTopOverlayTest = MultiSingleOnTopOverlayTest; |
| TEST_F(DeathMultiSingleOnTopOverlayTest, |
| RoundedDisplayMaskCandidatesOverlapsEachOther) { |
| auto pass = CreateRenderPass(); |
| |
| // Add a quad with rounded-display masks. |
| CreateQuadWithRoundedDisplayMasksAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, kOverlayTopLeftRect, |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(10, 0)); |
| |
| // Add a quad with rounded-display masks. |
| CreateFullscreenQuadWithRoundedDisplayMasks( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(10, 0)); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| EXPECT_DCHECK_DEATH(overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_)); |
| } |
| |
| TEST_F(DeathMultiSingleOnTopOverlayTest, |
| RoundedDisplayMaskCandidateIsNotOverlayCandidate) { |
| auto pass = CreateRenderPass(); |
| |
| // Add a quad with rounded-display masks that is not an overlay candidate. |
| CreateFullscreenQuadWithRoundedDisplayMasks( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/false, |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(10, 0)); |
| |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| EXPECT_DCHECK_DEATH(overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_)); |
| } |
| |
| TEST_F(MultiSingleOnTopOverlayTest, |
| PromoteRoundedDisplayMaskCandidateIfOccludeOtherCandidates) { |
| // Given different rect sizes, the candidate will be out of draw order once |
| // sorted. |
| auto pass = CreateRenderPass(); |
| |
| constexpr auto kCandidateRect1 = gfx::Rect(0, 0, 256, 10); |
| |
| // Add a quad with rounded-display masks that is an overlay candidate. |
| CreateQuadWithRoundedDisplayMasksAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, kCandidateRect1, |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(0, 10)); |
| |
| constexpr gfx::Rect kCandidateRect2 = {0, 0, 64, 64}; |
| |
| // This candidate is not occluded by quad with rounded-display masks was it |
| // does not intersect any of the mask rect. |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kCandidateRect2); |
| |
| // This candidate occludes quad with rounded-display masks. |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayTopRightRect); |
| |
| overlay_processor_->AddExpectedRect(kCandidateRect2, true); |
| overlay_processor_->AddExpectedRect(kOverlayTopRightRect, true); |
| |
| // Candidates with masks are appended at the end of the surfaces in |
| // `CheckOverlaySupportImpl()` in their respective draw order. |
| overlay_processor_->AddExpectedRect(kCandidateRect1, true); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| // All the quads will get promoted. |
| ASSERT_EQ(3U, candidate_list.size()); |
| EXPECT_EQ(0U, main_pass->quad_list.size()); |
| } |
| |
| TEST_F( |
| MultiSingleOnTopOverlayTest, |
| FailToPromoteRoundedDisplayMaskCandidatesIfTheyDontOccludeOtherCandidates) { |
| // Given different rect sizes, the candidate will be out of draw order once |
| // sorted. |
| auto pass = CreateRenderPass(); |
| |
| // Add a quad with rounded-display masks that is an overlay candidate. |
| CreateQuadWithRoundedDisplayMasksAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, gfx::Rect(0, 0, 256, 10), |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(0, 10)); |
| |
| // Add a quad with rounded-display masks that is an overlay candidate. |
| CreateQuadWithRoundedDisplayMasksAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, gfx::Rect(0, 20, 256, 10), |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(0, 10)); |
| |
| // This candidate does not occludes any of the quads with rounded-display |
| // masks. |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayTopLeftRect); |
| overlay_processor_->AddExpectedRect(kOverlayTopLeftRect, true); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| // Only the candidate without rounded-display masks will be promoted. |
| ASSERT_EQ(1U, candidate_list.size()); |
| // Confirm that candidates with rounded-display masks are not promoted. |
| EXPECT_FALSE(candidate_list.back().has_rounded_display_masks); |
| EXPECT_EQ(2U, main_pass->quad_list.size()); |
| } |
| |
| TEST_F(MultiSingleOnTopOverlayTest, |
| OcclusionOptimizationForRoundedDisplayMaskCandidate) { |
| auto pass = CreateRenderPass(); |
| |
| // Add a quad with rounded-display masks that is an overlay candidate. This |
| // will not get promoted since it does not occlude any other candidate. |
| const auto* not_promoted_candidate = CreateQuadWithRoundedDisplayMasksAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, gfx::Rect(0, 0, 100, 10), |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(10, 10)); |
| |
| constexpr gfx::Rect kCandidateWithMaskRect2 = gfx::Rect(0, 100, 100, 10); |
| |
| // Add a quad with rounded-display masks that is an overlay candidate. This |
| // will get promoted since it occlude candidate at bounds `kCandidateRect2`. |
| CreateQuadWithRoundedDisplayMasksAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, kCandidateWithMaskRect2, |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(10, 10)); |
| |
| constexpr gfx::Rect kCandidateRect1 = gfx::Rect(10, 0, 50, 50); |
| |
| // Even though it intersects the display_rect of quad with rounded-corners, it |
| // is not occluded since it does not intersects one of the mask rects. |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kCandidateRect1); |
| |
| constexpr gfx::Rect kCandidateRect2 = gfx::Rect(0, 90, 50, 50); |
| |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kCandidateRect2); |
| |
| overlay_processor_->AddExpectedRect(kCandidateRect1, /*response=*/true); |
| |
| overlay_processor_->AddExpectedRect(kCandidateRect2, /*response=*/true); |
| |
| // Candidates with masks are appended at the end of the surfaces in |
| // `CheckOverlaySupportImpl()` in their respective draw order. |
| overlay_processor_->AddExpectedRect(kCandidateWithMaskRect2, |
| /*response=*/true); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| ASSERT_EQ(3U, candidate_list.size()); |
| |
| // Check that the top quad is gone. |
| EXPECT_EQ(1U, main_pass->quad_list.size()); |
| |
| for (const auto& candidate : candidate_list) { |
| EXPECT_NE(candidate.resource_id, not_promoted_candidate->resource_id()); |
| } |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, PrioritizeBiggerOne) { |
| auto pass = CreateRenderPass(); |
| // Add a small quad. |
| const auto kSmallCandidateRect = gfx::Rect(0, 0, 16, 16); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kSmallCandidateRect); |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kSmallCandidateRect)); |
| |
| // Add a bigger quad below the previous one, but not occluded. |
| const auto kBigCandidateRect = gfx::Rect(20, 20, 32, 32); |
| TextureDrawQuad* quad_big = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kBigCandidateRect); |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kBigCandidateRect)); |
| |
| ResourceId resource_big = quad_big->resource_id(); |
| |
| // Add something behind it. |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| // Simplify by adding full root damage. |
| surface_damage_rect_list.push_back(pass->output_rect); |
| pass_list.push_back(std::move(pass)); |
| overlay_processor_->SetFrameSequenceNumber(1); |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| |
| // Check that one quad is gone. |
| EXPECT_EQ(2U, main_pass->quad_list.size()); |
| // Check that we have only one overlay. |
| EXPECT_EQ(1U, candidate_list.size()); |
| // Check that the right resource id (bigger quad) got extracted. |
| EXPECT_EQ(resource_big, candidate_list.front().resource_id); |
| } |
| |
| // It is possible (but unlikely) that the candidate tracking ids are not unique. |
| // This might result in some mis-prioritization but should not result in any |
| // significant error. Here we test that if we have two candidates with same |
| // tracking id the first candidate in the root is selected for overlay. |
| TEST_F(SingleOverlayOnTopTest, CandidateIdCollision) { |
| auto pass = CreateRenderPass(); |
| const auto kCandidateRect = gfx::Rect(0, 0, 16, 16); |
| TextureDrawQuad* quad_a = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kCandidateRect); |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kCandidateRect)); |
| ResourceId resource_a = quad_a->resource_id(); |
| |
| TextureDrawQuad* quad_b = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kCandidateRect); |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kCandidateRect)); |
| |
| // Add something behind it. |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| // Simplify by adding full root damage. |
| surface_damage_rect_list.push_back(pass->output_rect); |
| |
| // Code to make sure the 'unique' tracking ids are actually identical. |
| OverlayCandidate candidate_a; |
| auto color_mat = GetIdentityColorMatrix(); |
| auto candidate_factory = OverlayCandidateFactory( |
| pass.get(), resource_provider_.get(), &surface_damage_rect_list, |
| &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters, |
| kTestOverlayContext); |
| auto ret_a = candidate_factory.FromDrawQuad(quad_a, candidate_a); |
| OverlayCandidate candidate_b; |
| auto ret_b = candidate_factory.FromDrawQuad(quad_b, candidate_b); |
| EXPECT_EQ(OverlayCandidate::CandidateStatus::kSuccess, ret_a); |
| EXPECT_EQ(OverlayCandidate::CandidateStatus::kSuccess, ret_b); |
| |
| // This line confirms that these two quads have the same tracking id. |
| EXPECT_EQ(candidate_a.tracking_id, candidate_b.tracking_id); |
| |
| pass_list.push_back(std::move(pass)); |
| overlay_processor_->SetFrameSequenceNumber(1); |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, color_mat, render_pass_filters, |
| render_pass_backdrop_filters, std::move(surface_damage_rect_list), |
| nullptr, &candidate_list, &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| |
| // Check that one quad is gone. |
| EXPECT_EQ(2U, main_pass->quad_list.size()); |
| // Check that we have only one overlay. |
| EXPECT_EQ(1U, candidate_list.size()); |
| // Check that the right resource id (the first one) got extracted. |
| EXPECT_EQ(resource_a, candidate_list.front().resource_id); |
| } |
| |
| // Tests to make sure that quads from different surfaces have different |
| // candidate tracking ids. |
| TEST_F(SingleOverlayOnTopTest, CandidateTrackIdUniqueSurface) { |
| auto pass = CreateRenderPass(); |
| const auto kCandidateRect = gfx::Rect(0, 0, 16, 16); |
| TextureDrawQuad* quad_a = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kCandidateRect, SurfaceId(FrameSinkId(1, 1), LocalSurfaceId())); |
| TextureDrawQuad* quad_b = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kCandidateRect, SurfaceId(FrameSinkId(2, 2), LocalSurfaceId())); |
| // Code to make sure the 'unique' tracking ids are actually different. |
| OverlayCandidate candidate_a; |
| SurfaceDamageRectList surface_damage_rect_list; |
| auto color_mat = GetIdentityColorMatrix(); |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| auto candidate_factory = OverlayCandidateFactory( |
| pass.get(), resource_provider_.get(), &surface_damage_rect_list, |
| &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters, |
| kTestOverlayContext); |
| auto ret_a = candidate_factory.FromDrawQuad(quad_a, candidate_a); |
| OverlayCandidate candidate_b; |
| auto ret_b = candidate_factory.FromDrawQuad(quad_b, candidate_b); |
| EXPECT_EQ(OverlayCandidate::CandidateStatus::kSuccess, ret_a); |
| EXPECT_EQ(OverlayCandidate::CandidateStatus::kSuccess, ret_b); |
| |
| // This line confirms that these two quads have different tracking ids. |
| EXPECT_NE(candidate_a.tracking_id, candidate_b.tracking_id); |
| } |
| |
| // This test makes sure that the prioritization choices remain stable over a |
| // series of many frames. The example here would be two similar sized unoccluded |
| // videos running at 30fps. It is possible (observed on android) for these |
| // frames to get staggered such that each video frame updates on alternating |
| // frames of the 60fps vsync. Under specific damage conditions this will lead |
| // prioritization to be very indecisive and flip priorities every frame. The |
| // root cause for this issue has been resolved. |
| TEST_F(SingleOverlayOnTopTest, StablePrioritizeIntervalFrame) { |
| const auto kCandidateRectA = gfx::Rect(0, 0, 16, 16); |
| // Add a bigger quad below the previous one, but not occluded. |
| const auto kCandidateRectB = gfx::Rect(20, 20, 16, 16); |
| float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f}; |
| ResourceId resource_id_a = |
| CreateResource(resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), kCandidateRectA.size(), |
| true /*is_overlay_candidate*/); |
| ResourceId resource_id_b = |
| CreateResource(resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), kCandidateRectB.size(), |
| true /*is_overlay_candidate*/); |
| ResourceId prev_resource = kInvalidResourceId; |
| int num_overlay_swaps = 0; |
| // The number of frames here is very high to simulate two videos or animations |
| // playing each at 30fps. |
| constexpr int kNumFrames = 300; |
| for (int i = 1; i < kNumFrames; i++) { |
| auto pass = CreateRenderPass(); |
| SharedQuadState* shared_quad_state_a = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| shared_quad_state_a->overlay_damage_index = 0; |
| TextureDrawQuad* quad_small = |
| pass->CreateAndAppendDrawQuad<TextureDrawQuad>(); |
| quad_small->SetNew( |
| shared_quad_state_a, kCandidateRectA, kCandidateRectA, |
| false /*needs_blending*/, resource_id_a, false /*premultiplied_alpha*/, |
| kUVTopLeft, kUVBottomRight, SkColors::kTransparent, vertex_opacity, |
| false /*flipped*/, false /*nearest_neighbor*/, |
| false /*secure_output_only*/, gfx::ProtectedVideoType::kClear); |
| quad_small->set_resource_size_in_pixels(kCandidateRectA.size()); |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kCandidateRectA)); |
| |
| SharedQuadState* shared_quad_state_b = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| TextureDrawQuad* quad_big = |
| pass->CreateAndAppendDrawQuad<TextureDrawQuad>(); |
| |
| quad_big->SetNew(shared_quad_state_b, kCandidateRectB, kCandidateRectB, |
| false /*needs_blending*/, resource_id_b, |
| false /*premultiplied_alpha*/, kUVTopLeft, kUVBottomRight, |
| SkColors::kTransparent, vertex_opacity, false /*flipped*/, |
| false /*nearest_neighbor*/, false /*secure_output_only*/, |
| gfx::ProtectedVideoType::kClear); |
| quad_big->set_resource_size_in_pixels(kCandidateRectB.size()); |
| |
| shared_quad_state_b->overlay_damage_index = 1; |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kCandidateRectB)); |
| |
| // Add something behind it. |
| SharedQuadState* default_shared_quad_state = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| default_shared_quad_state, pass.get()); |
| |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| OverlayCandidateList candidate_list; |
| SurfaceDamageRectList surface_damage_rect_list; |
| // Alternatively add damage to each potential overlay. |
| surface_damage_rect_list.push_back((i % 2) == 0 ? kCandidateRectA |
| : gfx::Rect()); |
| surface_damage_rect_list.push_back((i % 2) == 0 ? gfx::Rect() |
| : kCandidateRectB); |
| |
| overlay_processor_->SetFrameSequenceNumber(i); |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| |
| if (prev_resource != candidate_list.front().resource_id) { |
| if (prev_resource != kInvalidResourceId) { |
| num_overlay_swaps++; |
| } |
| prev_resource = candidate_list.front().resource_id; |
| } |
| } |
| // Note the value of |kMaxNumSwaps| is not simply 2 or 3 due to some possible |
| // additional swaps that can occur as part of initial overlay tracking. |
| constexpr int kMaxNumSwaps = 10; |
| EXPECT_LE(num_overlay_swaps, kMaxNumSwaps); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, OpaqueOverlayDamageSubtract) { |
| // This tests a specific damage optimization where an opaque pure overlay can |
| // subtract damage from the primary plane even if the overlay does not have a |
| // |shared_quad_state| surface damage index. |
| constexpr int kCandidateSmall = 64; |
| const gfx::Rect kOverlayDisplayRect = {10, 10, kCandidateSmall, |
| kCandidateSmall}; |
| const gfx::Rect kDamageRect[] = { |
| gfx::Rect(10, 10, kCandidateSmall, kCandidateSmall), |
| gfx::Rect(0, 10, kCandidateSmall, kCandidateSmall), |
| gfx::Rect(6, 6, kCandidateSmall, kCandidateSmall)}; |
| |
| const gfx::Rect kExpectedDamage[] = { |
| gfx::Rect(), gfx::Rect(0, 10, 10, kCandidateSmall), |
| gfx::Rect(6, 6, kCandidateSmall, kCandidateSmall)}; |
| |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kOverlayDisplayRect)); |
| for (size_t i = 0; i < std::size(kDamageRect); ++i) { |
| SCOPED_TRACE(i); |
| |
| auto pass = CreateRenderPass(); |
| SharedQuadState* damaged_shared_quad_state = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| |
| auto* quad = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), damaged_shared_quad_state, pass.get(), |
| kOverlayDisplayRect); |
| |
| quad->needs_blending = false; |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| SurfaceDamageRectList surface_damage_rect_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| |
| pass_list.push_back(std::move(pass)); |
| damage_rect_ = kDamageRect[i]; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| EXPECT_EQ(damage_rect_, kExpectedDamage[i]); |
| } |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, NonOpaquePureOverlayFirstFrameDamage) { |
| // This test makes sure that non-opaque pure overlays fully damage the overlay |
| // rect region. This allows for a final update of the primary plane on the |
| // frame of promotion. The example for where this is necessary is the laser |
| // pointing stylus effect. The laser is regularly updated and is non-opaque |
| // overlay. On the frame of promotion the old laser position can be cleared |
| // with a new one now updated in what will be the overlay. We must also damage |
| // the primary plane in this case to clear the previously composited laser |
| // stylus effect. |
| constexpr int kCandidateSmall = 64; |
| const gfx::Rect kOverlayDisplayRect(10, 10, kCandidateSmall, kCandidateSmall); |
| |
| const gfx::Rect kExpectedDamage[] = { |
| kOverlayDisplayRect, |
| gfx::Rect(), |
| gfx::Rect(), |
| }; |
| |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kOverlayDisplayRect)); |
| for (size_t i = 0; i < std::size(kExpectedDamage); ++i) { |
| SCOPED_TRACE(i); |
| |
| auto pass = CreateRenderPass(); |
| |
| auto* quad = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayDisplayRect); |
| |
| // We are intentionally testing a non-opaque overlay . |
| quad->needs_blending = true; |
| |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| |
| pass_list.push_back(std::move(pass)); |
| damage_rect_ = gfx::Rect(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(damage_rect_, kExpectedDamage[i]); |
| } |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, NonOpaquePureOverlayNonOccludingDamage) { |
| // This tests a specific damage optimization where a pure overlay (aka not an |
| // underlay) which is non opaque removes the damage associated with the |
| // overlay quad but occludes no other surface damage. This is in contrast to |
| // opaque overlays which can occlude damage beneath them. |
| constexpr int kCandidateSmall = 64; |
| const gfx::Rect kOverlayDisplayRect = {10, 10, kCandidateSmall, |
| kCandidateSmall}; |
| const gfx::Rect kInFrontDamage = {0, 0, 16, 16}; |
| const gfx::Rect kBehindOverlayDamage = {10, 10, 32, 32}; |
| |
| const gfx::Rect kExpectedDamage[] = { |
| kInFrontDamage, kInFrontDamage, |
| // As the overlay transitions to transparent it must contribute damage. |
| gfx::UnionRects(kInFrontDamage, kOverlayDisplayRect), |
| // After the transition, the overlay itself doesn't contribute damage. |
| gfx::UnionRects(kInFrontDamage, kBehindOverlayDamage)}; |
| |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kOverlayDisplayRect)); |
| for (size_t i = 0; i < std::size(kExpectedDamage); ++i) { |
| SCOPED_TRACE(i); |
| |
| auto pass = CreateRenderPass(); |
| SharedQuadState* damaged_shared_quad_state = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| |
| // Create surface damages corresponding to the in front damage, the overlay |
| // damage, and finally the behind overlay damage. |
| SurfaceDamageRectList surface_damage_rect_list; |
| surface_damage_rect_list.push_back(kInFrontDamage); |
| surface_damage_rect_list.push_back(kOverlayDisplayRect); |
| damaged_shared_quad_state->overlay_damage_index = 1; |
| surface_damage_rect_list.push_back(kBehindOverlayDamage); |
| |
| auto* quad = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), damaged_shared_quad_state, pass.get(), |
| kOverlayDisplayRect); |
| |
| // After the first two frames we test non opaque overlays. |
| quad->needs_blending = i >= 2; |
| |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| |
| pass_list.push_back(std::move(pass)); |
| damage_rect_ = kOverlayRect; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(damage_rect_, kExpectedDamage[i]); |
| } |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, DamageRect) { |
| auto pass = CreateRenderPass(); |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| damage_rect_ = kOverlayRect; |
| |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| SharedQuadState* default_damaged_shared_quad_state = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| |
| auto* sqs = pass->shared_quad_state_list.front(); |
| surface_damage_rect_list.emplace_back(damage_rect_); |
| sqs->overlay_damage_index = 0; |
| |
| // Add something behind it. |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| default_damaged_shared_quad_state, pass.get()); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.front(), pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_TRUE(damage_rect_.IsEmpty()); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, DamageWithMutipleSurfaceDamage) { |
| // This test makes sure that damage is not unnecessarily expanded when there |
| // is |SurfaceDamageRectList| that is outside the original damage bounds. See |
| // https://crbug.com/1197609 for context. |
| auto pass = CreateRenderPass(); |
| damage_rect_ = kOverlayTopLeftRect; |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kOverlayTopLeftRect)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| SharedQuadState* default_damaged_shared_quad_state = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| |
| auto* sqs = pass->shared_quad_state_list.front(); |
| surface_damage_rect_list.emplace_back(damage_rect_); |
| // This line adds the unnecessarily damage. |
| surface_damage_rect_list.emplace_back(kOverlayRect); |
| sqs->overlay_damage_index = 0; |
| |
| auto* quad = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get(), kOverlayTopLeftRect); |
| quad->needs_blending = false; |
| // Add something behind it. |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| default_damaged_shared_quad_state, pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_TRUE(damage_rect_.IsEmpty()); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, NoCandidates) { |
| auto pass = CreateRenderPass(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| AggregatedRenderPassList original_pass_list; |
| AggregatedRenderPass::CopyAllForTest(pass_list, &original_pass_list); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(0U, candidate_list.size()); |
| // There should be nothing new here. |
| CompareRenderPassLists(pass_list, original_pass_list); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, OccludedCandidates) { |
| auto pass = CreateRenderPass(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| AggregatedRenderPassList original_pass_list; |
| AggregatedRenderPass::CopyAllForTest(pass_list, &original_pass_list); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(0U, candidate_list.size()); |
| // There should be nothing new here. |
| CompareRenderPassLists(pass_list, original_pass_list); |
| } |
| |
| // Test with multiple render passes. |
| TEST_F(SingleOverlayOnTopTest, MultipleRenderPasses) { |
| auto pass = CreateRenderPass(); |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Add something behind it. |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(1U, candidate_list.size()); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, AcceptBlending) { |
| auto pass = CreateRenderPass(); |
| TextureDrawQuad* quad = CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| quad->needs_blending = true; |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| damage_rect_ = quad->rect; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(1U, candidate_list.size()); |
| EXPECT_FALSE(damage_rect_.IsEmpty()); |
| gfx::Rect overlay_damage_rect = |
| overlay_processor_->GetAndResetOverlayDamage(); |
| EXPECT_FALSE(overlay_damage_rect.IsEmpty()); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, RejectBackgroundColor) { |
| auto pass = CreateRenderPass(); |
| TextureDrawQuad* quad = CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| quad->background_color = SkColors::kRed; |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(0U, candidate_list.size()); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, AcceptBlackBackgroundColor) { |
| auto pass = CreateRenderPass(); |
| TextureDrawQuad* quad = CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| quad->background_color = SkColors::kBlack; |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(1U, candidate_list.size()); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, RejectBlackBackgroundColorWithBlending) { |
| auto pass = CreateRenderPass(); |
| TextureDrawQuad* quad = CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| quad->background_color = SkColors::kBlack; |
| quad->needs_blending = true; |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(0U, candidate_list.size()); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, RejectBlendMode) { |
| auto pass = CreateRenderPass(); |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| pass->shared_quad_state_list.back()->blend_mode = SkBlendMode::kScreen; |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(0U, candidate_list.size()); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, RejectOpacity) { |
| auto pass = CreateRenderPass(); |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| pass->shared_quad_state_list.back()->opacity = 0.5f; |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(0U, candidate_list.size()); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, RejectNearestNeighbor) { |
| auto pass = CreateRenderPass(); |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| static_cast<TextureDrawQuad*>(pass->quad_list.back())->nearest_neighbor = |
| true; |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(0U, candidate_list.size()); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, RejectNonAxisAlignedTransform) { |
| auto pass = CreateRenderPass(); |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| pass->shared_quad_state_list.back() |
| ->quad_to_target_transform.RotateAboutXAxis(45.f); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(0U, candidate_list.size()); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, AllowClipped) { |
| auto pass = CreateRenderPass(); |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| pass->shared_quad_state_list.back()->clip_rect = kOverlayClipRect; |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(1U, candidate_list.size()); |
| } |
| |
| TEST_F(UnderlayTest, ReplacementQuad) { |
| auto pass = CreateRenderPass(); |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, pass_list.size()); |
| ASSERT_EQ(1U, pass_list.front()->quad_list.size()); |
| EXPECT_EQ(SkColors::kTransparent, static_cast<SolidColorDrawQuad*>( |
| pass_list.front()->quad_list.front()) |
| ->color); |
| EXPECT_FALSE(pass_list.front()->quad_list.front()->ShouldDrawWithBlending()); |
| EXPECT_FALSE(pass_list.front() |
| ->quad_list.front() |
| ->shared_quad_state->are_contents_opaque); |
| } |
| |
| TEST_F(UnderlayTest, AllowVerticalFlip) { |
| gfx::Rect rect = kOverlayRect; |
| rect.set_width(rect.width() / 2); |
| rect.Offset(0, -rect.height()); |
| auto pass = CreateRenderPass(); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), rect); |
| pass->shared_quad_state_list.back()->quad_to_target_transform.Scale(2.0f, |
| -1.0f); |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| EXPECT_EQ(gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL, |
| absl::get<gfx::OverlayTransform>(candidate_list.back().transform)); |
| } |
| |
| TEST_F(UnderlayTest, AllowHorizontalFlip) { |
| gfx::Rect rect = kOverlayRect; |
| rect.set_height(rect.height() / 2); |
| rect.Offset(-rect.width(), 0); |
| auto pass = CreateRenderPass(); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), rect); |
| pass->shared_quad_state_list.back()->quad_to_target_transform.Scale(-1.0f, |
| 2.0f); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| EXPECT_EQ(gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL, |
| absl::get<gfx::OverlayTransform>(candidate_list.back().transform)); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, AllowPositiveScaleTransform) { |
| gfx::Rect rect = kOverlayRect; |
| rect.set_width(rect.width() / 2); |
| auto pass = CreateRenderPass(); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), rect); |
| pass->shared_quad_state_list.back()->quad_to_target_transform.Scale(2.0f, |
| 1.0f); |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(1U, candidate_list.size()); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, AcceptMirrorYTransform) { |
| gfx::Rect rect = kOverlayRect; |
| rect.Offset(0, -rect.height()); |
| auto pass = CreateRenderPass(); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), rect); |
| pass->shared_quad_state_list.back()->quad_to_target_transform.Scale(1.f, |
| -1.f); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| } |
| |
| TEST_F(UnderlayTest, Allow90DegreeRotation) { |
| gfx::Rect rect = kOverlayRect; |
| rect.Offset(0, -rect.height()); |
| auto pass = CreateRenderPass(); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), rect); |
| pass->shared_quad_state_list.back() |
| ->quad_to_target_transform.RotateAboutZAxis(90.f); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_90, |
| absl::get<gfx::OverlayTransform>(candidate_list.back().transform)); |
| } |
| |
| TEST_F(UnderlayTest, Allow180DegreeRotation) { |
| gfx::Rect rect = kOverlayRect; |
| rect.Offset(-rect.width(), -rect.height()); |
| auto pass = CreateRenderPass(); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), rect); |
| pass->shared_quad_state_list.back() |
| ->quad_to_target_transform.RotateAboutZAxis(180.f); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_180, |
| absl::get<gfx::OverlayTransform>(candidate_list.back().transform)); |
| } |
| |
| TEST_F(UnderlayTest, Allow270DegreeRotation) { |
| gfx::Rect rect = kOverlayRect; |
| rect.Offset(-rect.width(), 0); |
| auto pass = CreateRenderPass(); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), rect); |
| pass->shared_quad_state_list.back() |
| ->quad_to_target_transform.RotateAboutZAxis(270.f); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_270, |
| absl::get<gfx::OverlayTransform>(candidate_list.back().transform)); |
| } |
| |
| TEST_F(UnderlayTest, AllowsOpaqueCandidates) { |
| auto pass = CreateRenderPass(); |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()) |
| ->needs_blending = false; |
| pass->shared_quad_state_list.front()->opacity = 1.0; |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| } |
| |
| TEST_F(UnderlayTest, DisallowsQuadsWithRoundedDisplayMasks) { |
| gfx::Rect rect = kOverlayRect; |
| |
| auto pass = CreateRenderPass(); |
| CreateQuadWithRoundedDisplayMasksAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, rect, |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(10, 0)); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| ASSERT_EQ(0U, candidate_list.size()); |
| } |
| |
| TEST_F(UnderlayTest, DisallowsTransparentCandidates) { |
| auto pass = CreateRenderPass(); |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()) |
| ->needs_blending = true; |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(0U, candidate_list.size()); |
| } |
| |
| TEST_F(UnderlayTest, DisallowFilteredQuadOnTop) { |
| auto pass = CreateRenderPass(); |
| |
| AggregatedRenderPassId render_pass_id{3}; |
| AggregatedRenderPassDrawQuad* quad = |
| pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); |
| quad->SetNew(pass->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, |
| render_pass_id, kInvalidResourceId, gfx::RectF(), gfx::Size(), |
| gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f); |
| |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()) |
| ->needs_blending = false; |
| pass->shared_quad_state_list.front()->opacity = 1.0; |
| |
| cc::FilterOperations filters; |
| filters.Append(cc::FilterOperation::CreateBlurFilter(10.f)); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| |
| render_pass_backdrop_filters[render_pass_id] = &filters; |
| |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(0U, candidate_list.size()); |
| } |
| |
| TEST_F(UnderlayTest, AllowFilteredQuadOnTopForProtectedVideo) { |
| auto pass = CreateRenderPass(); |
| |
| AggregatedRenderPassId render_pass_id{3}; |
| AggregatedRenderPassDrawQuad* quad = |
| pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); |
| quad->SetNew(pass->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, |
| render_pass_id, kInvalidResourceId, gfx::RectF(), gfx::Size(), |
| gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f); |
| |
| CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| pass->output_rect, gfx::ProtectedVideoType::kHardwareProtected, |
| MultiPlaneFormat::kNV12) |
| ->needs_blending = false; |
| pass->shared_quad_state_list.front()->opacity = 1.0; |
| |
| cc::FilterOperations filters; |
| filters.Append(cc::FilterOperation::CreateBlurFilter(10.f)); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| |
| render_pass_backdrop_filters[render_pass_id] = &filters; |
| |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| } |
| |
| TEST_F(TransparentUnderlayTest, AllowsOpaqueCandidates) { |
| auto pass = CreateRenderPass(); |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()) |
| ->needs_blending = false; |
| pass->shared_quad_state_list.front()->opacity = 1.0; |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| } |
| |
| TEST_F(TransparentUnderlayTest, AllowsTransparentCandidates) { |
| auto pass = CreateRenderPass(); |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()) |
| ->needs_blending = true; |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, AllowNotTopIfNotOccluded) { |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kOverlayBottomRightRect)); |
| |
| auto pass = CreateRenderPass(); |
| CreateOpaqueQuadAt(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayTopLeftRect); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayBottomRightRect); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(1U, candidate_list.size()); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, AllowTransparentOnTop) { |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kOverlayBottomRightRect)); |
| |
| auto pass = CreateRenderPass(); |
| SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState(); |
| shared_state->opacity = 0.f; |
| CreateSolidColorQuadAt(shared_state, SkColors::kBlack, pass.get(), |
| kOverlayBottomRightRect); |
| shared_state = pass->CreateAndAppendSharedQuadState(); |
| shared_state->opacity = 1.f; |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| shared_state, pass.get(), kOverlayBottomRightRect); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(1U, candidate_list.size()); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, AllowTransparentColorOnTop) { |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kOverlayBottomRightRect)); |
| |
| auto pass = CreateRenderPass(); |
| CreateSolidColorQuadAt(pass->shared_quad_state_list.back(), |
| SkColors::kTransparent, pass.get(), |
| kOverlayBottomRightRect); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayBottomRightRect); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(1U, candidate_list.size()); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, RejectOpaqueColorOnTop) { |
| auto pass = CreateRenderPass(); |
| SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState(); |
| shared_state->opacity = 0.5f; |
| CreateSolidColorQuadAt(shared_state, SkColors::kBlack, pass.get(), |
| kOverlayBottomRightRect); |
| shared_state = pass->CreateAndAppendSharedQuadState(); |
| shared_state->opacity = 1.f; |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| shared_state, pass.get(), kOverlayBottomRightRect); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(0U, candidate_list.size()); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, RejectTransparentColorOnTopWithoutBlending) { |
| auto pass = CreateRenderPass(); |
| SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState(); |
| CreateSolidColorQuadAt(shared_state, SkColors::kTransparent, pass.get(), |
| kOverlayBottomRightRect) |
| ->needs_blending = false; |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| shared_state, pass.get(), kOverlayBottomRightRect); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(0U, candidate_list.size()); |
| } |
| |
| // Test makes sure promotion hint (|overlay_priority_hint| in |TextureDrawQuad|) |
| // feature functions. The (current) expectation is that |kLow| will not promote |
| // and that |kRequired| will be promoted in preference to |kRegular| candidates. |
| TEST_F(SingleOverlayOnTopTest, CheckPromotionHintBasic) { |
| // Test has two passes: |
| // Pass 1 checks kLow and kRegular values. |
| constexpr size_t kTestRegularAtFrame = 3; |
| // Pass 2 checks kRequired against kRegular values. |
| constexpr size_t kTestRequiredAtFrame = 6; |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kOverlayTopLeftRect)); |
| for (size_t i = 0; i <= kTestRequiredAtFrame; ++i) { |
| auto pass = CreateRenderPass(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| SharedQuadState* sqs_partial = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| auto* tex_quad_partial = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), sqs_partial, pass.get(), kOverlayTopLeftRect); |
| auto inset_rect_cpy = kOverlayTopLeftRect; |
| // This 'Inset' makes sure the damage is a partial fraction of the |
| // |display_rect|. |
| inset_rect_cpy.Inset(8); |
| |
| sqs_partial->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.push_back(inset_rect_cpy); |
| tex_quad_partial->overlay_priority_hint = i > kTestRegularAtFrame |
| ? OverlayPriority::kRequired |
| : OverlayPriority::kRegular; |
| |
| // Full damaged quad with a different rect; specifically |
| // |kOverlayBottomRightRect|. |
| SharedQuadState* sqs_full = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| auto* tex_quad_full = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), sqs_full, pass.get(), kOverlayBottomRightRect); |
| sqs_partial->overlay_damage_index = surface_damage_rect_list.size(); |
| tex_quad_full->overlay_priority_hint = i > kTestRegularAtFrame |
| ? OverlayPriority::kRegular |
| : OverlayPriority::kLow; |
| // Damage is 100% of |display_rect|. |
| surface_damage_rect_list.push_back(kOverlayBottomRightRect); |
| |
| // Add something behind it. |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->SetFrameSequenceNumber(static_cast<int64_t>(i)); |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| if (i == kTestRegularAtFrame) { |
| EXPECT_EQ(1U, candidate_list.size()); |
| if (!candidate_list.empty()) { |
| // Check that it was the partial damaged one that got promoted. |
| EXPECT_EQ(kOverlayTopLeftRect, |
| gfx::ToRoundedRect(candidate_list.back().display_rect)); |
| } |
| } else if (i == kTestRequiredAtFrame) { |
| EXPECT_EQ(1U, candidate_list.size()); |
| if (!candidate_list.empty()) { |
| // Check that it was the partial damaged one that got promoted. |
| EXPECT_EQ(kOverlayTopLeftRect, |
| gfx::ToRoundedRect(candidate_list.back().display_rect)); |
| // Also check that the required flag is set. |
| EXPECT_TRUE(candidate_list.back().requires_overlay); |
| } |
| } |
| } |
| } |
| |
| TEST_F(ChangeSingleOnTopTest, DoNotPromoteIfContentsDontChange) { |
| // Overlay demotion for unchanging overlays is frame counter based because of |
| // overlay prioritization. |
| size_t kFramesSkippedBeforeNotPromoting = |
| overlay_processor_->TrackerConfigAccessor().max_num_frames_avg; |
| |
| ResourceId previous_resource_id; |
| int64_t frame_counter = 0; |
| for (size_t i = 0; i < 3 + kFramesSkippedBeforeNotPromoting; ++i) { |
| SCOPED_TRACE(i); |
| |
| auto pass = CreateRenderPass(); |
| AggregatedRenderPass* main_pass = pass.get(); |
| |
| ResourceId resource_id; |
| if (i == 0 || i == 1) { |
| // Create a unique resource only for the first 2 frames. |
| resource_id = CreateResource( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->output_rect.size(), |
| true /*is_overlay_candidate*/); |
| previous_resource_id = resource_id; |
| } else { |
| // Starting the 3rd frame, they should have the same resource ID. |
| resource_id = previous_resource_id; |
| } |
| |
| // Create a quad with the resource ID selected above. |
| TextureDrawQuad* original_quad = |
| main_pass->CreateAndAppendDrawQuad<TextureDrawQuad>(); |
| float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f}; |
| original_quad->SetNew( |
| pass->shared_quad_state_list.back(), pass->output_rect, |
| pass->output_rect, false /*needs_blending*/, resource_id, |
| false /*premultiplied_alpha*/, kUVTopLeft, kUVBottomRight, |
| SkColors::kTransparent, vertex_opacity, false /*flipped*/, |
| false /*nearest_neighbor*/, false /*secure_output_only*/, |
| gfx::ProtectedVideoType::kClear); |
| original_quad->set_resource_size_in_pixels(pass->output_rect.size()); |
| |
| // Add something behind it. |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), main_pass); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->SetFrameSequenceNumber(frame_counter); |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| frame_counter++; |
| if (i <= kFramesSkippedBeforeNotPromoting) { |
| EXPECT_EQ(1U, candidate_list.size()); |
| if (candidate_list.size()) { |
| // Check that the right resource id got extracted. |
| EXPECT_EQ(resource_id, candidate_list.back().resource_id); |
| } |
| // Check that the quad is gone. |
| EXPECT_EQ(1U, main_pass->quad_list.size()); |
| } else { |
| // Check nothing has been promoted. |
| EXPECT_EQ(2U, main_pass->quad_list.size()); |
| } |
| } |
| } |
| |
| TEST_F(FullThresholdTest, ThresholdTestForPrioritization) { |
| int64_t frame_counter = 0; |
| // This is a helper function to simulate framerates. |
| |
| // Damage rate here should be 1.0f |
| auto wait_1_frame = [&]() { frame_counter++; }; |
| |
| // Damage rate here should be 0.25 which should be less than |
| // |damage_rate_threshold| for this test to work |
| auto wait_4_frames = [&]() { frame_counter += 4; }; |
| |
| // This test uses many iterations to test prioritization threshold features |
| // due to frame averaging over samples. |
| OverlayCandidateTemporalTracker::Config config; |
| |
| // Computes the number of frames to clear out the existing temporal value |
| // using exponential smoothing. The computation looks like |
| // clear_threashold=1.0*(exp_rate)^num_frames_to_clear. In our case |
| // clear_threashold=1/num_frames_to_clear and |
| // exp_rate=(num_frames_to_clear-1)/num_frames_to_clear. We take the log to |
| // find num_frames_to_clear |
| const int kNumFramesClear = |
| static_cast<int>(std::ceil(std::log(1.0f / config.max_num_frames_avg) / |
| std::log((config.max_num_frames_avg - 1.0f) / |
| config.max_num_frames_avg))); |
| |
| size_t kDamageFrameTestStart = kNumFramesClear; |
| size_t kDamageFrameTestEnd = kDamageFrameTestStart + kNumFramesClear; |
| size_t kSlowFrameTestStart = kDamageFrameTestEnd + kNumFramesClear; |
| size_t kSlowFrameTestEnd = kSlowFrameTestStart + kNumFramesClear; |
| |
| // This quad is used to occlude the damage of the overlay candidate to the |
| // point that the damage is no longer considered significant. |
| auto nearly_occluding_quad = kOverlayRect; |
| nearly_occluding_quad.Inset(1); |
| |
| for (size_t i = 0; i < kSlowFrameTestEnd; ++i) { |
| SCOPED_TRACE(i); |
| |
| if (i >= kSlowFrameTestStart && i < kSlowFrameTestEnd) { |
| wait_4_frames(); |
| } else { |
| wait_1_frame(); |
| } |
| |
| auto pass = CreateRenderPass(); |
| AggregatedRenderPass* main_pass = pass.get(); |
| |
| bool nearly_occluded = |
| i >= kDamageFrameTestStart && i < kDamageFrameTestEnd; |
| CreateSolidColorQuadAt( |
| pass->shared_quad_state_list.back(), SkColors::kBlack, pass.get(), |
| nearly_occluded ? nearly_occluding_quad : kOverlayTopLeftRect); |
| |
| // Create a quad with the resource ID selected above. |
| TextureDrawQuad* quad_candidate = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayRect); |
| |
| quad_candidate->set_resource_size_in_pixels(pass->output_rect.size()); |
| |
| // Add something behind it. |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), main_pass); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| surface_damage_rect_list.push_back(kOverlayRect); |
| overlay_processor_->SetFrameSequenceNumber(frame_counter); |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| EXPECT_EQ(3u, main_pass->quad_list.size()); |
| |
| if (i == kDamageFrameTestStart - 1 || i == kSlowFrameTestStart - 1) { |
| // Test to make sure an overlay was promoted. |
| EXPECT_EQ(1U, candidate_list.size()); |
| } else if (i == kDamageFrameTestEnd - 1 || i == kSlowFrameTestEnd - 1) { |
| // Test to make sure no overlay was promoted |
| EXPECT_EQ(0u, candidate_list.size()); |
| } |
| } |
| } |
| |
| TEST_F(UnderlayTest, OverlayLayerUnderMainLayer) { |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kOverlayBottomRightRect)); |
| |
| auto pass = CreateRenderPass(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayBottomRightRect); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| EXPECT_EQ(-1, candidate_list[0].plane_z_order); |
| EXPECT_EQ(2U, main_pass->quad_list.size()); |
| // The overlay quad should have changed to a SOLID_COLOR quad. |
| EXPECT_EQ(main_pass->quad_list.back()->material, |
| DrawQuad::Material::kSolidColor); |
| auto* quad = static_cast<SolidColorDrawQuad*>(main_pass->quad_list.back()); |
| EXPECT_EQ(quad->rect, quad->visible_rect); |
| EXPECT_EQ(false, quad->needs_blending); |
| EXPECT_EQ(SkColors::kTransparent, quad->color); |
| } |
| |
| TEST_F(UnderlayTest, AllowOnTop) { |
| auto pass = CreateRenderPass(); |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| pass->CreateAndAppendSharedQuadState()->opacity = 0.5f; |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| EXPECT_EQ(-1, candidate_list[0].plane_z_order); |
| // The overlay quad should have changed to a SOLID_COLOR quad. |
| EXPECT_EQ(main_pass->quad_list.front()->material, |
| DrawQuad::Material::kSolidColor); |
| auto* quad = static_cast<SolidColorDrawQuad*>(main_pass->quad_list.front()); |
| EXPECT_EQ(quad->rect, quad->visible_rect); |
| EXPECT_EQ(false, quad->needs_blending); |
| EXPECT_EQ(SkColors::kTransparent, quad->color); |
| } |
| |
| // Pure overlays have a very specific optimization that does not produce damage |
| // on promotion because it is not required. However the same rect overlay |
| // transitions to an underlay the entire |display_rect| must damage the primary |
| // plane.This allows for the transparent black window to be drawn allowing the |
| // underlay to show through. |
| TEST_F(TransitionOverlayTypeTest, DamageChangeOnTransistionOverlayType) { |
| static const int kOverlayFrameStart = 3; |
| static const int kOverlayFrameEnd = 5; |
| for (int i = 0; i < 8; ++i) { |
| SCOPED_TRACE(i); |
| |
| auto pass = CreateRenderPass(); |
| damage_rect_ = kOverlayTopLeftRect; |
| |
| SharedQuadState* default_damaged_shared_quad_state = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| |
| SurfaceDamageRectList surface_damage_rect_list; |
| auto* sqs = pass->shared_quad_state_list.front(); |
| sqs->overlay_damage_index = 0; |
| surface_damage_rect_list.emplace_back(damage_rect_); |
| |
| // A partially occluding quad is used to force an underlay rather than pure |
| // overlay. |
| if (i >= kOverlayFrameStart && i < kOverlayFrameEnd) { |
| CreateSolidColorQuadAt(pass->shared_quad_state_list.back(), |
| SkColors::kBlack, pass.get(), kOverlayTopLeftRect); |
| } |
| |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.front(), |
| pass.get()); |
| |
| // Add something behind it. |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| default_damaged_shared_quad_state, pass.get()); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| ASSERT_EQ(candidate_list.size(), 1U); |
| |
| if (i < kOverlayFrameStart) { |
| // A pure overlay does not produce damage on promotion and all associated |
| // damage with this quad is excluded. |
| EXPECT_GE(candidate_list[0].plane_z_order, 0); |
| EXPECT_TRUE(damage_rect_.IsEmpty()); |
| } else if (i == kOverlayFrameStart) { |
| // An underlay must draw a transparent black window to the primary plane |
| // to show through. |
| EXPECT_LT(candidate_list[0].plane_z_order, 0); |
| EXPECT_EQ(damage_rect_, kOverlayRect); |
| } else if (i < kOverlayFrameEnd) { |
| EXPECT_LT(candidate_list[0].plane_z_order, 0); |
| // Empty damage is expect for an underlay for all frames after promotion. |
| EXPECT_TRUE(damage_rect_.IsEmpty()); |
| } else if (i >= kOverlayFrameEnd) { |
| EXPECT_GE(candidate_list[0].plane_z_order, 0); |
| // Underlay to pure overlay transition should not produce any damage. |
| EXPECT_TRUE(damage_rect_.IsEmpty()); |
| } |
| } |
| } |
| |
| TEST_F(TransitionOverlayTypeTest, DamageWhenOverlayBecomesTransparent) { |
| constexpr gfx::Rect kTopLeft(0, 0, 128, 128); |
| // constexpr gfx::Rect kOccludesTopLeft(64, 64, 128, 128); |
| |
| static const int kTransparentFrameStart = 3; |
| for (int i = 0; i < 8; ++i) { |
| SCOPED_TRACE(i); |
| |
| auto pass = CreateRenderPass(); |
| damage_rect_ = kTopLeft; |
| |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| if (i < kTransparentFrameStart) { |
| // Create opaque candidate in top left. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kTopLeft); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get(), kTopLeft); |
| } else { |
| // Create transparent candidate in top left. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kTopLeft); |
| CreateTransparentCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get(), kTopLeft); |
| } |
| overlay_processor_->AddExpectedRect(gfx::RectF(kTopLeft)); |
| |
| { |
| // Add something behind it. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), sqs, pass.get()); |
| } |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| ASSERT_EQ(candidate_list.size(), 1U); |
| |
| if (i < kTransparentFrameStart) { |
| // A pure overlay does not produce damage on promotion and all associated |
| // damage with this quad is excluded. |
| EXPECT_EQ(candidate_list[0].plane_z_order, 1); |
| EXPECT_TRUE(damage_rect_.IsEmpty()); |
| } else if (i == kTransparentFrameStart) { |
| // When an opaque overlay becomes transparent it must contribute damage to |
| // update any damage we may have occluded while it was opaque. |
| EXPECT_EQ(candidate_list[0].plane_z_order, 1); |
| EXPECT_EQ(damage_rect_, kTopLeft); |
| } else if (i > kTransparentFrameStart) { |
| // After the overlay is transparent it doesn't need to contribute damage. |
| EXPECT_EQ(candidate_list[0].plane_z_order, 1); |
| EXPECT_TRUE(damage_rect_.IsEmpty()); |
| } |
| } |
| } |
| |
| // A candidate with a mask filter must go to underlay, and not single on top. |
| // Also, the quad must be replaced by a black quad with |SkBlendMode::kDstOut|. |
| TEST_F(TransitionOverlayTypeTest, MaskFilterBringsUnderlay) { |
| auto pass = CreateRenderPass(); |
| damage_rect_ = kOverlayRect; |
| |
| SharedQuadState* default_damaged_shared_quad_state = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| |
| SurfaceDamageRectList surface_damage_rect_list; |
| auto* sqs = pass->shared_quad_state_list.front(); |
| sqs->overlay_damage_index = 0; |
| surface_damage_rect_list.emplace_back(damage_rect_); |
| sqs->mask_filter_info = |
| gfx::MaskFilterInfo(gfx::RectF(kOverlayRect), gfx::RoundedCornersF(1.f), |
| gfx::LinearGradient::GetEmpty()); |
| CreateFullscreenCandidateQuad(resource_provider_.get(), |
| child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get()); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| default_damaged_shared_quad_state, pass.get()); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| ASSERT_EQ(candidate_list.size(), 1U); |
| EXPECT_LT(candidate_list.front().plane_z_order, 0); |
| |
| ASSERT_EQ(1U, pass_list.size()); |
| ASSERT_EQ(2U, pass_list.front()->quad_list.size()); |
| EXPECT_EQ(SkColors::kBlack, static_cast<SolidColorDrawQuad*>( |
| pass_list.front()->quad_list.front()) |
| ->color); |
| EXPECT_FALSE(pass_list.front() |
| ->quad_list.front() |
| ->shared_quad_state->are_contents_opaque); |
| EXPECT_EQ( |
| SkBlendMode::kDstOut, |
| pass_list.front()->quad_list.front()->shared_quad_state->blend_mode); |
| } |
| |
| // The first time an underlay is scheduled its damage must not be excluded. |
| TEST_F(UnderlayTest, InitialUnderlayDamageNotExcluded) { |
| auto pass = CreateRenderPass(); |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| |
| damage_rect_ = kOverlayRect; |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| EXPECT_EQ(kOverlayRect, damage_rect_); |
| } |
| |
| // An identical underlay for two frames in a row means the damage can be |
| // excluded the second time. On demotion the frame damage will be the display |
| // rect of the underlay. After the demotion there will be no exclusion of |
| // damage. |
| TEST_F(UnderlayTest, DamageExcludedForConsecutiveIdenticalUnderlays) { |
| static const int kDemotionFrame = 3; |
| for (int i = 0; i < 5; ++i) { |
| SCOPED_TRACE(i); |
| |
| auto pass = CreateRenderPass(); |
| damage_rect_ = kOverlayTopLeftRect; |
| |
| SharedQuadState* default_damaged_shared_quad_state = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| |
| SurfaceDamageRectList surface_damage_rect_list; |
| if (i < kDemotionFrame) { |
| auto* sqs = pass->shared_quad_state_list.front(); |
| sqs->overlay_damage_index = 0; |
| surface_damage_rect_list.emplace_back(damage_rect_); |
| |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.front(), |
| pass.get()); |
| } |
| // Add something behind it. |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| default_damaged_shared_quad_state, pass.get()); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| if (i == 0) { |
| // A promoted underlay needs to damage the primary plane on the first |
| // frame of promotion. |
| EXPECT_EQ(kOverlayRect, damage_rect_); |
| } else if (i < kDemotionFrame) { |
| EXPECT_TRUE(damage_rect_.IsEmpty()); |
| } else if (i == kDemotionFrame) { |
| // A demoted underlay needs to damage the primary plane on the frame of |
| // demotion. |
| EXPECT_EQ(kOverlayRect, damage_rect_); |
| } else if (i > kDemotionFrame) { |
| // No overlay candidates so the damage will be whatever root damage was |
| // input to the overlay processsor. |
| EXPECT_EQ(damage_rect_, kOverlayTopLeftRect); |
| } |
| } |
| } |
| |
| // Underlay damage can only be excluded if the previous frame's underlay |
| // was the same rect. |
| TEST_F(UnderlayTest, DamageNotExcludedForNonIdenticalConsecutiveUnderlays) { |
| gfx::Rect kSmallTestRect = gfx::Rect(5, 5, 20, 20); |
| gfx::Rect overlay_rects[] = {kOverlayBottomRightRect, kSmallTestRect, |
| kOverlayTopLeftRect, kOverlayRect}; |
| gfx::Rect prev_rect; |
| for (auto&& curr_rect : overlay_rects) { |
| SCOPED_TRACE(curr_rect.ToString()); |
| |
| AddExpectedRectToOverlayProcessor(gfx::RectF(curr_rect)); |
| |
| auto pass = CreateRenderPass(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| auto* sqs = pass->shared_quad_state_list.front(); |
| sqs->overlay_damage_index = 0; |
| damage_rect_ = gfx::Rect(10, 10, 10, 10); |
| surface_damage_rect_list.emplace_back(damage_rect_); |
| |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| curr_rect); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| // This is a union as the demoted underlay display rect is added as damage |
| // as well as the newly promoted underlay display rect (updating the primary |
| // plane for underlays). |
| gfx::Rect temp_union = prev_rect; |
| temp_union.Union(curr_rect); |
| EXPECT_EQ(temp_union, damage_rect_); |
| prev_rect = curr_rect; |
| } |
| } |
| |
| // Underlay damage can only be excluded if the previous frame's underlay exists. |
| TEST_F(UnderlayTest, DamageNotExcludedForNonConsecutiveIdenticalUnderlays) { |
| bool has_fullscreen_candidate[] = {true, false, true, true, true, false}; |
| |
| for (int i = 0; i < 3; ++i) { |
| SCOPED_TRACE(i); |
| |
| auto pass = CreateRenderPass(); |
| damage_rect_ = kOverlayRect; |
| |
| SharedQuadState* default_damaged_shared_quad_state = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| |
| SurfaceDamageRectList surface_damage_rect_list; |
| auto* sqs = pass->shared_quad_state_list.front(); |
| sqs->overlay_damage_index = 0; |
| surface_damage_rect_list.emplace_back(damage_rect_); |
| |
| if (has_fullscreen_candidate[i]) { |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.front(), |
| pass.get()); |
| } |
| |
| // Add something behind it. |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| default_damaged_shared_quad_state, pass.get()); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| if (i == 0) { |
| EXPECT_FALSE(damage_rect_.IsEmpty()); |
| } else { |
| EXPECT_EQ(damage_rect_.IsEmpty(), |
| has_fullscreen_candidate[i] && has_fullscreen_candidate[i - 1]); |
| } |
| } |
| } |
| |
| // Underlay damage cannot be excluded if the underlay has a mask filter in the |
| // current frame but did not in the previous frame or vice versa. |
| TEST_F( |
| UnderlayTest, |
| DamageNotExcludedForConsecutiveUnderlaysIfOneHasMaskFilterAndOtherDoesNot) { |
| constexpr bool kHasMaskFilter[] = {true, false, true, false, true, |
| true, true, false, false, false}; |
| |
| for (int i = 0; i < 10; ++i) { |
| SCOPED_TRACE(i); |
| |
| auto pass = CreateRenderPass(); |
| damage_rect_ = kOverlayRect; |
| |
| SharedQuadState* default_damaged_shared_quad_state = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| |
| SurfaceDamageRectList surface_damage_rect_list; |
| auto* sqs = pass->shared_quad_state_list.front(); |
| sqs->overlay_damage_index = 0; |
| surface_damage_rect_list.emplace_back(damage_rect_); |
| if (kHasMaskFilter[i]) { |
| sqs->mask_filter_info = gfx::MaskFilterInfo( |
| gfx::RectF(kOverlayRect), gfx::RoundedCornersF(1.f), |
| gfx::LinearGradient::GetEmpty()); |
| } |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| sqs, pass.get(), kOverlayRect); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| default_damaged_shared_quad_state, pass.get()); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| if (i == 0) { |
| EXPECT_FALSE(damage_rect_.IsEmpty()); |
| } else { |
| EXPECT_EQ(damage_rect_.IsEmpty(), |
| kHasMaskFilter[i] == kHasMaskFilter[i - 1]); |
| } |
| } |
| } |
| |
| TEST_F(UnderlayTest, DamageExcludedForCandidateAndThoseOccluded) { |
| for (int i = 0; i < 3; ++i) { |
| SCOPED_TRACE(i); |
| |
| auto pass = CreateRenderPass(); |
| damage_rect_ = kOverlayRect; |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| if (i == 1) { |
| // Create damages in front which should not be excluded. |
| surface_damage_rect_list.emplace_back(kOverlayTopLeftRect); |
| auto* sqs = pass->shared_quad_state_list.front(); |
| sqs->overlay_damage_index = 1; |
| surface_damage_rect_list.emplace_back(damage_rect_); |
| } else { |
| auto* sqs = pass->shared_quad_state_list.front(); |
| sqs->overlay_damage_index = 0; |
| surface_damage_rect_list.emplace_back(damage_rect_); |
| } |
| |
| if (i > 1) { |
| // This damage is after our overlay will be excluded from our final |
| // damage. |
| surface_damage_rect_list.emplace_back(kOverlayTopLeftRect); |
| } |
| |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.front(), |
| pass.get()); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| // The damage rect should not be excluded if the underlay has been promoted |
| // this frame. |
| if (i == 0) { |
| EXPECT_FALSE(damage_rect_.IsEmpty()); |
| } else if (i == 1) { |
| // Additional damage at i == 1 should also not be excluded as it comes in |
| // front of our underlay. |
| EXPECT_EQ(damage_rect_, kOverlayTopLeftRect); |
| } |
| } |
| // The second time the same overlay rect is scheduled it should be excluded |
| // from the damage rect. |
| EXPECT_TRUE(damage_rect_.IsEmpty()); |
| } |
| |
| TEST_F(UnderlayTest, DamageExtractedWhenQuadsAboveDontOverlap) { |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kOverlayBottomRightRect)); |
| |
| for (int i = 0; i < 2; ++i) { |
| auto pass = CreateRenderPass(); |
| |
| SharedQuadState* default_damaged_shared_quad_state = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| |
| SurfaceDamageRectList surface_damage_rect_list; |
| default_damaged_shared_quad_state->overlay_damage_index = 0; |
| surface_damage_rect_list.emplace_back(kOverlayTopLeftRect); |
| |
| auto* sqs = pass->shared_quad_state_list.front(); |
| sqs->overlay_damage_index = 1; |
| surface_damage_rect_list.emplace_back(kOverlayBottomRightRect); |
| |
| // Add a non-overlapping quad above the candidate. |
| CreateOpaqueQuadAt(resource_provider_.get(), |
| default_damaged_shared_quad_state, pass.get(), |
| kOverlayTopLeftRect); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.front(), pass.get(), |
| kOverlayBottomRightRect); |
| |
| damage_rect_ = kOverlayBottomRightRect; |
| damage_rect_.Union(kOverlayTopLeftRect); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| } |
| |
| EXPECT_EQ(damage_rect_, kOverlayTopLeftRect); |
| } |
| |
| TEST_F(UnderlayTest, PrimaryPlaneOverlayIsTransparentWithUnderlay) { |
| auto pass = CreateRenderPass(); |
| gfx::Rect output_rect = pass->output_rect; |
| CreateOpaqueQuadAt(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| output_rect, SkColors::kWhite); |
| |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayRect); |
| |
| OverlayCandidateList candidate_list; |
| |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| auto output_surface_plane = overlay_processor_->ProcessOutputSurfaceAsOverlay( |
| kDisplaySize, kDisplaySize, kDefaultBufferFormat, gfx::ColorSpace(), |
| false /* has_alpha */, 1.0f /* opacity */, gpu::Mailbox()); |
| OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane = |
| &output_surface_plane; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), primary_plane, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| EXPECT_EQ(1U, candidate_list.size()); |
| ASSERT_EQ(true, output_surface_plane.enable_blending); |
| } |
| |
| TEST_F(UnderlayTest, UpdateDamageWhenChangingUnderlays) { |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kOverlayTopLeftRect)); |
| |
| for (size_t i = 0; i < 2; ++i) { |
| auto pass = CreateRenderPass(); |
| |
| if (i == 0) { |
| CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), |
| pass.get(), kOverlayRect); |
| } |
| |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayTopLeftRect); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| } |
| |
| EXPECT_EQ(kOverlayRect, damage_rect_); |
| } |
| |
| TEST_F(UnderlayTest, OverlayCandidateTemporalTracker) { |
| ResourceIdGenerator id_generator; |
| uint64_t frame_counter = 0; |
| |
| // Test the default configuration. |
| OverlayCandidateTemporalTracker::Config config; |
| float kDamageEpsilon = 1.0f / config.max_num_frames_avg; |
| float kBelowLowDamage = config.damage_rate_threshold - kDamageEpsilon; |
| float kAboveHighDamage = config.damage_rate_threshold + kDamageEpsilon; |
| float kFullDamage = 1.0f; |
| // This is a helper function to simulate framerates. |
| auto wait_1_frame = [&]() { frame_counter++; }; |
| |
| auto wait_inactive_frames = [&]() { |
| frame_counter += config.max_num_frames_avg + 1; |
| }; |
| |
| // Computes the number of frames to clear out the existing temporal value |
| // using exponential smoothing. The computation looks like |
| // clear_threashold=1.0*(exp_rate)^num_frames_to_clear. In our case |
| // clear_threashold=1/num_frames_to_clear and |
| // exp_rate=(num_frames_to_clear-1)/num_frames_to_clear. We take the log to |
| // find num_frames_to_clear |
| const int kNumFramesClear = |
| static_cast<int>(std::ceil(std::log(1.0f / config.max_num_frames_avg) / |
| std::log((config.max_num_frames_avg - 1.0f) / |
| config.max_num_frames_avg))); |
| |
| OverlayCandidateTemporalTracker tracker; |
| int fake_display_area = 256 * 256; |
| // We test internal hysteresis state by running this test twice. |
| for (int j = 0; j < 2; j++) { |
| SCOPED_TRACE(j); |
| tracker.Reset(); |
| // First setup a 60fps high damage candidate. |
| for (int i = 0; i < kNumFramesClear; i++) { |
| wait_1_frame(); |
| tracker.AddRecord(frame_counter, kFullDamage, |
| id_generator.GenerateNextId(), config); |
| } |
| |
| EXPECT_TRUE(tracker.IsActivelyChanging(frame_counter, config)); |
| auto opaque_power_gain_60_full = |
| tracker.GetModeledPowerGain(frame_counter, config, fake_display_area); |
| |
| EXPECT_NEAR(tracker.MeanFrameRatioRate(config), 1.0f, kDamageEpsilon); |
| EXPECT_GT(opaque_power_gain_60_full, 0); |
| |
| // Test our hysteresis categorization of power by ensuring a single frame |
| // drop does not change the end power categorization. |
| wait_1_frame(); |
| wait_1_frame(); |
| tracker.AddRecord(frame_counter, kFullDamage, id_generator.GenerateNextId(), |
| config); |
| |
| auto opaque_power_gain_60_stutter = |
| tracker.GetModeledPowerGain(frame_counter, config, fake_display_area); |
| |
| // A single frame drop even at 60fps should not change our power |
| // categorization. |
| EXPECT_EQ(opaque_power_gain_60_full, opaque_power_gain_60_stutter); |
| |
| wait_inactive_frames(); |
| EXPECT_FALSE(tracker.IsActivelyChanging(frame_counter, config)); |
| tracker.AddRecord(frame_counter, kFullDamage, id_generator.GenerateNextId(), |
| config); |
| |
| auto opaque_power_gain_60_inactive = |
| tracker.GetModeledPowerGain(frame_counter, config, fake_display_area); |
| // Simple test to make sure that power categorization is not completely |
| // invalidated when candidate becomes inactive. |
| EXPECT_GT(opaque_power_gain_60_inactive, 0); |
| |
| // Now simulate a overlay candidate with 30fps full damage. |
| for (int i = 0; i < kNumFramesClear; i++) { |
| wait_1_frame(); |
| wait_1_frame(); |
| tracker.AddRecord(frame_counter, kFullDamage, |
| id_generator.GenerateNextId(), config); |
| } |
| |
| // Insert single stutter frame here to avoid hysteresis boundary. |
| wait_1_frame(); |
| wait_1_frame(); |
| wait_1_frame(); |
| tracker.AddRecord(frame_counter, kFullDamage, id_generator.GenerateNextId(), |
| config); |
| |
| for (int i = 0; i < kNumFramesClear; i++) { |
| wait_1_frame(); |
| wait_1_frame(); |
| tracker.AddRecord(frame_counter, kFullDamage, |
| id_generator.GenerateNextId(), config); |
| } |
| |
| auto opaque_power_gain_30_full = |
| tracker.GetModeledPowerGain(frame_counter, config, fake_display_area); |
| |
| EXPECT_NEAR(tracker.MeanFrameRatioRate(config), 0.5f, kDamageEpsilon); |
| EXPECT_GT(opaque_power_gain_30_full, 0); |
| EXPECT_GT(opaque_power_gain_60_full, opaque_power_gain_30_full); |
| |
| // Test the hysteresis by checking that a stuttering frame will not change |
| // power categorization. |
| wait_1_frame(); |
| wait_1_frame(); |
| wait_1_frame(); |
| tracker.AddRecord(frame_counter, kFullDamage, id_generator.GenerateNextId(), |
| config); |
| |
| EXPECT_TRUE(tracker.IsActivelyChanging(frame_counter, config)); |
| auto opaque_power_gain_30_stutter = |
| tracker.GetModeledPowerGain(frame_counter, config, fake_display_area); |
| |
| EXPECT_EQ(opaque_power_gain_30_stutter, opaque_power_gain_30_full); |
| |
| wait_inactive_frames(); |
| EXPECT_FALSE(tracker.IsActivelyChanging(frame_counter, config)); |
| tracker.AddRecord(frame_counter, kFullDamage, id_generator.GenerateNextId(), |
| config); |
| |
| auto opaque_power_gain_30_inactive = |
| tracker.GetModeledPowerGain(frame_counter, config, fake_display_area); |
| // Simple test to make sure that power categorization is not completely |
| // invalidated when candidate becomes inactive. |
| EXPECT_GT(opaque_power_gain_30_inactive, 0); |
| |
| tracker.Reset(); |
| // Test low and high damage thresholds. |
| for (int i = 0; i < kNumFramesClear; i++) { |
| wait_1_frame(); |
| tracker.AddRecord(frame_counter, kAboveHighDamage, |
| id_generator.GenerateNextId(), config); |
| } |
| |
| auto opaque_power_gain_high_damage = |
| tracker.GetModeledPowerGain(frame_counter, config, fake_display_area); |
| |
| EXPECT_GT(opaque_power_gain_high_damage, 0); |
| EXPECT_GE(opaque_power_gain_60_full, opaque_power_gain_high_damage); |
| |
| for (int i = 0; i < kNumFramesClear; i++) { |
| wait_1_frame(); |
| tracker.AddRecord(frame_counter, kBelowLowDamage, |
| id_generator.GenerateNextId(), config); |
| } |
| |
| auto opaque_power_gain_low_damage = |
| tracker.GetModeledPowerGain(frame_counter, config, fake_display_area); |
| EXPECT_LT(opaque_power_gain_low_damage, 0); |
| |
| // Test our mean damage ratio computations for our tracker. |
| int avg_range_tracker = config.max_num_frames_avg - 1; |
| float expected_mean = 0.0f; |
| tracker.Reset(); |
| for (int i = 0; i < avg_range_tracker; i++) { |
| wait_1_frame(); |
| float dynamic_damage_ratio = static_cast<float>(i) / avg_range_tracker; |
| expected_mean += dynamic_damage_ratio; |
| tracker.AddRecord(frame_counter, dynamic_damage_ratio, |
| id_generator.GenerateNextId(), config); |
| } |
| |
| expected_mean = expected_mean / avg_range_tracker; |
| |
| EXPECT_FLOAT_EQ(expected_mean, tracker.MeanFrameRatioRate(config)); |
| } |
| |
| EXPECT_FALSE(tracker.IsAbsent()); |
| // After a many absent calls the 'IncAbsent()' function should eventually |
| // return true; indicating this tracker is no longer active. |
| EXPECT_TRUE(tracker.IsAbsent()); |
| |
| wait_1_frame(); |
| tracker.AddRecord(frame_counter, 0.0f, id_generator.GenerateNextId(), config); |
| EXPECT_FALSE(tracker.IsAbsent()); |
| |
| // Tracker forced updates were added to support quads that change content but |
| // not resource ids (example here is low latency ink surface). Here we test |
| // this small feature by keeping the resource id constant but passing in true |
| // to the force update param. |
| tracker.Reset(); |
| static const float kDamageRatio = 0.7f; |
| static const ResourceId kFakeConstantResourceId(13); |
| for (int i = 0; i < config.max_num_frames_avg; i++) { |
| wait_1_frame(); |
| tracker.AddRecord(frame_counter, kDamageRatio, kFakeConstantResourceId, |
| config, true); |
| } |
| EXPECT_FLOAT_EQ(kDamageRatio, tracker.MeanFrameRatioRate(config)); |
| |
| // Now test the false case for the force update param. |
| for (int i = 0; i < config.max_num_frames_avg; i++) { |
| wait_1_frame(); |
| tracker.AddRecord(frame_counter, 0.9f, kFakeConstantResourceId, config, |
| false); |
| } |
| // The damage should remain unchanged. |
| EXPECT_FLOAT_EQ(kDamageRatio, tracker.MeanFrameRatioRate(config)); |
| } |
| |
| TEST_F(UnderlayTest, UpdateDamageRectWhenNoPromotion) { |
| // In the first pass there is an overlay promotion and the expected damage is |
| // a union of the hole made for the underlay and the incoming damage. In the |
| // second pass there is no occluding damage so the incoming damage is |
| // attributed to the overlay candidate and the final output damage is zero. In |
| // the third pass there is no overlay promotion, but the damage should be the |
| // union of the damage_rect with CreateRenderPass's output_rect which is {0, |
| // 0, 256, 256}. This is due to the demotion of the current overlay. |
| bool has_fullscreen_candidate[] = {true, true, false}; |
| gfx::Rect damages[] = {gfx::Rect(0, 0, 32, 32), gfx::Rect(0, 0, 32, 32), |
| gfx::Rect(0, 0, 312, 16)}; |
| gfx::Rect expected_damages[] = {gfx::Rect(0, 0, 256, 256), |
| gfx::Rect(0, 0, 0, 0), |
| gfx::Rect(0, 0, 312, 256)}; |
| size_t expected_candidate_size[] = {1, 1, 0}; |
| |
| for (size_t i = 0; i < std::size(expected_damages); ++i) { |
| SCOPED_TRACE(i); |
| |
| auto pass = CreateRenderPass(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| SharedQuadState* default_damaged_shared_quad_state = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| default_damaged_shared_quad_state->overlay_damage_index = 0; |
| surface_damage_rect_list.emplace_back(gfx::Rect()); |
| |
| if (has_fullscreen_candidate[i]) { |
| auto* sqs = pass->shared_quad_state_list.front(); |
| surface_damage_rect_list.emplace_back(damages[i]); |
| sqs->overlay_damage_index = 1; |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.front(), |
| pass.get()); |
| } |
| |
| gfx::Rect damage_rect{damages[i]}; |
| |
| // Add something behind it. |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get()); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap |
| render_pass_background_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_background_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect, &content_bounds_); |
| |
| EXPECT_EQ(expected_damages[i], damage_rect); |
| ASSERT_EQ(expected_candidate_size[i], candidate_list.size()); |
| } |
| } |
| |
| // Tests that no damage occurs when the quad shared state has no occluding |
| // damage. |
| TEST_F(UnderlayTest, CandidateNoDamageWhenQuadSharedStateNoOccludingDamage) { |
| for (int i = 0; i < 4; ++i) { |
| SCOPED_TRACE(i); |
| |
| auto pass = CreateRenderPass(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| gfx::Rect rect(2, 3); |
| gfx::Rect kSmallDamageRect(1, 1, 10, 10); |
| SharedQuadState* default_damaged_shared_quad_state = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| if (i == 2) { |
| auto* sqs = pass->shared_quad_state_list.front(); |
| sqs->overlay_damage_index = 0; |
| surface_damage_rect_list.emplace_back(0, 0, 20, 20); |
| } else if (i == 3) { |
| auto* sqs = pass->shared_quad_state_list.front(); |
| // For the solid color quad kSmallDamageRect. |
| surface_damage_rect_list.emplace_back(kSmallDamageRect); |
| // For the overlay quad gfx::Rect(0, 0, 20, 20). |
| surface_damage_rect_list.emplace_back(0, 0, 20, 20); |
| sqs->overlay_damage_index = 1; |
| } |
| |
| CreateSolidColorQuadAt(default_damaged_shared_quad_state, SkColors::kBlack, |
| pass.get(), rect); |
| |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.front(), |
| pass.get()); |
| |
| damage_rect_ = kOverlayRect; |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| if (i == 0 || i == 1) { |
| EXPECT_EQ(candidate_list[0].overlay_damage_index, |
| OverlayCandidate::kInvalidDamageIndex); |
| EXPECT_FALSE(damage_rect_.IsEmpty()); |
| } else if (i == 3) { |
| EXPECT_EQ(candidate_list[0].overlay_damage_index, 1U); |
| EXPECT_EQ(damage_rect_, kSmallDamageRect); |
| } else if (i == 2) { |
| EXPECT_EQ(candidate_list[0].overlay_damage_index, 0U); |
| EXPECT_TRUE(damage_rect_.IsEmpty()); |
| } |
| } |
| } |
| #endif // !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_WIN) |
| |
| #if BUILDFLAG(ENABLE_CAST_OVERLAY_STRATEGY) |
| TEST_F(UnderlayCastTest, ReplacementQuad) { |
| auto pass = CreateRenderPass(); |
| CreateVideoHoleDrawQuadAt(pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayRect); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, pass_list.size()); |
| ASSERT_EQ(1U, pass_list.front()->quad_list.size()); |
| EXPECT_EQ(SkColors::kTransparent, static_cast<SolidColorDrawQuad*>( |
| pass_list.front()->quad_list.front()) |
| ->color); |
| EXPECT_FALSE(pass_list.front()->quad_list.front()->ShouldDrawWithBlending()); |
| EXPECT_FALSE(pass_list.front() |
| ->quad_list.front() |
| ->shared_quad_state->are_contents_opaque); |
| } |
| |
| TEST_F(UnderlayCastTest, NoOverlayContentBounds) { |
| auto pass = CreateRenderPass(); |
| |
| CreateOpaqueQuadAt(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayTopLeftRect); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(0U, content_bounds_.size()); |
| } |
| |
| TEST_F(UnderlayCastTest, FullScreenOverlayContentBounds) { |
| auto pass = CreateRenderPass(); |
| CreateVideoHoleDrawQuadAt(pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayRect); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| EXPECT_EQ(1U, content_bounds_.size()); |
| EXPECT_TRUE(content_bounds_[0].IsEmpty()); |
| } |
| |
| TEST_F(UnderlayCastTest, BlackOutsideOverlayContentBounds) { |
| AddExpectedRectToOverlayProcessor(gfx::RectF(kOverlayBottomRightRect)); |
| |
| const gfx::Rect kLeftSide(0, 0, 128, 256); |
| const gfx::Rect kTopRight(128, 0, 128, 128); |
| |
| auto pass = CreateRenderPass(); |
| CreateVideoHoleDrawQuadAt(pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayBottomRightRect); |
| CreateOpaqueQuadAt(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), kLeftSide, |
| SkColors::kBlack); |
| CreateOpaqueQuadAt(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), kTopRight, |
| SkColors::kBlack); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| EXPECT_EQ(1U, content_bounds_.size()); |
| EXPECT_TRUE(content_bounds_[0].IsEmpty()); |
| } |
| |
| TEST_F(UnderlayCastTest, OverlayOccludedContentBounds) { |
| auto pass = CreateRenderPass(); |
| CreateOpaqueQuadAt(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayTopLeftRect); |
| CreateVideoHoleDrawQuadAt(pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayRect); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| EXPECT_EQ(1U, content_bounds_.size()); |
| EXPECT_EQ(kOverlayTopLeftRect, content_bounds_[0]); |
| } |
| |
| TEST_F(UnderlayCastTest, OverlayOccludedUnionContentBounds) { |
| auto pass = CreateRenderPass(); |
| CreateOpaqueQuadAt(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayTopLeftRect); |
| CreateOpaqueQuadAt(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayBottomRightRect); |
| CreateVideoHoleDrawQuadAt(pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayRect); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| EXPECT_EQ(1U, content_bounds_.size()); |
| EXPECT_EQ(kOverlayRect, content_bounds_[0]); |
| } |
| |
| TEST_F(UnderlayCastTest, RoundOverlayContentBounds) { |
| // Check rounding behaviour on overlay quads. Be conservative (content |
| // potentially visible on boundary). |
| const gfx::Rect overlay_rect(1, 1, 8, 8); |
| AddExpectedRectToOverlayProcessor(gfx::RectF(1.5f, 1.5f, 8, 8)); |
| |
| gfx::Transform transform; |
| transform.Translate(0.5f, 0.5f); |
| |
| auto pass = CreateRenderPassWithTransform(transform); |
| CreateVideoHoleDrawQuadAt(pass->shared_quad_state_list.back(), pass.get(), |
| overlay_rect); |
| CreateOpaqueQuadAt(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| gfx::Rect(0, 0, 10, 10), SkColors::kWhite); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| EXPECT_EQ(1U, content_bounds_.size()); |
| EXPECT_EQ(gfx::Rect(0, 0, 11, 11), content_bounds_[0]); |
| } |
| |
| TEST_F(UnderlayCastTest, RoundContentBounds) { |
| // Check rounding behaviour on content quads (bounds should be enclosing |
| // rect). |
| gfx::Rect overlay_rect = kOverlayRect; |
| overlay_rect.Inset(gfx::Insets::TLBR(0, 0, 1, 1)); |
| AddExpectedRectToOverlayProcessor(gfx::RectF(0.5f, 0.5f, 255, 255)); |
| |
| gfx::Transform transform; |
| transform.Translate(0.5f, 0.5f); |
| |
| auto pass = CreateRenderPassWithTransform(transform); |
| CreateVideoHoleDrawQuadAt(pass->shared_quad_state_list.back(), pass.get(), |
| overlay_rect); |
| CreateOpaqueQuadAt(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| gfx::Rect(0, 0, 255, 255), SkColors::kWhite); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| EXPECT_EQ(1U, content_bounds_.size()); |
| EXPECT_EQ(kOverlayRect, content_bounds_[0]); |
| } |
| |
| TEST_F(UnderlayCastTest, NoOverlayPromotionWithoutProtectedContent) { |
| auto pass = CreateRenderPass(); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayRect); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| ASSERT_TRUE(candidate_list.empty()); |
| EXPECT_TRUE(content_bounds_.empty()); |
| } |
| |
| TEST_F(UnderlayCastTest, OverlayPromotionWithMaskFilter) { |
| auto pass = CreateRenderPass(); |
| |
| SurfaceDamageRectList surface_damage_rect_list; |
| auto* sqs = pass->shared_quad_state_list.front(); |
| sqs->overlay_damage_index = 0; |
| surface_damage_rect_list.emplace_back(damage_rect_); |
| sqs->mask_filter_info = |
| gfx::MaskFilterInfo(gfx::RectF(kOverlayRect), gfx::RoundedCornersF(1.f), |
| gfx::LinearGradient::GetEmpty()); |
| CreateVideoHoleDrawQuadAt(sqs, pass.get(), kOverlayRect); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| ASSERT_EQ(1U, content_bounds_.size()); |
| EXPECT_TRUE(content_bounds_.front().IsEmpty()); |
| |
| ASSERT_EQ(1U, pass_list.size()); |
| ASSERT_EQ(1U, pass_list.front()->quad_list.size()); |
| EXPECT_EQ(SkColors::kBlack, static_cast<SolidColorDrawQuad*>( |
| pass_list.front()->quad_list.front()) |
| ->color); |
| EXPECT_FALSE(pass_list.front() |
| ->quad_list.front() |
| ->shared_quad_state->are_contents_opaque); |
| EXPECT_EQ( |
| SkBlendMode::kDstOut, |
| pass_list.front()->quad_list.front()->shared_quad_state->blend_mode); |
| } |
| #endif // BUILDFLAG(ENABLE_CAST_OVERLAY_STRATEGY) |
| |
| #if BUILDFLAG(ALWAYS_ENABLE_BLENDING_FOR_PRIMARY) |
| TEST_F(UnderlayCastTest, PrimaryPlaneOverlayIsAlwaysTransparent) { |
| auto pass = CreateRenderPass(); |
| gfx::Rect output_rect = pass->output_rect; |
| CreateOpaqueQuadAt(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| output_rect, SkColors::kWhite); |
| |
| OverlayCandidateList candidate_list; |
| |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| auto output_surface_plane = overlay_processor_->ProcessOutputSurfaceAsOverlay( |
| kDisplaySize, kDisplaySize, kDefaultBufferFormat, gfx::ColorSpace(), |
| false /* has_alpha */, 1.0f /* opacity */, gpu::Mailbox()); |
| |
| SurfaceDamageRectList surface_damage_rect_list; |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), &output_surface_plane, |
| &candidate_list, &damage_rect_, &content_bounds_); |
| |
| ASSERT_EQ(true, output_surface_plane.enable_blending); |
| EXPECT_EQ(0U, content_bounds_.size()); |
| } |
| #endif // BUILDFLAG(ALWAYS_ENABLE_BLENDING_FOR_PRIMARY) |
| |
| #if BUILDFLAG(IS_APPLE) |
| class CALayerOverlayRPDQTest : public CALayerOverlayTest { |
| protected: |
| void SetUp() override { |
| CALayerOverlayTest::SetUp(); |
| pass_list_.push_back(CreateRenderPass()); |
| pass_ = pass_list_.back().get(); |
| quad_ = pass_->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); |
| render_pass_id_ = 3; |
| } |
| |
| void ProcessForOverlays() { |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list_, GetIdentityColorMatrix(), |
| render_pass_filters_, render_pass_backdrop_filters_, |
| std::move(surface_damage_rect_list_), nullity, &ca_layer_list_, |
| &damage_rect_, &content_bounds_); |
| } |
| AggregatedRenderPassList pass_list_; |
| AggregatedRenderPass* pass_; |
| AggregatedRenderPassDrawQuad* quad_; |
| int render_pass_id_; |
| cc::FilterOperations filters_; |
| cc::FilterOperations backdrop_filters_; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters_; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters_; |
| CALayerOverlayList ca_layer_list_; |
| SurfaceDamageRectList surface_damage_rect_list_; |
| }; |
| |
| TEST_F(CALayerOverlayRPDQTest, AggregatedRenderPassDrawQuadNoFilters) { |
| quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, |
| kOverlayRect, render_pass_id_, kInvalidResourceId, gfx::RectF(), |
| gfx::Size(), gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), |
| false, 1.0f); |
| ProcessForOverlays(); |
| |
| EXPECT_EQ(1U, ca_layer_list_.size()); |
| } |
| |
| TEST_F(CALayerOverlayRPDQTest, AggregatedRenderPassDrawQuadAllValidFilters) { |
| filters_.Append(cc::FilterOperation::CreateGrayscaleFilter(0.1f)); |
| filters_.Append(cc::FilterOperation::CreateSepiaFilter(0.2f)); |
| filters_.Append(cc::FilterOperation::CreateSaturateFilter(0.3f)); |
| filters_.Append(cc::FilterOperation::CreateHueRotateFilter(0.4f)); |
| filters_.Append(cc::FilterOperation::CreateInvertFilter(0.5f)); |
| filters_.Append(cc::FilterOperation::CreateBrightnessFilter(0.6f)); |
| filters_.Append(cc::FilterOperation::CreateContrastFilter(0.7f)); |
| filters_.Append(cc::FilterOperation::CreateOpacityFilter(0.8f)); |
| filters_.Append(cc::FilterOperation::CreateBlurFilter(0.9f)); |
| filters_.Append(cc::FilterOperation::CreateDropShadowFilter( |
| gfx::Point(10, 20), 1.0f, SK_ColorGREEN)); |
| render_pass_filters_[render_pass_id_] = &filters_; |
| quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, |
| kOverlayRect, render_pass_id_, kInvalidResourceId, gfx::RectF(), |
| gfx::Size(), gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), |
| false, 1.0f); |
| ProcessForOverlays(); |
| |
| EXPECT_EQ(1U, ca_layer_list_.size()); |
| } |
| |
| TEST_F(CALayerOverlayRPDQTest, AggregatedRenderPassDrawQuadOpacityFilterScale) { |
| filters_.Append(cc::FilterOperation::CreateOpacityFilter(0.8f)); |
| render_pass_filters_[render_pass_id_] = &filters_; |
| quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, |
| kOverlayRect, render_pass_id_, kInvalidResourceId, gfx::RectF(), |
| gfx::Size(), gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), |
| false, 1.0f); |
| ProcessForOverlays(); |
| EXPECT_EQ(1U, ca_layer_list_.size()); |
| } |
| |
| TEST_F(CALayerOverlayRPDQTest, AggregatedRenderPassDrawQuadBlurFilterScale) { |
| filters_.Append(cc::FilterOperation::CreateBlurFilter(0.8f)); |
| render_pass_filters_[render_pass_id_] = &filters_; |
| quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, |
| kOverlayRect, render_pass_id_, kInvalidResourceId, gfx::RectF(), |
| gfx::Size(), gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), |
| false, 1.0f); |
| ProcessForOverlays(); |
| EXPECT_EQ(1U, ca_layer_list_.size()); |
| } |
| |
| TEST_F(CALayerOverlayRPDQTest, |
| AggregatedRenderPassDrawQuadDropShadowFilterScale) { |
| filters_.Append(cc::FilterOperation::CreateDropShadowFilter( |
| gfx::Point(10, 20), 1.0f, SK_ColorGREEN)); |
| render_pass_filters_[render_pass_id_] = &filters_; |
| quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, |
| kOverlayRect, render_pass_id_, kInvalidResourceId, gfx::RectF(), |
| gfx::Size(), gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), |
| false, 1.0f); |
| ProcessForOverlays(); |
| EXPECT_EQ(1U, ca_layer_list_.size()); |
| } |
| |
| TEST_F(CALayerOverlayRPDQTest, AggregatedRenderPassDrawQuadBackgroundFilter) { |
| backdrop_filters_.Append(cc::FilterOperation::CreateGrayscaleFilter(0.1f)); |
| render_pass_backdrop_filters_[render_pass_id_] = &backdrop_filters_; |
| quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, |
| kOverlayRect, render_pass_id_, kInvalidResourceId, gfx::RectF(), |
| gfx::Size(), gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), |
| false, 1.0f); |
| ProcessForOverlays(); |
| EXPECT_EQ(0U, ca_layer_list_.size()); |
| } |
| |
| TEST_F(CALayerOverlayRPDQTest, AggregatedRenderPassDrawQuadMask) { |
| quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, |
| kOverlayRect, render_pass_id_, ResourceId(2), gfx::RectF(), |
| gfx::Size(), gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), |
| false, 1.0f); |
| ProcessForOverlays(); |
| EXPECT_EQ(1U, ca_layer_list_.size()); |
| } |
| |
| TEST_F(CALayerOverlayRPDQTest, AggregatedRenderPassDrawQuadUnsupportedFilter) { |
| filters_.Append(cc::FilterOperation::CreateZoomFilter(0.9f, 1)); |
| render_pass_filters_[render_pass_id_] = &filters_; |
| quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, |
| kOverlayRect, render_pass_id_, kInvalidResourceId, gfx::RectF(), |
| gfx::Size(), gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), |
| false, 1.0f); |
| ProcessForOverlays(); |
| EXPECT_EQ(0U, ca_layer_list_.size()); |
| } |
| |
| TEST_F(CALayerOverlayRPDQTest, TooManyRenderPassDrawQuads) { |
| filters_.Append(cc::FilterOperation::CreateBlurFilter(0.8f)); |
| int count = 35; |
| |
| for (int i = 0; i < count; ++i) { |
| auto* quad = pass_->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); |
| quad->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, |
| kOverlayRect, render_pass_id_, ResourceId(2), gfx::RectF(), |
| gfx::Size(), gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), |
| false, 1.0f); |
| } |
| |
| ProcessForOverlays(); |
| EXPECT_EQ(0U, ca_layer_list_.size()); |
| } |
| #endif |
| |
| void AddQuad(gfx::Rect quad_rect, |
| const gfx::Transform& quad_to_target_transform, |
| AggregatedRenderPass* render_pass) { |
| SharedQuadState* quad_state = render_pass->CreateAndAppendSharedQuadState(); |
| |
| quad_state->SetAll( |
| /*quad_to_target_transform=*/quad_to_target_transform, quad_rect, |
| /*visible_layer_rect=*/quad_rect, |
| /*mask_filter_info=*/gfx::MaskFilterInfo(), |
| /*clip_rect=*/absl::nullopt, |
| /*are contents opaque=*/true, |
| /*opacity=*/1.f, |
| /*blend_mode=*/SkBlendMode::kSrcOver, /*sorting_context_id=*/0); |
| |
| SolidColorDrawQuad* solid_quad = |
| render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); |
| solid_quad->SetNew(quad_state, quad_rect, quad_rect, SkColors::kBlack, |
| false /* force_anti_aliasing_off */); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, IsOverlayRequiredBasic) { |
| // Add a small quad. |
| auto pass = CreateRenderPass(); |
| const auto kSmallCandidateRect = gfx::Rect(0, 0, 16, 16); |
| auto* new_quad = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kSmallCandidateRect); |
| SurfaceDamageRectList surface_damage_rect_list; |
| OverlayCandidate candidate; |
| auto color_mat = GetIdentityColorMatrix(); |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| auto candidate_factory = OverlayCandidateFactory( |
| pass.get(), resource_provider_.get(), &surface_damage_rect_list, |
| &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters, |
| kTestOverlayContext); |
| candidate_factory.FromDrawQuad(new_quad, candidate); |
| |
| // Verify that a default candidate is not a required overlay. |
| EXPECT_FALSE(candidate.requires_overlay); |
| |
| ASSERT_EQ(gfx::ToRoundedRect(candidate.display_rect), kSmallCandidateRect); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, IsOverlayRequiredHwProtectedVideo) { |
| // Add a small quad. |
| auto pass = CreateRenderPass(); |
| const auto kSmallCandidateRect = gfx::Rect(0, 0, 16, 16); |
| auto* new_quad = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kSmallCandidateRect, gfx::ProtectedVideoType::kHardwareProtected, |
| MultiPlaneFormat::kNV12); |
| SurfaceDamageRectList surface_damage_rect_list; |
| OverlayCandidate candidate; |
| auto color_mat = GetIdentityColorMatrix(); |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| auto candidate_factory = OverlayCandidateFactory( |
| pass.get(), resource_provider_.get(), &surface_damage_rect_list, |
| &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters, |
| kTestOverlayContext); |
| candidate_factory.FromDrawQuad(new_quad, candidate); |
| |
| // Verify that a HW protected video candidate requires overlay. |
| EXPECT_TRUE(candidate.requires_overlay); |
| |
| ASSERT_EQ(gfx::ToRoundedRect(candidate.display_rect), kSmallCandidateRect); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, RequiredOverlayClippingAndSubsampling) { |
| // Add a small quad. |
| auto pass = CreateRenderPass(); |
| const auto kVideoCandidateRect = gfx::Rect(-19, -20, 320, 240); |
| auto* new_quad = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kVideoCandidateRect, gfx::ProtectedVideoType::kHardwareProtected, |
| MultiPlaneFormat::kNV12); |
| pass->shared_quad_state_list.back()->clip_rect = kOverlayClipRect; |
| SurfaceDamageRectList surface_damage_rect_list; |
| OverlayCandidate candidate; |
| auto color_mat = GetIdentityColorMatrix(); |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| auto candidate_factory = OverlayCandidateFactory( |
| pass.get(), resource_provider_.get(), &surface_damage_rect_list, |
| &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters, |
| kTestOverlayContext); |
| candidate_factory.FromDrawQuad(new_quad, candidate); |
| |
| // Default uv rect is 0.1, 0.2, 1.0, 1.0 which in the 320x240 buffer |
| // corresponds to 32, 48, 288x192. That maps to |kVideoCandidateRect| in the |
| // destination space. After clipping by |kOverlayClipRect| that clips the src |
| // rect to be 49.1, 64, 115.2x102.4. After rounding to the nearest subsample |
| // (2x), the result is 48, 64, 114x102. |
| const auto kTargetSrcRect = gfx::Rect(48, 64, 114, 102); |
| EXPECT_EQ(kTargetSrcRect, |
| gfx::ToRoundedRect(gfx::ScaleRect( |
| candidate.uv_rect, candidate.resource_size_in_pixels.width(), |
| candidate.resource_size_in_pixels.height()))); |
| EXPECT_TRUE(candidate.requires_overlay); |
| EXPECT_FALSE(candidate.clip_rect); |
| EXPECT_EQ(gfx::ToRoundedRect(candidate.display_rect), kOverlayClipRect); |
| } |
| |
| TEST_F(SingleOverlayOnTopTest, |
| RequiredOverlayClippingAndSubsamplingWithPrimary) { |
| // Add a small quad. |
| auto pass = CreateRenderPass(); |
| const auto kVideoCandidateRect = gfx::Rect(-19, -20, 320, 240); |
| auto* new_quad = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kVideoCandidateRect, gfx::ProtectedVideoType::kHardwareProtected, |
| MultiPlaneFormat::kNV12); |
| pass->shared_quad_state_list.back()->clip_rect = kOverlayClipRect; |
| SurfaceDamageRectList surface_damage_rect_list; |
| gfx::RectF primary_rect(0, 0, 100, 120); |
| OverlayProcessorInterface::OutputSurfaceOverlayPlane primary_plane; |
| OverlayCandidate candidate; |
| auto color_mat = GetIdentityColorMatrix(); |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| auto candidate_factory = OverlayCandidateFactory( |
| pass.get(), resource_provider_.get(), &surface_damage_rect_list, |
| &color_mat, primary_rect, &render_pass_filters, kTestOverlayContext); |
| candidate_factory.FromDrawQuad(new_quad, candidate); |
| |
| // Default uv rect is 0.1, 0.2, 1.0, 1.0 which in the 320x240 buffer |
| // corresponds to 32, 48, 288x192. That maps to |kVideoCandidateRect| in the |
| // destination space. After clipping by |kOverlayClipRect| and the |
| // |primary_rect| that clips the src rect to be 49.1, 64, 90x96. After |
| // rounding to the nearest subsample (2x), the result is 48, 64, 90x96. |
| const auto kTargetSrcRect = gfx::Rect(48, 64, 90, 96); |
| EXPECT_EQ(kTargetSrcRect, |
| gfx::ToRoundedRect(gfx::ScaleRect( |
| candidate.uv_rect, candidate.resource_size_in_pixels.width(), |
| candidate.resource_size_in_pixels.height()))); |
| EXPECT_TRUE(candidate.requires_overlay); |
| EXPECT_FALSE(candidate.clip_rect); |
| EXPECT_EQ(candidate.display_rect, primary_rect); |
| } |
| |
| TEST_F(UnderlayTest, EstimateOccludedDamage) { |
| // A visual depiction of how this test works. |
| // * - Candidate |
| // # - Occluder |
| // |
| // The first candidate has no quad occlusions. |
| /// |
| // *********** |
| // * * |
| // * * |
| // * * |
| // *********** |
| // |
| // The second candidate has only one quad occlusion. |
| // ######***************** |
| // # # * * |
| // ###### * * |
| // * * * |
| // **********###### * |
| // * # # * |
| // * ###### * |
| // * * |
| // * * |
| // *********************** |
| // Finally the third larger candidate is occluded by both quads. |
| // The |damage_area_estimate| reflects this damage occlusion when |
| // 'EstimateOccludedDamage' is called |
| |
| auto pass = CreateRenderPass(); |
| gfx::Transform identity; |
| identity.MakeIdentity(); |
| |
| // These quads will server to occlude some of our test overlay candidates. |
| const int kOccluderWidth = 10; |
| AddQuad(gfx::Rect(100, 100, kOccluderWidth, kOccluderWidth), identity, |
| pass.get()); |
| AddQuad(gfx::Rect(150, 150, kOccluderWidth, kOccluderWidth), identity, |
| pass.get()); |
| |
| const int kCandidateSmall = 50; |
| const int kCandidateLarge = 100; |
| const gfx::Rect kCandidateRects[] = { |
| gfx::Rect(0, 0, kCandidateSmall, kCandidateSmall), |
| gfx::Rect(100, 100, kCandidateSmall, kCandidateSmall), |
| gfx::Rect(100, 100, kCandidateLarge, kCandidateLarge), kOverlayRect}; |
| |
| const bool kCandidateUseSurfaceIndex[] = {true, true, true, false}; |
| |
| const int kExpectedDamages[] = { |
| kCandidateSmall * kCandidateSmall, |
| kCandidateSmall * kCandidateSmall - kOccluderWidth * kOccluderWidth, |
| kCandidateLarge * kCandidateLarge - kOccluderWidth * kOccluderWidth * 2, |
| kOverlayRect.width() * kOverlayRect.height() - |
| kOccluderWidth * kOccluderWidth * 2}; |
| |
| static_assert( |
| std::size(kCandidateRects) == std::size(kCandidateUseSurfaceIndex), |
| "Number of elements in each list should be the identical."); |
| static_assert(std::size(kCandidateRects) == std::size(kExpectedDamages), |
| "Number of elements in each list should be the identical."); |
| |
| QuadList& quad_list = pass->quad_list; |
| auto occluder_iter_count = quad_list.size(); |
| |
| SurfaceDamageRectList surface_damage_rect_list; |
| for (size_t i = 0; i < std::size(kCandidateRects); ++i) { |
| SCOPED_TRACE(i); |
| |
| // Create fake surface damage for this candidate. |
| SharedQuadState* damaged_shared_quad_state = |
| pass->shared_quad_state_list.AllocateAndCopyFrom( |
| pass->shared_quad_state_list.back()); |
| // We want to test what happens when an overlay candidate does not have an |
| // associated damage index. An estimate damage for this candidate will still |
| // be computed but it will be derived from a union of all surface damages. |
| // TODO(petermcneeley): Update this code when surface damage is made more |
| // reliable. |
| if (kCandidateUseSurfaceIndex[i]) { |
| damaged_shared_quad_state->overlay_damage_index = |
| surface_damage_rect_list.size(); |
| } else { |
| damaged_shared_quad_state->overlay_damage_index.reset(); |
| } |
| surface_damage_rect_list.emplace_back(kCandidateRects[i]); |
| |
| auto* quad_candidate = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), damaged_shared_quad_state, pass.get(), |
| kCandidateRects[i]); |
| |
| OverlayCandidate candidate; |
| auto color_mat = GetIdentityColorMatrix(); |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| auto candidate_factory = OverlayCandidateFactory( |
| pass.get(), resource_provider_.get(), &surface_damage_rect_list, |
| &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters, |
| kTestOverlayContext); |
| candidate_factory.FromDrawQuad(quad_candidate, candidate); |
| |
| // Before the 'EstimateOccludedDamage' function is called the damage area |
| // will just be whatever comes from the |surface_damage_rect_list|. |
| if (kCandidateUseSurfaceIndex[i]) { |
| ASSERT_EQ(kCandidateRects[i].size().GetArea(), |
| candidate.damage_area_estimate); |
| ASSERT_EQ(candidate.overlay_damage_index, i); |
| } else { |
| // In the special case where we have no surface damage index the candidate |
| // area will not simply be the |damage_area_estimate|. |
| ASSERT_FALSE( |
| quad_candidate->shared_quad_state->overlay_damage_index.has_value()); |
| } |
| |
| // Now we test the opaque occlusion provided by 'EstimateOccludedDamage' |
| // function. |
| candidate.damage_area_estimate = candidate_factory.EstimateVisibleDamage( |
| quad_candidate, candidate, quad_list.begin(), |
| std::next(quad_list.begin(), occluder_iter_count)); |
| |
| ASSERT_EQ(kExpectedDamages[i], candidate.damage_area_estimate); |
| } |
| } |
| |
| TEST_F(UnderlayTest, ProtectedVideoOverlayScaling) { |
| // This test verifies the algorithm used when adjusting the scaling for |
| // protected content due to HW overlay scaling limitations where we resort |
| // to clipping when we need to downscale beyond the HW's limits. |
| |
| // 0.5 should fail, and then it'll increase it by 0.5 each try until it |
| // succeeds. Have it succeed at 0.65f. |
| AddScalingSequenceToOverlayProcessor(0.5f, false); |
| AddScalingSequenceToOverlayProcessor(0.55f, false); |
| AddScalingSequenceToOverlayProcessor(0.6f, false); |
| AddScalingSequenceToOverlayProcessor(0.65f, true); |
| // Then send one in at 0.625, which we let pass, that'll establish a high |
| // end working bound of 0.626. |
| AddScalingSequenceToOverlayProcessor(0.625f, true); |
| // Send another one in at 0.5f, which fails and then it should try 0.626 since |
| // it knows 0.6 and below fails and 0.625 worked (and the 0.001 is to keep the |
| // bounds separated). |
| AddScalingSequenceToOverlayProcessor(0.5f, false); |
| AddScalingSequenceToOverlayProcessor(0.626f, true); |
| |
| float initial_scalings[] = {0.5f, 0.625f, 0.5f}; |
| for (auto initial_scaling : initial_scalings) { |
| SCOPED_TRACE(initial_scaling); |
| |
| auto pass = CreateRenderPass(); |
| |
| AggregatedRenderPassId render_pass_id{3}; |
| AggregatedRenderPassDrawQuad* quad = |
| pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); |
| quad->SetNew(pass->shared_quad_state_list.back(), kOverlayRect, |
| kOverlayRect, render_pass_id, kInvalidResourceId, gfx::RectF(), |
| gfx::Size(), gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), |
| false, 1.0f); |
| |
| // First, we want the overlay to be scaled by 0.5 and have it rejected. |
| float res_scale = 1.0f / (initial_scaling * (1.0f - kUVTopLeft.x())); |
| CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| pass->output_rect, gfx::ProtectedVideoType::kHardwareProtected, |
| MultiPlaneFormat::kNV12, |
| gfx::ScaleToRoundedSize(kDisplaySize, res_scale)) |
| ->needs_blending = false; |
| pass->shared_quad_state_list.front()->opacity = 1.0; |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| ASSERT_EQ(1U, candidate_list.size()); |
| } |
| } |
| |
| #if BUILDFLAG(IS_OZONE) |
| |
| TileDrawQuad* CreateTileCandidateQuadAt( |
| DisplayResourceProvider* parent_resource_provider, |
| ClientResourceProvider* child_resource_provider, |
| ContextProvider* child_context_provider, |
| const SharedQuadState* shared_quad_state, |
| AggregatedRenderPass* render_pass, |
| const gfx::Rect& rect) { |
| bool needs_blending = false; |
| bool premultiplied_alpha = false; |
| bool force_anti_aliasing_off = false; |
| bool nearest_neighbor = false; |
| bool is_overlay_candidate = true; |
| ResourceId resource_id = |
| CreateResource(parent_resource_provider, child_resource_provider, |
| child_context_provider, rect.size(), is_overlay_candidate, |
| SinglePlaneFormat::kRGBA_8888, SurfaceId()); |
| |
| auto* overlay_quad = render_pass->CreateAndAppendDrawQuad<TileDrawQuad>(); |
| overlay_quad->SetNew(shared_quad_state, rect, rect, needs_blending, |
| resource_id, gfx::RectF(0, 0, 1, 1), rect.size(), |
| premultiplied_alpha, nearest_neighbor, |
| force_anti_aliasing_off); |
| |
| return overlay_quad; |
| } |
| |
| class TestDelegatedOverlayProcessor : public OverlayProcessorDelegated { |
| public: |
| using PrimaryPlane = OverlayProcessorInterface::OutputSurfaceOverlayPlane; |
| TestDelegatedOverlayProcessor() |
| : OverlayProcessorDelegated(nullptr, {}, nullptr) {} |
| ~TestDelegatedOverlayProcessor() override = default; |
| |
| OverlayProcessorInterface::OutputSurfaceOverlayPlane* |
| GetDefaultPrimaryPlane() { |
| primary_plane_ = ProcessOutputSurfaceAsOverlay( |
| kDisplaySize, kDisplaySize, kDefaultBufferFormat, gfx::ColorSpace(), |
| false /* has_alpha */, 1.0f /* opacity */, gpu::Mailbox()); |
| return &primary_plane_; |
| } |
| |
| bool IsOverlaySupported() const override { return true; } |
| bool NeedsSurfaceDamageRectList() const override { return false; } |
| void CheckOverlaySupportImpl(const PrimaryPlane* primary_plane, |
| OverlayCandidateList* surfaces) override { |
| for (auto&& each_candidate : *surfaces) { |
| each_candidate.overlay_handled = true; |
| } |
| } |
| size_t GetStrategyCount() const { return strategies_.size(); } |
| void AddExpectedRect(const gfx::RectF& rect) {} |
| |
| OverlayProcessorInterface::OutputSurfaceOverlayPlane primary_plane_; |
| }; |
| |
| class DelegatedTest : public OverlayTest<TestDelegatedOverlayProcessor> { |
| public: |
| DelegatedTest() { |
| scoped_features.InitAndEnableFeature(features::kDelegatedCompositing); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_features; |
| }; |
| |
| gfx::Transform MakePerspectiveTransform() { |
| gfx::Transform transform; |
| transform.ApplyPerspectiveDepth(100.f); |
| transform.RotateAbout(gfx::Vector3dF(1.f, 1.f, 1.f), 30); |
| return transform; |
| } |
| |
| gfx::Transform MakeShearTransform() { |
| gfx::Transform transform; |
| transform.Skew(0, 30); |
| return transform; |
| } |
| |
| gfx::Transform MakeRotationTransform() { |
| gfx::Transform transform; |
| transform.RotateAboutZAxis(30); |
| return transform; |
| } |
| |
| TEST_F(DelegatedTest, ForwardMultipleBasic) { |
| auto pass = CreateRenderPass(); |
| constexpr size_t kNumTestQuads = 5; |
| for (size_t i = 0; i < kNumTestQuads; i++) { |
| const auto kSmallCandidateRect = gfx::Rect(0, 0, 16 * (i + 1), 16); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kSmallCandidateRect); |
| } |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| // AggregatedRenderPass* main_pass = pass.get(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| // Simplify by adding full root damage. |
| surface_damage_rect_list.push_back(pass->output_rect); |
| pass_list.push_back(std::move(pass)); |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), |
| overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(kNumTestQuads, candidate_list.size()); |
| for (size_t i = 0; i < kNumTestQuads; i++) { |
| const auto kSmallCandidateRect = gfx::RectF(0, 0, 16 * (i + 1), 16); |
| EXPECT_RECTF_EQ(kSmallCandidateRect, candidate_list[i].display_rect); |
| } |
| } |
| |
| // Transparent colors are important for delegating overlays. Overlays that have |
| // an alpha channel but are required to be drawn as opaque will have solid black |
| // placed behind them. This solid black can interfer with overlay |
| // promotion/blend optimizations. |
| TEST_F(DelegatedTest, ForwardBackgroundColor) { |
| auto pass = CreateRenderPass(); |
| |
| auto* quad = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayRect); |
| quad->background_color = SkColors::kTransparent; |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| // AggregatedRenderPass* main_pass = pass.get(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| // Simplify by adding full root damage. |
| surface_damage_rect_list.push_back(pass->output_rect); |
| pass_list.push_back(std::move(pass)); |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), |
| overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| EXPECT_RECTF_EQ(gfx::RectF(kOverlayRect), candidate_list[0].display_rect); |
| EXPECT_EQ(SkColors::kTransparent, candidate_list[0].color.value()); |
| } |
| |
| TEST_F(DelegatedTest, DoNotDelegateCopyRequest) { |
| auto pass = CreateRenderPass(); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kOverlayTopLeftRect); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| // AggregatedRenderPass* main_pass = pass.get(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| // Simplify by adding full root damage. |
| surface_damage_rect_list.push_back(pass->output_rect); |
| pass->copy_requests.push_back(CopyOutputRequest::CreateStubForTesting()); |
| pass_list.push_back(std::move(pass)); |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), |
| overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list, |
| &damage_rect_, &content_bounds_); |
| EXPECT_EQ(0u, candidate_list.size()); |
| } |
| |
| TEST_F(DelegatedTest, BlockDelegationWithNonRootCopies) { |
| auto child_pass = CreateRenderPass(); |
| AggregatedRenderPassId child_id{3}; |
| child_pass->id = child_id; |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| child_pass->shared_quad_state_list.back(), |
| child_pass.get(), kOverlayTopLeftRect); |
| |
| auto root_pass = CreateRenderPass(); |
| AggregatedRenderPassDrawQuad* quad = |
| root_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); |
| quad->SetNew(root_pass->shared_quad_state_list.back(), kOverlayRect, |
| kOverlayRect, child_id, kInvalidResourceId, gfx::RectF(), |
| gfx::Size(), gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), |
| false, 1.0f); |
| |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| SurfaceDamageRectList surface_damage_rect_list; |
| // Simplify by adding full root damage. |
| surface_damage_rect_list.push_back(kOverlayRect); |
| pass_list.push_back(std::move(child_pass)); |
| pass_list.push_back(std::move(root_pass)); |
| |
| // The second copy frame will reset the counter. Three frames after that |
| // delegation can resume. |
| std::vector<bool> copy_frames = {true, false, true, false, |
| false, false, false}; |
| std::vector<size_t> expected_overlays = {0, 0, 0, 0, 0, 1, 1}; |
| ASSERT_EQ(copy_frames.size(), expected_overlays.size()); |
| |
| for (size_t i = 0; i < copy_frames.size(); ++i) { |
| SCOPED_TRACE(i); |
| |
| pass_list[0]->copy_requests.clear(); |
| // Add copy request to child pass on certain frames. |
| if (copy_frames[i]) { |
| pass_list[0]->copy_requests.push_back( |
| CopyOutputRequest::CreateStubForTesting()); |
| } |
| |
| OverlayCandidateList candidate_list; |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| surface_damage_rect_list, overlay_processor_->GetDefaultPrimaryPlane(), |
| &candidate_list, &damage_rect_, &content_bounds_); |
| EXPECT_EQ(expected_overlays[i], candidate_list.size()); |
| } |
| } |
| |
| TEST_F(DelegatedTest, TestClipHandCrafted) { |
| auto pass = CreateRenderPass(); |
| const auto kSmallCandidateRect = gfx::Rect(0, 0, 100, 100); |
| const auto kTestClip = gfx::Rect(0, 50, 50, 50); |
| auto* tex_rect = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kSmallCandidateRect); |
| tex_rect->uv_bottom_right = gfx::PointF(1, 1); |
| tex_rect->uv_top_left = gfx::PointF(0, 0); |
| pass->shared_quad_state_list.back()->clip_rect = kTestClip; |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| // AggregatedRenderPass* main_pass = pass.get(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| // Simplify by adding full root damage. |
| surface_damage_rect_list.push_back(pass->output_rect); |
| pass_list.push_back(std::move(pass)); |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), |
| overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| const auto uv_rect = gfx::RectF(0, 0.5f, 0.5f, 0.5f); |
| EXPECT_EQ(1U, candidate_list.size()); |
| EXPECT_RECTF_NEAR(gfx::RectF(kTestClip), candidate_list[0].display_rect, |
| 0.01f); |
| EXPECT_RECTF_NEAR(uv_rect, candidate_list[0].uv_rect, 0.01f); |
| } |
| |
| TEST_F(DelegatedTest, TestVisibleRectClip) { |
| auto pass = CreateRenderPass(); |
| const auto kSmallCandidateRect = gfx::Rect(0, 0, 100, 100); |
| const auto kTestClip = gfx::Rect(0, 50, 50, 50); |
| auto* tex_rect = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kSmallCandidateRect); |
| tex_rect->uv_bottom_right = gfx::PointF(1, 1); |
| tex_rect->uv_top_left = gfx::PointF(0, 0); |
| tex_rect->visible_rect = kTestClip; |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| // AggregatedRenderPass* main_pass = pass.get(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| // Simplify by adding full root damage. |
| surface_damage_rect_list.push_back(pass->output_rect); |
| pass_list.push_back(std::move(pass)); |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), |
| overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| const auto uv_rect = gfx::RectF(0, 0.5f, 0.5f, 0.5f); |
| EXPECT_EQ(1U, candidate_list.size()); |
| EXPECT_RECTF_NEAR(gfx::RectF(kTestClip), candidate_list[0].display_rect, |
| 0.01f); |
| EXPECT_RECTF_NEAR(uv_rect, candidate_list[0].uv_rect, 0.01f); |
| } |
| |
| TEST_F(DelegatedTest, TestClipComputed) { |
| auto pass = CreateRenderPass(); |
| const auto kSmallCandidateRect = gfx::Rect(5, 10, 128, 64); |
| const auto kTestClip = gfx::Rect(0, 15, 70, 64); |
| auto* tex_rect = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kSmallCandidateRect); |
| |
| pass->shared_quad_state_list.back()->clip_rect = kTestClip; |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| // AggregatedRenderPass* main_pass = pass.get(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| // Simplify by adding full root damage. |
| surface_damage_rect_list.push_back(pass->output_rect); |
| pass_list.push_back(std::move(pass)); |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), |
| overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| EXPECT_EQ(1U, candidate_list.size()); |
| auto expected_rect = kTestClip; |
| expected_rect.Intersect(kSmallCandidateRect); |
| gfx::RectF uv_rect = cc::MathUtil::ScaleRectProportional( |
| BoundingRect(tex_rect->uv_top_left, tex_rect->uv_bottom_right), |
| gfx::RectF(kSmallCandidateRect), gfx::RectF(expected_rect)); |
| EXPECT_RECTF_NEAR(gfx::RectF(expected_rect), candidate_list[0].display_rect, |
| 0.01f); |
| EXPECT_RECTF_NEAR(uv_rect, candidate_list[0].uv_rect, 0.01f); |
| } |
| |
| TEST_F(DelegatedTest, TestClipAggregateRenderPass) { |
| auto pass = CreateRenderPass(); |
| const auto kSmallCandidateRect = gfx::Rect(5, 10, 128, 64); |
| const auto kTestClip = gfx::Rect(0, 15, 70, 64); |
| |
| AggregatedRenderPassId render_pass_id{3}; |
| AggregatedRenderPassDrawQuad* quad = |
| pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); |
| quad->SetNew(pass->shared_quad_state_list.back(), kSmallCandidateRect, |
| kSmallCandidateRect, render_pass_id, kInvalidResourceId, |
| gfx::RectF(), gfx::Size(), gfx::Vector2dF(1, 1), gfx::PointF(), |
| gfx::RectF(), false, 1.0f); |
| |
| pass->shared_quad_state_list.back()->clip_rect = kTestClip; |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| // AggregatedRenderPass* main_pass = pass.get(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| // Simplify by adding full root damage. |
| surface_damage_rect_list.push_back(pass->output_rect); |
| pass_list.push_back(std::move(pass)); |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), |
| overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| EXPECT_EQ(1U, candidate_list.size()); |
| EXPECT_RECTF_NEAR(gfx::RectF(kSmallCandidateRect), |
| candidate_list[0].display_rect, 0.01f); |
| EXPECT_EQ(kTestClip, candidate_list[0].clip_rect.value()); |
| } |
| |
| TEST_F(DelegatedTest, TestClipWithPrimary) { |
| auto pass = CreateRenderPass(); |
| // This is a quad with a rect that is twice is large as the primary plane and |
| // will be clipped in this test. |
| const auto kOversizedCandidateRect = |
| gfx::Rect(kDisplaySize.height() * 2, kDisplaySize.width() * 2); |
| auto* tex_rect = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kOversizedCandidateRect); |
| tex_rect->uv_bottom_right = gfx::PointF(1, 1); |
| tex_rect->uv_top_left = gfx::PointF(0, 0); |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| // AggregatedRenderPass* main_pass = pass.get(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| // Simplify by adding full root damage. |
| surface_damage_rect_list.push_back(pass->output_rect); |
| pass_list.push_back(std::move(pass)); |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), |
| overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| const auto uv_rect = gfx::RectF(0, 0.0f, 0.5f, 0.5f); |
| EXPECT_EQ(1U, candidate_list.size()); |
| // We clip all rects to the primary display to ensure delegated and composited |
| // paths have identical results. |
| EXPECT_RECTF_NEAR(gfx::RectF(gfx::Rect(kDisplaySize)), |
| candidate_list[0].display_rect, 0.01f); |
| EXPECT_RECTF_NEAR(uv_rect, candidate_list[0].uv_rect, 0.01f); |
| } |
| |
| TEST_F(DelegatedTest, ScaledBufferDamage) { |
| auto pass = CreateRenderPass(); |
| const auto kSmallCandidateRect = gfx::Rect(15, 10, 32, 64); |
| const auto kResourceSize = gfx::Size(16, 16); |
| const auto kDisplayDamage = gfx::Rect(25, 0, 5, 32); |
| |
| // Specify a resource size to be much smaller than the display size of this |
| // quad. |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kSmallCandidateRect, gfx::ProtectedVideoType::kClear, |
| SinglePlaneFormat::kRGBA_8888, kResourceSize); |
| |
| // Here resource size and rect size on screen will match 1:1. |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kSmallCandidateRect); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| |
| AggregatedRenderPass* main_pass = pass.get(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| // Simplify by adding full root damage. |
| surface_damage_rect_list.push_back(kDisplayDamage); |
| pass_list.push_back(std::move(pass)); |
| damage_rect_ = kDisplayDamage; |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), |
| overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| // Damage is not assigned to specific candidates. |
| EXPECT_EQ(main_pass->quad_list.size(), candidate_list.size()); |
| EXPECT_TRUE(damage_rect_.IsEmpty()); |
| EXPECT_TRUE(candidate_list[0].damage_rect.IsEmpty()); |
| EXPECT_TRUE(candidate_list[1].damage_rect.IsEmpty()); |
| EXPECT_RECTF_NEAR(overlay_processor_->GetUnassignedDamage(), |
| gfx::RectF(kDisplayDamage), 0.0001f); |
| } |
| |
| TEST_F(DelegatedTest, QuadTypes) { |
| auto pass = CreateRenderPass(); |
| const auto kSmallCandidateRect = gfx::Rect(5, 10, 57, 64); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kSmallCandidateRect); |
| CreateTileCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kSmallCandidateRect); |
| CreateSolidColorQuadAt(pass->shared_quad_state_list.back(), SkColors::kDkGray, |
| pass.get(), kOverlayBottomRightRect); |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| // Simplify by adding full root damage. |
| surface_damage_rect_list.push_back(pass->output_rect); |
| pass_list.push_back(std::move(pass)); |
| damage_rect_ = kOverlayRect; |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), |
| overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| EXPECT_EQ(main_pass->quad_list.size(), candidate_list.size()); |
| EXPECT_TRUE(damage_rect_.IsEmpty()); |
| } |
| |
| TEST_F(DelegatedTest, NonAxisAlignedCandidateStatus) { |
| auto pass = CreateRenderPass(); |
| const auto kSmallCandidateRect = gfx::Rect(5, 10, 57, 64); |
| auto* quad = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kSmallCandidateRect); |
| SurfaceDamageRectList surface_damage_rect_list; |
| OverlayCandidate candidate; |
| auto color_mat = GetIdentityColorMatrix(); |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| |
| auto candidate_factory = OverlayCandidateFactory( |
| pass.get(), resource_provider_.get(), &surface_damage_rect_list, |
| &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters, |
| OverlayCandidateFactory::OverlayContext{.is_delegated_context = true}); |
| |
| pass->shared_quad_state_list.back()->quad_to_target_transform = |
| MakePerspectiveTransform(); |
| EXPECT_EQ(OverlayCandidate::CandidateStatus::kFailNotAxisAligned3dTransform, |
| candidate_factory.FromDrawQuad(quad, candidate)); |
| |
| pass->shared_quad_state_list.back()->quad_to_target_transform = |
| MakeShearTransform(); |
| EXPECT_EQ(OverlayCandidate::CandidateStatus::kFailNotAxisAligned2dShear, |
| candidate_factory.FromDrawQuad(quad, candidate)); |
| |
| pass->shared_quad_state_list.back()->quad_to_target_transform = |
| MakeRotationTransform(); |
| EXPECT_EQ(OverlayCandidate::CandidateStatus::kFailNotAxisAligned2dRotation, |
| candidate_factory.FromDrawQuad(quad, candidate)); |
| } |
| |
| // These tests check to make sure that candidate quads that should fail (aka |
| // non-delegatable) do fail. These tests might need to be changed if we have |
| // improved delegation support. |
| class DelegatedTestNonDelegated : public DelegatedTest { |
| public: |
| void TestExpectCandidateFailure(std::unique_ptr<AggregatedRenderPass> pass) { |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| SurfaceDamageRectList surface_damage_rect_list; |
| // Simplify by adding full root damage. |
| surface_damage_rect_list.push_back(pass->output_rect); |
| pass_list.push_back(std::move(pass)); |
| damage_rect_ = kOverlayRect; |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| EXPECT_EQ(0U, candidate_list.size()); |
| EXPECT_EQ(damage_rect_, kOverlayRect); |
| } |
| }; |
| |
| TEST_F(DelegatedTestNonDelegated, TextureQuadNearest) { |
| auto pass = CreateRenderPass(); |
| const auto kSmallCandidateRect = gfx::Rect(5, 10, 57, 64); |
| auto* texture_quad = CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kSmallCandidateRect); |
| texture_quad->nearest_neighbor = true; |
| TestExpectCandidateFailure(std::move(pass)); |
| } |
| |
| TEST_F(DelegatedTestNonDelegated, TileQuadNearest) { |
| auto pass = CreateRenderPass(); |
| const auto kSmallCandidateRect = gfx::Rect(5, 10, 57, 64); |
| auto* tile_quad = CreateTileCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| kSmallCandidateRect); |
| tile_quad->nearest_neighbor = true; |
| TestExpectCandidateFailure(std::move(pass)); |
| } |
| |
| #endif // BUILDFLAG(IS_OZONE) |
| |
| TEST_F(MultiUnderlayTest, DamageWhenDemotingTwoUnderlays) { |
| constexpr gfx::Rect kTopLeft(0, 0, 128, 128); |
| constexpr gfx::Rect kTopRight(128, 0, 128, 128); |
| constexpr gfx::Rect kTopHalf(0, 0, 256, 128); |
| |
| constexpr int kDemotionFrame = 3; |
| for (int i = 0; i < 5; ++i) { |
| SCOPED_TRACE(i); |
| |
| auto pass = CreateRenderPass(); |
| damage_rect_ = kTopHalf; |
| SurfaceDamageRectList surface_damage_rect_list; |
| overlay_processor_->ClearExpectedRects(); |
| |
| // Stop promoting the candidates on this frame. |
| bool promoted = i < kDemotionFrame; |
| |
| { |
| // Create a candidate in the top left. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kTopLeft); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get(), kTopLeft); |
| // This candidate will always be promoted. |
| overlay_processor_->AddExpectedRect(kTopLeft, promoted); |
| } |
| { |
| // Create a candidate in the top right. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kTopRight); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get(), kTopRight); |
| // The second candidate won't get promoted after kDemotionFrame. |
| overlay_processor_->AddExpectedRect(kTopRight, promoted); |
| } |
| { |
| // Add something behind it. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), sqs, pass.get()); |
| } |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| if (i == 0) { |
| // The promoted underlays need to damage the primary plane on the first |
| // frame of promotion. |
| EXPECT_EQ(damage_rect_, kTopHalf); |
| } else if (i < kDemotionFrame) { |
| // No damage after they're promoted. |
| EXPECT_TRUE(damage_rect_.IsEmpty()); |
| } else if (i >= kDemotionFrame) { |
| // The demoted underlays need to damage the primary plane. |
| EXPECT_EQ(damage_rect_, kTopHalf); |
| } |
| } |
| } |
| |
| TEST_F(MultiUnderlayTest, DamageWhenDemotingOneUnderlay) { |
| constexpr gfx::Rect kTopLeft(0, 0, 128, 128); |
| constexpr gfx::Rect kTopRight(128, 0, 128, 128); |
| constexpr gfx::Rect kTopHalf(0, 0, 256, 128); |
| |
| constexpr int kDemotionFrame = 3; |
| for (int i = 0; i < 5; ++i) { |
| SCOPED_TRACE(i); |
| |
| auto pass = CreateRenderPass(); |
| damage_rect_ = kTopHalf; |
| SurfaceDamageRectList surface_damage_rect_list; |
| overlay_processor_->ClearExpectedRects(); |
| |
| { |
| // Create a candidate in the top left. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kTopLeft); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get(), kTopLeft); |
| // This candidate will always be promoted. |
| overlay_processor_->AddExpectedRect(kTopLeft, true); |
| } |
| { |
| // Create a candidate in the top right. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kTopRight); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get(), kTopRight); |
| // Stop promoting this candidate on kDemotionFrame. |
| bool promoted = i < kDemotionFrame; |
| overlay_processor_->AddExpectedRect(kTopRight, promoted); |
| } |
| { |
| // Add something behind it. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), sqs, pass.get()); |
| } |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| if (i == 0) { |
| // The promoted underlays need to damage the primary plane on the first |
| // frame of promotion. |
| EXPECT_EQ(damage_rect_, kTopHalf); |
| } else if (i < kDemotionFrame) { |
| // No damage after they're promoted. |
| EXPECT_TRUE(damage_rect_.IsEmpty()); |
| } else if (i >= kDemotionFrame) { |
| // Only the demoted underlay needs to damage the primary plane. The top |
| // left candidate is still in an underlay, so it doesn't need damage the |
| // primary plane. |
| EXPECT_EQ(damage_rect_, kTopRight); |
| } |
| } |
| } |
| |
| TEST_F(MultiOverlayTest, DamageOnlyForNewUnderlays) { |
| constexpr gfx::Rect kTopLeft(0, 0, 128, 128); |
| constexpr gfx::Rect kTopRight(128, 0, 128, 128); |
| constexpr gfx::Rect kMidRight(192, 64, 64, 128); |
| constexpr gfx::Rect kTopHalf(0, 0, 256, 128); |
| |
| constexpr int kPromotionFrame = 2; |
| for (int i = 0; i < 5; ++i) { |
| SCOPED_TRACE(i); |
| |
| auto pass = CreateRenderPass(); |
| damage_rect_ = kTopHalf; |
| SurfaceDamageRectList surface_damage_rect_list; |
| overlay_processor_->ClearExpectedRects(); |
| |
| bool promoted = i >= kPromotionFrame; |
| |
| { |
| // Create quad partially covering up top right candidate, forcing it to |
| // be an underlay. |
| CreateOpaqueQuadAt(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kMidRight); |
| } |
| { |
| // Create a candidate in the top left. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kTopLeft); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get(), kTopLeft); |
| overlay_processor_->AddExpectedRect(kTopLeft, promoted); |
| } |
| { |
| // Create a candidate in the top right. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kTopRight); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get(), kTopRight); |
| overlay_processor_->AddExpectedRect(kTopRight, promoted); |
| } |
| { |
| // Add something behind it. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), sqs, pass.get()); |
| } |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| if (i < kPromotionFrame) { |
| // Full damage before promotion. |
| EXPECT_EQ(damage_rect_, kTopHalf); |
| } else if (i == kPromotionFrame) { |
| // Only the underlay needs damage on the promotion frame. |
| EXPECT_EQ(damage_rect_, kTopRight); |
| } else if (i > kPromotionFrame) { |
| // No damage after both are promoted. |
| EXPECT_TRUE(damage_rect_.IsEmpty()); |
| } |
| } |
| } |
| |
| TEST_F(MultiOverlayTest, DamageMaskFilterChange) { |
| constexpr gfx::Rect kTopRight(128, 0, 128, 128); |
| constexpr gfx::Rect kBottomLeft(0, 128, 128, 128); |
| constexpr gfx::Rect kMidRight(192, 64, 64, 128); |
| constexpr gfx::Rect kFullRect(0, 0, 256, 256); |
| |
| constexpr int kMaskFilterStartFrame = 2; |
| constexpr int kMaskFilterEndFrame = 4; |
| for (int i = 0; i < 7; ++i) { |
| SCOPED_TRACE(i); |
| |
| auto pass = CreateRenderPass(); |
| damage_rect_ = kFullRect; |
| SurfaceDamageRectList surface_damage_rect_list; |
| overlay_processor_->ClearExpectedRects(); |
| |
| bool mask_filter = i >= kMaskFilterStartFrame && i < kMaskFilterEndFrame; |
| |
| { |
| // Create quad partially covering up top right candidate, forcing it to |
| // be an underlay. |
| CreateOpaqueQuadAt(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kMidRight); |
| } |
| { |
| // Create a candidate in the bottom left that won't be promoted. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| if (mask_filter) { |
| sqs->mask_filter_info = gfx::MaskFilterInfo( |
| gfx::RectF(kOverlayRect), gfx::RoundedCornersF(1.f), |
| gfx::LinearGradient::GetEmpty()); |
| } |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kBottomLeft); |
| CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get(), kBottomLeft); |
| overlay_processor_->AddExpectedRect(kBottomLeft, false); |
| } |
| { |
| // Create an underlay candidate in the top right. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| if (mask_filter) { |
| sqs->mask_filter_info = gfx::MaskFilterInfo( |
| gfx::RectF(kOverlayRect), gfx::RoundedCornersF(1.f), |
| gfx::LinearGradient::GetEmpty()); |
| } |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kTopRight); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get(), kTopRight); |
| overlay_processor_->AddExpectedRect(kTopRight, true); |
| } |
| { |
| // Add something behind it. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), sqs, pass.get()); |
| } |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| ASSERT_EQ(candidate_list.size(), 1u); |
| |
| if (i == 0) { |
| // Damage for both candidates |
| EXPECT_EQ(damage_rect_, kFullRect); |
| } else if (i < kMaskFilterStartFrame) { |
| EXPECT_EQ(damage_rect_, kBottomLeft); |
| } else if (i == kMaskFilterStartFrame || i == kMaskFilterEndFrame) { |
| // Damage added for underlay candidate when mask filter changes. |
| EXPECT_EQ(damage_rect_, kFullRect); |
| } else { |
| // Otherwise damage for just the unpromoted candidate. |
| EXPECT_EQ(damage_rect_, kBottomLeft); |
| } |
| } |
| } |
| |
| TEST_F(MultiOverlayTest, DamageOccluded) { |
| constexpr gfx::Rect kTopRight(128, 0, 128, 128); |
| constexpr gfx::Rect kUnderTopRight(129, 1, 64, 64); |
| constexpr gfx::Rect kBottomLeft(0, 128, 128, 128); |
| constexpr gfx::Rect kUnderBottomLeft(1, 129, 64, 64); |
| constexpr gfx::Rect kMidRight(192, 64, 64, 128); |
| constexpr gfx::Rect kFullRect(0, 0, 256, 256); |
| |
| constexpr int kTopRightGone = 3; |
| |
| for (int i = 0; i < 6; ++i) { |
| SCOPED_TRACE(i); |
| |
| auto pass = CreateRenderPass(); |
| damage_rect_ = kFullRect; |
| SurfaceDamageRectList surface_damage_rect_list; |
| overlay_processor_->ClearExpectedRects(); |
| |
| { |
| // Create quad partially covering up top right candidate, forcing it to |
| // be an underlay. |
| CreateOpaqueQuadAt(resource_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kMidRight); |
| } |
| { |
| // Create a transparent candidate in the bottom left. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kBottomLeft); |
| CreateTransparentCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get(), kBottomLeft); |
| overlay_processor_->AddExpectedRect(kBottomLeft, true); |
| } |
| { |
| // Create unpromoted quad that would be underneath bottom left quad |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kUnderBottomLeft); |
| CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get(), kUnderBottomLeft); |
| overlay_processor_->AddExpectedRect(kUnderBottomLeft, false); |
| } |
| if (i < kTopRightGone) { |
| // Create an underlay candidate in the top right. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kTopRight); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get(), kTopRight); |
| overlay_processor_->AddExpectedRect(kTopRight, true); |
| } |
| { |
| // Create an opaque damaging quad under the top right candidate. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kUnderTopRight); |
| CreateOpaqueQuadAt(resource_provider_.get(), sqs, pass.get(), |
| kUnderTopRight); |
| } |
| { |
| // Add something behind it. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), sqs, pass.get()); |
| } |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| if (i < kTopRightGone) { |
| ASSERT_EQ(candidate_list.size(), 2u); |
| // Transparent overlay |
| EXPECT_GT(candidate_list[0].plane_z_order, 0); |
| EXPECT_FALSE(candidate_list[0].is_opaque); |
| // Opaque underlay |
| EXPECT_LT(candidate_list[1].plane_z_order, 0); |
| EXPECT_TRUE(candidate_list[1].is_opaque); |
| } else { |
| ASSERT_EQ(candidate_list.size(), 1u); |
| // Transparent overlay |
| EXPECT_GT(candidate_list[0].plane_z_order, 0); |
| EXPECT_FALSE(candidate_list[0].is_opaque); |
| } |
| |
| if (i == 0) { |
| // Damage for both candidates |
| EXPECT_EQ(damage_rect_, kFullRect); |
| } else if (i < kTopRightGone) { |
| // This quad isn't occluded because it's under a transparent overlay, so |
| // its damage persists. |
| EXPECT_EQ(damage_rect_, kUnderBottomLeft); |
| } else if (i == kTopRightGone) { |
| // The top right candidate is demoted, so |
| // damage = kUnderBottomLeft union kTopRight |
| EXPECT_EQ(damage_rect_, gfx::Rect(1, 0, 255, 193)); |
| } else if (i >= kTopRightGone) { |
| // The quad under top right is now visible, so |
| // damage = kUnderBottomLeft union kUnderTopRight |
| EXPECT_EQ(damage_rect_, gfx::Rect(1, 1, 192, 192)); |
| } |
| } |
| } |
| |
| TEST_F(MultiOverlayTest, FullscreenOnly) { |
| constexpr gfx::Rect kTopLeft(0, 0, 128, 128); |
| constexpr gfx::Rect kTopRight(128, 0, 128, 128); |
| constexpr gfx::Rect kFullRect(0, 0, 256, 256); |
| |
| auto pass = CreateRenderPass(); |
| damage_rect_ = kFullRect; |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| { |
| // Create a fullscreen candidate. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kFullRect); |
| CreateFullscreenCandidateQuad(resource_provider_.get(), |
| child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get()); |
| overlay_processor_->AddExpectedRect(kFullRect, true); |
| } |
| { |
| // Create a candidate in the top left. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kTopLeft); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| sqs, pass.get(), kTopLeft); |
| } |
| { |
| // Create a candidate in the top right. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kTopRight); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| sqs, pass.get(), kTopRight); |
| } |
| { |
| // Add something behind it. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), sqs, pass.get()); |
| } |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| ASSERT_EQ(candidate_list.size(), 1u); |
| // Fullscreen overlay |
| EXPECT_EQ(candidate_list[0].plane_z_order, 0); |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[0].display_rect), kFullRect); |
| // No damage required for a fullscreen overlay. |
| EXPECT_TRUE(damage_rect_.IsEmpty()); |
| } |
| |
| TEST_F(MultiOverlayTest, RequiredOverlayOnly) { |
| constexpr gfx::Rect kTopLeft(0, 0, 128, 128); |
| constexpr gfx::Rect kTopRight(128, 0, 128, 128); |
| constexpr gfx::Rect kTopHalf(0, 0, 256, 128); |
| constexpr gfx::Rect kBottomLeft(0, 128, 128, 128); |
| constexpr gfx::Rect kFullRect(0, 0, 256, 256); |
| |
| auto pass = CreateRenderPass(); |
| damage_rect_ = kFullRect; |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| { |
| // Create a candidate in the top left. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kTopLeft); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| sqs, pass.get(), kTopLeft); |
| } |
| { |
| // Create a candidate in the top right. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kTopRight); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| sqs, pass.get(), kTopRight); |
| } |
| { |
| // Create an overlay required candidate. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kBottomLeft); |
| CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get(), kBottomLeft, |
| gfx::ProtectedVideoType::kHardwareProtected, MultiPlaneFormat::kNV12); |
| overlay_processor_->AddExpectedRect(kBottomLeft, true); |
| } |
| { |
| // Add something behind it. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), sqs, pass.get()); |
| } |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| ASSERT_EQ(candidate_list.size(), 1u); |
| // Only the required overlay is promoted. |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[0].display_rect), kBottomLeft); |
| EXPECT_TRUE(candidate_list[0].requires_overlay); |
| // The two unpromoted candidates still have damage. |
| EXPECT_EQ(damage_rect_, kTopHalf); |
| } |
| |
| TEST_F(MultiOverlayTest, CappedAtMaxOverlays) { |
| constexpr gfx::Rect kCandRects[]{{0, 0, 64, 64}, {64, 0, 64, 64}, |
| {0, 64, 64, 64}, {64, 64, 64, 64}, |
| {0, 128, 64, 64}, {64, 128, 64, 64}}; |
| constexpr gfx::Rect kBottomTwo(0, 128, 128, 64); |
| |
| damage_rect_ = gfx::Rect(0, 0, 128, 192); |
| auto pass = CreateRenderPass(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| constexpr int kMaxOverlays = 4; |
| |
| for (int i = 0; i < 6; ++i) { |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kCandRects[i]); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| sqs, pass.get(), kCandRects[i]); |
| // Only the first 4 overlays should be attempted. |
| if (i < kMaxOverlays) { |
| overlay_processor_->AddExpectedRect(kCandRects[i], true); |
| } |
| } |
| |
| { |
| // Add something behind it. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), sqs, pass.get()); |
| } |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| ASSERT_EQ(candidate_list.size(), 4u); |
| // Expect the first four candidates promoted to on top overlays. |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[0].display_rect), kCandRects[0]); |
| EXPECT_EQ(candidate_list[0].plane_z_order, 1); |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[1].display_rect), kCandRects[1]); |
| EXPECT_EQ(candidate_list[1].plane_z_order, 1); |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[2].display_rect), kCandRects[2]); |
| EXPECT_EQ(candidate_list[2].plane_z_order, 1); |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[3].display_rect), kCandRects[3]); |
| EXPECT_EQ(candidate_list[3].plane_z_order, 1); |
| // Only the bottom two candidates still have damage. |
| EXPECT_EQ(damage_rect_, kBottomTwo); |
| } |
| |
| TEST_F(MultiOverlayTest, RoundedDisplayMaskCandidateFailsToPromote) { |
| overlay_processor_->SetMaximumOverlaysConsidered(6); |
| |
| const gfx::Rect kRoundedDisplayMaskLeftRect(0, 0, 32, 100); |
| const gfx::Rect kRoundedDisplayMaskRightRect(224, 0, 32, 100); |
| const gfx::Rect kNormalLeft(kOverlayTopLeftRect); |
| const gfx::Rect kNormalRight(kOverlayTopRightRect); |
| |
| auto pass = CreateRenderPass(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| const auto kRoundedDisplayMaskInfo = |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(16, 16); |
| |
| // Create a candidate with rounded display masks in top left. |
| CreateQuadWithRoundedDisplayMasksAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, kRoundedDisplayMaskLeftRect, |
| kRoundedDisplayMaskInfo); |
| |
| // Create a candidate with rounded display masks in top right. |
| CreateQuadWithRoundedDisplayMasksAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, kRoundedDisplayMaskRightRect, |
| kRoundedDisplayMaskInfo); |
| |
| // This candidate is occluded by top left candidate with rounded display |
| // masks. |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kNormalLeft); |
| |
| // This candidate is occluded by top right candidate with rounded display |
| // masks. |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kNormalRight); |
| |
| // Create a quad that will the next quad to become an underlay. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), sqs, pass.get()); |
| |
| CreateFullscreenCandidateQuad(resource_provider_.get(), |
| child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get()); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->AddExpectedRect(kNormalLeft, true); |
| overlay_processor_->AddExpectedRect(kNormalRight, true); |
| overlay_processor_->AddExpectedRect(kOverlayRect, true); |
| |
| // Candidates with masks are appended at the end of the surfaces in |
| // `CheckOverlaySupportImpl()` |
| overlay_processor_->AddExpectedRect(kRoundedDisplayMaskLeftRect, true); |
| overlay_processor_->AddExpectedRect(kRoundedDisplayMaskRightRect, false); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| // Since the mask candidate in top right will fail to promote, we will also |
| // composite the SingleOnTop candidate occlude by this failing candidate. The |
| // underlay candidate will promoted normally since it is not effected by the |
| // failing mask candidate (even though it is occluded). |
| ASSERT_EQ(candidate_list.size(), 3u); |
| |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[0].display_rect), kNormalLeft); |
| EXPECT_EQ(candidate_list[0].plane_z_order, 1); |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[1].display_rect), kOverlayRect); |
| EXPECT_EQ(candidate_list[1].plane_z_order, -1); |
| |
| // Candidates with masks are appended at the end of the `candidate_list` in |
| // draw order. |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[2].display_rect), |
| kRoundedDisplayMaskLeftRect); |
| EXPECT_EQ(candidate_list[2].plane_z_order, 2); |
| } |
| |
| TEST_F(MultiOverlayTest, |
| DontPromoteCandidatesWithMasksIfAreOnlyOverlayCandidatesNotRejected) { |
| overlay_processor_->SetMaximumOverlaysConsidered(6); |
| |
| const gfx::Rect kRoundedDisplayMaskLeftRect(0, 0, 32, 100); |
| const gfx::Rect kRoundedDisplayMaskRightRect(224, 0, 32, 100); |
| const gfx::Rect kNormalLeft(kOverlayTopLeftRect); |
| const gfx::Rect kNormalRight(kOverlayTopRightRect); |
| |
| auto pass = CreateRenderPass(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| const auto kRoundedDisplayMaskInfo = |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(16, 16); |
| |
| // Create a candidate with rounded display masks in top left. |
| CreateQuadWithRoundedDisplayMasksAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, kRoundedDisplayMaskLeftRect, |
| kRoundedDisplayMaskInfo); |
| |
| // Create a candidate with rounded display masks in top right. |
| CreateQuadWithRoundedDisplayMasksAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, kRoundedDisplayMaskRightRect, |
| kRoundedDisplayMaskInfo); |
| |
| // This candidate is occluded by top left candidate with rounded display |
| // masks. |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kNormalLeft); |
| |
| // This candidate is occluded by top right candidate with rounded display |
| // masks. |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| kNormalRight); |
| |
| // Create a quad that will the next quad to become an underlay. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), sqs, pass.get()); |
| |
| CreateFullscreenCandidateQuad(resource_provider_.get(), |
| child_resource_provider_.get(), |
| child_provider_.get(), sqs, pass.get()); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->AddExpectedRect(kNormalLeft, false); |
| overlay_processor_->AddExpectedRect(kNormalRight, false); |
| overlay_processor_->AddExpectedRect(kOverlayRect, true); |
| |
| // Candidates with masks are appended at the end of the surfaces in |
| // `CheckOverlaySupportImpl()` |
| overlay_processor_->AddExpectedRect(kRoundedDisplayMaskLeftRect, true); |
| overlay_processor_->AddExpectedRect(kRoundedDisplayMaskRightRect, true); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| // Since both the overlay (SingleOnTop) candidates without masks failed to |
| // promote, we end up composting both candidates with masks as well. Only the |
| // underlay candidate is promoted. |
| ASSERT_EQ(candidate_list.size(), 1u); |
| |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[0].display_rect), kOverlayRect); |
| EXPECT_EQ(candidate_list[0].plane_z_order, -1); |
| } |
| |
| // Since AllowCandidateWithMasksSortedMultiOverlayProcessor will only allow |
| // candidates with masks after sorting in `SortProposedOverlayCandidates()`. |
| TEST_F(AllowCandidateWithMasksSortedMultiOverlayTest, |
| DontPromoteCandidatesWithMasksIfAreOnlySingleOnTopCandidates) { |
| auto pass = CreateRenderPass(); |
| |
| // Add a quad with rounded-display masks. |
| CreateQuadWithRoundedDisplayMasksAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, kOverlayTopLeftRect, |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(10, 0)); |
| |
| // Add a quad with rounded-display masks. |
| CreateQuadWithRoundedDisplayMasksAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, kOverlayTopRightRect, |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(10, 0)); |
| |
| CreateFullscreenCandidateQuad( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); |
| |
| // Check for potential candidates. |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| AggregatedRenderPass* main_pass = pass.get(); |
| pass_list.push_back(std::move(pass)); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| // None of the candidates will be promoted. |
| // Since AllowCandidateWithMasksSortedMultiOverlayProcessor will only allow |
| // candidates with rounded-display masks after sorting(i.e only candidates to |
| // pass sorting hubristic), we can skip promoting these candidates. |
| ASSERT_EQ(0U, candidate_list.size()); |
| EXPECT_EQ(3U, main_pass->quad_list.size()); |
| } |
| |
| TEST_F(TypeAndSizeSortedMultiOverlayTest, |
| PrioritizationWithRoundedDisplayMasks) { |
| enum OverlayCandidateType { kNormal, kHasRoundedDisplayMasks }; |
| |
| constexpr gfx::Rect kRoundedDisplayMaskRectSmallest(0, 0, 32, 32); |
| constexpr gfx::Rect kRoundedDisplayMaskRectSmall(64, 0, 64, 64); |
| constexpr gfx::Rect kNormalBiggest(128, 0, 256, 256); |
| constexpr gfx::Rect kNormalBig(0, 0, 128, 128); |
| constexpr gfx::Rect kNormalSmallest(128, 0, 32, 32); |
| // Intersects with both mask rects. |
| constexpr gfx::Rect kNormalSmall(128, 64, 64, 64); |
| |
| // The draw order of these candidates is scrambled, so we can verify that the |
| // plane_z_orders are are based on draw quad order. |
| constexpr std::pair<gfx::Rect, OverlayCandidateType> kCandidatesInfo[]{ |
| {kRoundedDisplayMaskRectSmall, |
| OverlayCandidateType::kHasRoundedDisplayMasks}, |
| {kRoundedDisplayMaskRectSmallest, |
| OverlayCandidateType::kHasRoundedDisplayMasks}, |
| {kNormalBiggest, OverlayCandidateType::kNormal}, |
| {kNormalSmallest, OverlayCandidateType::kNormal}, |
| {kNormalBig, OverlayCandidateType::kNormal}, |
| {kNormalSmall, OverlayCandidateType::kNormal}}; |
| |
| constexpr gfx::Rect kAllCands(0, 0, 128, 128); |
| const auto kRoundedDisplayMaskInfo = |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(16, 16); |
| |
| damage_rect_ = kAllCands; |
| auto pass = CreateRenderPass(); |
| |
| for (auto info : kCandidatesInfo) { |
| switch (info.second) { |
| case kNormal: |
| CreateCandidateQuadAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), |
| pass.get(), info.first); |
| break; |
| case kHasRoundedDisplayMasks: |
| CreateQuadWithRoundedDisplayMasksAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), |
| pass.get(), |
| /*is_overlay_candidate=*/true, info.first, kRoundedDisplayMaskInfo); |
| break; |
| } |
| } |
| |
| { |
| // Add something behind them. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), sqs, pass.get()); |
| } |
| |
| overlay_processor_->AddExpectedRect(kNormalBiggest, true); |
| overlay_processor_->AddExpectedRect(kNormalBig, true); |
| |
| // Candidates with masks are appended at the end of the surfaces in |
| // `CheckOverlaySupportImpl()` |
| overlay_processor_->AddExpectedRect(kRoundedDisplayMaskRectSmall, true); |
| overlay_processor_->AddExpectedRect(kRoundedDisplayMaskRectSmallest, true); |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| SurfaceDamageRectList surface_damage_rect_list; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| ASSERT_EQ(candidate_list.size(), 4u); |
| |
| // We expect the two rounded masks to get promoted regardless of their surface |
| // area. Candidates with rounded masks are followed by candidate with largest |
| // surface area. |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[0].display_rect), kNormalBiggest); |
| EXPECT_EQ(candidate_list[0].plane_z_order, 1); |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[1].display_rect), kNormalBig); |
| EXPECT_EQ(candidate_list[1].plane_z_order, 1); |
| |
| // Candidates with masks are appended at the end of the `candidate_list` in |
| // draw order. |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[2].display_rect), |
| kRoundedDisplayMaskRectSmall); |
| EXPECT_EQ(candidate_list[2].plane_z_order, 2); |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[3].display_rect), |
| kRoundedDisplayMaskRectSmallest); |
| EXPECT_EQ(candidate_list[3].plane_z_order, 2); |
| } |
| |
| TEST_F(SizeSortedMultiOverlayTest, OverlaysAreSorted) { |
| constexpr gfx::Rect kBiggest(0, 0, 128, 128); |
| constexpr gfx::Rect kBig(128, 28, 100, 100); |
| // kSmall rect intersects with kSmallest rect. |
| constexpr gfx::Rect kSmall(66, 128, 64, 64); |
| constexpr gfx::Rect kSmallest(128, 128, 32, 32); |
| // The draw order of these candidates is scrambled, so we can verify that the |
| // plane_z_orders are are based on draw quad order. |
| constexpr gfx::Rect kCandRects[]{kBiggest, kSmallest, kBig}; |
| constexpr gfx::Rect kAllCands(0, 0, 228, 192); |
| |
| damage_rect_ = kAllCands; |
| auto pass = CreateRenderPass(); |
| |
| // Quad with rounded_display mask occluded candidate at `kSmallest`. |
| CreateQuadWithRoundedDisplayMasksAt( |
| resource_provider_.get(), child_resource_provider_.get(), |
| child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), |
| /*is_overlay_candidate=*/true, kSmall, |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(16, 16)); |
| |
| for (auto& cand_rect : kCandRects) { |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| pass->shared_quad_state_list.back(), pass.get(), |
| cand_rect); |
| } |
| // Candidates will be sorted by surface area. All will be promoted. |
| overlay_processor_->AddExpectedRect(kBiggest, true); |
| overlay_processor_->AddExpectedRect(kBig, true); |
| overlay_processor_->AddExpectedRect(kSmall, true); |
| overlay_processor_->AddExpectedRect(kSmallest, true); |
| |
| { |
| // Add something behind them. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), sqs, pass.get()); |
| } |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| SurfaceDamageRectList surface_damage_rect_list; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| ASSERT_EQ(candidate_list.size(), 4u); |
| // Expect all four are promoted to overlays, and their plane_z_order is based |
| // on draw order. |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[0].display_rect), kBiggest); |
| EXPECT_EQ(candidate_list[0].plane_z_order, 1); |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[1].display_rect), kBig); |
| EXPECT_EQ(candidate_list[1].plane_z_order, 1); |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[2].display_rect), kSmall); |
| EXPECT_EQ(candidate_list[2].plane_z_order, 2); |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[3].display_rect), kSmallest); |
| EXPECT_EQ(candidate_list[3].plane_z_order, 1); |
| } |
| |
| TEST_F(SizeSortedMultiUnderlayOverlayTest, UnderlaysAreSorted) { |
| constexpr gfx::Rect kBiggest(0, 0, 128, 128); |
| constexpr gfx::Rect kBig(128, 28, 100, 100); |
| constexpr gfx::Rect kSmall(64, 128, 64, 64); |
| constexpr gfx::Rect kSmallest(128, 128, 32, 32); |
| // The draw order of these candidates is scrambled, so we can verify that the |
| // plane_z_orders are are based on draw quad order. |
| constexpr gfx::Rect kCandRects[]{kSmall, kBiggest, kSmallest, kBig}; |
| constexpr gfx::Rect kSmallCenter(112, 112, 32, 32); |
| constexpr gfx::Rect kAllCands(0, 0, 228, 192); |
| |
| damage_rect_ = kAllCands; |
| auto pass = CreateRenderPass(); |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| { |
| // Create a quad partially covering up all candidates, forcing them to all |
| // be underlays. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| CreateOpaqueQuadAt(resource_provider_.get(), sqs, pass.get(), kSmallCenter); |
| } |
| |
| for (auto& cand_rect : kCandRects) { |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(cand_rect); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| sqs, pass.get(), cand_rect); |
| } |
| // Candidates will be sorted by surface area. All will be promoted. |
| overlay_processor_->AddExpectedRect(kBiggest, true); |
| overlay_processor_->AddExpectedRect(kBig, true); |
| overlay_processor_->AddExpectedRect(kSmall, true); |
| overlay_processor_->AddExpectedRect(kSmallest, true); |
| |
| { |
| // Add something behind them. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), sqs, pass.get()); |
| } |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), nullptr, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| ASSERT_EQ(candidate_list.size(), 4u); |
| // Expect all four are promoted to underlay, and their plane_z_order is based |
| // on draw order. |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[0].display_rect), kBiggest); |
| EXPECT_EQ(candidate_list[0].plane_z_order, -2); |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[1].display_rect), kBig); |
| EXPECT_EQ(candidate_list[1].plane_z_order, -4); |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[2].display_rect), kSmall); |
| EXPECT_EQ(candidate_list[2].plane_z_order, -1); |
| EXPECT_EQ(gfx::ToRoundedRect(candidate_list[3].display_rect), kSmallest); |
| EXPECT_EQ(candidate_list[3].plane_z_order, -3); |
| // Full damage because these are underlays on their first frame of promotion. |
| EXPECT_EQ(damage_rect_, kAllCands); |
| } |
| |
| class MultiUnderlayPromotedTest : public MultiUnderlayTest, |
| public testing::WithParamInterface<bool> {}; |
| |
| TEST_P(MultiUnderlayPromotedTest, UnderlaysBlendPrimaryPlane) { |
| bool promoted = GetParam(); |
| |
| constexpr gfx::Rect kTopLeft(0, 0, 128, 128); |
| constexpr gfx::Rect kTopRight(128, 0, 128, 128); |
| constexpr gfx::Rect kTopHalf(0, 0, 256, 128); |
| |
| auto pass = CreateRenderPass(); |
| damage_rect_ = kTopHalf; |
| SurfaceDamageRectList surface_damage_rect_list; |
| |
| { |
| // Create a candidate in the top left. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kTopLeft); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| sqs, pass.get(), kTopLeft); |
| overlay_processor_->AddExpectedRect(kTopLeft, promoted); |
| } |
| { |
| // Create a candidate in the top right. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| sqs->overlay_damage_index = surface_damage_rect_list.size(); |
| surface_damage_rect_list.emplace_back(kTopRight); |
| CreateCandidateQuadAt(resource_provider_.get(), |
| child_resource_provider_.get(), child_provider_.get(), |
| sqs, pass.get(), kTopRight); |
| overlay_processor_->AddExpectedRect(kTopRight, promoted); |
| } |
| { |
| // Add something behind it. |
| auto* sqs = pass->CreateAndAppendSharedQuadState(); |
| CreateFullscreenOpaqueQuad(resource_provider_.get(), sqs, pass.get()); |
| } |
| |
| OverlayCandidateList candidate_list; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; |
| AggregatedRenderPassList pass_list; |
| pass_list.push_back(std::move(pass)); |
| auto output_surface_plane = overlay_processor_->ProcessOutputSurfaceAsOverlay( |
| kDisplaySize, kDisplaySize, kDefaultBufferFormat, gfx::ColorSpace(), |
| false /* has_alpha */, 1.0f /* opacity */, gpu::Mailbox()); |
| OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane = |
| &output_surface_plane; |
| |
| overlay_processor_->ProcessForOverlays( |
| resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), |
| render_pass_filters, render_pass_backdrop_filters, |
| std::move(surface_damage_rect_list), primary_plane, &candidate_list, |
| &damage_rect_, &content_bounds_); |
| |
| if (promoted) { |
| // Both candidates are promoted. |
| ASSERT_EQ(candidate_list.size(), 2u); |
| // Blending enabled on primary plane. |
| EXPECT_TRUE(primary_plane->enable_blending); |
| } else { |
| // No candidates are promoted. |
| EXPECT_TRUE(candidate_list.empty()); |
| // Blending not enabled on primary plane. |
| EXPECT_FALSE(primary_plane->enable_blending); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(PromotedTrueFalse, |
| MultiUnderlayPromotedTest, |
| testing::Bool()); |
| |
| } // namespace |
| } // namespace viz |