diff options
Diffstat (limited to 'chromium/components/viz/service/display')
38 files changed, 1500 insertions, 487 deletions
diff --git a/chromium/components/viz/service/display/DEPS b/chromium/components/viz/service/display/DEPS index e21abf9abcb..39b3ff37c99 100644 --- a/chromium/components/viz/service/display/DEPS +++ b/chromium/components/viz/service/display/DEPS @@ -62,5 +62,6 @@ specific_include_rules = { "+gpu/GLES2", "+media", "+third_party/libyuv", + "+ui/gl/gl_implementation.h", ], } diff --git a/chromium/components/viz/service/display/ca_layer_overlay.h b/chromium/components/viz/service/display/ca_layer_overlay.h index 2594167459e..5e9c2f27322 100644 --- a/chromium/components/viz/service/display/ca_layer_overlay.h +++ b/chromium/components/viz/service/display/ca_layer_overlay.h @@ -9,6 +9,7 @@ #include "base/memory/ref_counted.h" #include "components/viz/common/quads/render_pass.h" #include "components/viz/service/viz_service_export.h" +#include "gpu/command_buffer/common/mailbox.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkMatrix44.h" #include "ui/gfx/geometry/rect_f.h" @@ -23,7 +24,7 @@ class RenderPassDrawQuad; // Holds information that is frequently shared between consecutive // CALayerOverlays. class VIZ_SERVICE_EXPORT CALayerOverlaySharedState - : public base::RefCounted<CALayerOverlaySharedState> { + : public base::RefCountedThreadSafe<CALayerOverlaySharedState> { public: CALayerOverlaySharedState() {} // Layers in a non-zero sorting context exist in the same 3D space and should @@ -40,7 +41,7 @@ class VIZ_SERVICE_EXPORT CALayerOverlaySharedState SkMatrix44 transform = SkMatrix44(SkMatrix44::kIdentity_Constructor); private: - friend class base::RefCounted<CALayerOverlaySharedState>; + friend class base::RefCountedThreadSafe<CALayerOverlaySharedState>; ~CALayerOverlaySharedState() {} }; @@ -57,6 +58,8 @@ class VIZ_SERVICE_EXPORT CALayerOverlay { // Texture that corresponds to an IOSurface to set as the content of the // CALayer. If this is 0 then the CALayer is a solid color. unsigned contents_resource_id = 0; + // Mailbox from contents_resource_id. It is used by SkiaRenderer. + gpu::Mailbox mailbox; // The contents rect property for the CALayer. gfx::RectF contents_rect; // The bounds for the CALayer in pixels. diff --git a/chromium/components/viz/service/display/copy_output_scaling_pixeltest.cc b/chromium/components/viz/service/display/copy_output_scaling_pixeltest.cc index dad254b2c68..c684f2d5498 100644 --- a/chromium/components/viz/service/display/copy_output_scaling_pixeltest.cc +++ b/chromium/components/viz/service/display/copy_output_scaling_pixeltest.cc @@ -169,6 +169,9 @@ class CopyOutputScalingPixelTest renderer()->DecideRenderPassAllocationsForFrame(list); renderer()->DrawFrame(&list, 1.0f, viewport_size, gfx::DisplayColorSpaces()); + // Call SwapBuffersSkipped(), so the renderer can release related + // resources. + renderer()->SwapBuffersSkipped(); loop.Run(); } diff --git a/chromium/components/viz/service/display/dc_layer_overlay.cc b/chromium/components/viz/service/display/dc_layer_overlay.cc index 647d33f7a2a..089e265f0f5 100644 --- a/chromium/components/viz/service/display/dc_layer_overlay.cc +++ b/chromium/components/viz/service/display/dc_layer_overlay.cc @@ -17,7 +17,6 @@ #include "components/viz/service/display/display_resource_provider.h" #include "components/viz/service/display/overlay_processor_interface.h" #include "gpu/GLES2/gl2extchromium.h" -#include "gpu/config/gpu_finch_features.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_conversions.h" diff --git a/chromium/components/viz/service/display/direct_renderer.cc b/chromium/components/viz/service/display/direct_renderer.cc index 15ac491792a..9691ccf8bed 100644 --- a/chromium/components/viz/service/display/direct_renderer.cc +++ b/chromium/components/viz/service/display/direct_renderer.cc @@ -4,6 +4,7 @@ #include "components/viz/service/display/direct_renderer.h" +#include <limits.h> #include <stddef.h> #include <utility> @@ -11,6 +12,7 @@ #include "base/auto_reset.h" #include "base/containers/circular_deque.h" +#include "base/logging.h" #include "base/metrics/histogram_macros.h" #include "base/numerics/safe_conversions.h" #include "base/stl_util.h" @@ -465,6 +467,15 @@ void DirectRenderer::DrawFrame( current_frame_valid_ = false; } +gfx::Rect DirectRenderer::GetTargetDamageBoundingRect() const { + gfx::Rect bounding_rect = output_surface_->GetCurrentFramebufferDamage(); + if (overlay_processor_) { + bounding_rect.Union( + overlay_processor_->GetPreviousFrameOverlaysBoundingRect()); + } + return bounding_rect; +} + gfx::Rect DirectRenderer::DeviceViewportRectInDrawSpace() const { gfx::Rect device_viewport_rect(current_frame()->device_viewport_size); device_viewport_rect -= current_viewport_rect_.OffsetFromOrigin(); @@ -808,48 +819,67 @@ gfx::Rect DirectRenderer::ComputeScissorRectForRenderPass( gfx::Rect root_damage_rect = current_frame()->root_damage_rect; if (render_pass == root_render_pass) { - auto display_area = current_frame()->device_viewport_size.GetArea(); - DCHECK(display_area); - - auto frame_buffer_damage = output_surface_->GetCurrentFramebufferDamage(); - auto root_damage_area = root_damage_rect.size().GetArea(); - - UMA_HISTOGRAM_PERCENTAGE( - "Compositing.DirectRenderer.PartialSwap.FrameBufferDamage", - 100ull * frame_buffer_damage.size().GetArea() / display_area); - UMA_HISTOGRAM_PERCENTAGE( - "Compositing.DirectRenderer.PartialSwap.RootDamage", - 100ull * root_damage_area / display_area); - - root_damage_rect.Union(frame_buffer_damage); - - // If the root damage rect intersects any child render pass that has a - // pixel-moving backdrop-filter, expand the damage to include the entire - // child pass. See crbug.com/986206 for context. - if (!backdrop_filter_output_rects_.empty() && !root_damage_rect.IsEmpty()) { - for (auto* quad : render_pass->quad_list) { - if (quad->material == DrawQuad::Material::kRenderPass) { - auto iter = backdrop_filter_output_rects_.find( - RenderPassDrawQuad::MaterialCast(quad)->render_pass_id); - if (iter != backdrop_filter_output_rects_.end()) { - auto this_output_rect = iter->second; - if (root_damage_rect.Intersects(this_output_rect)) - root_damage_rect.Union(this_output_rect); + base::CheckedNumeric<int> display_area = + current_frame()->device_viewport_size.GetCheckedArea(); + gfx::Rect frame_buffer_damage = + output_surface_->GetCurrentFramebufferDamage(); + base::CheckedNumeric<int> root_damage_area = + root_damage_rect.size().GetCheckedArea(); + if (display_area.IsValid() && root_damage_area.IsValid()) { + DCHECK_GT(static_cast<int>(display_area.ValueOrDie()), 0); + { + base::CheckedNumeric<int> frame_buffer_damage_area = + frame_buffer_damage.size().GetCheckedArea(); + int ratio = + (frame_buffer_damage_area / display_area).ValueOrDefault(INT_MAX); + UMA_HISTOGRAM_PERCENTAGE( + "Compositing.DirectRenderer.PartialSwap.FrameBufferDamage", + 100ull * ratio); + } + { + int ratio = (root_damage_area / display_area).ValueOrDie(); + UMA_HISTOGRAM_PERCENTAGE( + "Compositing.DirectRenderer.PartialSwap.RootDamage", + 100ull * ratio); + } + + root_damage_rect.Union(frame_buffer_damage); + + // If the root damage rect intersects any child render pass that has a + // pixel-moving backdrop-filter, expand the damage to include the entire + // child pass. See crbug.com/986206 for context. + if (!backdrop_filter_output_rects_.empty() && + !root_damage_rect.IsEmpty()) { + for (auto* quad : render_pass->quad_list) { + if (quad->material == DrawQuad::Material::kRenderPass) { + auto iter = backdrop_filter_output_rects_.find( + RenderPassDrawQuad::MaterialCast(quad)->render_pass_id); + if (iter != backdrop_filter_output_rects_.end()) { + gfx::Rect this_output_rect = iter->second; + if (root_damage_rect.Intersects(this_output_rect)) + root_damage_rect.Union(this_output_rect); + } } } } - } - - // Total damage after all adjustments. - auto total_damage_area = root_damage_rect.size().GetArea(); - - UMA_HISTOGRAM_PERCENTAGE( - "Compositing.DirectRenderer.PartialSwap.TotalDamage", - 100ull * total_damage_area / display_area); - UMA_HISTOGRAM_PERCENTAGE( - "Compositing.DirectRenderer.PartialSwap.ExtraDamage", - 100ull * (total_damage_area - root_damage_area) / display_area); + // Total damage after all adjustments. + base::CheckedNumeric<int> total_damage_area = + root_damage_rect.size().GetCheckedArea(); + { + int ratio = (total_damage_area / display_area).ValueOrDefault(INT_MAX); + UMA_HISTOGRAM_PERCENTAGE( + "Compositing.DirectRenderer.PartialSwap.TotalDamage", + 100ull * ratio); + } + { + int ratio = ((total_damage_area - root_damage_area) / display_area) + .ValueOrDefault(INT_MAX); + UMA_HISTOGRAM_PERCENTAGE( + "Compositing.DirectRenderer.PartialSwap.ExtraDamage", + 100ull * ratio); + } + } return root_damage_rect; } diff --git a/chromium/components/viz/service/display/direct_renderer.h b/chromium/components/viz/service/display/direct_renderer.h index 259ec01ce1c..ef57383011a 100644 --- a/chromium/components/viz/service/display/direct_renderer.h +++ b/chromium/components/viz/service/display/direct_renderer.h @@ -69,6 +69,11 @@ class VIZ_SERVICE_EXPORT DirectRenderer { const gfx::Size& device_viewport_size, const gfx::DisplayColorSpaces& display_color_spaces); + // The renderer might expand the damage (e.g: HW overlays were used, + // invalidation rects on previous buffers). This function returns a + // bounding rect of the area that might need to be recomposited. + gfx::Rect GetTargetDamageBoundingRect() const; + // Public interface implemented by subclasses. struct SwapFrameData { SwapFrameData(); diff --git a/chromium/components/viz/service/display/display.cc b/chromium/components/viz/service/display/display.cc index 80382efd040..fd92ca7cfee 100644 --- a/chromium/components/viz/service/display/display.cc +++ b/chromium/components/viz/service/display/display.cc @@ -5,7 +5,9 @@ #include "components/viz/service/display/display.h" #include <stddef.h> +#include <algorithm> #include <limits> +#include <utility> #include "base/debug/dump_without_crashing.h" #include "base/metrics/histogram_macros.h" @@ -55,6 +57,14 @@ namespace viz { namespace { +enum class TypeOfVideoInFrame { + kNoVideo = 0, + kVideo = 1, + + // This should be the last entry/largest value above. + kMaxValue = kVideo, +}; + const DrawQuad::Material kNonSplittableMaterials[] = { // Exclude debug quads from quad splitting DrawQuad::Material::kDebugBorder, @@ -97,8 +107,11 @@ gfx::PresentationFeedback SanitizePresentationFeedback( // therefore the timestamp can be slightly in the future in comparison with // base::TimeTicks::Now(). Such presentation feedbacks should not be rejected. // See https://crbug.com/1040178 + // Sometimes we snap the feedback's time stamp to the nearest vsync, and that + // can be offset by one vsync-internal. These feedback has kVSync set. const auto allowed_delta_from_future = - ((feedback.flags & gfx::PresentationFeedback::kHWClock) != 0) + ((feedback.flags & (gfx::PresentationFeedback::kHWClock | + gfx::PresentationFeedback::kVSync)) != 0) ? kAllowedDeltaFromFuture : base::TimeDelta(); if (feedback.timestamp > now + allowed_delta_from_future) { @@ -165,25 +178,41 @@ gfx::Rect SafeConvertRectForRegion(const gfx::Rect& r) { return safe_rect; } -// Computes the accumulated area of all the rectangles in the list of |rects|. -int ComputeArea(const std::vector<gfx::Rect>& rects) { - int area = 0; - for (const auto& r : rects) - area += r.size().GetArea(); - return area; -} - // Decides whether or not a DrawQuad should be split into a more complex visible // region in order to avoid overdraw. bool CanSplitQuad(const DrawQuad::Material m, - const int visible_region_area, - const int visible_region_bounding_area, - const int minimum_fragments_reduced, + const std::vector<gfx::Rect>& visible_region_rects, + const gfx::Size& visible_region_bounding_size, + int minimum_fragments_reduced, const float device_scale_factor) { - return !base::Contains(kNonSplittableMaterials, m) && - (visible_region_bounding_area - visible_region_area) * - device_scale_factor * device_scale_factor > - minimum_fragments_reduced; + if (base::Contains(kNonSplittableMaterials, m)) + return false; + + base::CheckedNumeric<int> area = 0; + for (const auto& r : visible_region_rects) { + area += r.size().GetCheckedArea(); + // In calculations below, assume false if this addition overflows. + if (!area.IsValid()) { + return false; + } + } + + base::CheckedNumeric<int> visible_region_bounding_area = + visible_region_bounding_size.GetCheckedArea(); + if (!visible_region_bounding_area.IsValid()) { + // In calculations below, assume true if this overflows. + return true; + } + + area = visible_region_bounding_area - area; + if (!area.IsValid()) { + // In calculations below, assume false if this subtraction underflows. + return false; + } + + int int_area = area.ValueOrDie(); + return int_area * device_scale_factor * device_scale_factor > + minimum_fragments_reduced; } // Attempts to consolidate rectangles that were only split because of the @@ -521,10 +550,13 @@ void Display::InitializeRenderer(bool enable_shared_images) { // Outputting a partial list of quads might not work in cases where contents // outside the damage rect might be needed by the renderer. + bool might_invalidate_outside_damage = + !output_surface_->capabilities().only_invalidates_damage_rect || + overlay_processor_->IsOverlaySupported(); bool output_partial_list = - output_surface_->capabilities().only_invalidates_damage_rect && renderer_->use_partial_swap() && - !overlay_processor_->IsOverlaySupported(); + (!might_invalidate_outside_damage || + output_surface_->capabilities().supports_target_damage); aggregator_ = std::make_unique<SurfaceAggregator>( surface_manager_, resource_provider_.get(), output_partial_list, @@ -611,9 +643,32 @@ bool Display::DrawAndSwap(base::TimeTicks expected_display_time) { { FrameRateDecider::ScopedAggregate scoped_aggregate( frame_rate_decider_.get()); - frame = - aggregator_->Aggregate(current_surface_id_, expected_display_time, - current_display_transform, ++swapped_trace_id_); + gfx::Rect target_damage_bounding_rect; + if (output_surface_->capabilities().supports_target_damage) + target_damage_bounding_rect = renderer_->GetTargetDamageBoundingRect(); + + frame = aggregator_->Aggregate( + current_surface_id_, expected_display_time, current_display_transform, + target_damage_bounding_rect, ++swapped_trace_id_); + } + + // Records whether the aggregated frame contains video or not. + // TODO(vikassoni) : Extend this capability to record whether a video frame is + // inline or fullscreen. + UMA_HISTOGRAM_ENUMERATION("Compositing.SurfaceAggregator.FrameContainsVideo", + frame.metadata.may_contain_video + ? TypeOfVideoInFrame::kVideo + : TypeOfVideoInFrame::kNoVideo); + + if (frame.metadata.delegated_ink_metadata) { + TRACE_EVENT_INSTANT2( + "viz", "Delegated Ink Metadata was aggregated for DrawAndSwap.", + TRACE_EVENT_SCOPE_THREAD, "point", + frame.metadata.delegated_ink_metadata->point().ToString(), "area", + frame.metadata.delegated_ink_metadata->presentation_area().ToString()); + // TODO(1052145): This metadata will be stored here and used to determine + // which points should be drawn onto the back buffer (via Skia or OS APIs) + // before being swapped onto the screen. } #if defined(OS_ANDROID) @@ -1039,9 +1094,12 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) { settings_.kMaximumOccluderComplexity) { gfx::Rect smallest_rect = *occlusion_in_target_space.begin(); for (const auto& occluding_rect : occlusion_in_target_space) { - if (occluding_rect.size().GetArea() < - smallest_rect.size().GetArea()) + if (occluding_rect.size().GetCheckedArea().ValueOrDefault( + INT_MAX) < + smallest_rect.size().GetCheckedArea().ValueOrDefault( + INT_MAX)) { smallest_rect = occluding_rect; + } } occlusion_in_target_space.Subtract(smallest_rect); } @@ -1130,8 +1188,8 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) { !visible_region.Intersects(render_pass_quads_in_content_space) && ReduceComplexity(visible_region, settings_.quad_split_limit, &cached_visible_region_) && - CanSplitQuad(quad->material, ComputeArea(cached_visible_region_), - visible_region.bounds().size().GetArea(), + CanSplitQuad(quad->material, cached_visible_region_, + visible_region.bounds().size(), settings_.minimum_fragments_reduced, device_scale_factor_); if (should_split_quads) { diff --git a/chromium/components/viz/service/display/display_resource_provider.cc b/chromium/components/viz/service/display/display_resource_provider.cc index bcf21f8135b..faa7a2179be 100644 --- a/chromium/components/viz/service/display/display_resource_provider.cc +++ b/chromium/components/viz/service/display/display_resource_provider.cc @@ -198,7 +198,7 @@ void DisplayResourceProvider::SendPromotionHints( if (it->second.marked_for_deletion) continue; - const ChildResource* resource = LockForRead(id); + const ChildResource* resource = LockForRead(id, false /* overlay_only */); // TODO(ericrk): We should never fail LockForRead, but we appear to be // doing so on Android in rare cases. Handle this gracefully until a better // solution can be found. https://crbug.com/811858 @@ -218,7 +218,7 @@ void DisplayResourceProvider::SendPromotionHints( promotable ? iter->second.width() : 0, promotable ? iter->second.height() : 0); } - UnlockForRead(id); + UnlockForRead(id, false /* overlay_only */); } #endif } @@ -493,7 +493,7 @@ GLES2Interface* DisplayResourceProvider::ContextGL() const { } const DisplayResourceProvider::ChildResource* -DisplayResourceProvider::LockForRead(ResourceId id) { +DisplayResourceProvider::LockForRead(ResourceId id, bool overlay_only) { // TODO(ericrk): We should never fail TryGetResource, but we appear to be // doing so on Android in rare cases. Handle this gracefully until a better // solution can be found. https://crbug.com/811858 @@ -519,10 +519,27 @@ DisplayResourceProvider::LockForRead(ResourceId id) { } resource->SetLocallyUsed(); } - if (mailbox.IsSharedImage() && enable_shared_images_ && - resource->lock_for_read_count == 0) { - gl->BeginSharedImageAccessDirectCHROMIUM( - resource->gl_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM); + if (mailbox.IsSharedImage() && enable_shared_images_) { + if (overlay_only) { + if (resource->lock_for_overlay_count == 0) { + // If |lock_for_read_count| > 0, then BeginSharedImageAccess has + // already been called with READ, so don't re-lock with OVERLAY. + if (resource->lock_for_read_count == 0) { + gl->BeginSharedImageAccessDirectCHROMIUM( + resource->gl_id, GL_SHARED_IMAGE_ACCESS_MODE_OVERLAY_CHROMIUM); + } + } + } else { + if (resource->lock_for_read_count == 0) { + // If |lock_for_overlay_count| > 0, then we have already begun access + // for OVERLAY. End this access and "upgrade" it to READ. + // See https://crbug.com/1113925 for how this can go wrong. + if (resource->lock_for_overlay_count > 0) + gl->EndSharedImageAccessDirectCHROMIUM(resource->gl_id); + gl->BeginSharedImageAccessDirectCHROMIUM( + resource->gl_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM); + } + } } } @@ -540,7 +557,10 @@ DisplayResourceProvider::LockForRead(ResourceId id) { } } - resource->lock_for_read_count++; + if (overlay_only) + resource->lock_for_overlay_count++; + else + resource->lock_for_read_count++; if (resource->transferable.read_lock_fences_enabled) { if (current_read_lock_fence_.get()) current_read_lock_fence_->Set(); @@ -550,7 +570,7 @@ DisplayResourceProvider::LockForRead(ResourceId id) { return resource; } -void DisplayResourceProvider::UnlockForRead(ResourceId id) { +void DisplayResourceProvider::UnlockForRead(ResourceId id, bool overlay_only) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); auto it = resources_.find(id); // TODO(ericrk): We should never fail to find id, but we appear to be @@ -560,16 +580,23 @@ void DisplayResourceProvider::UnlockForRead(ResourceId id) { return; ChildResource* resource = &it->second; - DCHECK_GT(resource->lock_for_read_count, 0); if (resource->transferable.mailbox_holder.mailbox.IsSharedImage() && - resource->is_gpu_resource_type() && enable_shared_images_ && - resource->lock_for_read_count == 1) { - DCHECK(resource->gl_id); - GLES2Interface* gl = ContextGL(); - DCHECK(gl); - gl->EndSharedImageAccessDirectCHROMIUM(resource->gl_id); + resource->is_gpu_resource_type() && enable_shared_images_) { + // If this is the last READ or OVERLAY access, then end access. + if (resource->lock_for_read_count + resource->lock_for_overlay_count == 1) { + DCHECK(resource->gl_id); + GLES2Interface* gl = ContextGL(); + DCHECK(gl); + gl->EndSharedImageAccessDirectCHROMIUM(resource->gl_id); + } + } + if (overlay_only) { + DCHECK_GT(resource->lock_for_overlay_count, 0); + resource->lock_for_overlay_count--; + } else { + DCHECK_GT(resource->lock_for_read_count, 0); + resource->lock_for_read_count--; } - resource->lock_for_read_count--; TryReleaseResource(id, resource); } @@ -851,7 +878,8 @@ DisplayResourceProvider::ScopedReadLockGL::ScopedReadLockGL( DisplayResourceProvider* resource_provider, ResourceId resource_id) : resource_provider_(resource_provider), resource_id_(resource_id) { - const ChildResource* resource = resource_provider->LockForRead(resource_id); + const ChildResource* resource = + resource_provider->LockForRead(resource_id, false /* overlay_only */); // TODO(ericrk): We should never fail LockForRead, but we appear to be // doing so on Android in rare cases. Handle this gracefully until a better // solution can be found. https://crbug.com/811858 @@ -865,7 +893,23 @@ DisplayResourceProvider::ScopedReadLockGL::ScopedReadLockGL( } DisplayResourceProvider::ScopedReadLockGL::~ScopedReadLockGL() { - resource_provider_->UnlockForRead(resource_id_); + resource_provider_->UnlockForRead(resource_id_, false /* overlay_only */); +} + +DisplayResourceProvider::ScopedOverlayLockGL::ScopedOverlayLockGL( + DisplayResourceProvider* resource_provider, + ResourceId resource_id) + : resource_provider_(resource_provider), resource_id_(resource_id) { + const ChildResource* resource = + resource_provider->LockForRead(resource_id, true /* overlay_only */); + if (!resource) + return; + + texture_id_ = resource->gl_id; +} + +DisplayResourceProvider::ScopedOverlayLockGL::~ScopedOverlayLockGL() { + resource_provider_->UnlockForRead(resource_id_, true /* overlay_only */); } DisplayResourceProvider::ScopedSamplerGL::ScopedSamplerGL( @@ -893,7 +937,8 @@ DisplayResourceProvider::ScopedReadLockSkImage::ScopedReadLockSkImage( SkAlphaType alpha_type, GrSurfaceOrigin origin) : resource_provider_(resource_provider), resource_id_(resource_id) { - const ChildResource* resource = resource_provider->LockForRead(resource_id); + const ChildResource* resource = + resource_provider->LockForRead(resource_id, false /* overlay_only */); DCHECK(resource); // Use cached SkImage if possible. @@ -942,7 +987,7 @@ DisplayResourceProvider::ScopedReadLockSkImage::ScopedReadLockSkImage( } DisplayResourceProvider::ScopedReadLockSkImage::~ScopedReadLockSkImage() { - resource_provider_->UnlockForRead(resource_id_); + resource_provider_->UnlockForRead(resource_id_, false /* overlay_only */); } DisplayResourceProvider::ScopedReadLockSharedImage::ScopedReadLockSharedImage( diff --git a/chromium/components/viz/service/display/display_resource_provider.h b/chromium/components/viz/service/display/display_resource_provider.h index 71abecc9fd6..2b5c0a95167 100644 --- a/chromium/components/viz/service/display/display_resource_provider.h +++ b/chromium/components/viz/service/display/display_resource_provider.h @@ -158,6 +158,23 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider gfx::ColorSpace color_space_; }; + class VIZ_SERVICE_EXPORT ScopedOverlayLockGL { + public: + ScopedOverlayLockGL(DisplayResourceProvider* resource_provider, + ResourceId resource_id); + ~ScopedOverlayLockGL(); + + ScopedOverlayLockGL(const ScopedOverlayLockGL&) = delete; + ScopedOverlayLockGL& operator=(const ScopedOverlayLockGL&) = delete; + + GLuint texture_id() const { return texture_id_; } + + private: + DisplayResourceProvider* const resource_provider_; + const ResourceId resource_id_; + GLuint texture_id_ = 0; + }; + class VIZ_SERVICE_EXPORT ScopedSamplerGL { public: ScopedSamplerGL(DisplayResourceProvider* resource_provider, @@ -521,8 +538,8 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider // Returns null if we do not have a ContextProvider. gpu::gles2::GLES2Interface* ContextGL() const; - const ChildResource* LockForRead(ResourceId id); - void UnlockForRead(ResourceId id); + const ChildResource* LockForRead(ResourceId id, bool overlay_only); + void UnlockForRead(ResourceId id, bool overlay_only); void TryReleaseResource(ResourceId id, ChildResource* resource); // Binds the given GL resource to a texture target for sampling using the diff --git a/chromium/components/viz/service/display/display_unittest.cc b/chromium/components/viz/service/display/display_unittest.cc index 94f6984ba7f..58082642162 100644 --- a/chromium/components/viz/service/display/display_unittest.cc +++ b/chromium/components/viz/service/display/display_unittest.cc @@ -36,6 +36,7 @@ #include "components/viz/test/fake_output_surface.h" #include "components/viz/test/mock_compositor_frame_sink_client.h" #include "components/viz/test/test_gles2_interface.h" +#include "components/viz/test/viz_test_suite.h" #include "gpu/GLES2/gl2extchromium.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -102,8 +103,11 @@ class StubDisplayClient : public DisplayClient { } }; -void CopyCallback(bool* called, std::unique_ptr<CopyOutputResult> result) { +void CopyCallback(bool* called, + base::OnceClosure finished, + std::unique_ptr<CopyOutputResult> result) { *called = true; + std::move(finished).Run(); } gfx::SwapTimings GetTestSwapTimings() { @@ -214,10 +218,7 @@ class DisplayTest : public testing::Test { void ResetDamageForTest() { scheduler_->ResetDamageForTest(); } - void RunAllPendingInMessageLoop() { - base::RunLoop run_loop; - run_loop.RunUntilIdle(); - } + void RunUntilIdle() { VizTestSuite::RunUntilIdle(); } void LatencyInfoCapTest(bool over_capacity); @@ -424,10 +425,12 @@ TEST_F(DisplayTest, DisplayDamaged) { pass = RenderPass::Create(); pass->output_rect = gfx::Rect(0, 0, 100, 100); pass->damage_rect = gfx::Rect(10, 10, 0, 0); + base::RunLoop copy_run_loop; bool copy_called = false; pass->copy_requests.push_back(std::make_unique<CopyOutputRequest>( CopyOutputRequest::ResultFormat::RGBA_BITMAP, - base::BindOnce(&CopyCallback, ©_called))); + base::BindOnce(&CopyCallback, ©_called, + copy_run_loop.QuitClosure()))); pass->id = 1u; pass_list.push_back(std::move(pass)); @@ -441,6 +444,7 @@ TEST_F(DisplayTest, DisplayDamaged) { display_->DrawAndSwap(base::TimeTicks::Now()); EXPECT_TRUE(scheduler_->swapped()); EXPECT_EQ(5u, output_surface_->num_sent_frames()); + copy_run_loop.Run(); EXPECT_TRUE(copy_called); } @@ -3543,7 +3547,7 @@ TEST_F(DisplayTest, CompositorFrameWithPresentationToken) { pass_list.push_back(std::move(pass)); SubmitCompositorFrame(&pass_list, local_surface_id); display_->DrawAndSwap(base::TimeTicks::Now()); - RunAllPendingInMessageLoop(); + RunUntilIdle(); } { @@ -3557,7 +3561,7 @@ TEST_F(DisplayTest, CompositorFrameWithPresentationToken) { sub_support->SubmitCompositorFrame(sub_local_surface_id, std::move(frame)); display_->DrawAndSwap(base::TimeTicks::Now()); - RunAllPendingInMessageLoop(); + RunUntilIdle(); // Both frames with frame-tokens 1 and 2 requested presentation-feedback. ASSERT_EQ(2u, sub_support->timing_details().size()); @@ -3575,7 +3579,7 @@ TEST_F(DisplayTest, CompositorFrameWithPresentationToken) { sub_support->SubmitCompositorFrame(sub_local_surface_id, std::move(frame)); display_->DrawAndSwap(base::TimeTicks::Now()); - RunAllPendingInMessageLoop(); + RunUntilIdle(); } } @@ -4500,10 +4504,12 @@ TEST_F(DisplayTest, DisplaySizeMismatch) { std::unique_ptr<RenderPass> pass = RenderPass::Create(); pass->output_rect = gfx::Rect(0, 0, 99, 99); pass->damage_rect = gfx::Rect(10, 10, 0, 0); + base::RunLoop copy_run_loop; bool copy_called = false; pass->copy_requests.push_back(std::make_unique<CopyOutputRequest>( CopyOutputRequest::ResultFormat::RGBA_BITMAP, - base::BindOnce(&CopyCallback, ©_called))); + base::BindOnce(&CopyCallback, ©_called, + copy_run_loop.QuitClosure()))); pass->id = 1u; RenderPassList pass_list; @@ -4516,6 +4522,8 @@ TEST_F(DisplayTest, DisplaySizeMismatch) { display_->DrawAndSwap(base::TimeTicks::Now()); + copy_run_loop.Run(); + // Expect no swap happen EXPECT_EQ(0u, output_surface_->num_sent_frames()); diff --git a/chromium/components/viz/service/display/gl_renderer.cc b/chromium/components/viz/service/display/gl_renderer.cc index 1efef996517..591e5c4e6f7 100644 --- a/chromium/components/viz/service/display/gl_renderer.cc +++ b/chromium/components/viz/service/display/gl_renderer.cc @@ -83,6 +83,10 @@ #include "ui/gfx/rrect_f.h" #include "ui/gfx/skia_util.h" +#if defined(USE_X11) +#include "ui/base/ui_base_features.h" +#endif + using gpu::gles2::GLES2Interface; namespace viz { @@ -2826,7 +2830,12 @@ void GLRenderer::FinishDrawingFrame() { ScheduleOutputSurfaceAsOverlay(); #if defined(OS_ANDROID) || defined(USE_OZONE) - ScheduleOverlays(); + bool schedule_overlays = true; +#if defined(USE_X11) + schedule_overlays = features::IsUsingOzonePlatform(); +#endif + if (schedule_overlays) + ScheduleOverlays(); #elif defined(OS_MACOSX) ScheduleCALayers(); #elif defined(OS_WIN) @@ -2855,8 +2864,9 @@ void GLRenderer::FinishDrawingQuadList() { // Use the current surface area as max result. The effect is that overdraw // is reported as a percentage of the output surface size. ie. 2x overdraw // for the whole screen is reported as 200. - const int surface_area = current_surface_size_.GetArea(); - DCHECK_GT(surface_area, 0); + base::CheckedNumeric<int> surface_area = + current_surface_size_.GetCheckedArea(); + DCHECK_GT(static_cast<int>(surface_area.ValueOrDefault(INT_MAX)), 0); gl_->EndQueryEXT(GL_SAMPLES_PASSED_ARB); context_support_->SignalQuery( @@ -3528,7 +3538,7 @@ void GLRenderer::ScheduleCALayers() { unsigned texture_id = 0; if (contents_resource_id) { pending_overlay_resources_.push_back( - std::make_unique<DisplayResourceProvider::ScopedReadLockGL>( + std::make_unique<DisplayResourceProvider::ScopedOverlayLockGL>( resource_provider_, contents_resource_id)); texture_id = pending_overlay_resources_.back()->texture_id(); } @@ -3585,7 +3595,7 @@ void GLRenderer::ScheduleDCLayers() { if (resource_id == kInvalidResourceId) break; pending_overlay_resources_.push_back( - std::make_unique<DisplayResourceProvider::ScopedReadLockGL>( + std::make_unique<DisplayResourceProvider::ScopedOverlayLockGL>( resource_provider_, resource_id)); texture_ids[i] = pending_overlay_resources_.back()->texture_id(); } @@ -3624,7 +3634,7 @@ void GLRenderer::ScheduleOverlays() { OverlayCandidateList& overlays = current_frame()->overlay_list; for (const auto& overlay_candidate : overlays) { pending_overlay_resources_.push_back( - std::make_unique<DisplayResourceProvider::ScopedReadLockGL>( + std::make_unique<DisplayResourceProvider::ScopedOverlayLockGL>( resource_provider_, overlay_candidate.resource_id)); unsigned texture_id = pending_overlay_resources_.back()->texture_id(); @@ -3981,7 +3991,7 @@ void GLRenderer::FlushOverdrawFeedback(const gfx::Rect& output_rect) { } } -void GLRenderer::ProcessOverdrawFeedback(int surface_area, +void GLRenderer::ProcessOverdrawFeedback(base::CheckedNumeric<int> surface_area, unsigned occlusion_query) { unsigned result = 0; DCHECK(occlusion_query); @@ -3990,7 +4000,8 @@ void GLRenderer::ProcessOverdrawFeedback(int surface_area, // Report GPU overdraw as a percentage of |surface_area|. TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("viz.overdraw"), "GPU Overdraw", - (result * 100.0 / surface_area)); + (result * 100.0 / + static_cast<int>(surface_area.ValueOrDefault(INT_MAX)))); } void GLRenderer::UpdateRenderPassTextures( @@ -4041,8 +4052,11 @@ void GLRenderer::AllocateRenderPassResourceIfNeeded( const RenderPassId& render_pass_id, const RenderPassRequirements& requirements) { auto contents_texture_it = render_pass_textures_.find(render_pass_id); - if (contents_texture_it != render_pass_textures_.end()) + if (contents_texture_it != render_pass_textures_.end()) { + DCHECK(gfx::Rect(contents_texture_it->second.size()) + .Contains(gfx::Rect(requirements.size))); return; + } ScopedRenderPassTexture contents_texture( output_surface_->context_provider(), requirements.size, diff --git a/chromium/components/viz/service/display/gl_renderer.h b/chromium/components/viz/service/display/gl_renderer.h index f4943ca0721..94e7a088b39 100644 --- a/chromium/components/viz/service/display/gl_renderer.h +++ b/chromium/components/viz/service/display/gl_renderer.h @@ -155,7 +155,7 @@ class VIZ_SERVICE_EXPORT GLRenderer : public DirectRenderer { friend class GLRendererTest; using OverlayResourceLock = - std::unique_ptr<DisplayResourceProvider::ScopedReadLockGL>; + std::unique_ptr<DisplayResourceProvider::ScopedOverlayLockGL>; using OverlayResourceLockList = std::vector<OverlayResourceLock>; // If a RenderPass is used as an overlay, we render the RenderPass with any @@ -351,7 +351,8 @@ class VIZ_SERVICE_EXPORT GLRenderer : public DirectRenderer { void SetupOverdrawFeedback(); // Process overdraw feedback from query. - void ProcessOverdrawFeedback(int surface_area, unsigned query); + void ProcessOverdrawFeedback(base::CheckedNumeric<int> surface_area, + unsigned query); bool OverdrawTracingEnabled(); ResourceFormat CurrentRenderPassResourceFormat() const; diff --git a/chromium/components/viz/service/display/gl_renderer_copier.cc b/chromium/components/viz/service/display/gl_renderer_copier.cc index cd54ec7f65b..eca6218676e 100644 --- a/chromium/components/viz/service/display/gl_renderer_copier.cc +++ b/chromium/components/viz/service/display/gl_renderer_copier.cc @@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/process/memory.h" #include "base/stl_util.h" +#include "base/threading/sequenced_task_runner_handle.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/frame_sinks/copy_output_result.h" #include "components/viz/common/frame_sinks/copy_output_util.h" @@ -215,7 +216,9 @@ void GLRendererCopier::CopyFromTextureOrFramebuffer( // requires that the result be accessed via a task in the same task runner // sequence as the GLRendererCopier. Since I420_PLANES requests are meant // to be VIZ-internal, this is an acceptable limitation to enforce. - DCHECK(request->SendsResultsInCurrentSequence() || async_gl_task_runner_); + if (!request->SendsResultsInCurrentSequence() && !async_gl_task_runner_) { + request->set_result_task_runner(base::SequencedTaskRunnerHandle::Get()); + } const gfx::Rect aligned_rect = RenderI420Textures( *request, flipped_source, color_space, source_texture, @@ -508,9 +511,10 @@ class ReadPixelsWorkflow { // Create a buffer for the pixel transfer. gl->GenBuffers(1, &transfer_buffer_); gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer_); - gl->BufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, - kRGBABytesPerPixel * result_rect.size().GetArea(), nullptr, - GL_STREAM_READ); + gl->BufferData( + GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, + (result_rect.size().GetCheckedArea() * kRGBABytesPerPixel).ValueOrDie(), + nullptr, GL_STREAM_READ); // Execute an asynchronous read-pixels operation, with a query that triggers // when Finish() should be run. @@ -749,13 +753,15 @@ class ReadI420PlanesWorkflow auto* const gl = context_provider_->ContextGL(); gl->GenBuffers(1, &transfer_buffer_); gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer_); - const int y_plane_bytes = kRGBABytesPerPixel * y_texture_size().GetArea(); - const int chroma_plane_bytes = - kRGBABytesPerPixel * chroma_texture_size().GetArea(); + base::CheckedNumeric<int> y_plane_bytes = + y_texture_size().GetCheckedArea() * kRGBABytesPerPixel; + base::CheckedNumeric<int> chroma_plane_bytes = + chroma_texture_size().GetCheckedArea() * kRGBABytesPerPixel; gl->BufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, - y_plane_bytes + 2 * chroma_plane_bytes, nullptr, - GL_STREAM_READ); - data_offsets_ = {0, y_plane_bytes, y_plane_bytes + chroma_plane_bytes}; + (y_plane_bytes + chroma_plane_bytes * 2).ValueOrDie(), + nullptr, GL_STREAM_READ); + data_offsets_ = {0, y_plane_bytes.ValueOrDie(), + (y_plane_bytes + chroma_plane_bytes).ValueOrDie()}; gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); // Generate the three queries used for determining when each of the plane diff --git a/chromium/components/viz/service/display/gl_renderer_copier_unittest.cc b/chromium/components/viz/service/display/gl_renderer_copier_unittest.cc index aebb5ed5d56..fb3452b9e0f 100644 --- a/chromium/components/viz/service/display/gl_renderer_copier_unittest.cc +++ b/chromium/components/viz/service/display/gl_renderer_copier_unittest.cc @@ -113,7 +113,7 @@ TEST_F(GLRendererCopierTest, ReusesThingsFromSameSource) { const base::UnguessableToken no_source; EXPECT_EQ(0u, GetCopierCacheSize()); auto things = TakeReusableThingsOrCreate(no_source); - EXPECT_TRUE(!!things); + EXPECT_TRUE(things); StashReusableThingsOrDelete(no_source, std::move(things)); EXPECT_EQ(nullptr, PeekReusableThings(no_source)); EXPECT_EQ(0u, GetCopierCacheSize()); @@ -122,7 +122,7 @@ TEST_F(GLRendererCopierTest, ReusesThingsFromSameSource) { const auto source = base::UnguessableToken::Create(); things = TakeReusableThingsOrCreate(source); ReusableThings* things_raw_ptr = things.get(); - EXPECT_TRUE(!!things_raw_ptr); + EXPECT_TRUE(things_raw_ptr); StashReusableThingsOrDelete(source, std::move(things)); EXPECT_EQ(things_raw_ptr, PeekReusableThings(source)); EXPECT_EQ(1u, GetCopierCacheSize()); @@ -131,7 +131,7 @@ TEST_F(GLRendererCopierTest, ReusesThingsFromSameSource) { const auto source2 = base::UnguessableToken::Create(); things = TakeReusableThingsOrCreate(source2); things_raw_ptr = things.get(); - EXPECT_TRUE(!!things_raw_ptr); + EXPECT_TRUE(things_raw_ptr); EXPECT_EQ(1u, GetCopierCacheSize()); StashReusableThingsOrDelete(source2, std::move(things)); EXPECT_EQ(things_raw_ptr, PeekReusableThings(source2)); @@ -144,14 +144,14 @@ TEST_F(GLRendererCopierTest, FreesUnusedResources) { const base::UnguessableToken source = base::UnguessableToken::Create(); EXPECT_EQ(0u, GetCopierCacheSize()); StashReusableThingsOrDelete(source, TakeReusableThingsOrCreate(source)); - EXPECT_TRUE(!!PeekReusableThings(source)); + EXPECT_TRUE(PeekReusableThings(source)); EXPECT_EQ(1u, GetCopierCacheSize()); // Call FreesUnusedCachedResources() the maximum number of times before the // cache entry would be considered for freeing. for (int i = 0; i < kKeepalivePeriod - 1; ++i) { FreeUnusedCachedResources(); - EXPECT_TRUE(!!PeekReusableThings(source)); + EXPECT_TRUE(PeekReusableThings(source)); EXPECT_EQ(1u, GetCopierCacheSize()); if (HasFailure()) break; @@ -160,7 +160,7 @@ TEST_F(GLRendererCopierTest, FreesUnusedResources) { // Calling FreeUnusedCachedResources() just one more time should cause the // cache entry to be freed. FreeUnusedCachedResources(); - EXPECT_FALSE(!!PeekReusableThings(source)); + EXPECT_FALSE(PeekReusableThings(source)); EXPECT_EQ(0u, GetCopierCacheSize()); } diff --git a/chromium/components/viz/service/display/gl_renderer_unittest.cc b/chromium/components/viz/service/display/gl_renderer_unittest.cc index a698822e170..6e7ecb769eb 100644 --- a/chromium/components/viz/service/display/gl_renderer_unittest.cc +++ b/chromium/components/viz/service/display/gl_renderer_unittest.cc @@ -37,6 +37,7 @@ #include "components/viz/test/fake_output_surface.h" #include "components/viz/test/test_gles2_interface.h" #include "components/viz/test/test_shared_bitmap_manager.h" +#include "components/viz/test/viz_test_suite.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/context_support.h" #include "gpu/config/gpu_finch_features.h" @@ -85,6 +86,11 @@ MATCHER_P(MatchesSyncToken, sync_token, "") { class GLRendererTest : public testing::Test { protected: + ~GLRendererTest() override { + // Some tests create CopyOutputRequests which will PostTask ensure + // they are all cleaned up and completed before destroying the test. + VizTestSuite::RunUntilIdle(); + } RenderPass* root_render_pass() { return render_passes_in_draw_order_.back().get(); } diff --git a/chromium/components/viz/service/display/output_surface.h b/chromium/components/viz/service/display/output_surface.h index 6476ec148f2..b58adf77f08 100644 --- a/chromium/components/viz/service/display/output_surface.h +++ b/chromium/components/viz/service/display/output_surface.h @@ -9,7 +9,6 @@ #include <vector> #include "base/callback_helpers.h" -#include "base/containers/circular_deque.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/threading/thread_checker.h" @@ -57,6 +56,10 @@ class VIZ_SERVICE_EXPORT OutputSurface { Capabilities(const Capabilities& capabilities); int max_frames_pending = 1; + // The number of buffers for the SkiaOutputDevice. If the + // |supports_post_sub_buffer| true, SkiaOutputSurfaceImpl will track target + // damaged area based on this number. + int number_of_buffers = 2; // Whether this output surface renders to the default OpenGL zero // framebuffer or to an offscreen framebuffer. bool uses_default_gl_framebuffer = true; @@ -88,6 +91,9 @@ class VIZ_SERVICE_EXPORT OutputSurface { // When this is false contents outside the damaged area might need to be // recomposited to the surface. bool only_invalidates_damage_rect = true; + // Whether OutputSurface::GetTargetDamageBoundingRect is implemented and + // will return a bounding rectangle of the target buffer invalidated area. + bool supports_target_damage = false; // Whether the gpu supports surfaceless surface (equivalent of using buffer // queue). bool supports_surfaceless = false; @@ -96,6 +102,10 @@ class VIZ_SERVICE_EXPORT OutputSurface { bool android_surface_control_feature_enabled = false; // True if the buffer content will be preserved after presenting. bool preserve_buffer_content = false; + // True if the SkiaOutputDevice will set + // SwapBuffersCompleteParams::frame_buffer_damage_area for every + // SwapBuffers complete callback. + bool damage_area_from_skia_output_device = false; // The SkColorType and GrBackendFormat for non-HDR and HDR. // TODO(penghuang): remove SkColorType and GrBackendFormat when // OutputSurface uses the |format| passed to Reshape(). diff --git a/chromium/components/viz/service/display/overlay_processor_interface.cc b/chromium/components/viz/service/display/overlay_processor_interface.cc index 5f9990fbd50..f09e8ac00fb 100644 --- a/chromium/components/viz/service/display/overlay_processor_interface.cc +++ b/chromium/components/viz/service/display/overlay_processor_interface.cc @@ -8,6 +8,7 @@ #include "base/metrics/histogram_macros.h" #include "components/viz/common/display/renderer_settings.h" #include "components/viz/common/features.h" +#include "components/viz/service/display/overlay_processor_stub.h" #if defined(OS_MACOSX) #include "components/viz/service/display/overlay_processor_mac.h" @@ -18,10 +19,9 @@ #include "components/viz/service/display/overlay_processor_surface_control.h" #elif defined(USE_OZONE) #include "components/viz/service/display/overlay_processor_ozone.h" +#include "ui/base/ui_base_features.h" #include "ui/ozone/public/overlay_manager_ozone.h" #include "ui/ozone/public/ozone_platform.h" -#else -#include "components/viz/service/display/overlay_processor_stub.h" #endif namespace viz { @@ -98,6 +98,8 @@ OverlayProcessorInterface::CreateOverlayProcessor( enable_dc_overlay, std::make_unique<DCLayerOverlayProcessor>(renderer_settings))); #elif defined(USE_OZONE) + if (!features::IsUsingOzonePlatform()) + return std::make_unique<OverlayProcessorStub>(); bool overlay_enabled = surface_handle != gpu::kNullSurfaceHandle; overlay_enabled &= !renderer_settings.overlay_strategies.empty(); std::unique_ptr<ui::OverlayCandidatesOzone> overlay_candidates; diff --git a/chromium/components/viz/service/display/overlay_processor_interface.h b/chromium/components/viz/service/display/overlay_processor_interface.h index c5db304223f..e1779e03646 100644 --- a/chromium/components/viz/service/display/overlay_processor_interface.h +++ b/chromium/components/viz/service/display/overlay_processor_interface.h @@ -107,6 +107,10 @@ class VIZ_SERVICE_EXPORT OverlayProcessorInterface { virtual ~OverlayProcessorInterface() {} virtual bool IsOverlaySupported() const = 0; + // Returns a bounding rectangle of the last set of overlay planes scheduled. + // It's expected to be called after ProcessForOverlays at frame N-1 has been + // called and before GetAndResetOverlayDamage at frame N. + virtual gfx::Rect GetPreviousFrameOverlaysBoundingRect() const = 0; virtual gfx::Rect GetAndResetOverlayDamage() = 0; // Returns true if the platform supports hw overlays and surface occluding diff --git a/chromium/components/viz/service/display/overlay_processor_mac.cc b/chromium/components/viz/service/display/overlay_processor_mac.cc index d972c9873dd..2a3b10b4336 100644 --- a/chromium/components/viz/service/display/overlay_processor_mac.cc +++ b/chromium/components/viz/service/display/overlay_processor_mac.cc @@ -37,6 +37,12 @@ bool OverlayProcessorMac::IsOverlaySupported() const { return could_overlay_; } +gfx::Rect OverlayProcessorMac::GetPreviousFrameOverlaysBoundingRect() const { + // TODO(dcastagna): Implement me. + NOTIMPLEMENTED(); + return gfx::Rect(); +} + gfx::Rect OverlayProcessorMac::GetAndResetOverlayDamage() { gfx::Rect result = ca_overlay_damage_rect_; ca_overlay_damage_rect_ = gfx::Rect(); diff --git a/chromium/components/viz/service/display/overlay_processor_mac.h b/chromium/components/viz/service/display/overlay_processor_mac.h index 03a8112ad4f..26e583a2b6b 100644 --- a/chromium/components/viz/service/display/overlay_processor_mac.h +++ b/chromium/components/viz/service/display/overlay_processor_mac.h @@ -35,6 +35,7 @@ class VIZ_SERVICE_EXPORT OverlayProcessorMac bool DisableSplittingQuads() const override; bool IsOverlaySupported() const override; + gfx::Rect GetPreviousFrameOverlaysBoundingRect() const override; gfx::Rect GetAndResetOverlayDamage() override; // Returns true if the platform supports hw overlays and surface occluding diff --git a/chromium/components/viz/service/display/overlay_processor_ozone.cc b/chromium/components/viz/service/display/overlay_processor_ozone.cc index 9dc16e17d09..9f3e7ef0679 100644 --- a/chromium/components/viz/service/display/overlay_processor_ozone.cc +++ b/chromium/components/viz/service/display/overlay_processor_ozone.cc @@ -4,6 +4,7 @@ #include "components/viz/service/display/overlay_processor_ozone.h" +#include "base/logging.h" #include "components/viz/common/features.h" #include "components/viz/service/display/overlay_strategy_fullscreen.h" #include "components/viz/service/display/overlay_strategy_single_on_top.h" diff --git a/chromium/components/viz/service/display/overlay_processor_stub.cc b/chromium/components/viz/service/display/overlay_processor_stub.cc index 799083887a6..0be55eec1c4 100644 --- a/chromium/components/viz/service/display/overlay_processor_stub.cc +++ b/chromium/components/viz/service/display/overlay_processor_stub.cc @@ -11,6 +11,10 @@ bool OverlayProcessorStub::IsOverlaySupported() const { gfx::Rect OverlayProcessorStub::GetAndResetOverlayDamage() { return gfx::Rect(); } +gfx::Rect OverlayProcessorStub::GetPreviousFrameOverlaysBoundingRect() const { + return gfx::Rect(); +} + bool OverlayProcessorStub::NeedsSurfaceOccludingDamageRect() const { return false; } diff --git a/chromium/components/viz/service/display/overlay_processor_stub.h b/chromium/components/viz/service/display/overlay_processor_stub.h index 92069a7e077..1dee0517889 100644 --- a/chromium/components/viz/service/display/overlay_processor_stub.h +++ b/chromium/components/viz/service/display/overlay_processor_stub.h @@ -18,6 +18,7 @@ class VIZ_SERVICE_EXPORT OverlayProcessorStub // Overrides OverlayProcessorInterface's pure virtual functions. bool IsOverlaySupported() const final; + gfx::Rect GetPreviousFrameOverlaysBoundingRect() const final; gfx::Rect GetAndResetOverlayDamage() final; bool NeedsSurfaceOccludingDamageRect() const final; void ProcessForOverlays( diff --git a/chromium/components/viz/service/display/overlay_processor_using_strategy.cc b/chromium/components/viz/service/display/overlay_processor_using_strategy.cc index a218f08c331..86b00f87f22 100644 --- a/chromium/components/viz/service/display/overlay_processor_using_strategy.cc +++ b/chromium/components/viz/service/display/overlay_processor_using_strategy.cc @@ -35,6 +35,13 @@ OverlayProcessorUsingStrategy::OverlayProcessorUsingStrategy() OverlayProcessorUsingStrategy::~OverlayProcessorUsingStrategy() = default; +gfx::Rect OverlayProcessorUsingStrategy::GetPreviousFrameOverlaysBoundingRect() + const { + gfx::Rect result = overlay_damage_rect_; + result.Union(previous_frame_underlay_rect_); + return result; +} + gfx::Rect OverlayProcessorUsingStrategy::GetAndResetOverlayDamage() { gfx::Rect result = overlay_damage_rect_; overlay_damage_rect_ = gfx::Rect(); diff --git a/chromium/components/viz/service/display/overlay_processor_using_strategy.h b/chromium/components/viz/service/display/overlay_processor_using_strategy.h index f946919c378..faa34d1b99b 100644 --- a/chromium/components/viz/service/display/overlay_processor_using_strategy.h +++ b/chromium/components/viz/service/display/overlay_processor_using_strategy.h @@ -66,6 +66,7 @@ class VIZ_SERVICE_EXPORT OverlayProcessorUsingStrategy ~OverlayProcessorUsingStrategy() override; + gfx::Rect GetPreviousFrameOverlaysBoundingRect() const final; gfx::Rect GetAndResetOverlayDamage() final; // Override OverlayProcessor. diff --git a/chromium/components/viz/service/display/overlay_processor_win.cc b/chromium/components/viz/service/display/overlay_processor_win.cc index ffa85923c69..9c5b852fb69 100644 --- a/chromium/components/viz/service/display/overlay_processor_win.cc +++ b/chromium/components/viz/service/display/overlay_processor_win.cc @@ -27,6 +27,12 @@ bool OverlayProcessorWin::IsOverlaySupported() const { return enable_dc_overlay_; } +gfx::Rect OverlayProcessorWin::GetPreviousFrameOverlaysBoundingRect() const { + // TODO(dcastagna): Implement me. + NOTIMPLEMENTED(); + return gfx::Rect(); +} + gfx::Rect OverlayProcessorWin::GetAndResetOverlayDamage() { return gfx::Rect(); } diff --git a/chromium/components/viz/service/display/overlay_processor_win.h b/chromium/components/viz/service/display/overlay_processor_win.h index f7863ad20da..95147c7dfc3 100644 --- a/chromium/components/viz/service/display/overlay_processor_win.h +++ b/chromium/components/viz/service/display/overlay_processor_win.h @@ -34,6 +34,7 @@ class VIZ_SERVICE_EXPORT OverlayProcessorWin ~OverlayProcessorWin() override; bool IsOverlaySupported() const override; + gfx::Rect GetPreviousFrameOverlaysBoundingRect() const override; gfx::Rect GetAndResetOverlayDamage() override; // Returns true if the platform supports hw overlays and surface occluding diff --git a/chromium/components/viz/service/display/overlay_strategy_underlay_cast.cc b/chromium/components/viz/service/display/overlay_strategy_underlay_cast.cc index 4d1fda11a4b..5436d5dea3c 100644 --- a/chromium/components/viz/service/display/overlay_strategy_underlay_cast.cc +++ b/chromium/components/viz/service/display/overlay_strategy_underlay_cast.cc @@ -6,6 +6,7 @@ #include "base/containers/adapters.h" #include "base/lazy_instance.h" +#include "base/logging.h" #include "base/unguessable_token.h" #include "build/chromecast_buildflags.h" #include "components/viz/common/quads/draw_quad.h" diff --git a/chromium/components/viz/service/display/program_binding.cc b/chromium/components/viz/service/display/program_binding.cc index 0c246b056d8..975d1d01845 100644 --- a/chromium/components/viz/service/display/program_binding.cc +++ b/chromium/components/viz/service/display/program_binding.cc @@ -4,6 +4,7 @@ #include "components/viz/service/display/program_binding.h" +#include "base/logging.h" #include "base/trace_event/trace_event.h" #include "components/viz/service/display/geometry_binding.h" #include "gpu/GLES2/gl2extchromium.h" diff --git a/chromium/components/viz/service/display/program_binding.h b/chromium/components/viz/service/display/program_binding.h index 19bf651da6a..bba6169e8ab 100644 --- a/chromium/components/viz/service/display/program_binding.h +++ b/chromium/components/viz/service/display/program_binding.h @@ -7,7 +7,7 @@ #include <string> -#include "base/logging.h" +#include "base/check_op.h" #include "base/macros.h" #include "build/build_config.h" #include "components/viz/common/gpu/context_provider.h" diff --git a/chromium/components/viz/service/display/renderer_perftest.cc b/chromium/components/viz/service/display/renderer_perftest.cc index 27ac29e25c9..19a0a751da4 100644 --- a/chromium/components/viz/service/display/renderer_perftest.cc +++ b/chromium/components/viz/service/display/renderer_perftest.cc @@ -58,6 +58,7 @@ #include "ui/gfx/color_space.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" +#include "ui/gl/gl_implementation.h" namespace viz { @@ -296,6 +297,7 @@ class RendererPerfTest : public testing::Test { GpuServiceImpl* gpu_service); void SetUp() override { + enable_pixel_output_ = std::make_unique<gl::DisableNullDrawGLBindings>(); renderer_settings_.use_skia_renderer = std::is_base_of<SkiaRenderer, RendererType>::value; if (renderer_settings_.use_skia_renderer) @@ -685,6 +687,7 @@ class RendererPerfTest : public testing::Test { std::unique_ptr<ClientResourceProvider> child_resource_provider_; std::vector<TransferableResource> resource_list_; base::LapTimer timer_; + std::unique_ptr<gl::DisableNullDrawGLBindings> enable_pixel_output_; DISALLOW_COPY_AND_ASSIGN(RendererPerfTest); }; diff --git a/chromium/components/viz/service/display/shader.h b/chromium/components/viz/service/display/shader.h index 6d154b623cb..f64ae804946 100644 --- a/chromium/components/viz/service/display/shader.h +++ b/chromium/components/viz/service/display/shader.h @@ -7,7 +7,6 @@ #include <string> -#include "base/logging.h" #include "base/macros.h" #include "base/strings/string_piece.h" #include "components/viz/service/viz_service_export.h" diff --git a/chromium/components/viz/service/display/skia_readback_pixeltest.cc b/chromium/components/viz/service/display/skia_readback_pixeltest.cc index 93264575e8c..d2f1b7cc176 100644 --- a/chromium/components/viz/service/display/skia_readback_pixeltest.cc +++ b/chromium/components/viz/service/display/skia_readback_pixeltest.cc @@ -199,6 +199,9 @@ TEST_P(SkiaReadbackPixelTest, ExecutesCopyRequest) { renderer_->DecideRenderPassAllocationsForFrame(pass_list); renderer_->DrawFrame(&pass_list, 1.0f, kSourceSize, gfx::DisplayColorSpaces()); + // Call SwapBuffersSkipped(), so the renderer can have a chance to release + // resources. + renderer_->SwapBuffersSkipped(); loop.Run(); diff --git a/chromium/components/viz/service/display/skia_renderer.cc b/chromium/components/viz/service/display/skia_renderer.cc index 4a56dcc9a10..cff81b3c901 100644 --- a/chromium/components/viz/service/display/skia_renderer.cc +++ b/chromium/components/viz/service/display/skia_renderer.cc @@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/bits.h" #include "base/command_line.h" +#include "base/logging.h" #include "base/optional.h" #include "base/synchronization/waitable_event.h" #include "base/trace_event/trace_event.h" @@ -63,6 +64,10 @@ #include "ui/gfx/skia_util.h" #include "ui/gfx/transform.h" +#if defined(USE_OZONE) +#include "ui/base/ui_base_features.h" +#endif + namespace viz { namespace { @@ -836,8 +841,6 @@ void SkiaRenderer::BindFramebufferToOutputSurface() { switch (draw_mode_) { case DrawMode::DDL: { root_canvas_ = skia_output_surface_->BeginPaintCurrentFrame(); - // TODO(https://crbug.com/1038107): Handle BeginPaintCurrentFrame() fail. - CHECK(root_canvas_); break; } case DrawMode::SKPRECORD: { @@ -1119,12 +1122,25 @@ void SkiaRenderer::PrepareCanvasForRPDQ(const DrawRPDQParams& rpdq_params, } } + if (rpdq_params.mask_image.get()) { + // The old behavior (in skia) was to filter the clipmask based on the + // setting in the layer's paint. Now we can set that to whatever we want + // when we make the clip-shader. For now, I will replicate the old (impl) + // logic. + SkFilterQuality filtering = + layer_paint.getFilterQuality() == kNone_SkFilterQuality + ? kNone_SkFilterQuality + : kLow_SkFilterQuality; + current_canvas_->save(); + current_canvas_->clipShader(rpdq_params.mask_image->makeShader( + SkTileMode::kClamp, SkTileMode::kClamp, + &rpdq_params.mask_to_quad_matrix, filtering)); + } SkRect bounds = gfx::RectFToSkRect(rpdq_params.bypass_clip.has_value() ? *rpdq_params.bypass_clip : params->visible_rect); - current_canvas_->saveLayer(SkCanvas::SaveLayerRec( - &bounds, &layer_paint, backdrop_filter.get(), - rpdq_params.mask_image.get(), &rpdq_params.mask_to_quad_matrix, 0)); + current_canvas_->saveLayer( + SkCanvas::SaveLayerRec(&bounds, &layer_paint, backdrop_filter.get(), 0)); // If we have backdrop filtered content (and not transparent black like with // regular render passes), we have to clear out the parts of the layer that @@ -2167,11 +2183,47 @@ void SkiaRenderer::ScheduleOverlays() { } skia_output_surface_->ScheduleOverlays( std::move(current_frame()->overlay_list), std::move(sync_tokens)); -#elif defined(OS_MACOSX) || defined(USE_OZONE) +#elif defined(OS_MACOSX) + DCHECK(output_surface_->capabilities().supports_surfaceless); + auto& locks = pending_overlay_locks_.back(); + std::vector<gpu::SyncToken> sync_tokens; + for (CALayerOverlay& ca_layer_overlay : current_frame()->overlay_list) { + // Some overlays are for solid-color layers. + if (!ca_layer_overlay.contents_resource_id) + continue; + + // TODO(https://crbug.com/894929): Track IOSurface in-use instead of just + // unlocking after the next SwapBuffers is completed. + locks.emplace_back(resource_provider_, + ca_layer_overlay.contents_resource_id); + auto& lock = locks.back(); + + // Sync tokens ensure the texture to be overlaid is available before + // scheduling it for display. + if (lock.sync_token().HasData()) + sync_tokens.push_back(lock.sync_token()); + + // Populate the |mailbox| of the CALayerOverlay which will be used to look + // up the corresponding GLImageIOSurface when building the CALayer tree. + ca_layer_overlay.mailbox = lock.mailbox(); + DCHECK(!ca_layer_overlay.mailbox.IsZero()); + } + skia_output_surface_->ScheduleOverlays( + std::move(current_frame()->overlay_list), std::move(sync_tokens)); +#elif defined(USE_OZONE) + // For platforms that don't support overlays, the + // current_frame()->overlay_list should be empty, and this code should not be + // reached. + if (!features::IsUsingOzonePlatform()) { + NOTREACHED(); + return; + } + NOTIMPLEMENTED_LOG_ONCE(); #else - // For platforms doesn't support overlays, the current_frame()->overlay_list - // should be empty, and here should not be reached. + // For platforms that don't support overlays, the + // current_frame()->overlay_list should be empty, and this code should not be + // reached. NOTREACHED(); #endif } @@ -2411,6 +2463,10 @@ void SkiaRenderer::DrawRenderPassQuad(const RenderPassDrawQuad* quad, } } + if (!content_image) { + return; + } + // If the RP generated mipmaps when it was created, set quality to medium, // which turns on mipmap filtering in Skia. if (backing.generate_mipmap) @@ -2562,8 +2618,10 @@ void SkiaRenderer::AllocateRenderPassResourceIfNeeded( const RenderPassId& render_pass_id, const RenderPassRequirements& requirements) { auto it = render_pass_backings_.find(render_pass_id); - if (it != render_pass_backings_.end()) + if (it != render_pass_backings_.end()) { + DCHECK(gfx::Rect(it->second.size).Contains(gfx::Rect(requirements.size))); return; + } // TODO(penghuang): check supported format correctly. gpu::Capabilities caps; diff --git a/chromium/components/viz/service/display/software_renderer.cc b/chromium/components/viz/service/display/software_renderer.cc index ebbaa150688..3321bf0001a 100644 --- a/chromium/components/viz/service/display/software_renderer.cc +++ b/chromium/components/viz/service/display/software_renderer.cc @@ -925,8 +925,11 @@ void SoftwareRenderer::AllocateRenderPassResourceIfNeeded( const RenderPassId& render_pass_id, const RenderPassRequirements& requirements) { auto it = render_pass_bitmaps_.find(render_pass_id); - if (it != render_pass_bitmaps_.end()) + if (it != render_pass_bitmaps_.end()) { + DCHECK(it->second.width() >= requirements.size.width() && + it->second.height() >= requirements.size.height()); return; + } // The |requirements.mipmap| is only used for gpu-based rendering, so not used // here. diff --git a/chromium/components/viz/service/display/surface_aggregator.cc b/chromium/components/viz/service/display/surface_aggregator.cc index ea0032fe854..a94b9c8f540 100644 --- a/chromium/components/viz/service/display/surface_aggregator.cc +++ b/chromium/components/viz/service/display/surface_aggregator.cc @@ -118,39 +118,9 @@ struct SurfaceAggregator::RoundedCornerInfo { bool is_fast_rounded_corner; }; -struct SurfaceAggregator::ChildSurfaceInfo { - struct QuadStateInfo { - gfx::Transform transform_to_root_target; - gfx::Transform quad_to_target_transform; - gfx::Rect clip_rect; - bool is_clipped; - }; - - ChildSurfaceInfo(RenderPassId parent_pass_id, - const gfx::Rect& quad_rect, - bool stretch_content_to_fill_bounds) - : parent_pass_id(parent_pass_id), - quad_rect(quad_rect), - stretch_content_to_fill_bounds(stretch_content_to_fill_bounds) { - // In most cases there would be one or two different embeddings of a - // surface in the render pass tree. Reserve two elements to avoid - // unnecessary copies. - quad_state_infos.reserve(2); - } - - RenderPassId parent_pass_id; - gfx::Rect quad_rect; - bool stretch_content_to_fill_bounds; - bool has_moved_pixels = false; - std::vector<QuadStateInfo> quad_state_infos; -}; - struct SurfaceAggregator::RenderPassMapEntry { - RenderPassMapEntry(RenderPass* render_pass, - bool has_pixel_moving_backdrop_filter) - : render_pass(render_pass), - damage_rect(render_pass->output_rect), - has_pixel_moving_backdrop_filter(has_pixel_moving_backdrop_filter) {} + explicit RenderPassMapEntry(RenderPass* render_pass) + : render_pass(render_pass) {} // Make this move-only. RenderPassMapEntry(RenderPassMapEntry&&) = default; @@ -161,12 +131,8 @@ struct SurfaceAggregator::RenderPassMapEntry { RenderPass* render_pass; // Damage rect of the render pass in its own content space. gfx::Rect damage_rect; - bool has_pixel_moving_backdrop_filter; bool is_visited = false; - // If the render pass contains any surfaces in its quad list, either from - // SurfaceDrawQuads or from render passes referenced by RPDQs. - bool contains_surfaces = false; }; SurfaceAggregator::SurfaceAggregator(SurfaceManager* manager, @@ -198,18 +164,16 @@ SurfaceAggregator::GenerateRenderPassMap(const RenderPassList& render_pass_list, // This data is created once and typically small or empty. Collect all items // and pass to a flat_map to sort once. std::vector<std::pair<RenderPassId, RenderPassMapEntry>> render_pass_data; + render_pass_data.reserve(render_pass_list.size()); for (const auto& render_pass : render_pass_list) { - bool has_pixel_moving_backdrop_filter = - render_pass->backdrop_filters.HasFilterThatMovesPixels(); - if (has_pixel_moving_backdrop_filter) { + if (render_pass->backdrop_filters.HasFilterThatMovesPixels()) { DCHECK_NE(render_pass.get(), root_pass_in_root_surface) << "The root render pass on the root surface can not have backdrop " "affecting filters"; } - render_pass_data.emplace_back( - std::piecewise_construct, std::forward_as_tuple(render_pass->id), - std::forward_as_tuple(render_pass.get(), - has_pixel_moving_backdrop_filter)); + render_pass_data.emplace_back(std::piecewise_construct, + std::forward_as_tuple(render_pass->id), + std::forward_as_tuple(render_pass.get())); } return base::flat_map<RenderPassId, RenderPassMapEntry>( std::move(render_pass_data)); @@ -308,15 +272,25 @@ gfx::Rect SurfaceAggregator::CalculateOccludingSurfaceDamageRect( // Transform the quad to the parent root target space // Note: this quad is on the child root render pass. - gfx::Transform transform(parent_quad_to_root_target_transform, - quad->shared_quad_state->quad_to_target_transform); - gfx::Rect surface_in_root_target_space = - cc::MathUtil::MapEnclosingClippedRect(transform, quad->visible_rect); + gfx::Rect quad_in_root_target_space; + if (quad->shared_quad_state->is_clipped) { + gfx::Rect quad_in_target_space = cc::MathUtil::MapEnclosingClippedRect( + quad->shared_quad_state->quad_to_target_transform, quad->visible_rect); + quad_in_target_space.Intersect(quad->shared_quad_state->clip_rect); + quad_in_root_target_space = cc::MathUtil::MapEnclosingClippedRect( + parent_quad_to_root_target_transform, quad_in_target_space); + + } else { + gfx::Transform transform(parent_quad_to_root_target_transform, + quad->shared_quad_state->quad_to_target_transform); + quad_in_root_target_space = + cc::MathUtil::MapEnclosingClippedRect(transform, quad->visible_rect); + } // damage_rects_union_of_surfaces_on_top_ is already in the parent root target // space. gfx::Rect occluding_damage_rect = damage_rects_union_of_surfaces_on_top_; - occluding_damage_rect.Intersect(surface_in_root_target_space); + occluding_damage_rect.Intersect(quad_in_root_target_space); return occluding_damage_rect; } @@ -560,6 +534,12 @@ void SurfaceAggregator::EmitSurfaceContent( CanMergeRoundedCorner(rounded_corner_info, *render_pass_list.back()) && source_sqs->de_jelly_delta_y == 0; + if (frame.metadata.delegated_ink_metadata) { + TransformAndStoreDelegatedInkMetadata( + gfx::Transform(dest_pass->transform_to_root_target, combined_transform), + frame.metadata.delegated_ink_metadata.get()); + } + gfx::Rect occluding_damage_rect; bool occluding_damage_rect_valid = ProcessSurfaceOccludingDamage( surface, render_pass_list, combined_transform, dest_pass, @@ -702,14 +682,23 @@ void SurfaceAggregator::EmitSurfaceContent( // We can't produce content outside of |quad_rect|, so clip the visible // rect if necessary. quad_visible_rect.Intersect(quad_rect); - - auto* quad = dest_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>(); RenderPassId remapped_pass_id = RemapPassId(last_pass.id, surface_id); - quad->SetNew(shared_quad_state, quad_rect, quad_visible_rect, - remapped_pass_id, 0, gfx::RectF(), gfx::Size(), - gfx::Vector2dF(), gfx::PointF(), tex_coord_rect, - /*force_anti_aliasing_off=*/false, - /* backdrop_filter_quality*/ 1.0f); + if (quad_visible_rect.IsEmpty()) { + dest_pass_list_->erase( + std::remove_if( + dest_pass_list_->begin(), dest_pass_list_->end(), + [&remapped_pass_id](const std::unique_ptr<RenderPass>& pass) { + return pass->id == remapped_pass_id; + }), + dest_pass_list_->end()); + } else { + auto* quad = dest_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>(); + quad->SetNew(shared_quad_state, quad_rect, quad_visible_rect, + remapped_pass_id, 0, gfx::RectF(), gfx::Size(), + gfx::Vector2dF(), gfx::PointF(), tex_coord_rect, + /*force_anti_aliasing_off=*/false, + /* backdrop_filter_quality*/ 1.0f); + } } referenced_surfaces_.erase(surface_id); @@ -1144,6 +1133,13 @@ void SurfaceAggregator::CopyPasses(const CompositorFrame& frame, const gfx::Transform surface_transform = IsRootSurface(surface) ? root_surface_transform_ : gfx::Transform(); + if (frame.metadata.delegated_ink_metadata) { + TransformAndStoreDelegatedInkMetadata( + gfx::Transform(source_pass_list.back()->transform_to_root_target, + surface_transform), + frame.metadata.delegated_ink_metadata.get()); + } + gfx::Rect occluding_damage_rect; bool occluding_damage_rect_valid = ProcessSurfaceOccludingDamage( surface, source_pass_list, surface_transform, @@ -1235,125 +1231,52 @@ void SurfaceAggregator::ProcessAddedAndRemovedSurfaces() { } } -void SurfaceAggregator::FindChildSurfaces( - SurfaceId surface_id, +gfx::Rect SurfaceAggregator::PrewalkRenderPass( + RenderPassId render_pass_id, + const Surface* surface, base::flat_map<RenderPassId, RenderPassMapEntry>* render_pass_map, - RenderPassMapEntry* current_pass_entry, + bool will_draw, const gfx::Transform& transform_to_root_target, - base::flat_map<SurfaceRange, ChildSurfaceInfo>* child_surfaces, std::vector<gfx::Rect>* pixel_moving_backdrop_filters_rects, - bool* has_backdrop_cache_flags_to_update) { - if (current_pass_entry->is_visited) { - // This means that this render pass is an ancestor of itself. This is not - // supported. Stop processing the render pass again. - return; + PrewalkResult* result) { + auto it = render_pass_map->find(render_pass_id); + DCHECK(it != render_pass_map->end()); + RenderPassMapEntry& render_pass_entry = it->second; + if (render_pass_entry.is_visited) { + // This render pass is an ancestor of itself (not supported) or has been + // processed. + return render_pass_entry.damage_rect; } - base::AutoReset<bool> reset_is_visited(¤t_pass_entry->is_visited, true); - RenderPass* render_pass = current_pass_entry->render_pass; - if (current_pass_entry->has_pixel_moving_backdrop_filter) { + render_pass_entry.is_visited = true; + const RenderPass& render_pass = *render_pass_entry.render_pass; + + if (render_pass.backdrop_filters.HasFilterThatMovesPixels()) { has_pixel_moving_backdrop_filter_ = true; // If the render pass has a backdrop filter that moves pixels, its entire // bounds, with proper transform applied, may be added to the damage // rect if it intersects. pixel_moving_backdrop_filters_rects->push_back( cc::MathUtil::MapEnclosingClippedRect(transform_to_root_target, - render_pass->output_rect)); + render_pass.output_rect)); } - RenderPassId remapped_pass_id = RemapPassId(render_pass->id, surface_id); - bool has_pixel_moving_filter = - render_pass->filters.HasFilterThatMovesPixels(); + + RenderPassId remapped_pass_id = + RemapPassId(render_pass.id, surface->surface_id()); + // |moved_pixel_passes_| stores all the render passes affected by filters + // that move pixels, so |has_pixel_moving_filter| should be set to true either + // if the current render pass has pixel_moving_filter(s) or if it is inside an + // ancestor render pass that has pixel_moving_filter(s). + bool has_pixel_moving_filter = render_pass.filters.HasFilterThatMovesPixels(); if (has_pixel_moving_filter) moved_pixel_passes_.insert(remapped_pass_id); bool in_moved_pixel_pass = has_pixel_moving_filter || base::Contains(moved_pixel_passes_, remapped_pass_id); - for (auto* quad : render_pass->quad_list) { - if (quad->material == DrawQuad::Material::kSurfaceContent) { - // A child surface has been found. Add necessary info from this surface to - // the set of child surfaces that can be used to update damage rect for - // the parent surface. If this child surface has been visited previously, - // we only need to update |has_moved_pixels| and add the transform - // corresponding to this visit; rest of the info would remain the same. - const auto* surface_quad = SurfaceDrawQuad::MaterialCast(quad); - auto it = child_surfaces->find(surface_quad->surface_range); - if (it == child_surfaces->end()) { - auto insert_pair = child_surfaces->emplace( - std::piecewise_construct, - std::forward_as_tuple(surface_quad->surface_range), - std::forward_as_tuple( - remapped_pass_id, surface_quad->rect, - surface_quad->stretch_content_to_fill_bounds)); - DCHECK(insert_pair.second); - it = insert_pair.first; - } - auto& child_surface_info = it->second; - if (in_moved_pixel_pass) - child_surface_info.has_moved_pixels = true; - child_surface_info.quad_state_infos.push_back( - {transform_to_root_target, - surface_quad->shared_quad_state->quad_to_target_transform, - surface_quad->shared_quad_state->clip_rect, - surface_quad->shared_quad_state->is_clipped}); - current_pass_entry->contains_surfaces = true; - } else if (quad->material == DrawQuad::Material::kRenderPass) { - // A child render pass has been found. Find its child surfaces - // recursively. - const auto* render_pass_quad = RenderPassDrawQuad::MaterialCast(quad); - *has_backdrop_cache_flags_to_update |= - render_pass_quad->can_use_backdrop_filter_cache; - RenderPassId child_pass_id = render_pass_quad->render_pass_id; - RenderPassId remapped_child_pass_id = - RemapPassId(child_pass_id, surface_id); - if (in_moved_pixel_pass) - moved_pixel_passes_.insert(remapped_child_pass_id); - auto child_pass_it = render_pass_map->find(child_pass_id); - DCHECK(child_pass_it != render_pass_map->end()); - RenderPassMapEntry& child_pass_entry = child_pass_it->second; - // TODO(crbug/1011042): Here, we used to set |in_moved_pixel_pass| to true - // if the child render pass has a pixel-moving backdrop filter. This - // behavior was added in r687426 to fix another problem, but caused a huge - // performance issue in some cases that enabled background blur, by - // expanding the damage rect unnecessarily to the entire screen - // (crbug/1008740). This is removed now, but a proper fix for the - // pixel-moving backdrop filter should be implemented. - render_pass_dependencies_[remapped_pass_id].insert( - remapped_child_pass_id); - FindChildSurfaces( - surface_id, render_pass_map, &child_pass_entry, - gfx::Transform( - transform_to_root_target, - render_pass_quad->shared_quad_state->quad_to_target_transform), - child_surfaces, pixel_moving_backdrop_filters_rects, - has_backdrop_cache_flags_to_update); - current_pass_entry->contains_surfaces |= - child_pass_entry.contains_surfaces; - } - } -} -gfx::Rect -SurfaceAggregator::UpdateRPDQCanUseBackdropFilterCacheWithSurfaceDamage( - RenderPassId id, - PrewalkResult* result, - base::flat_map<RenderPassId, RenderPassMapEntry>* render_pass_map) { - auto render_pass_it = render_pass_map->find(id); - DCHECK(render_pass_it != render_pass_map->end()); - RenderPassMapEntry& render_pass_entry = render_pass_it->second; - - // If there's no surface embedded in the render pass, return an empty rect. - if (!render_pass_entry.contains_surfaces) - return gfx::Rect(); - - if (render_pass_entry.is_visited) { - // This render pass is an ancestor of itself (not supported) or has been - // processed. - return render_pass_entry.damage_rect; - } - render_pass_entry.is_visited = true; + const CompositorFrame& frame = surface->GetActiveFrame(); + gfx::Rect full_damage = frame.render_pass_list.back()->output_rect; - const RenderPass& render_pass = *render_pass_entry.render_pass; gfx::Rect damage_rect; - // Iterate through the quad list back-to-front and accumulate damage from // all quads (only SurfaceDrawQuads and RenderPassDrawQuads can have damage // at this point). |damage_rect| has damage from all quads below the current @@ -1362,36 +1285,50 @@ SurfaceAggregator::UpdateRPDQCanUseBackdropFilterCacheWithSurfaceDamage( for (QuadList::ConstReverseIterator it = render_pass.quad_list.rbegin(); it != render_pass.quad_list.rend(); ++it) { const DrawQuad* quad = *it; + gfx::Rect quad_damage_rect; if (quad->material == DrawQuad::Material::kSurfaceContent) { const auto* surface_quad = SurfaceDrawQuad::MaterialCast(quad); - Surface* surface = + Surface* child_surface = manager_->GetLatestInFlightSurface(surface_quad->surface_range); - auto it = result->damage_on_surfaces.end(); - if (surface) - it = result->damage_on_surfaces.find(surface->surface_id()); - if (it != result->damage_on_surfaces.end()) { - gfx::Rect surface_damage_rect = it->second; + // If the primary surface is not available then we assume the damage is + // the full size of the SurfaceDrawQuad because we might need to introduce + // gutter. + if (!child_surface || + child_surface->surface_id() != surface_quad->surface_range.end()) { + quad_damage_rect = quad->rect; + } + + if (child_surface) { + gfx::Rect child_rect; + auto it = result->damage_on_surfaces.find(child_surface->surface_id()); + if (it != result->damage_on_surfaces.end()) { + // the surface damage has been accummulated previously + child_rect = it->second; + } else { + // first encounter of the surface + child_rect = PrewalkSurface(child_surface, in_moved_pixel_pass, + remapped_pass_id, will_draw, result); + } + if (surface_quad->stretch_content_to_fill_bounds) { - if (surface->size_in_pixels().GetCheckedArea().ValueOrDefault(0) > - 0) { + if (!child_surface->size_in_pixels().IsEmpty()) { float y_scale = static_cast<float>(surface_quad->rect.height()) / - surface->size_in_pixels().height(); + child_surface->size_in_pixels().height(); float x_scale = static_cast<float>(surface_quad->rect.width()) / - surface->size_in_pixels().width(); - surface_damage_rect = gfx::ScaleToEnclosingRect(surface_damage_rect, - x_scale, y_scale); + child_surface->size_in_pixels().width(); + child_rect = + gfx::ScaleToEnclosingRect(child_rect, x_scale, y_scale); } } - gfx::Rect rect_in_target_space = cc::MathUtil::MapEnclosingClippedRect( - quad->shared_quad_state->quad_to_target_transform, - surface_damage_rect); - damage_rect.Union(rect_in_target_space); - } else { - // The damage info was not found for the (probably invalid) surface, - // take the whole quad rect as damaged. - gfx::Rect rect_in_target_space = cc::MathUtil::MapEnclosingClippedRect( - quad->shared_quad_state->quad_to_target_transform, quad->rect); - damage_rect.Union(rect_in_target_space); + quad_damage_rect.Union(child_rect); + } + + if (quad_damage_rect.IsEmpty()) + continue; + + if (in_moved_pixel_pass) { + damage_rect = full_damage; + continue; } } else if (quad->material == DrawQuad::Material::kRenderPass) { auto* render_pass_quad = RenderPassDrawQuad::MaterialCast(quad); @@ -1406,28 +1343,42 @@ SurfaceAggregator::UpdateRPDQCanUseBackdropFilterCacheWithSurfaceDamage( render_pass_quad->can_use_backdrop_filter_cache = false; } - gfx::Rect render_pass_damage_rect = - UpdateRPDQCanUseBackdropFilterCacheWithSurfaceDamage( - render_pass_quad->render_pass_id, result, render_pass_map); + RenderPassId child_pass_id = render_pass_quad->render_pass_id; + RenderPassId remapped_child_pass_id = + RemapPassId(child_pass_id, surface->surface_id()); + if (in_moved_pixel_pass) + moved_pixel_passes_.insert(remapped_child_pass_id); - gfx::Rect rect = cc::MathUtil::MapEnclosingClippedRect( - render_pass_quad->shared_quad_state->quad_to_target_transform, - render_pass_damage_rect); - damage_rect.Union(rect); + render_pass_dependencies_[remapped_pass_id].insert( + remapped_child_pass_id); + quad_damage_rect = PrewalkRenderPass( + child_pass_id, surface, render_pass_map, will_draw, + gfx::Transform( + transform_to_root_target, + render_pass_quad->shared_quad_state->quad_to_target_transform), + pixel_moving_backdrop_filters_rects, result); + + } else { + continue; } + // Convert the quad damage rect into its target space and clip it + // if needed. + gfx::Rect rect_in_target_space = cc::MathUtil::MapEnclosingClippedRect( + quad->shared_quad_state->quad_to_target_transform, quad_damage_rect); + if (quad->shared_quad_state->is_clipped) { + rect_in_target_space.Intersect(quad->shared_quad_state->clip_rect); + } + damage_rect.Union(rect_in_target_space); } render_pass_entry.damage_rect = damage_rect; return damage_rect; } -// Walk the Surface tree from surface_id. Validate the resources of the current -// surface and its descendants, check if there are any copy requests, and -// return the combined damage rect. -gfx::Rect SurfaceAggregator::PrewalkTree(Surface* surface, - bool in_moved_pixel_surface, - int parent_pass_id, - bool will_draw, - PrewalkResult* result) { +gfx::Rect SurfaceAggregator::PrewalkSurface(Surface* surface, + bool in_moved_pixel_surface, + int parent_pass_id, + bool will_draw, + PrewalkResult* result) { if (referenced_surfaces_.count(surface->surface_id())) return gfx::Rect(); @@ -1470,17 +1421,6 @@ gfx::Rect SurfaceAggregator::PrewalkTree(Surface* surface, base::flat_map<RenderPassId, RenderPassMapEntry> render_pass_map = GenerateRenderPassMap(frame.render_pass_list, IsRootSurface(surface)); - auto root_pass_it = render_pass_map.find(frame.render_pass_list.back()->id); - DCHECK(root_pass_it != render_pass_map.end()); - RenderPassMapEntry& root_pass_entry = root_pass_it->second; - base::flat_map<SurfaceRange, ChildSurfaceInfo> child_surfaces; - std::vector<gfx::Rect> pixel_moving_backdrop_filters_rects; - bool has_backdrop_cache_flags_to_update = false; - FindChildSurfaces(surface->surface_id(), &render_pass_map, &root_pass_entry, - root_pass_transform, &child_surfaces, - &pixel_moving_backdrop_filters_rects, - &has_backdrop_cache_flags_to_update); - std::vector<ResourceId> referenced_resources; referenced_resources.reserve(frame.resource_list.size()); @@ -1518,82 +1458,12 @@ gfx::Rect SurfaceAggregator::PrewalkTree(Surface* surface, // Avoid infinite recursion by adding current surface to // |referenced_surfaces_|. referenced_surfaces_.insert(surface->surface_id()); - for (const auto& child_surface_info_pair : child_surfaces) { - auto& child_surface_range = child_surface_info_pair.first; - auto& child_surface_info = child_surface_info_pair.second; - // TODO(fsamuel): Consider caching this value somewhere so that - // HandleSurfaceQuad doesn't need to call it again. - Surface* child_surface = - manager_->GetLatestInFlightSurface(child_surface_range); - - // If the primary surface is not available then we assume the damage is - // the full size of the SurfaceDrawQuad because we might need to introduce - // gutter. - gfx::Rect child_surface_damage; - if (!child_surface || - child_surface->surface_id() != child_surface_range.end()) { - child_surface_damage = child_surface_info.quad_rect; - } - - if (child_surface) { - if (child_surface_info.stretch_content_to_fill_bounds) { - // Scale up the damage_quad generated by the child_surface to fit - // the containing quad_rect. - gfx::Rect child_rect = - PrewalkTree(child_surface, child_surface_info.has_moved_pixels, - child_surface_info.parent_pass_id, will_draw, result); - if (child_surface->size_in_pixels().GetCheckedArea().ValueOrDefault(0) > - 0) { - float y_scale = - static_cast<float>(child_surface_info.quad_rect.height()) / - child_surface->size_in_pixels().height(); - float x_scale = - static_cast<float>(child_surface_info.quad_rect.width()) / - child_surface->size_in_pixels().width(); - child_surface_damage.Union( - gfx::ScaleToEnclosingRect(child_rect, x_scale, y_scale)); - } - } else { - child_surface_damage.Union( - PrewalkTree(child_surface, child_surface_info.has_moved_pixels, - child_surface_info.parent_pass_id, will_draw, result)); - } - } - - if (child_surface_damage.IsEmpty()) - continue; - - if (child_surface_info.has_moved_pixels) { - // Areas outside the rect hit by target_to_surface_transform may be - // modified if there is a filter that moves pixels. - damage_rect = full_damage; - continue; - } - - // Add the child surface damage rect to the parent surface damage rect. The - // child surface damage rect is first transformed to the parent surface - // coordinate space. There would be multiple transforms for a child surface - // if it is embedded multiple times which means its damage rect should be - // added multiple times. - for (const auto& quad_state_info : child_surface_info.quad_state_infos) { - gfx::Transform target_to_surface_transform( - quad_state_info.transform_to_root_target, - quad_state_info.quad_to_target_transform); - - gfx::Rect child_surface_damage_in_root_target_space = - cc::MathUtil::MapEnclosingClippedRect(target_to_surface_transform, - child_surface_damage); - if (quad_state_info.is_clipped) { - gfx::Rect clip_rect_in_root_target_space = - cc::MathUtil::MapEnclosingClippedRect( - quad_state_info.transform_to_root_target, - quad_state_info.clip_rect); - child_surface_damage_in_root_target_space.Intersect( - clip_rect_in_root_target_space); - } - damage_rect.Union(child_surface_damage_in_root_target_space); - } - } + std::vector<gfx::Rect> pixel_moving_backdrop_filters_rects; + const gfx::Rect surface_root_damage = PrewalkRenderPass( + frame.render_pass_list.back()->id, surface, &render_pass_map, will_draw, + root_pass_transform, &pixel_moving_backdrop_filters_rects, result); + damage_rect.Union(cc::MathUtil::MapEnclosedRectWith2dAxisAlignedTransform( + root_pass_transform, surface_root_damage)); if (!damage_rect.IsEmpty()) { // The following call can cause one or more copy requests to be added to the @@ -1635,7 +1505,7 @@ gfx::Rect SurfaceAggregator::PrewalkTree(Surface* surface, result->undrawn_surfaces.insert(surface_id); Surface* undrawn_surface = manager_->GetSurfaceForId(surface_id); if (undrawn_surface) - PrewalkTree(undrawn_surface, false, 0, false /* will_draw */, result); + PrewalkSurface(undrawn_surface, false, 0, /*will_draw=*/false, result); } } @@ -1673,11 +1543,6 @@ gfx::Rect SurfaceAggregator::PrewalkTree(Surface* surface, std::forward_as_tuple(damage_rect)); DCHECK(emplace_result.second); - if (has_backdrop_cache_flags_to_update) { - UpdateRPDQCanUseBackdropFilterCacheWithSurfaceDamage( - frame.render_pass_list.back()->id, result, &render_pass_map); - } - return damage_rect; } @@ -1763,6 +1628,7 @@ CompositorFrame SurfaceAggregator::Aggregate( const SurfaceId& surface_id, base::TimeTicks expected_display_time, gfx::OverlayTransform display_transform, + const gfx::Rect& target_damage, int64_t display_trace_id) { DCHECK(!expected_display_time.is_null()); @@ -1810,8 +1676,14 @@ CompositorFrame SurfaceAggregator::Aggregate( new_surfaces_.clear(); DCHECK(referenced_surfaces_.empty()); PrewalkResult prewalk_result; - root_damage_rect_ = - PrewalkTree(surface, false, 0, true /* will_draw */, &prewalk_result); + gfx::Rect surfaces_damage_rect = + PrewalkSurface(surface, false, 0, /*will_draw=*/true, &prewalk_result); + + root_damage_rect_ = surfaces_damage_rect; + // |root_damage_rect_| is used to restrict aggregating quads only if they + // intersect this area. + root_damage_rect_.Union(target_damage); + root_content_color_usage_ = prewalk_result.content_color_usage; if (prewalk_result.frame_sinks_changed) @@ -1827,6 +1699,15 @@ CompositorFrame SurfaceAggregator::Aggregate( CopyPasses(root_surface_frame, surface); referenced_surfaces_.erase(surface_id); + // The root render pass damage might have been expanded by target_damage (the + // area that might need to be recomposited on the target surface). We restrict + // the damage_rect of the root render pass to the one caused by the source + // surfaces. + // The damage on the root render pass should not include the expanded area + // since Renderer and OverlayProcessor expect the non expanded damage. + if (!RenderPassNeedsFullDamage(dest_pass_list_->back().get())) + dest_pass_list_->back()->damage_rect.Intersect(surfaces_damage_rect); + // Now that we've handled our main surface aggregation, apply de-jelly effect // if enabled. if (de_jelly_enabled_) @@ -1875,6 +1756,8 @@ CompositorFrame SurfaceAggregator::Aggregate( } } + frame.metadata.delegated_ink_metadata = std::move(delegated_ink_metadata_); + if (frame_annotator_) frame_annotator_->AnnotateAggregatedFrame(&frame); @@ -1941,6 +1824,43 @@ bool SurfaceAggregator::IsRootSurface(const Surface* surface) const { return surface->surface_id() == root_surface_id_; } +// Transform the point and presentation area of the metadata to be in the root +// target space. They need to be in the root target space because they will +// eventually be drawn directly onto the buffer just before being swapped onto +// the screen, so root target space is required so that they are positioned +// correctly. After transforming, they are stored in the +// |delegated_ink_metadata_| member in order to be placed on the final +// aggregated frame, after which the member is then cleared. +void SurfaceAggregator::TransformAndStoreDelegatedInkMetadata( + const gfx::Transform& parent_quad_to_root_target_transform, + DelegatedInkMetadata* metadata) { + if (delegated_ink_metadata_) { + // This member could already be populated in two scenarios: + // 1. The delegated ink metadata was committed to a frame's metadata that + // wasn't ultimately used to produce a frame, but is now being used. + // 2. There are two or more ink strokes requesting a delegated ink trail + // simultaneously. + // In both cases, we want to default to using a "last write wins" strategy + // to determine the metadata to put on the final aggregated frame. This + // avoids potential issues of using stale ink metadata in the first scenario + // by always using the newest one. For the second scenario, it would be a + // very niche use case to have more than one at a time, so the explainer + // specifies using last write wins to decide. + base::TimeTicks stored_time = delegated_ink_metadata_->timestamp(); + base::TimeTicks new_time = metadata->timestamp(); + if (new_time < stored_time) + return; + } + + gfx::PointF point(metadata->point()); + gfx::RectF area(metadata->presentation_area()); + parent_quad_to_root_target_transform.TransformPoint(&point); + parent_quad_to_root_target_transform.TransformRect(&area); + delegated_ink_metadata_ = std::make_unique<DelegatedInkMetadata>( + point, metadata->diameter(), metadata->color(), metadata->timestamp(), + area); +} + void SurfaceAggregator::HandleDeJelly(Surface* surface) { TRACE_EVENT0("viz", "SurfaceAggregator::HandleDeJelly"); diff --git a/chromium/components/viz/service/display/surface_aggregator.h b/chromium/components/viz/service/display/surface_aggregator.h index 6da752f8739..9f3c9a41a79 100644 --- a/chromium/components/viz/service/display/surface_aggregator.h +++ b/chromium/components/viz/service/display/surface_aggregator.h @@ -8,11 +8,14 @@ #include <memory> #include <string> #include <unordered_map> +#include <utility> +#include <vector> #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "components/viz/common/delegated_ink_metadata.h" #include "components/viz/common/quads/draw_quad.h" #include "components/viz/common/quads/render_pass.h" #include "components/viz/common/resources/transferable_resource.h" @@ -50,9 +53,14 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator { bool needs_surface_occluding_damage_rect); ~SurfaceAggregator(); + // |target_damage| represents an area on the output surface that might have + // been invalidated. It can be used in cases where we still want to support + // partial damage but the target surface might need contents outside the + // damage rect of the root surface. CompositorFrame Aggregate(const SurfaceId& surface_id, base::TimeTicks expected_display_time, gfx::OverlayTransform display_transform, + const gfx::Rect& target_damage = gfx::Rect(), int64_t display_trace_id = -1); void ReleaseResources(const SurfaceId& surface_id); const SurfaceIndexMap& previous_contained_surfaces() const { @@ -181,61 +189,42 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator { const gfx::Rect& occluding_damage_rect, bool occluding_damage_rect_valid); - // Helper function that uses backtracking on the render pass tree of a surface - // to find all surfaces embedded in it. If a surface is embedded multiple - // times (due to use of a MirrorLayer), it will be reachable via multiple - // paths from the root render pass. For each such a path the appropriate - // transform is calculated. - // - |surface_id| specifies the surface to find all child surfaces of. - // - |render_pass_map| is a pre-computed map from render pass id to some info - // about the render pass, including the render pass itself and whether it - // has pixel moving backdrop filter. - // - |current_pass_entry| is the info about the current render pass to - // process. + // Recursively walks through the render pass and updates the + // |can_use_backdrop_filter_cache| flag on all RenderPassDrawQuads(RPDQ). + // The function returns the damage rect of the render pass in its own content + // space. + // - |render_pass_id| specifies the id of the render pass. + // - |surface| is the surface containing the render pass. + // - |render_pass_map| is a map that contains all render passes and their + // entry data. + // - |will_draw| indicates that the surface can be aggregated into the final + // frame and might be drawn (based on damage/occlusion/etc.) if it is set + // to true. Or the surface isn't in the aggregated frame and is only + // needed for CopyOutputRequests if set to false. // - |transform_to_root_target| is the accumulated transform of all render - // passes along the way to the current render pass. - // - |child_surfaces| is the main output of the function containing all child - // surfaces found in the process. - // - |pixel_moving_backdrop_filters_rect| is another output that is union of - // bounds of render passes that have a pixel moving backdrop filter. - // - |has_backdrop_cache_flags_to_update| indicates if any - // RenderPassDrawQuad(s) contained in the surface have - // |can_use_backdrop_filter_cache| flag set to true and having to be - // updated. This is used to avoid iterating through all the render passes - // in the surface frame when not needed (i.e. no flag needs to be - // updated). - // TODO(mohsen): Consider refactoring this backtracking algorithm into a - // self-contained class. - void FindChildSurfaces( - SurfaceId surface_id, + // passes in the containing surface along the way to the current render + // pass. + // - |pixel_moving_backdrop_filters_rects| is a vector of bounds of render + // passes that have a pixel moving backdrop filter. + // - |result| is the result of a prewalk of the surface that contains the + // render pass. + gfx::Rect PrewalkRenderPass( + RenderPassId render_pass_id, + const Surface* surface, base::flat_map<RenderPassId, RenderPassMapEntry>* render_pass_map, - RenderPassMapEntry* current_pass_entry, + bool will_draw, const gfx::Transform& transform_to_root_target, - base::flat_map<SurfaceRange, ChildSurfaceInfo>* child_surfaces, std::vector<gfx::Rect>* pixel_moving_backdrop_filters_rects, - bool* has_backdrop_cache_flags_to_update); - - // Recursively updates the |can_use_backdrop_filter_cache| flag on all - // RenderPassDrawQuads(RPDQ) in the specified render pass. The function - // recursively traverses any render pass referenced by a RPDQ but doesn't - // traverse any render passes in the frame of any embedded surfaces. The - // function returns the damage rect of the render pass in its own content - // space. - // - |id| specifies the render pass whose quads are to be updated - // - |result| is the result of a prewalk of a root surface that contains the - // render pass - // - |render_pass_map| is a map that contains all render passes and their - // entry data - gfx::Rect UpdateRPDQCanUseBackdropFilterCacheWithSurfaceDamage( - RenderPassId id, - PrewalkResult* result, - base::flat_map<RenderPassId, RenderPassMapEntry>* render_pass_map); - - gfx::Rect PrewalkTree(Surface* surface, - bool in_moved_pixel_surface, - int parent_pass, - bool will_draw, - PrewalkResult* result); + PrewalkResult* result); + + // Walk the Surface tree from |surface|. Validate the resources of the + // current surface and its descendants, check if there are any copy requests, + // and return the combined damage rect. + gfx::Rect PrewalkSurface(Surface* surface, + bool in_moved_pixel_surface, + int parent_pass, + bool will_draw, + PrewalkResult* result); void CopyUndrawnSurfaces(PrewalkResult* prewalk); void CopyPasses(const CompositorFrame& frame, Surface* surface); void AddColorConversionPass(); @@ -276,6 +265,15 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator { static void UnrefResources(base::WeakPtr<SurfaceClient> surface_client, const std::vector<ReturnedResource>& resources); + // This method transforms the delegated ink metadata to be in the root target + // space, so that it can eventually be drawn onto the back buffer in the + // correct position. It should only ever be called when a frame contains + // delegated ink metadata, in which case this function will transform it and + // then store it in the |delegated_ink_metadata_| member. + void TransformAndStoreDelegatedInkMetadata( + const gfx::Transform& parent_quad_to_root_target_transform, + DelegatedInkMetadata* metadata); + // De-Jelly Effect: // HandleDeJelly applies a de-jelly transform to quads in the root render // pass. @@ -436,6 +434,13 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator { // production on Windows only (does not interact with jelly). bool last_frame_had_color_conversion_pass_ = false; + // The metadata used for drawing a delegated ink trail on the end of a normal + // ink stroke. It needs to be transformed to root coordinates and then put on + // the final aggregated frame. This is only populated during aggregation when + // a surface contains delegated ink metadata on its frame, and it is cleared + // after it is placed on the final aggregated frame during aggregation. + std::unique_ptr<DelegatedInkMetadata> delegated_ink_metadata_; + base::WeakPtrFactory<SurfaceAggregator> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(SurfaceAggregator); diff --git a/chromium/components/viz/service/display/surface_aggregator_unittest.cc b/chromium/components/viz/service/display/surface_aggregator_unittest.cc index 0a3f1ae681d..085c6cd27cb 100644 --- a/chromium/components/viz/service/display/surface_aggregator_unittest.cc +++ b/chromium/components/viz/service/display/surface_aggregator_unittest.cc @@ -7,6 +7,8 @@ #include <stddef.h> #include <stdint.h> +#include <algorithm> +#include <map> #include <set> #include <utility> #include <vector> @@ -132,10 +134,11 @@ class SurfaceAggregatorTest : public testing::Test, public DisplayTimeSource { testing::Test::TearDown(); } - CompositorFrame AggregateFrame(const SurfaceId& surface_id) { + CompositorFrame AggregateFrame(const SurfaceId& surface_id, + gfx::Rect target_damage = gfx::Rect()) { return aggregator_.Aggregate( surface_id, GetNextDisplayTimeAndIncrement(), - gfx::OVERLAY_TRANSFORM_NONE /* display_transform */); + /*display_transform=*/gfx::OVERLAY_TRANSFORM_NONE, target_damage); } struct Quad { @@ -4399,6 +4402,133 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) { } } +TEST_F(SurfaceAggregatorPartialSwapTest, ExpandByTargetDamage) { + ParentLocalSurfaceIdAllocator allocator; + allocator.GenerateId(); + LocalSurfaceId child_local_surface_id = + allocator.GetCurrentLocalSurfaceIdAllocation().local_surface_id(); + SurfaceId child_surface_id(child_sink_->frame_sink_id(), + child_local_surface_id); + constexpr float device_scale_factor = 1.0f; + + // The child surface has one quad. + { + int child_pass_id = 1; + std::vector<Quad> child_quads1 = { + Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))}; + std::vector<Pass> child_passes = { + Pass(child_quads1, child_pass_id, gfx::Rect(5, 5))}; + + RenderPassList child_pass_list; + std::vector<SurfaceRange> referenced_surfaces; + AddPasses(&child_pass_list, child_passes, &referenced_surfaces); + + SubmitPassListAsFrame(child_sink_.get(), child_local_surface_id, + &child_pass_list, std::move(referenced_surfaces), + device_scale_factor); + } + + { + std::vector<Quad> root_quads = {Quad::SurfaceQuad( + SurfaceRange(base::nullopt, child_surface_id), SK_ColorWHITE, + gfx::Rect(SurfaceSize()), /*stretch_content_to_fill_bounds=*/false)}; + + std::vector<Pass> root_passes = {Pass(root_quads, SurfaceSize())}; + + RenderPassList root_pass_list; + std::vector<SurfaceRange> referenced_surfaces; + AddPasses(&root_pass_list, root_passes, &referenced_surfaces); + // No damage, this is the first frame submitted, so all quads should be + // produced. + SubmitPassListAsFrame(root_sink_.get(), root_local_surface_id_, + &root_pass_list, std::move(referenced_surfaces), + device_scale_factor); + } + + SurfaceId root_surface_id(root_sink_->frame_sink_id(), + root_local_surface_id_); + CompositorFrame aggregated_frame = AggregateFrame(root_surface_id); + + const auto& aggregated_pass_list = aggregated_frame.render_pass_list; + + ASSERT_EQ(1u, aggregated_pass_list.size()); + + // Damage rect for first aggregation should contain entire root surface. + EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list.back()->damage_rect); + EXPECT_EQ(1u, aggregated_pass_list[0]->quad_list.size()); + + // Create a root surface with a smaller damage rect. + // This time the damage should be smaller. + { + std::vector<Quad> root_quads = {Quad::SurfaceQuad( + SurfaceRange(base::nullopt, child_surface_id), SK_ColorWHITE, + gfx::Rect(SurfaceSize()), /*stretch_content_to_fill_bounds=*/false)}; + + std::vector<Pass> root_passes = {Pass(root_quads, SurfaceSize())}; + + RenderPassList root_pass_list; + std::vector<SurfaceRange> referenced_surfaces; + AddPasses(&root_pass_list, root_passes, &referenced_surfaces); + + auto* root_pass = root_pass_list[0].get(); + root_pass->damage_rect = gfx::Rect(10, 10, 2, 2); + SubmitPassListAsFrame(root_sink_.get(), root_local_surface_id_, + &root_pass_list, std::move(referenced_surfaces), + device_scale_factor); + } + + { + CompositorFrame aggregated_frame = AggregateFrame(root_surface_id); + + const auto& aggregated_pass_list = aggregated_frame.render_pass_list; + + ASSERT_EQ(1u, aggregated_pass_list.size()); + + // No quads inside the damage + EXPECT_EQ(gfx::Rect(10, 10, 2, 2), + aggregated_pass_list.back()->damage_rect); + EXPECT_EQ(0u, aggregated_pass_list.back()->quad_list.size()); + } + + // This pass has damage that does not intersect the quad in the child + // surface. + { + std::vector<Quad> root_quads = { + Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id), + SK_ColorWHITE, gfx::Rect(SurfaceSize()), false)}; + + std::vector<Pass> root_passes = {Pass(root_quads, SurfaceSize())}; + + RenderPassList root_pass_list; + std::vector<SurfaceRange> referenced_surfaces; + AddPasses(&root_pass_list, root_passes, &referenced_surfaces); + + auto* root_pass = root_pass_list[0].get(); + root_pass->damage_rect = gfx::Rect(10, 10, 2, 2); + SubmitPassListAsFrame(root_sink_.get(), root_local_surface_id_, + &root_pass_list, std::move(referenced_surfaces), + device_scale_factor); + } + + // The target surface invalidates one pixel in the top left, the quad in the + // child surface should be added even if it's not causing damage nor in the + // root render pass damage. + { + gfx::Rect target_damage(0, 0, 1, 1); + CompositorFrame aggregated_frame = + AggregateFrame(root_surface_id, target_damage); + + const auto& aggregated_pass_list = aggregated_frame.render_pass_list; + ASSERT_EQ(1u, aggregated_pass_list.size()); + + // The damage rect of the root render pass should not be changed. + EXPECT_EQ(gfx::Rect(10, 10, 2, 2), + aggregated_pass_list.back()->damage_rect); + // We expect one quad + ASSERT_EQ(1u, aggregated_pass_list[0]->quad_list.size()); + } +} + class SurfaceAggregatorWithResourcesTest : public testing::Test, public DisplayTimeSource { public: @@ -5667,6 +5797,41 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, OverlayOccludingDamageRect) { EXPECT_EQ(gfx::Rect(60, 0, 30, 40), video_sqs->occluding_damage_rect.value()); } + // Frame #4 - Has occluding damage and clipping of the video quad is on + { + CompositorFrame child_surface_frame = MakeEmptyCompositorFrame(); + AddPasses(&child_surface_frame.render_pass_list, child_surface_passes, + &child_surface_frame.metadata.referenced_surfaces); + + auto* render_pass = child_surface_frame.render_pass_list[0].get(); + auto* surface_quad_sqs = render_pass->shared_quad_state_list.front(); + surface_quad_sqs->is_clipped = true; + surface_quad_sqs->clip_rect = gfx::Rect(20, 0, 60, 80); + + child_sink_->SubmitCompositorFrame(child_local_surface_id, + std::move(child_surface_frame)); + + CompositorFrame root_frame = MakeEmptyCompositorFrame(); + AddPasses(&root_frame.render_pass_list, root_passes, + &root_frame.metadata.referenced_surfaces); + + root_sink_->SubmitCompositorFrame(root_local_surface_id_, + std::move(root_frame)); + + CompositorFrame aggregated_frame = AggregateFrame(root_surface_id); + + auto* output_root_pass = aggregated_frame.render_pass_list.back().get(); + // The video quad (10, 0, 80, 80) unions the solid quad on top (60, 0, 40, + // 40) + EXPECT_EQ(gfx::Rect(10, 0, 90, 80), output_root_pass->damage_rect); + + const SharedQuadState* video_sqs = + output_root_pass->quad_list.back()->shared_quad_state; + // The solid quad on top (60, 0, 40, 40) intersects the clipped video quad + // (26, 0, 48, 64) + EXPECT_EQ(gfx::Rect(60, 0, 14, 40), + video_sqs->occluding_damage_rect.value()); + } } // Tests that quads outside the damage rect are not ignored for cached render @@ -5833,7 +5998,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, DisplayTransformDamageCallback) { std::vector<Quad> root_quads = {Quad::SurfaceQuad( SurfaceRange(primary_child_surface_id), SK_ColorWHITE, surface_quad_rect, /*stretch_content_to_fill_bounds=*/true)}; - std::vector<Pass> root_passes = {Pass(root_quads, SurfaceSize())}; + + constexpr gfx::Size surface_size(60, 100); + std::vector<Pass> root_passes = {Pass(root_quads, surface_size)}; MockAggregatedDamageCallback aggregated_damage_callback; root_sink_->SetAggregatedDamageCallbackForTesting( @@ -5844,15 +6011,14 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, DisplayTransformDamageCallback) { SubmitCompositorFrame(root_sink_.get(), root_passes, root_local_surface_id_, 0.5f); - EXPECT_CALL( - aggregated_damage_callback, - OnAggregatedDamage(root_local_surface_id_, SurfaceSize(), - gfx::Rect(SurfaceSize()), next_display_time())); + EXPECT_CALL(aggregated_damage_callback, + OnAggregatedDamage(root_local_surface_id_, surface_size, + gfx::Rect(surface_size), next_display_time())); - gfx::Rect transformed_rect(SurfaceSize().height(), SurfaceSize().width()); CompositorFrame frame = aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement(), gfx::OVERLAY_TRANSFORM_ROTATE_90); + gfx::Rect transformed_rect(surface_size.height(), surface_size.width()); EXPECT_EQ(frame.render_pass_list.back()->output_rect, transformed_rect); EXPECT_EQ(frame.render_pass_list.back()->damage_rect, transformed_rect); } @@ -6434,6 +6600,63 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AllowMerge) { } } +// Check that if a non-merged surface is invisible, its entire render pass is +// skipped. +TEST_F(SurfaceAggregatorValidSurfaceTest, SkipInvisibleSurface) { + // Child surface. + gfx::Rect child_rect(5, 5); + ParentLocalSurfaceIdAllocator child_allocator; + child_allocator.GenerateId(); + + LocalSurfaceId child_local_surface_id = + child_allocator.GetCurrentLocalSurfaceIdAllocation().local_surface_id(); + SurfaceId child_surface_id(child_sink_->frame_sink_id(), + child_local_surface_id); + { + std::vector<Quad> child_quads = { + Quad::SolidColorQuad(SK_ColorGREEN, child_rect)}; + // Offset child output rect so it's outside the root visible rect. + gfx::Rect output_rect(SurfaceSize()); + output_rect.Offset(output_rect.width(), output_rect.height()); + std::vector<Pass> child_passes = {Pass(child_quads, 1, output_rect)}; + + CompositorFrame child_frame = MakeEmptyCompositorFrame(); + AddPasses(&child_frame.render_pass_list, child_passes, + &child_frame.metadata.referenced_surfaces); + + child_sink_->SubmitCompositorFrame(child_local_surface_id, + std::move(child_frame)); + } + + gfx::Rect root_rect(SurfaceSize()); + + auto pass = RenderPass::Create(); + pass->SetNew(1, root_rect, root_rect, gfx::Transform()); + auto* sqs = pass->CreateAndAppendSharedQuadState(); + sqs->opacity = 1.f; + + // Disallow merge. + auto* surface_quad = pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>(); + surface_quad->SetAll(sqs, child_rect, child_rect, + /*needs_blending=*/false, + SurfaceRange(base::nullopt, child_surface_id), + SK_ColorWHITE, + /*stretch_content_to_fill_bounds=*/false, + /*is_reflection=*/false, + /*allow_merge=*/false); + + CompositorFrame frame = + CompositorFrameBuilder().AddRenderPass(std::move(pass)).Build(); + root_sink_->SubmitCompositorFrame(root_local_surface_id_, std::move(frame)); + + SurfaceId root_surface_id(root_sink_->frame_sink_id(), + root_local_surface_id_); + + CompositorFrame aggregated_frame = AggregateFrame(root_surface_id); + // Merging not allowed, but child rect should be dropped. + EXPECT_EQ(1u, aggregated_frame.render_pass_list.size()); +} + // Verify that a SurfaceDrawQuad's root RenderPass has correct texture // parameters if being drawn via RPDQ. TEST_F(SurfaceAggregatorValidSurfaceTest, RenderPassDoesNotFillSurface) { @@ -6691,5 +6914,562 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, hit_test_region_index); } +void ExpectDelegatedInkMetadataIsEqual(const DelegatedInkMetadata& lhs, + const DelegatedInkMetadata& rhs) { + EXPECT_FLOAT_EQ(lhs.point().y(), rhs.point().y()); + EXPECT_FLOAT_EQ(lhs.point().x(), rhs.point().x()); + EXPECT_EQ(lhs.diameter(), rhs.diameter()); + EXPECT_EQ(lhs.color(), rhs.color()); + EXPECT_EQ(lhs.timestamp(), rhs.timestamp()); + EXPECT_FLOAT_EQ(lhs.presentation_area().y(), rhs.presentation_area().y()); + EXPECT_FLOAT_EQ(lhs.presentation_area().x(), rhs.presentation_area().x()); + EXPECT_FLOAT_EQ(lhs.presentation_area().width(), + rhs.presentation_area().width()); + EXPECT_FLOAT_EQ(lhs.presentation_area().height(), + rhs.presentation_area().height()); +} + +// Basic test to confirm that ink metadata on a child surface will be +// transformed by the parent. +TEST_F(SurfaceAggregatorValidSurfaceTest, DelegatedInkMetadataTest) { + std::vector<Quad> child_quads = { + Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))}; + std::vector<Pass> child_passes = {Pass(child_quads, 1, gfx::Size(100, 100))}; + + CompositorFrame child_frame = MakeEmptyCompositorFrame(); + DelegatedInkMetadata metadata(gfx::PointF(100, 100), 1.5, SK_ColorRED, + base::TimeTicks::Now(), + gfx::RectF(10, 10, 200, 200)); + child_frame.metadata.delegated_ink_metadata = + std::make_unique<DelegatedInkMetadata>(metadata); + AddPasses(&child_frame.render_pass_list, child_passes, + &child_frame.metadata.referenced_surfaces); + + ParentLocalSurfaceIdAllocator child_allocator; + child_allocator.GenerateId(); + LocalSurfaceId child_local_surface_id = + child_allocator.GetCurrentLocalSurfaceIdAllocation().local_surface_id(); + SurfaceId child_surface_id(child_sink_->frame_sink_id(), + child_local_surface_id); + child_sink_->SubmitCompositorFrame(child_local_surface_id, + std::move(child_frame)); + + std::vector<Quad> root_quads = {Quad::SurfaceQuad( + SurfaceRange(base::nullopt, child_surface_id), SK_ColorWHITE, + gfx::Rect(5, 5), /*stretch_content_to_fill_bounds=*/false)}; + + std::vector<Pass> root_passes = {Pass(root_quads, 1, gfx::Size(30, 30))}; + + CompositorFrame root_frame = MakeEmptyCompositorFrame(); + AddPasses(&root_frame.render_pass_list, root_passes, + &root_frame.metadata.referenced_surfaces); + + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.Scale(1.5, 1.5); + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.Translate(70, 240); + + // Update the expected metadata to reflect the transforms to point and area + // that are expected to occur. + gfx::PointF pt = metadata.point(); + gfx::RectF area = metadata.presentation_area(); + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.TransformPoint(&pt); + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.TransformRect(&area); + metadata = DelegatedInkMetadata(pt, metadata.diameter(), metadata.color(), + metadata.timestamp(), area); + + root_sink_->SubmitCompositorFrame(root_local_surface_id_, + std::move(root_frame)); + + SurfaceId root_surface_id(root_sink_->frame_sink_id(), + root_local_surface_id_); + CompositorFrame aggregated_frame = AggregateFrame(root_surface_id); + + std::unique_ptr<DelegatedInkMetadata> actual_metadata = + std::move(aggregated_frame.metadata.delegated_ink_metadata); + EXPECT_TRUE(actual_metadata); + ExpectDelegatedInkMetadataIsEqual(*actual_metadata.get(), metadata); +} + +// Confirm that transforms are aggregated as the tree is walked and correctly +// applied to the ink metadata. +TEST_F(SurfaceAggregatorValidSurfaceTest, + TransformDelegatedInkMetadataTallTree) { + auto greatgrand_child_support = std::make_unique<CompositorFrameSinkSupport>( + nullptr, &manager_, kArbitraryFrameSinkId2, kChildIsRoot); + std::vector<Quad> greatgrandchild_quads = { + Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))}; + std::vector<Pass> greatgrandchild_passes = { + Pass(greatgrandchild_quads, 1, gfx::Size(100, 100))}; + + DelegatedInkMetadata metadata(gfx::PointF(100, 100), 1.5, SK_ColorRED, + base::TimeTicks::Now(), + gfx::RectF(10, 10, 200, 200)); + CompositorFrame greatgrandchild_frame = MakeEmptyCompositorFrame(); + greatgrandchild_frame.metadata.delegated_ink_metadata = + std::make_unique<DelegatedInkMetadata>(metadata); + AddPasses(&greatgrandchild_frame.render_pass_list, greatgrandchild_passes, + &greatgrandchild_frame.metadata.referenced_surfaces); + + ParentLocalSurfaceIdAllocator greatgrandchild_allocator; + greatgrandchild_allocator.GenerateId(); + LocalSurfaceId greatgrandchild_local_surface_id = + greatgrandchild_allocator.GetCurrentLocalSurfaceIdAllocation() + .local_surface_id(); + SurfaceId great_grandchild_surface_id( + greatgrand_child_support->frame_sink_id(), + greatgrandchild_local_surface_id); + greatgrand_child_support->SubmitCompositorFrame( + greatgrandchild_local_surface_id, std::move(greatgrandchild_frame)); + + auto grand_child_support = std::make_unique<CompositorFrameSinkSupport>( + nullptr, &manager_, kArbitraryMiddleFrameSinkId, kChildIsRoot); + std::vector<Quad> grandchild_quads = {Quad::SurfaceQuad( + SurfaceRange(base::nullopt, great_grandchild_surface_id), SK_ColorWHITE, + gfx::Rect(7, 7), /*stretch_content_to_fill_bounds=*/false)}; + std::vector<Pass> grandchild_passes = { + Pass(grandchild_quads, 1, gfx::Size(100, 100))}; + + CompositorFrame grandchild_frame = MakeEmptyCompositorFrame(); + + AddPasses(&grandchild_frame.render_pass_list, grandchild_passes, + &grandchild_frame.metadata.referenced_surfaces); + + grandchild_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.Scale(1.5, 1.5); + grandchild_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.Translate(37, 82); + + // Update the expected metadata to reflect the transforms to point and area + // that are expected to occur. + gfx::PointF pt = metadata.point(); + gfx::RectF area = metadata.presentation_area(); + grandchild_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.TransformPoint(&pt); + grandchild_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.TransformRect(&area); + + ParentLocalSurfaceIdAllocator grandchild_allocator; + grandchild_allocator.GenerateId(); + LocalSurfaceId grandchild_local_surface_id = + grandchild_allocator.GetCurrentLocalSurfaceIdAllocation() + .local_surface_id(); + SurfaceId grandchild_surface_id(grand_child_support->frame_sink_id(), + grandchild_local_surface_id); + grand_child_support->SubmitCompositorFrame(grandchild_local_surface_id, + std::move(grandchild_frame)); + + std::vector<Quad> child_quads = {Quad::SurfaceQuad( + SurfaceRange(base::nullopt, grandchild_surface_id), SK_ColorWHITE, + gfx::Rect(7, 7), /*stretch_content_to_fill_bounds=*/false)}; + std::vector<Pass> child_passes = {Pass(child_quads, 1, gfx::Size(30, 30))}; + + CompositorFrame child_frame = MakeEmptyCompositorFrame(); + AddPasses(&child_frame.render_pass_list, child_passes, + &child_frame.metadata.referenced_surfaces); + + child_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.Translate(36, 15); + + child_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.TransformPoint(&pt); + child_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.TransformRect(&area); + + ParentLocalSurfaceIdAllocator child_allocator; + child_allocator.GenerateId(); + LocalSurfaceId child_local_surface_id = + child_allocator.GetCurrentLocalSurfaceIdAllocation().local_surface_id(); + SurfaceId child_surface_id(child_sink_->frame_sink_id(), + child_local_surface_id); + child_sink_->SubmitCompositorFrame(child_local_surface_id, + std::move(child_frame)); + + std::vector<Quad> root_quads = {Quad::SurfaceQuad( + SurfaceRange(base::nullopt, child_surface_id), SK_ColorWHITE, + gfx::Rect(5, 5), /*stretch_content_to_fill_bounds=*/false)}; + + std::vector<Pass> root_passes = {Pass(root_quads, 1, gfx::Size(30, 30))}; + + CompositorFrame root_frame = MakeEmptyCompositorFrame(); + AddPasses(&root_frame.render_pass_list, root_passes, + &root_frame.metadata.referenced_surfaces); + + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.Scale(0.7, 0.7); + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.Translate(70, 240); + + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.TransformPoint(&pt); + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.TransformRect(&area); + + root_sink_->SubmitCompositorFrame(root_local_surface_id_, + std::move(root_frame)); + + SurfaceId root_surface_id(root_sink_->frame_sink_id(), + root_local_surface_id_); + CompositorFrame aggregated_frame = AggregateFrame(root_surface_id); + + metadata = DelegatedInkMetadata(pt, metadata.diameter(), metadata.color(), + metadata.timestamp(), area); + + std::unique_ptr<DelegatedInkMetadata> actual_metadata = + std::move(aggregated_frame.metadata.delegated_ink_metadata); + EXPECT_TRUE(actual_metadata); + ExpectDelegatedInkMetadataIsEqual(*actual_metadata.get(), metadata); +} + +// Confirm the metadata is transformed correctly and makes it to the aggregated +// frame when there are multiple children. +TEST_F(SurfaceAggregatorValidSurfaceTest, + DelegatedInkMetadataMultipleChildren) { + auto child_2_support = std::make_unique<CompositorFrameSinkSupport>( + nullptr, &manager_, kArbitraryMiddleFrameSinkId, kChildIsRoot); + auto child_3_support = std::make_unique<CompositorFrameSinkSupport>( + nullptr, &manager_, kArbitraryFrameSinkId2, kChildIsRoot); + + std::vector<Quad> child_1_quads = { + Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))}; + std::vector<Pass> child_1_passes = { + Pass(child_1_quads, 1, gfx::Size(100, 100))}; + + CompositorFrame child_1_frame = MakeEmptyCompositorFrame(); + AddPasses(&child_1_frame.render_pass_list, child_1_passes, + &child_1_frame.metadata.referenced_surfaces); + + ParentLocalSurfaceIdAllocator child_1_allocator; + child_1_allocator.GenerateId(); + LocalSurfaceId child_1_local_surface_id = + child_1_allocator.GetCurrentLocalSurfaceIdAllocation().local_surface_id(); + SurfaceId child_1_surface_id(child_sink_->frame_sink_id(), + child_1_local_surface_id); + child_sink_->SubmitCompositorFrame(child_1_local_surface_id, + std::move(child_1_frame)); + + std::vector<Quad> child_2_quads = { + Quad::SolidColorQuad(SK_ColorMAGENTA, gfx::Rect(5, 5))}; + std::vector<Pass> child_2_passes = { + Pass(child_2_quads, 1, gfx::Size(100, 100))}; + + DelegatedInkMetadata metadata = DelegatedInkMetadata( + gfx::PointF(88, 34), 1.8, SK_ColorBLACK, base::TimeTicks::Now(), + gfx::RectF(50, 50, 300, 300)); + CompositorFrame child_2_frame = MakeEmptyCompositorFrame(); + child_2_frame.metadata.delegated_ink_metadata = + std::make_unique<DelegatedInkMetadata>(metadata); + AddPasses(&child_2_frame.render_pass_list, child_2_passes, + &child_2_frame.metadata.referenced_surfaces); + + ParentLocalSurfaceIdAllocator child_2_allocator; + child_2_allocator.GenerateId(); + LocalSurfaceId child_2_local_surface_id = + child_2_allocator.GetCurrentLocalSurfaceIdAllocation().local_surface_id(); + SurfaceId child_2_surface_id(child_2_support->frame_sink_id(), + child_2_local_surface_id); + child_2_support->SubmitCompositorFrame(child_2_local_surface_id, + std::move(child_2_frame)); + + std::vector<Quad> child_3_quads = { + Quad::SolidColorQuad(SK_ColorCYAN, gfx::Rect(5, 5))}; + std::vector<Pass> child_3_passes = { + Pass(child_3_quads, 1, gfx::Size(100, 100))}; + + CompositorFrame child_3_frame = MakeEmptyCompositorFrame(); + AddPasses(&child_3_frame.render_pass_list, child_3_passes, + &child_3_frame.metadata.referenced_surfaces); + + ParentLocalSurfaceIdAllocator child_3_allocator; + child_3_allocator.GenerateId(); + LocalSurfaceId child_3_local_surface_id = + child_3_allocator.GetCurrentLocalSurfaceIdAllocation().local_surface_id(); + SurfaceId child_3_surface_id(child_3_support->frame_sink_id(), + child_3_local_surface_id); + child_3_support->SubmitCompositorFrame(child_3_local_surface_id, + std::move(child_3_frame)); + + std::vector<Quad> root_quads = { + Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_1_surface_id), + SK_ColorWHITE, gfx::Rect(5, 5), + /*stretch_content_to_fill_bounds=*/false), + Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_2_surface_id), + SK_ColorWHITE, gfx::Rect(5, 5), + /*stretch_content_to_fill_bounds=*/false), + Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_3_surface_id), + SK_ColorWHITE, gfx::Rect(5, 5), + /*stretch_content_to_fill_bounds=*/false)}; + + std::vector<Pass> root_passes = {Pass(root_quads, 1, gfx::Size(30, 30))}; + + CompositorFrame root_frame = MakeEmptyCompositorFrame(); + AddPasses(&root_frame.render_pass_list, root_passes, + &root_frame.metadata.referenced_surfaces); + + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.Translate(9, 87); + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(1) + ->quad_to_target_transform.Scale(0.7, 0.7); + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(1) + ->quad_to_target_transform.Translate(70, 240); + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(2) + ->quad_to_target_transform.Scale(2.7, 0.2); + + // Update the expected metadata to reflect the transforms to point and area + // that are expected to occur. + gfx::PointF pt = metadata.point(); + gfx::RectF area = metadata.presentation_area(); + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(1) + ->quad_to_target_transform.TransformPoint(&pt); + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(1) + ->quad_to_target_transform.TransformRect(&area); + + root_sink_->SubmitCompositorFrame(root_local_surface_id_, + std::move(root_frame)); + + SurfaceId root_surface_id(root_sink_->frame_sink_id(), + root_local_surface_id_); + CompositorFrame aggregated_frame = AggregateFrame(root_surface_id); + + metadata = DelegatedInkMetadata(pt, metadata.diameter(), metadata.color(), + metadata.timestamp(), area); + + std::unique_ptr<DelegatedInkMetadata> actual_metadata = + std::move(aggregated_frame.metadata.delegated_ink_metadata); + EXPECT_TRUE(actual_metadata); + ExpectDelegatedInkMetadataIsEqual(*actual_metadata.get(), metadata); +} + +// Confirm the the metadata with the most recent timestamp is used when +// multiple children have delegated ink metadata. +TEST_F(SurfaceAggregatorValidSurfaceTest, + MultipleChildrenHaveDelegatedInkMetadata) { + auto child_2_support = std::make_unique<CompositorFrameSinkSupport>( + nullptr, &manager_, kArbitraryMiddleFrameSinkId, kChildIsRoot); + auto child_3_support = std::make_unique<CompositorFrameSinkSupport>( + nullptr, &manager_, kArbitraryFrameSinkId2, kChildIsRoot); + + std::vector<Quad> child_1_quads = { + Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))}; + std::vector<Pass> child_1_passes = { + Pass(child_1_quads, 1, gfx::Size(100, 100))}; + + CompositorFrame child_1_frame = MakeEmptyCompositorFrame(); + AddPasses(&child_1_frame.render_pass_list, child_1_passes, + &child_1_frame.metadata.referenced_surfaces); + + ParentLocalSurfaceIdAllocator child_1_allocator; + child_1_allocator.GenerateId(); + LocalSurfaceId child_1_local_surface_id = + child_1_allocator.GetCurrentLocalSurfaceIdAllocation().local_surface_id(); + SurfaceId child_1_surface_id(child_sink_->frame_sink_id(), + child_1_local_surface_id); + child_sink_->SubmitCompositorFrame(child_1_local_surface_id, + std::move(child_1_frame)); + + std::vector<Quad> child_2_quads = { + Quad::SolidColorQuad(SK_ColorMAGENTA, gfx::Rect(5, 5))}; + std::vector<Pass> child_2_passes = { + Pass(child_2_quads, 1, gfx::Size(100, 100))}; + + // Making both metadatas here so that the one with a later timestamp can be + // on child 2. This will cause the test to fail if we don't default to using + // the metadata with the later timestamp. Specifically setting the + // later_metadata timestamp to be 50 microseconds later than Now() to avoid + // issues with both metadatas sometimes having the same time in Release. + DelegatedInkMetadata early_metadata = DelegatedInkMetadata( + gfx::PointF(88, 34), 1.8, SK_ColorBLACK, base::TimeTicks::Now(), + gfx::RectF(50, 50, 300, 300)); + DelegatedInkMetadata later_metadata = DelegatedInkMetadata( + gfx::PointF(92, 35), 0.08, SK_ColorYELLOW, + base::TimeTicks::Now() + base::TimeDelta::FromMicroseconds(50), + gfx::RectF(35, 55, 128, 256)); + + CompositorFrame child_2_frame = MakeEmptyCompositorFrame(); + child_2_frame.metadata.delegated_ink_metadata = + std::make_unique<DelegatedInkMetadata>(later_metadata); + AddPasses(&child_2_frame.render_pass_list, child_2_passes, + &child_2_frame.metadata.referenced_surfaces); + + ParentLocalSurfaceIdAllocator child_2_allocator; + child_2_allocator.GenerateId(); + LocalSurfaceId child_2_local_surface_id = + child_2_allocator.GetCurrentLocalSurfaceIdAllocation().local_surface_id(); + SurfaceId child_2_surface_id(child_2_support->frame_sink_id(), + child_2_local_surface_id); + child_2_support->SubmitCompositorFrame(child_2_local_surface_id, + std::move(child_2_frame)); + + std::vector<Quad> child_3_quads = { + Quad::SolidColorQuad(SK_ColorCYAN, gfx::Rect(5, 5))}; + std::vector<Pass> child_3_passes = { + Pass(child_3_quads, 1, gfx::Size(100, 100))}; + + CompositorFrame child_3_frame = MakeEmptyCompositorFrame(); + child_3_frame.metadata.delegated_ink_metadata = + std::make_unique<DelegatedInkMetadata>(early_metadata); + AddPasses(&child_3_frame.render_pass_list, child_3_passes, + &child_3_frame.metadata.referenced_surfaces); + + ParentLocalSurfaceIdAllocator child_3_allocator; + child_3_allocator.GenerateId(); + LocalSurfaceId child_3_local_surface_id = + child_3_allocator.GetCurrentLocalSurfaceIdAllocation().local_surface_id(); + SurfaceId child_3_surface_id(child_3_support->frame_sink_id(), + child_3_local_surface_id); + child_3_support->SubmitCompositorFrame(child_3_local_surface_id, + std::move(child_3_frame)); + + std::vector<Quad> root_quads = { + Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_1_surface_id), + SK_ColorWHITE, gfx::Rect(5, 5), + /*stretch_content_to_fill_bounds=*/false), + Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_2_surface_id), + SK_ColorWHITE, gfx::Rect(5, 5), + /*stretch_content_to_fill_bounds=*/false), + Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_3_surface_id), + SK_ColorWHITE, gfx::Rect(5, 5), + /*stretch_content_to_fill_bounds=*/false)}; + + std::vector<Pass> root_passes = {Pass(root_quads, 1, gfx::Size(30, 30))}; + + CompositorFrame root_frame = MakeEmptyCompositorFrame(); + AddPasses(&root_frame.render_pass_list, root_passes, + &root_frame.metadata.referenced_surfaces); + + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.Translate(9, 87); + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(1) + ->quad_to_target_transform.Scale(1.4, 1.7); + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(1) + ->quad_to_target_transform.Translate(214, 144); + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(2) + ->quad_to_target_transform.Scale(2.7, 0.2); + + // Two surfaces have delegated ink metadata on them, and when this happens + // on the metadata with the most recent timestamp should be used. Take this + // metadata and transform it to what should be expected. + gfx::PointF pt = later_metadata.point(); + gfx::RectF area = later_metadata.presentation_area(); + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(1) + ->quad_to_target_transform.TransformPoint(&pt); + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(1) + ->quad_to_target_transform.TransformRect(&area); + + root_sink_->SubmitCompositorFrame(root_local_surface_id_, + std::move(root_frame)); + + SurfaceId root_surface_id(root_sink_->frame_sink_id(), + root_local_surface_id_); + CompositorFrame aggregated_frame = AggregateFrame(root_surface_id); + + DelegatedInkMetadata expected_metadata = DelegatedInkMetadata( + pt, later_metadata.diameter(), later_metadata.color(), + later_metadata.timestamp(), area); + + std::unique_ptr<DelegatedInkMetadata> actual_metadata = + std::move(aggregated_frame.metadata.delegated_ink_metadata); + EXPECT_TRUE(actual_metadata); + ExpectDelegatedInkMetadataIsEqual(*actual_metadata.get(), expected_metadata); +} + +// Confirm that delegated ink metadata on an undrawn surface is not on the +// aggregated surface unless the undrawn surface contains a CopyOutputRequest. +TEST_F(SurfaceAggregatorValidSurfaceTest, + DelegatedInkMetadataOnUndrawnSurface) { + std::vector<Quad> child_quads = { + Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))}; + std::vector<Pass> child_passes = {Pass(child_quads, 1, gfx::Size(100, 100))}; + + CompositorFrame child_frame = MakeEmptyCompositorFrame(); + DelegatedInkMetadata metadata(gfx::PointF(34, 89), 1.597, SK_ColorBLUE, + base::TimeTicks::Now(), + gfx::RectF(2.3, 3.2, 177, 212)); + child_frame.metadata.delegated_ink_metadata = + std::make_unique<DelegatedInkMetadata>(metadata); + AddPasses(&child_frame.render_pass_list, child_passes, + &child_frame.metadata.referenced_surfaces); + + ParentLocalSurfaceIdAllocator child_allocator; + child_allocator.GenerateId(); + LocalSurfaceId child_local_surface_id = + child_allocator.GetCurrentLocalSurfaceIdAllocation().local_surface_id(); + SurfaceId child_surface_id(child_sink_->frame_sink_id(), + child_local_surface_id); + child_sink_->SubmitCompositorFrame(child_local_surface_id, + std::move(child_frame)); + + // Do not put the child surface in a SurfaceDrawQuad so that it remains + // undrawn. + std::vector<Quad> root_quads = { + Quad::SolidColorQuad(SK_ColorMAGENTA, gfx::Rect(5, 5))}; + + std::vector<Pass> root_passes = {Pass(root_quads, 1, gfx::Size(30, 30))}; + + CompositorFrame root_frame = MakeEmptyCompositorFrame(); + root_frame.metadata.referenced_surfaces.emplace_back( + SurfaceRange(base::nullopt, child_surface_id)); + AddPasses(&root_frame.render_pass_list, root_passes, + &root_frame.metadata.referenced_surfaces); + + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.Scale(1.5, 1.5); + root_frame.render_pass_list[0] + ->shared_quad_state_list.ElementAt(0) + ->quad_to_target_transform.Translate(70, 240); + + root_sink_->SubmitCompositorFrame(root_local_surface_id_, + std::move(root_frame)); + + SurfaceId root_surface_id(root_sink_->frame_sink_id(), + root_local_surface_id_); + CompositorFrame aggregated_frame = AggregateFrame(root_surface_id); + + EXPECT_FALSE(aggregated_frame.metadata.delegated_ink_metadata); + + // Now add a CopyOutputRequest on the child surface, so that the delegated + // ink metadata does get populated on the aggregated frame. + auto copy_request = CopyOutputRequest::CreateStubForTesting(); + child_sink_->RequestCopyOfOutput(child_local_surface_id, + std::move(copy_request)); + + aggregated_frame = AggregateFrame(root_surface_id); + + std::unique_ptr<DelegatedInkMetadata> actual_metadata = + std::move(aggregated_frame.metadata.delegated_ink_metadata); + EXPECT_TRUE(actual_metadata); + ExpectDelegatedInkMetadataIsEqual(*actual_metadata.get(), metadata); +} + } // namespace } // namespace viz |