diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-12 14:27:29 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-13 09:35:20 +0000 |
commit | c30a6232df03e1efbd9f3b226777b07e087a1122 (patch) | |
tree | e992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/components/viz | |
parent | 7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff) | |
download | qtwebengine-chromium-85-based.tar.gz |
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/components/viz')
119 files changed, 4094 insertions, 1537 deletions
diff --git a/chromium/components/viz/client/client_resource_provider.cc b/chromium/components/viz/client/client_resource_provider.cc index 68fa8738572..ee2871b3f49 100644 --- a/chromium/components/viz/client/client_resource_provider.cc +++ b/chromium/components/viz/client/client_resource_provider.cc @@ -375,7 +375,7 @@ ClientResourceProvider::ScopedSkSurface::ScopedSkSurface( SkSurfaceProps surface_props = ComputeSurfaceProps(can_use_lcd_text); // This type is used only for gpu raster, which implies gpu compositing. bool gpu_compositing = true; - surface_ = SkSurface::MakeFromBackendTextureAsRenderTarget( + surface_ = SkSurface::MakeFromBackendTexture( gr_context, backend_texture, kTopLeft_GrSurfaceOrigin, msaa_sample_count, ResourceFormatToClosestSkColorType(gpu_compositing, format), color_space, &surface_props); @@ -383,7 +383,7 @@ ClientResourceProvider::ScopedSkSurface::ScopedSkSurface( ClientResourceProvider::ScopedSkSurface::~ScopedSkSurface() { if (surface_) - surface_->flush(); + surface_->flushAndSubmit(); } SkSurfaceProps ClientResourceProvider::ScopedSkSurface::ComputeSurfaceProps( diff --git a/chromium/components/viz/client/frame_eviction_manager.cc b/chromium/components/viz/client/frame_eviction_manager.cc index 3a5c8cb684d..48056fdc038 100644 --- a/chromium/components/viz/client/frame_eviction_manager.cc +++ b/chromium/components/viz/client/frame_eviction_manager.cc @@ -103,6 +103,7 @@ size_t FrameEvictionManager::GetMaxNumberOfSavedFrames() const { FrameEvictionManager::FrameEvictionManager() : memory_pressure_listener_(new base::MemoryPressureListener( + FROM_HERE, base::BindRepeating(&FrameEvictionManager::OnMemoryPressure, base::Unretained(this)))) { max_number_of_saved_frames_ = diff --git a/chromium/components/viz/common/BUILD.gn b/chromium/components/viz/common/BUILD.gn index e98ecb33505..926d753ad76 100644 --- a/chromium/components/viz/common/BUILD.gn +++ b/chromium/components/viz/common/BUILD.gn @@ -19,6 +19,7 @@ viz_component("resource_format_utils") { sources = [ "resources/resource_format_utils.cc", "resources/resource_format_utils.h", + "resources/resource_format_utils_mac.mm", "resources/resource_sizes.h", "viz_resource_format_export.h", ] @@ -141,6 +142,7 @@ viz_component("common") { sources = [ "constants.cc", "constants.h", + "delegated_ink_metadata.h", "display/de_jelly.cc", "display/de_jelly.h", "display/overlay_strategy.cc", diff --git a/chromium/components/viz/common/DEPS b/chromium/components/viz/common/DEPS index 71bb14cbcb1..f40bbfd2a81 100644 --- a/chromium/components/viz/common/DEPS +++ b/chromium/components/viz/common/DEPS @@ -7,12 +7,12 @@ include_rules = [ # Exception is struct_traits.h which is used for defining friends only. "+mojo/public/cpp/bindings/struct_traits.h", "+third_party/perfetto/protos/perfetto/trace/track_event", + "+third_party/skia", ] specific_include_rules = { "skia_helper.(cc|h)": [ "+cc/base", - "+third_party/skia", ], # DEPS for GLHelper and friends which are in the root common/ directory. "(yuv_readback|gl_helper|gl_scaler).*\.(cc|h)": [ @@ -21,7 +21,6 @@ specific_include_rules = { "+gpu/command_buffer/common", "+gpu/command_buffer/service", "+gpu/ipc/common", - "+third_party/skia", ], ".*(_unittest|_pixeltest|test_util)\.cc": [ "+cc/test", @@ -29,7 +28,6 @@ specific_include_rules = { "+gpu/ipc/gl_in_process_context.h", "+gpu/ipc/test_gpu_thread_holder.h", "+media/base", - "+third_party/skia/include/core", "+ui/gl", ], ".*_benchmark\.cc": [ diff --git a/chromium/components/viz/common/delegated_ink_metadata.h b/chromium/components/viz/common/delegated_ink_metadata.h new file mode 100644 index 00000000000..3f0584444f0 --- /dev/null +++ b/chromium/components/viz/common/delegated_ink_metadata.h @@ -0,0 +1,62 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_VIZ_COMMON_DELEGATED_INK_METADATA_H_ +#define COMPONENTS_VIZ_COMMON_DELEGATED_INK_METADATA_H_ + +#include "base/time/time.h" +#include "components/viz/common/viz_common_export.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/geometry/rect_f.h" + +namespace viz { + +// This class stores all the metadata that is gathered when the WebAPI +// updateInkTrailStartPoint is called. This metadata flows from blink, +// through cc, and into viz in order to produce a delegated ink trail on the +// end of what was already rendered. +// +// Explainer for the feature: +// https://github.com/WICG/ink-enhancement/blob/master/README.md +class VIZ_COMMON_EXPORT DelegatedInkMetadata { + public: + DelegatedInkMetadata() = default; + DelegatedInkMetadata(const gfx::PointF& pt, + double diameter, + SkColor color, + base::TimeTicks timestamp, + const gfx::RectF& area) + : point_(pt), + diameter_(diameter), + color_(color), + timestamp_(timestamp), + presentation_area_(area) {} + DelegatedInkMetadata(const DelegatedInkMetadata& other) = default; + + const gfx::PointF& point() const { return point_; } + double diameter() const { return diameter_; } + SkColor color() const { return color_; } + base::TimeTicks timestamp() const { return timestamp_; } + const gfx::RectF& presentation_area() const { return presentation_area_; } + + private: + // Location of the pointerevent relative to the root frame. + gfx::PointF point_; + + // Width of the trail, in physical pixels. + double diameter_ = 0; + + // Color to draw the ink trail. + SkColor color_ = 0; + + // Timestamp from the pointerevent for the ink point. + base::TimeTicks timestamp_; + + // The rect to clip the ink trail to, defaults to the containing viewport. + gfx::RectF presentation_area_; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_DELEGATED_INK_METADATA_H_ diff --git a/chromium/components/viz/common/features.cc b/chromium/components/viz/common/features.cc index 23722815a87..ba807b6597c 100644 --- a/chromium/components/viz/common/features.cc +++ b/chromium/components/viz/common/features.cc @@ -53,18 +53,24 @@ const base::Feature kVizForWebView{"VizForWebView", const base::Feature kVizFrameSubmissionForWebView{ "VizFrameSubmissionForWebView", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kUsePreferredIntervalForVideo{ + "UsePreferredIntervalForVideo", base::FEATURE_DISABLED_BY_DEFAULT}; + // Whether we should use the real buffers corresponding to overlay candidates in // order to do a pageflip test rather than allocating test buffers. const base::Feature kUseRealBuffersForPageFlipTest{ "UseRealBuffersForPageFlipTest", base::FEATURE_DISABLED_BY_DEFAULT}; +#if defined(OS_FUCHSIA) +// Enables SkiaOutputDeviceBufferQueue instead of Vulkan swapchain on Fuchsia. +const base::Feature kUseSkiaOutputDeviceBufferQueue{ + "UseSkiaOutputDeviceBufferQueue", base::FEATURE_DISABLED_BY_DEFAULT}; +#endif + // Whether we should split partially occluded quads to reduce overdraw. const base::Feature kSplitPartiallyOccludedQuads{ "SplitPartiallyOccludedQuads", base::FEATURE_ENABLED_BY_DEFAULT}; -const base::Feature kUsePreferredIntervalForVideo{ - "UsePreferredIntervalForVideo", base::FEATURE_DISABLED_BY_DEFAULT}; - // Whether we should log extra debug information to webrtc native log. const base::Feature kWebRtcLogCapturePipeline{ "WebRtcLogCapturePipeline", base::FEATURE_DISABLED_BY_DEFAULT}; diff --git a/chromium/components/viz/common/features.h b/chromium/components/viz/common/features.h index 2a42f537bfd..1b077aa7ab7 100644 --- a/chromium/components/viz/common/features.h +++ b/chromium/components/viz/common/features.h @@ -24,6 +24,9 @@ VIZ_COMMON_EXPORT extern const base::Feature kVizForWebView; VIZ_COMMON_EXPORT extern const base::Feature kVizFrameSubmissionForWebView; VIZ_COMMON_EXPORT extern const base::Feature kUsePreferredIntervalForVideo; VIZ_COMMON_EXPORT extern const base::Feature kUseRealBuffersForPageFlipTest; +#if defined(OS_FUCHSIA) +VIZ_COMMON_EXPORT extern const base::Feature kUseSkiaOutputDeviceBufferQueue; +#endif VIZ_COMMON_EXPORT extern const base::Feature kSplitPartiallyOccludedQuads; VIZ_COMMON_EXPORT extern const base::Feature kWebRtcLogCapturePipeline; diff --git a/chromium/components/viz/common/frame_sinks/begin_frame_source.h b/chromium/components/viz/common/frame_sinks/begin_frame_source.h index 8be3ad72924..2ca5c252021 100644 --- a/chromium/components/viz/common/frame_sinks/begin_frame_source.h +++ b/chromium/components/viz/common/frame_sinks/begin_frame_source.h @@ -10,8 +10,8 @@ #include <string> +#include "base/check.h" #include "base/containers/flat_set.h" -#include "base/logging.h" #include "base/macros.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" diff --git a/chromium/components/viz/common/frame_sinks/copy_output_request.cc b/chromium/components/viz/common/frame_sinks/copy_output_request.cc index 1a10284b8af..ef836fb3a44 100644 --- a/chromium/components/viz/common/frame_sinks/copy_output_request.cc +++ b/chromium/components/viz/common/frame_sinks/copy_output_request.cc @@ -6,6 +6,8 @@ #include "base/bind.h" #include "base/check_op.h" +#include "base/task/task_traits.h" +#include "base/task/thread_pool.h" #include "base/trace_event/trace_event.h" #include "components/viz/common/frame_sinks/copy_output_result.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -53,20 +55,24 @@ void CopyOutputRequest::SetUniformScaleRatio(int scale_from, int scale_to) { } void CopyOutputRequest::SendResult(std::unique_ptr<CopyOutputResult> result) { - TRACE_EVENT_NESTABLE_ASYNC_END1("viz", "CopyOutputRequest", this, "success", - !result->IsEmpty()); - if (result_task_runner_) { - result_task_runner_->PostTask( - FROM_HERE, - base::BindOnce(std::move(result_callback_), std::move(result))); - result_task_runner_ = nullptr; - } else { - std::move(result_callback_).Run(std::move(result)); - } + TRACE_EVENT_NESTABLE_ASYNC_END2( + "viz", "CopyOutputRequest", this, "success", !result->IsEmpty(), + "has_provided_task_runner", !!result_task_runner_); + // Serializing the result requires an expensive copy, so to not block the + // any important thread we PostTask onto the threadpool by default, but if the + // user has provided a task runner use that instead. + auto runner = + result_task_runner_ + ? result_task_runner_ + : base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}); + runner->PostTask(FROM_HERE, base::BindOnce(std::move(result_callback_), + std::move(result))); + // Remove the reference to the task runner (no-op if we didn't have one). + result_task_runner_ = nullptr; } bool CopyOutputRequest::SendsResultsInCurrentSequence() const { - return !result_task_runner_ || + return result_task_runner_ && result_task_runner_->RunsTasksInCurrentSequence(); } diff --git a/chromium/components/viz/common/gl_scaler_test_util.cc b/chromium/components/viz/common/gl_scaler_test_util.cc index 62560e8755a..9a1754e7492 100644 --- a/chromium/components/viz/common/gl_scaler_test_util.cc +++ b/chromium/components/viz/common/gl_scaler_test_util.cc @@ -9,6 +9,7 @@ #include "base/files/file_path.h" #include "base/logging.h" +#include "base/notreached.h" #include "base/path_service.h" #include "cc/test/pixel_test_utils.h" #include "components/viz/test/paths.h" diff --git a/chromium/components/viz/common/gpu/context_cache_controller_unittest.cc b/chromium/components/viz/common/gpu/context_cache_controller_unittest.cc index c380ea621e7..03696829f15 100644 --- a/chromium/components/viz/common/gpu/context_cache_controller_unittest.cc +++ b/chromium/components/viz/common/gpu/context_cache_controller_unittest.cc @@ -177,7 +177,7 @@ TEST(ContextCacheControllerTest, CheckSkiaResourcePurgeAPI) { SkPixmap pixmap(image_info, image_data.data(), image_info.minRowBytes()); auto image = SkImage::MakeRasterCopy(pixmap); auto image_gpu = image->makeTextureImage(gr_context); - gr_context->flush(); + gr_context->flushAndSubmit(); } // Ensure we see size taken up for the image (now released, but cached for diff --git a/chromium/components/viz/common/gpu/context_lost_reason.h b/chromium/components/viz/common/gpu/context_lost_reason.h index 823b5c5b822..9a4ea393e59 100644 --- a/chromium/components/viz/common/gpu/context_lost_reason.h +++ b/chromium/components/viz/common/gpu/context_lost_reason.h @@ -26,12 +26,14 @@ enum ContextLostReason { CONTEXT_LOST_OUT_OF_MEMORY = 10, CONTEXT_LOST_MAKECURRENT_FAILED = 11, CONTEXT_LOST_INVALID_GPU_MESSAGE = 12, - // SkiaRenderer marked context as lost because of failed Reshape call CONTEXT_LOST_RESHAPE_FAILED = 13, - // Update kMaxValue and //tools/metrics/histograms/histograms.xml when adding - // new values. - kMaxValue = CONTEXT_LOST_RESHAPE_FAILED + CONTEXT_LOST_SET_DRAW_RECTANGLE_FAILED = 14, + CONTEXT_LOST_DIRECT_COMPOSITION_OVERLAY_FAILED = 15, + CONTEXT_LOST_SWAP_FAILED = 16, + // Update kMaxValue here and <enum name="ContextLostReason"> in + // tools/metrics/histograms/enum.xml when adding new values. + kMaxValue = CONTEXT_LOST_SWAP_FAILED }; VIZ_COMMON_EXPORT ContextLostReason diff --git a/chromium/components/viz/common/gpu/metal_api_proxy.h b/chromium/components/viz/common/gpu/metal_api_proxy.h index 39c1a1a2a03..c32a55c3ea1 100644 --- a/chromium/components/viz/common/gpu/metal_api_proxy.h +++ b/chromium/components/viz/common/gpu/metal_api_proxy.h @@ -5,6 +5,9 @@ #ifndef COMPONENTS_VIZ_COMMON_GPU_METAL_API_PROXY_H_ #define COMPONENTS_VIZ_COMMON_GPU_METAL_API_PROXY_H_ +#include <memory> +#include <string> + #import <Metal/Metal.h> #include <os/availability.h> diff --git a/chromium/components/viz/common/gpu/metal_context_provider.mm b/chromium/components/viz/common/gpu/metal_context_provider.mm index f3d7e1d49f9..40e0c5c2390 100644 --- a/chromium/components/viz/common/gpu/metal_context_provider.mm +++ b/chromium/components/viz/common/gpu/metal_context_provider.mm @@ -7,6 +7,7 @@ #import <Metal/Metal.h> #include "base/bind.h" +#include "base/logging.h" #include "base/mac/scoped_nsobject.h" #include "base/memory/ref_counted.h" #include "base/metrics/histogram_macros.h" diff --git a/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.cc b/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.cc index 204fea1b374..4d098c579d1 100644 --- a/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.cc +++ b/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.cc @@ -4,6 +4,8 @@ #include "components/viz/common/gpu/vulkan_in_process_context_provider.h" +#include "base/task/thread_pool.h" +#include "base/task/thread_pool/thread_pool_instance.h" #include "gpu/vulkan/buildflags.h" #include "gpu/vulkan/init/gr_vk_memory_allocator_impl.h" #include "gpu/vulkan/vulkan_device_queue.h" @@ -12,9 +14,30 @@ #include "gpu/vulkan/vulkan_implementation.h" #include "gpu/vulkan/vulkan_instance.h" #include "gpu/vulkan/vulkan_util.h" +#include "third_party/skia/include/core/SkExecutor.h" #include "third_party/skia/include/gpu/GrContext.h" #include "third_party/skia/include/gpu/vk/GrVkExtensions.h" +namespace { + +class VizExecutor : public SkExecutor { + public: + VizExecutor() = default; + ~VizExecutor() override = default; + VizExecutor(const VizExecutor&) = delete; + VizExecutor& operator=(const VizExecutor&) = delete; + + // std::function is used by SkExecutor in //third_party/skia. nocheck + using Fn = std::function<void(void)>; // nocheck + // SkExecutor: + void add(Fn task) override { + base::ThreadPool::PostTask( + FROM_HERE, base::BindOnce([](Fn task) { task(); }, std::move(task))); + } +}; + +} // namespace + namespace viz { // static @@ -102,7 +125,16 @@ bool VulkanInProcessContextProvider::Initialize( vulkan_implementation_->enforce_protected_memory() ? GrProtected::kYes : GrProtected::kNo; - gr_context_ = GrContext::MakeVulkan(backend_context, context_options); + GrContextOptions options; + if (base::ThreadPoolInstance::Get()) { + // For some tests, ThreadPoolInstance is not initialized. VizExecutor will + // not be used for this case. + // TODO(penghuang): Make sure ThreadPoolInstance is initialized for related + // tests. + executor_ = std::make_unique<VizExecutor>(); + options.fExecutor = executor_.get(); + } + gr_context_ = GrContext::MakeVulkan(backend_context, options); return gr_context_ != nullptr; } @@ -122,6 +154,8 @@ void VulkanInProcessContextProvider::Destroy() { gr_context_.reset(); } + executor_.reset(); + if (device_queue_) { device_queue_->Destroy(); device_queue_.reset(); diff --git a/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.h b/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.h index 1215dff230d..05ea0a5c815 100644 --- a/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.h +++ b/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.h @@ -16,6 +16,8 @@ #include "third_party/skia/include/gpu/vk/GrVkBackendContext.h" #endif +class SkExecutor; + namespace gpu { class VulkanImplementation; class VulkanDeviceQueue; @@ -53,6 +55,7 @@ class VIZ_VULKAN_CONTEXT_PROVIDER_EXPORT VulkanInProcessContextProvider #if BUILDFLAG(ENABLE_VULKAN) sk_sp<GrContext> gr_context_; + std::unique_ptr<SkExecutor> executor_; gpu::VulkanImplementation* vulkan_implementation_; std::unique_ptr<gpu::VulkanDeviceQueue> device_queue_; #endif diff --git a/chromium/components/viz/common/quads/compositor_frame_metadata.cc b/chromium/components/viz/common/quads/compositor_frame_metadata.cc index 96c246eeb84..c9709eae9de 100644 --- a/chromium/components/viz/common/quads/compositor_frame_metadata.cc +++ b/chromium/components/viz/common/quads/compositor_frame_metadata.cc @@ -22,6 +22,32 @@ CompositorFrameMetadata CompositorFrameMetadata::Clone() const { } CompositorFrameMetadata::CompositorFrameMetadata( - const CompositorFrameMetadata& other) = default; + const CompositorFrameMetadata& other) + : device_scale_factor(other.device_scale_factor), + root_scroll_offset(other.root_scroll_offset), + page_scale_factor(other.page_scale_factor), + scrollable_viewport_size(other.scrollable_viewport_size), + content_color_usage(other.content_color_usage), + may_contain_video(other.may_contain_video), + is_resourceless_software_draw_with_scroll_or_animation( + other.is_resourceless_software_draw_with_scroll_or_animation), + root_background_color(other.root_background_color), + latency_info(other.latency_info), + referenced_surfaces(other.referenced_surfaces), + activation_dependencies(other.activation_dependencies), + deadline(other.deadline), + begin_frame_ack(other.begin_frame_ack), + frame_token(other.frame_token), + send_frame_token_to_embedder(other.send_frame_token_to_embedder), + min_page_scale_factor(other.min_page_scale_factor), + top_controls_visible_height(other.top_controls_visible_height), + local_surface_id_allocation_time(other.local_surface_id_allocation_time), + preferred_frame_interval(other.preferred_frame_interval), + display_transform_hint(other.display_transform_hint) { + if (other.delegated_ink_metadata) { + delegated_ink_metadata = std::make_unique<DelegatedInkMetadata>( + *other.delegated_ink_metadata.get()); + } +} } // namespace viz diff --git a/chromium/components/viz/common/quads/compositor_frame_metadata.h b/chromium/components/viz/common/quads/compositor_frame_metadata.h index de4513fe572..06b680d5de5 100644 --- a/chromium/components/viz/common/quads/compositor_frame_metadata.h +++ b/chromium/components/viz/common/quads/compositor_frame_metadata.h @@ -11,6 +11,7 @@ #include "base/optional.h" #include "base/time/time.h" #include "build/build_config.h" +#include "components/viz/common/delegated_ink_metadata.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "components/viz/common/quads/frame_deadline.h" #include "components/viz/common/surfaces/surface_id.h" @@ -152,6 +153,19 @@ class VIZ_COMMON_EXPORT CompositorFrameMetadata { // applicable to frames of the root surface. gfx::OverlayTransform display_transform_hint = gfx::OVERLAY_TRANSFORM_NONE; + // Contains the metadata required for drawing a delegated ink trail onto the + // end of a rendered ink stroke. This should only be present when two + // conditions are met: + // 1. The JS API |updateInkTrailStartPoint| is used - This gathers the + // metadata and puts it onto a compositor frame to be sent to viz. + // 2. This frame will not be submitted to the root surface - The browser UI + // does not use this, and the frame must be contained within a + // SurfaceDrawQuad. + // The ink trail created with this metadata will only last for a single frame + // before it disappears, regardless of whether or not the next frame contains + // delegated ink metadata. + std::unique_ptr<DelegatedInkMetadata> delegated_ink_metadata; + private: CompositorFrameMetadata(const CompositorFrameMetadata& other); CompositorFrameMetadata operator=(const CompositorFrameMetadata&) = delete; diff --git a/chromium/components/viz/common/quads/draw_quad.cc b/chromium/components/viz/common/quads/draw_quad.cc index a0b0255820d..7de9198ef27 100644 --- a/chromium/components/viz/common/quads/draw_quad.cc +++ b/chromium/components/viz/common/quads/draw_quad.cc @@ -27,7 +27,9 @@ void DrawQuad::SetAll(const SharedQuadState* shared_quad_state, const gfx::Rect& rect, const gfx::Rect& visible_rect, bool needs_blending) { - DCHECK(rect.Contains(visible_rect)) + // TODO(boliu): Temporarily making this a release check to catch + // crbug.com/1072407. + CHECK(rect.Contains(visible_rect)) << "rect: " << rect.ToString() << " visible_rect: " << visible_rect.ToString(); diff --git a/chromium/components/viz/common/quads/draw_quad_unittest.cc b/chromium/components/viz/common/quads/draw_quad_unittest.cc index 32e4828180d..4ddc34dde7d 100644 --- a/chromium/components/viz/common/quads/draw_quad_unittest.cc +++ b/chromium/components/viz/common/quads/draw_quad_unittest.cc @@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/compiler_specific.h" +#include "base/logging.h" #include "base/unguessable_token.h" #include "cc/base/math_util.h" #include "cc/paint/filter_operations.h" diff --git a/chromium/components/viz/common/quads/render_pass.cc b/chromium/components/viz/common/quads/render_pass.cc index 2e08e454b64..9ff21584a2f 100644 --- a/chromium/components/viz/common/quads/render_pass.cc +++ b/chromium/components/viz/common/quads/render_pass.cc @@ -229,7 +229,9 @@ void RenderPass::SetNew(uint64_t id, const gfx::Rect& output_rect, const gfx::Rect& damage_rect, const gfx::Transform& transform_to_root_target) { - DCHECK(id); + // TODO(boliu): Temporarily making this a release check to catch + // crbug.com/1072407. + CHECK(id); DCHECK(damage_rect.IsEmpty() || output_rect.Contains(damage_rect)) << "damage_rect: " << damage_rect.ToString() << " output_rect: " << output_rect.ToString(); @@ -256,7 +258,9 @@ void RenderPass::SetAll( bool cache_render_pass, bool has_damage_from_contributing_content, bool generate_mipmap) { - DCHECK(id); + // TODO(boliu): Temporarily making this a release check to catch + // crbug.com/1072407. + CHECK(id); this->id = id; this->output_rect = output_rect; diff --git a/chromium/components/viz/common/resources/platform_color.h b/chromium/components/viz/common/resources/platform_color.h index 6e8790d0a06..3d573570d8c 100644 --- a/chromium/components/viz/common/resources/platform_color.h +++ b/chromium/components/viz/common/resources/platform_color.h @@ -5,8 +5,8 @@ #ifndef COMPONENTS_VIZ_COMMON_RESOURCES_PLATFORM_COLOR_H_ #define COMPONENTS_VIZ_COMMON_RESOURCES_PLATFORM_COLOR_H_ -#include "base/logging.h" #include "base/macros.h" +#include "base/notreached.h" #include "components/viz/common/resources/resource_format.h" #include "gpu/command_buffer/common/capabilities.h" #include "third_party/skia/include/core/SkTypes.h" diff --git a/chromium/components/viz/common/resources/resource_format_utils.cc b/chromium/components/viz/common/resources/resource_format_utils.cc index ea1ae8de815..2bfdfd5d2e4 100644 --- a/chromium/components/viz/common/resources/resource_format_utils.cc +++ b/chromium/components/viz/common/resources/resource_format_utils.cc @@ -4,6 +4,8 @@ #include "components/viz/common/resources/resource_format_utils.h" +#include <ostream> + #include "base/check_op.h" #include "base/notreached.h" #include "base/stl_util.h" @@ -324,10 +326,11 @@ unsigned int TextureStorageFormat(ResourceFormat format) { case RGBA_1010102: case BGRA_1010102: return GL_RGB10_A2_EXT; - case BGR_565: - case BGRX_8888: case YVU_420: case YUV_420_BIPLANAR: + return GL_RGB8_OES; + case BGR_565: + case BGRX_8888: case P010: break; } diff --git a/chromium/components/viz/common/resources/resource_format_utils.h b/chromium/components/viz/common/resources/resource_format_utils.h index ca10863b695..ce691952e0a 100644 --- a/chromium/components/viz/common/resources/resource_format_utils.h +++ b/chromium/components/viz/common/resources/resource_format_utils.h @@ -5,6 +5,7 @@ #ifndef COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_FORMAT_UTILS_H_ #define COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_FORMAT_UTILS_H_ +#include "build/build_config.h" #include "components/viz/common/resources/resource_format.h" #include "components/viz/common/viz_resource_format_export.h" #include "gpu/vulkan/buildflags.h" @@ -73,6 +74,10 @@ VIZ_RESOURCE_FORMAT_EXPORT wgpu::TextureFormat ToDawnFormat( VIZ_RESOURCE_FORMAT_EXPORT WGPUTextureFormat ToWGPUFormat(ResourceFormat format); +#if defined(OS_MACOSX) +VIZ_RESOURCE_FORMAT_EXPORT unsigned int ToMTLPixelFormat(ResourceFormat format); +#endif + } // namespace viz #endif // COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_FORMAT_UTILS_H_ diff --git a/chromium/components/viz/common/resources/resource_format_utils_mac.mm b/chromium/components/viz/common/resources/resource_format_utils_mac.mm new file mode 100644 index 00000000000..5a095e7f30d --- /dev/null +++ b/chromium/components/viz/common/resources/resource_format_utils_mac.mm @@ -0,0 +1,40 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/viz/common/resources/resource_format_utils.h" + +#include <Metal/MTLPixelFormat.h> + +#include "base/logging.h" + +namespace viz { + +unsigned int ToMTLPixelFormat(ResourceFormat format) { + if (@available(macOS 10.11, *)) { + MTLPixelFormat mtl_pixel_format = MTLPixelFormatInvalid; + switch (format) { + case RED_8: + case ALPHA_8: + case LUMINANCE_8: + mtl_pixel_format = MTLPixelFormatR8Unorm; + break; + case RG_88: + mtl_pixel_format = MTLPixelFormatRG8Unorm; + break; + case RGBA_8888: + mtl_pixel_format = MTLPixelFormatRGBA8Unorm; + break; + case BGRA_8888: + mtl_pixel_format = MTLPixelFormatBGRA8Unorm; + break; + default: + DLOG(ERROR) << "Invalid Metal pixel format."; + break; + } + return static_cast<unsigned int>(mtl_pixel_format); + } + return 0; +} + +} // namespace viz diff --git a/chromium/components/viz/common/resources/resource_sizes.h b/chromium/components/viz/common/resources/resource_sizes.h index 9c4d9eee7b7..f2745793207 100644 --- a/chromium/components/viz/common/resources/resource_sizes.h +++ b/chromium/components/viz/common/resources/resource_sizes.h @@ -9,7 +9,7 @@ #include <limits> -#include "base/logging.h" +#include "base/check_op.h" #include "base/macros.h" #include "base/numerics/safe_math.h" #include "cc/base/math_util.h" diff --git a/chromium/components/viz/common/yuv_readback_unittest.cc b/chromium/components/viz/common/yuv_readback_unittest.cc index ab37ff6fd71..c1d7f1be9eb 100644 --- a/chromium/components/viz/common/yuv_readback_unittest.cc +++ b/chromium/components/viz/common/yuv_readback_unittest.cc @@ -102,16 +102,15 @@ class YUVReadbackTest : public testing::Test { run_loop.Run(); json_data.append("]"); - std::string error_msg; - std::unique_ptr<base::Value> trace_data = - base::JSONReader::ReadAndReturnErrorDeprecated(json_data, 0, nullptr, - &error_msg); - CHECK(trace_data) << "JSON parsing failed (" << error_msg - << ") JSON data:" << std::endl - << json_data; + base::JSONReader::ValueWithError parsed_json = + base::JSONReader::ReadAndReturnValueWithError(json_data); + CHECK(parsed_json.value) + << "JSON parsing failed (" << parsed_json.error_message + << ") JSON data:" << std::endl + << json_data; base::ListValue* list; - CHECK(trace_data->GetAsList(&list)); + CHECK(parsed_json.value->GetAsList(&list)); for (size_t i = 0; i < list->GetSize(); i++) { base::Value* item = nullptr; if (list->Get(i, &item)) { diff --git a/chromium/components/viz/demo/demo_main.cc b/chromium/components/viz/demo/demo_main.cc index f470f6c7d47..6de8e394c3d 100644 --- a/chromium/components/viz/demo/demo_main.cc +++ b/chromium/components/viz/demo/demo_main.cc @@ -20,6 +20,7 @@ #include "mojo/core/embedder/scoped_ipc_support.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" +#include "ui/base/ui_base_features.h" #include "ui/events/platform/platform_event_source.h" #include "ui/platform_window/platform_window.h" #include "ui/platform_window/platform_window_delegate.h" @@ -30,7 +31,6 @@ #endif #if defined(OS_WIN) -#include "ui/base/cursor/cursor_loader_win.h" #include "ui/platform_window/win/win_window.h" #endif @@ -85,7 +85,8 @@ class InitUI { public: InitUI() { #if defined(USE_X11) - XInitThreads(); + if (!features::IsUsingOzonePlatform()) + XInitThreads(); #endif event_source_ = ui::PlatformEventSource::CreateDefault(); } @@ -117,15 +118,22 @@ class DemoWindow : public ui::PlatformWindowDelegate { std::unique_ptr<ui::PlatformWindow> CreatePlatformWindow( const gfx::Rect& bounds) { ui::PlatformWindowInitProperties props(bounds); +#if defined(USE_X11) || defined(USE_OZONE) #if defined(USE_OZONE) - return ui::OzonePlatform::GetInstance()->CreatePlatformWindow( - this, std::move(props)); -#elif defined(OS_WIN) - return std::make_unique<ui::WinWindow>(this, props.bounds); -#elif defined(USE_X11) + if (features::IsUsingOzonePlatform()) { + return ui::OzonePlatform::GetInstance()->CreatePlatformWindow( + this, std::move(props)); + } +#endif +#if defined(USE_X11) auto x11_window = std::make_unique<ui::X11Window>(this); x11_window->Initialize(std::move(props)); return x11_window; +#endif + NOTREACHED(); + return nullptr; +#elif defined(OS_WIN) + return std::make_unique<ui::WinWindow>(this, props.bounds); #else NOTIMPLEMENTED(); return nullptr; diff --git a/chromium/components/viz/host/BUILD.gn b/chromium/components/viz/host/BUILD.gn index 8bf87caba72..09fff133014 100644 --- a/chromium/components/viz/host/BUILD.gn +++ b/chromium/components/viz/host/BUILD.gn @@ -95,6 +95,10 @@ viz_source_set("unit_tests") { if (use_ozone) { deps += [ "//ui/ozone" ] } + + if (use_x11 || use_ozone) { + deps += [ "//ui/base:features" ] + } } fuzzer_test("hit_test_query_fuzzer") { diff --git a/chromium/components/viz/host/gpu_host_impl.cc b/chromium/components/viz/host/gpu_host_impl.cc index 414ba48b659..c3d6c9a3fd0 100644 --- a/chromium/components/viz/host/gpu_host_impl.cc +++ b/chromium/components/viz/host/gpu_host_impl.cc @@ -36,6 +36,10 @@ #include "ui/gfx/win/rendering_window_manager.h" #endif +#if defined(USE_OZONE) +#include "ui/base/ui_base_features.h" +#endif + namespace viz { namespace { @@ -125,7 +129,8 @@ GpuHostImpl::GpuHostImpl(Delegate* delegate, GetFontRenderParams().Get()->subpixel_rendering); #if defined(USE_OZONE) - InitOzone(); + if (features::IsUsingOzonePlatform()) + InitOzone(); #endif // defined(USE_OZONE) } @@ -294,6 +299,7 @@ mojom::InfoCollectionGpuService* GpuHostImpl::info_collection_gpu_service() { #if defined(USE_OZONE) void GpuHostImpl::InitOzone() { + DCHECK(features::IsUsingOzonePlatform()); // Ozone needs to send the primary DRM device to GPU service as early as // possible to ensure the latter always has a valid device. // https://crbug.com/608839 @@ -512,6 +518,10 @@ void GpuHostImpl::DidUpdateOverlayInfo(const gpu::OverlayInfo& overlay_info) { delegate_->DidUpdateOverlayInfo(overlay_info); } +void GpuHostImpl::DidUpdateHDRStatus(bool hdr_enabled) { + delegate_->DidUpdateHDRStatus(hdr_enabled); +} + void GpuHostImpl::SetChildSurface(gpu::SurfaceHandle parent, gpu::SurfaceHandle child) { if (pid_ != base::kNullProcessId) { diff --git a/chromium/components/viz/host/gpu_host_impl.h b/chromium/components/viz/host/gpu_host_impl.h index 6481e8f0ed5..640814e68d0 100644 --- a/chromium/components/viz/host/gpu_host_impl.h +++ b/chromium/components/viz/host/gpu_host_impl.h @@ -73,6 +73,7 @@ class VIZ_HOST_EXPORT GpuHostImpl : public mojom::GpuHost { virtual void MaybeShutdownGpuProcess() = 0; #if defined(OS_WIN) virtual void DidUpdateOverlayInfo(const gpu::OverlayInfo& overlay_info) = 0; + virtual void DidUpdateHDRStatus(bool hdr_enabled) = 0; #endif virtual void BlockDomainFrom3DAPIs(const GURL& url, gpu::DomainGuilt guilt) = 0; @@ -231,6 +232,7 @@ class VIZ_HOST_EXPORT GpuHostImpl : public mojom::GpuHost { void DisableGpuCompositing() override; #if defined(OS_WIN) void DidUpdateOverlayInfo(const gpu::OverlayInfo& overlay_info) override; + void DidUpdateHDRStatus(bool hdr_enabled) override; void SetChildSurface(gpu::SurfaceHandle parent, gpu::SurfaceHandle child) override; #endif diff --git a/chromium/components/viz/host/hit_test/hit_test_query.cc b/chromium/components/viz/host/hit_test/hit_test_query.cc index 54cd565e50a..9ca5eba8860 100644 --- a/chromium/components/viz/host/hit_test/hit_test_query.cc +++ b/chromium/components/viz/host/hit_test/hit_test_query.cc @@ -4,6 +4,8 @@ #include "components/viz/host/hit_test/hit_test_query.h" +#include <sstream> + #include "base/containers/stack.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string_util.h" @@ -224,8 +226,8 @@ bool HitTestQuery::FindTargetInRegionForLocation( // hit test data, e.g. overlapped by ShelfApp on ChromeOS. // The kHitTestAsk flag should be ignored in such a case because there is no // need to do async hit testing on the root merely because it was overlapped. - // TODO(yigu): Do not set the kHitTestAsk and kOverlappedRegion flags for - // root when building hit test data. + // TODO(crbug.com/1001238): Do not set the kHitTestAsk and kOverlappedRegion + // flags for root when building hit test data. bool root_view_overlapped = hit_test_data_[region_index].frame_sink_id == root_view_frame_sink_id && hit_test_data_[region_index].async_hit_test_reasons == diff --git a/chromium/components/viz/host/host_gpu_memory_buffer_manager.cc b/chromium/components/viz/host/host_gpu_memory_buffer_manager.cc index 913efda7a10..34af06414c2 100644 --- a/chromium/components/viz/host/host_gpu_memory_buffer_manager.cc +++ b/chromium/components/viz/host/host_gpu_memory_buffer_manager.cc @@ -15,6 +15,7 @@ #include "gpu/ipc/common/gpu_memory_buffer_impl_shared_memory.h" #include "gpu/ipc/common/gpu_memory_buffer_support.h" #include "services/viz/privileged/mojom/gl/gpu_service.mojom.h" +#include "ui/base/ui_base_features.h" #include "ui/gfx/buffer_format_util.h" #include "ui/gfx/buffer_usage_util.h" @@ -46,11 +47,15 @@ HostGpuMemoryBufferManager::HostGpuMemoryBufferManager( client_id_(client_id), gpu_memory_buffer_support_(std::move(gpu_memory_buffer_support)), task_runner_(std::move(task_runner)) { -#if !defined(USE_X11) - native_configurations_ = gpu::GetNativeGpuMemoryBufferConfigurations( - gpu_memory_buffer_support_.get()); - native_configurations_initialized_.Signal(); + bool should_get_native_configs = true; +#if defined(USE_X11) + should_get_native_configs = features::IsUsingOzonePlatform(); #endif + if (should_get_native_configs) { + native_configurations_ = gpu::GetNativeGpuMemoryBufferConfigurations( + gpu_memory_buffer_support_.get()); + native_configurations_initialized_.Signal(); + } base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( this, "HostGpuMemoryBufferManager", task_runner_); } @@ -283,7 +288,12 @@ void HostGpuMemoryBufferManager::OnConnectionError() { << ", size = " << buffer.size.ToString() << ", format = " << gfx::BufferFormatToString(buffer.format) << ", usage = " << gfx::BufferUsageToString(buffer.usage) - << ", surface_handle = " << buffer.surface_handle + << ", surface_handle = " +#if defined(USE_X11) + << static_cast<uint32_t>(buffer.surface_handle) +#else + << buffer.surface_handle +#endif << " due to connection error"; AllocateGpuMemoryBuffer( buffer_pair.first, client_pair.first, buffer.size, buffer.format, diff --git a/chromium/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc b/chromium/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc index 6620f76f584..6a62df062f5 100644 --- a/chromium/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc +++ b/chromium/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc @@ -19,6 +19,10 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/client_native_pixmap_factory.h" +#if defined(USE_OZONE) || defined(USE_X11) +#include "ui/base/ui_base_features.h" // nogncheck +#endif + #if defined(USE_OZONE) #include "ui/ozone/public/ozone_platform.h" #endif @@ -229,7 +233,8 @@ class HostGpuMemoryBufferManagerTest : public ::testing::Test { base::ThreadTaskRunnerHandle::Get()); #if defined(USE_X11) // X11 requires GPU process initialization to determine GMB support. - gpu_memory_buffer_manager_->native_configurations_initialized_.Signal(); + if (!features::IsUsingOzonePlatform()) + gpu_memory_buffer_manager_->native_configurations_initialized_.Signal(); #endif } @@ -238,9 +243,11 @@ class HostGpuMemoryBufferManagerTest : public ::testing::Test { bool IsNativePixmapConfigSupported() { bool native_pixmap_supported = false; #if defined(USE_OZONE) - native_pixmap_supported = - ui::OzonePlatform::GetInstance()->IsNativePixmapConfigSupported( - gfx::BufferFormat::RGBA_8888, gfx::BufferUsage::GPU_READ); + if (features::IsUsingOzonePlatform()) { + native_pixmap_supported = + ui::OzonePlatform::GetInstance()->IsNativePixmapConfigSupported( + gfx::BufferFormat::RGBA_8888, gfx::BufferUsage::GPU_READ); + } #elif defined(OS_ANDROID) native_pixmap_supported = base::AndroidHardwareBufferCompat::IsSupportAvailable(); diff --git a/chromium/components/viz/host/renderer_settings_creation.cc b/chromium/components/viz/host/renderer_settings_creation.cc index 0fc4813b2f7..f28c3218baa 100644 --- a/chromium/components/viz/host/renderer_settings_creation.cc +++ b/chromium/components/viz/host/renderer_settings_creation.cc @@ -20,6 +20,7 @@ #endif #if defined(USE_OZONE) +#include "ui/base/ui_base_features.h" #include "ui/ozone/public/ozone_platform.h" #endif @@ -86,16 +87,18 @@ RendererSettings CreateRendererSettings() { } #if defined(USE_OZONE) - if (command_line->HasSwitch(switches::kEnableHardwareOverlays)) { - renderer_settings.overlay_strategies = ParseOverlayStrategies( - command_line->GetSwitchValueASCII(switches::kEnableHardwareOverlays)); - } else { - auto& host_properties = - ui::OzonePlatform::GetInstance()->GetInitializedHostProperties(); - if (host_properties.supports_overlays) { - renderer_settings.overlay_strategies = {OverlayStrategy::kFullscreen, - OverlayStrategy::kSingleOnTop, - OverlayStrategy::kUnderlay}; + if (features::IsUsingOzonePlatform()) { + if (command_line->HasSwitch(switches::kEnableHardwareOverlays)) { + renderer_settings.overlay_strategies = ParseOverlayStrategies( + command_line->GetSwitchValueASCII(switches::kEnableHardwareOverlays)); + } else { + auto& host_properties = + ui::OzonePlatform::GetInstance()->GetInitializedHostProperties(); + if (host_properties.supports_overlays) { + renderer_settings.overlay_strategies = {OverlayStrategy::kFullscreen, + OverlayStrategy::kSingleOnTop, + OverlayStrategy::kUnderlay}; + } } } #endif diff --git a/chromium/components/viz/service/BUILD.gn b/chromium/components/viz/service/BUILD.gn index b2ba22d8da6..09797704091 100644 --- a/chromium/components/viz/service/BUILD.gn +++ b/chromium/components/viz/service/BUILD.gn @@ -330,6 +330,10 @@ viz_source_set("gpu_service_dependencies") { sources = [ "display_embedder/image_context_impl.cc", "display_embedder/image_context_impl.h", + "display_embedder/output_presenter.cc", + "display_embedder/output_presenter.h", + "display_embedder/output_presenter_gl.cc", + "display_embedder/output_presenter_gl.h", "display_embedder/skia_output_device.cc", "display_embedder/skia_output_device.h", "display_embedder/skia_output_device_buffer_queue.cc", @@ -365,7 +369,10 @@ viz_source_set("gpu_service_dependencies") { defines = [ "VIZ_SERVICE_IMPLEMENTATION" ] - deps = [ "//gpu/config" ] + deps = [ + "//base", + "//gpu/config", + ] if (is_win) { sources += [ @@ -409,7 +416,7 @@ viz_source_set("gpu_service_dependencies") { deps += [ "//ui/gfx/x" ] } - if (skia_use_dawn) { + if (skia_use_dawn && is_win) { sources += [ "display_embedder/skia_output_device_dawn.cc", "display_embedder/skia_output_device_dawn.h", @@ -423,6 +430,19 @@ viz_source_set("gpu_service_dependencies") { "//third_party/dawn/src/dawn_native", ] } + + if (is_fuchsia) { + sources += [ + "display_embedder/output_presenter_fuchsia.cc", + "display_embedder/output_presenter_fuchsia.h", + ] + + deps += [ + "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.images", + "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.sysmem", + "//third_party/fuchsia-sdk/sdk/pkg/sys_inspect_cpp", + ] + } } viz_source_set("unit_tests") { @@ -491,6 +511,7 @@ viz_source_set("unit_tests") { "//components/viz/common", "//components/viz/host", "//components/viz/service/main:main", + "//components/viz/test:test_suite", "//components/viz/test:test_support", "//gpu/command_buffer/client", "//gpu/command_buffer/client:gles2_implementation", @@ -554,7 +575,9 @@ viz_source_set("unit_tests") { # TODO(samans): Support more configurations. # CFI issue: https://crbug.com/967819 # LSAN issue: https://crbug.com/971357 - if (use_x11 && !is_cfi && !is_lsan) { + # Fuchsia ARM64 https://crbug.com/1058247 + if ((use_x11 && !is_cfi && !is_lsan) || + (is_fuchsia && target_cpu == "x64")) { defines += [ "ENABLE_VIZ_VULKAN_TESTS" ] } } 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 diff --git a/chromium/components/viz/service/display_embedder/buffer_queue.cc b/chromium/components/viz/service/display_embedder/buffer_queue.cc index 85241086b23..ddec6ab1dae 100644 --- a/chromium/components/viz/service/display_embedder/buffer_queue.cc +++ b/chromium/components/viz/service/display_embedder/buffer_queue.cc @@ -7,6 +7,7 @@ #include <utility> #include "base/containers/adapters.h" +#include "base/logging.h" #include "build/build_config.h" #include "components/viz/common/resources/resource_format_utils.h" #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h" @@ -52,8 +53,18 @@ void BufferQueue::UpdateBufferDamage(const gfx::Rect& damage) { } gfx::Rect BufferQueue::CurrentBufferDamage() const { - DCHECK(current_surface_); - return current_surface_->damage; + if (current_surface_) + return current_surface_->damage; + + // In case there is no current_surface_, we get the damage from the surface + // that will be set as current_surface_ by the next call to GetNextSurface. + if (!available_surfaces_.empty()) { + return available_surfaces_.back()->damage; + } + + // If we can't determine which surface will be the next current_surface_, we + // conservatively invalidate the whole buffer. + return gfx::Rect(size_); } void BufferQueue::SwapBuffers(const gfx::Rect& damage) { diff --git a/chromium/components/viz/service/display_embedder/buffer_queue_unittest.cc b/chromium/components/viz/service/display_embedder/buffer_queue_unittest.cc index 9c921dab300..4cb71e78984 100644 --- a/chromium/components/viz/service/display_embedder/buffer_queue_unittest.cc +++ b/chromium/components/viz/service/display_embedder/buffer_queue_unittest.cc @@ -92,7 +92,7 @@ class StubGpuMemoryBufferManager : public TestGpuMemoryBufferManager { gfx::BufferFormat format, gfx::BufferUsage usage, gpu::SurfaceHandle surface_handle) override { - if (!surface_handle) { + if (surface_handle == gpu::kNullSurfaceHandle) { return TestGpuMemoryBufferManager::CreateGpuMemoryBuffer( size, format, usage, surface_handle); } @@ -110,6 +110,9 @@ class StubGpuMemoryBufferManager : public TestGpuMemoryBufferManager { #if defined(OS_WIN) const gpu::SurfaceHandle kFakeSurfaceHandle = reinterpret_cast<gpu::SurfaceHandle>(1); +#elif defined(USE_X11) +const gpu::SurfaceHandle kFakeSurfaceHandle = + static_cast<gpu::SurfaceHandle>(1); #else const gpu::SurfaceHandle kFakeSurfaceHandle = 1; #endif diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc b/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc index cde84eac27f..dfbcf050e87 100644 --- a/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc +++ b/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc @@ -8,6 +8,8 @@ #include "base/bind.h" #include "base/command_line.h" +#include "base/logging.h" +#include "build/build_config.h" #include "components/viz/common/frame_sinks/begin_frame_source.h" #include "components/viz/common/gpu/context_provider.h" #include "components/viz/common/switches.h" @@ -40,7 +42,13 @@ GLOutputSurfaceBufferQueue::GLOutputSurfaceBufferQueue( // shifts the start of the new frame forward relative to the old // implementation. capabilities_.max_frames_pending = 2; - + // GetCurrentFramebufferDamage will return an upper bound of the part of the + // buffer that needs to be recomposited. +#if defined(OS_MACOSX) + capabilities_.supports_target_damage = false; +#else + capabilities_.supports_target_damage = true; +#endif // Force the number of max pending frames to one when the switch // "double-buffer-compositing" is passed. // This will keep compositing in double buffered mode assuming |buffer_queue_| diff --git a/chromium/components/viz/service/display_embedder/output_presenter.cc b/chromium/components/viz/service/display_embedder/output_presenter.cc new file mode 100644 index 00000000000..240c7d442a8 --- /dev/null +++ b/chromium/components/viz/service/display_embedder/output_presenter.cc @@ -0,0 +1,115 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/viz/service/display_embedder/output_presenter.h" + +#include "components/viz/service/display_embedder/skia_output_surface_dependency.h" +#include "gpu/command_buffer/service/shared_context_state.h" +#include "third_party/skia/include/gpu/GrBackendSemaphore.h" +#include "third_party/skia/include/gpu/GrContext.h" + +namespace viz { + +OutputPresenter::Image::Image() = default; + +OutputPresenter::Image::~Image() { + // TODO(vasilyt): As we are going to delete image anyway we should be able + // to abort write to avoid unnecessary flush to submit semaphores. + if (scoped_skia_write_access_) { + EndWriteSkia(); + } + DCHECK(!scoped_skia_write_access_); +} + +bool OutputPresenter::Image::Initialize( + gpu::SharedImageFactory* factory, + gpu::SharedImageRepresentationFactory* representation_factory, + const gpu::Mailbox& mailbox, + SkiaOutputSurfaceDependency* deps) { + skia_representation_ = representation_factory->ProduceSkia( + mailbox, deps->GetSharedContextState()); + if (!skia_representation_) { + DLOG(ERROR) << "ProduceSkia() failed."; + return false; + } + + // Initialize |shared_image_deleter_| to make sure the shared image backing + // will be released with the Image. + shared_image_deleter_.ReplaceClosure(base::BindOnce( + base::IgnoreResult(&gpu::SharedImageFactory::DestroySharedImage), + base::Unretained(factory), mailbox)); + + return true; +} + +void OutputPresenter::Image::BeginWriteSkia() { + DCHECK(!scoped_skia_write_access_); + DCHECK(!present_count()); + DCHECK(end_semaphores_.empty()); + + std::vector<GrBackendSemaphore> begin_semaphores; + // LegacyFontHost will get LCD text and skia figures out what type to use. + SkSurfaceProps surface_props(0 /* flags */, + SkSurfaceProps::kLegacyFontHost_InitType); + + // Buffer queue is internal to GPU proc and handles texture initialization, + // so allow uncleared access. + // TODO(vasilyt): Props and MSAA + scoped_skia_write_access_ = skia_representation_->BeginScopedWriteAccess( + 0 /* final_msaa_count */, surface_props, &begin_semaphores, + &end_semaphores_, + gpu::SharedImageRepresentation::AllowUnclearedAccess::kYes); + DCHECK(scoped_skia_write_access_); + if (!begin_semaphores.empty()) { + scoped_skia_write_access_->surface()->wait(begin_semaphores.size(), + begin_semaphores.data()); + } +} + +SkSurface* OutputPresenter::Image::sk_surface() { + return scoped_skia_write_access_ ? scoped_skia_write_access_->surface() + : nullptr; +} + +std::vector<GrBackendSemaphore> +OutputPresenter::Image::TakeEndWriteSkiaSemaphores() { + std::vector<GrBackendSemaphore> result; + result.swap(end_semaphores_); + return result; +} + +void OutputPresenter::Image::EndWriteSkia() { + // The Flush now takes place in finishPaintCurrentBuffer on the CPU side. + // check if end_semaphores is not empty then flash here + DCHECK(scoped_skia_write_access_); + if (!end_semaphores_.empty()) { + GrFlushInfo flush_info = { + .fFlags = kNone_GrFlushFlags, + .fNumSemaphores = end_semaphores_.size(), + .fSignalSemaphores = end_semaphores_.data(), + }; + scoped_skia_write_access_->surface()->flush( + SkSurface::BackendSurfaceAccess::kNoAccess, flush_info); + DCHECK(scoped_skia_write_access_->surface()->getContext()); + scoped_skia_write_access_->surface()->getContext()->submit(); + } + scoped_skia_write_access_.reset(); + end_semaphores_.clear(); + + // SkiaRenderer always draws the full frame. + skia_representation_->SetCleared(); +} + +OutputPresenter::OverlayData::OverlayData( + std::unique_ptr<gpu::SharedImageRepresentationOverlay> representation, + std::unique_ptr<gpu::SharedImageRepresentationOverlay::ScopedReadAccess> + scoped_read_access) + : representation_(std::move(representation)), + scoped_read_access_(std::move(scoped_read_access)) {} +OutputPresenter::OverlayData::OverlayData(OverlayData&&) = default; +OutputPresenter::OverlayData::~OverlayData() = default; +OutputPresenter::OverlayData& OutputPresenter::OverlayData::operator=( + OverlayData&&) = default; + +} // namespace viz diff --git a/chromium/components/viz/service/display_embedder/output_presenter.h b/chromium/components/viz/service/display_embedder/output_presenter.h new file mode 100644 index 00000000000..70a30fb3fbe --- /dev/null +++ b/chromium/components/viz/service/display_embedder/output_presenter.h @@ -0,0 +1,117 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_PRESENTER_H_ +#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_PRESENTER_H_ + +#include <memory> +#include <vector> + +#include "components/viz/service/display/output_surface.h" +#include "components/viz/service/display/overlay_processor_interface.h" +#include "components/viz/service/display/skia_output_surface.h" +#include "components/viz/service/viz_service_export.h" +#include "gpu/command_buffer/service/shared_image_factory.h" +#include "ui/gfx/presentation_feedback.h" +#include "ui/gfx/swap_result.h" + +namespace viz { + +class SkiaOutputSurfaceDependency; + +class VIZ_SERVICE_EXPORT OutputPresenter { + public: + class Image { + public: + Image(); + virtual ~Image(); + + Image(const Image&) = delete; + Image& operator=(const Image&) = delete; + + bool Initialize( + gpu::SharedImageFactory* factory, + gpu::SharedImageRepresentationFactory* representation_factory, + const gpu::Mailbox& mailbox, + SkiaOutputSurfaceDependency* deps); + + gpu::SharedImageRepresentationSkia* skia_representation() { + return skia_representation_.get(); + } + + void BeginWriteSkia(); + SkSurface* sk_surface(); + std::vector<GrBackendSemaphore> TakeEndWriteSkiaSemaphores(); + void EndWriteSkia(); + + virtual void BeginPresent() = 0; + virtual void EndPresent() = 0; + virtual int present_count() const = 0; + + base::WeakPtr<Image> GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } + + private: + base::ScopedClosureRunner shared_image_deleter_; + std::unique_ptr<gpu::SharedImageRepresentationSkia> skia_representation_; + std::unique_ptr<gpu::SharedImageRepresentationSkia::ScopedWriteAccess> + scoped_skia_write_access_; + + std::vector<GrBackendSemaphore> end_semaphores_; + base::WeakPtrFactory<Image> weak_ptr_factory_{this}; + }; + + class OverlayData { + public: + OverlayData( + std::unique_ptr<gpu::SharedImageRepresentationOverlay> representation, + std::unique_ptr<gpu::SharedImageRepresentationOverlay::ScopedReadAccess> + scoped_read_access); + OverlayData(OverlayData&&); + ~OverlayData(); + OverlayData& operator=(OverlayData&&); + + private: + std::unique_ptr<gpu::SharedImageRepresentationOverlay> representation_; + std::unique_ptr<gpu::SharedImageRepresentationOverlay::ScopedReadAccess> + scoped_read_access_; + }; + + OutputPresenter() = default; + virtual ~OutputPresenter() = default; + + using BufferPresentedCallback = + base::OnceCallback<void(const gfx::PresentationFeedback& feedback)>; + using SwapCompletionCallback = + base::OnceCallback<void(gfx::SwapCompletionResult)>; + + virtual void InitializeCapabilities( + OutputSurface::Capabilities* capabilities) = 0; + virtual bool Reshape(const gfx::Size& size, + float device_scale_factor, + const gfx::ColorSpace& color_space, + gfx::BufferFormat format, + gfx::OverlayTransform transform) = 0; + virtual std::vector<std::unique_ptr<Image>> AllocateImages( + gfx::ColorSpace color_space, + gfx::Size image_size, + size_t num_images) = 0; + virtual void SwapBuffers(SwapCompletionCallback completion_callback, + BufferPresentedCallback presentation_callback) = 0; + virtual void PostSubBuffer(const gfx::Rect& rect, + SwapCompletionCallback completion_callback, + BufferPresentedCallback presentation_callback) = 0; + virtual void CommitOverlayPlanes( + SwapCompletionCallback completion_callback, + BufferPresentedCallback presentation_callback) = 0; + virtual void SchedulePrimaryPlane( + const OverlayProcessorInterface::OutputSurfaceOverlayPlane& plane, + Image* image, + bool is_submitted) = 0; + virtual std::vector<OverlayData> ScheduleOverlays( + SkiaOutputSurface::OverlayList overlays) = 0; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_PRESENTER_H_ diff --git a/chromium/components/viz/service/display_embedder/output_presenter_fuchsia.cc b/chromium/components/viz/service/display_embedder/output_presenter_fuchsia.cc new file mode 100644 index 00000000000..50a65e98c66 --- /dev/null +++ b/chromium/components/viz/service/display_embedder/output_presenter_fuchsia.cc @@ -0,0 +1,490 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/viz/service/display_embedder/output_presenter_fuchsia.h" + +#include <fuchsia/sysmem/cpp/fidl.h> +#include <lib/sys/cpp/component_context.h> +#include <lib/sys/inspect/cpp/component.h> + +#include <memory> +#include <utility> +#include <vector> + +#include "base/feature_list.h" +#include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/process_context.h" +#include "base/trace_event/trace_event.h" +#include "components/viz/common/features.h" +#include "components/viz/common/gpu/vulkan_context_provider.h" +#include "components/viz/service/display_embedder/skia_output_surface_dependency.h" +#include "gpu/command_buffer/service/shared_context_state.h" +#include "gpu/ipc/common/gpu_client_ids.h" +#include "gpu/vulkan/vulkan_device_queue.h" +#include "gpu/vulkan/vulkan_function_pointers.h" +#include "gpu/vulkan/vulkan_implementation.h" +#include "third_party/skia/include/gpu/GrBackendSemaphore.h" +#include "ui/ozone/public/platform_window_surface.h" + +namespace viz { + +namespace { + +void GrSemaphoresToZxEvents(gpu::VulkanImplementation* vulkan_implementation, + VkDevice vk_device, + const std::vector<GrBackendSemaphore>& semaphores, + std::vector<zx::event>* events) { + for (auto& semaphore : semaphores) { + gpu::SemaphoreHandle handle = vulkan_implementation->GetSemaphoreHandle( + vk_device, semaphore.vkSemaphore()); + DCHECK(handle.is_valid()); + events->push_back(handle.TakeHandle()); + } +} + +class PresenterImageFuchsia : public OutputPresenter::Image { + public: + explicit PresenterImageFuchsia(uint32_t image_id); + ~PresenterImageFuchsia() override; + + void BeginPresent() final; + void EndPresent() final; + int present_count() const final; + + uint32_t image_id() const { return image_id_; } + + void TakeSemaphores(std::vector<GrBackendSemaphore>* read_begin_semaphores, + std::vector<GrBackendSemaphore>* read_end_semaphores); + + private: + const uint32_t image_id_; + + int present_count_ = 0; + + std::unique_ptr<gpu::SharedImageRepresentationSkia::ScopedReadAccess> + read_access_; + + std::vector<GrBackendSemaphore> read_begin_semaphores_; + std::vector<GrBackendSemaphore> read_end_semaphores_; +}; + +PresenterImageFuchsia::PresenterImageFuchsia(uint32_t image_id) + : image_id_(image_id) {} + +PresenterImageFuchsia::~PresenterImageFuchsia() { + DCHECK(read_begin_semaphores_.empty()); + DCHECK(read_end_semaphores_.empty()); +} + +void PresenterImageFuchsia::BeginPresent() { + ++present_count_; + + if (present_count_ == 1) { + DCHECK(!read_access_); + DCHECK(read_begin_semaphores_.empty()); + DCHECK(read_end_semaphores_.empty()); + read_access_ = skia_representation()->BeginScopedReadAccess( + &read_begin_semaphores_, &read_end_semaphores_); + } +} + +void PresenterImageFuchsia::EndPresent() { + DCHECK(present_count_); + --present_count_; + if (!present_count_) + read_access_.reset(); +} + +int PresenterImageFuchsia::present_count() const { + return present_count_; +} + +void PresenterImageFuchsia::TakeSemaphores( + std::vector<GrBackendSemaphore>* read_begin_semaphores, + std::vector<GrBackendSemaphore>* read_end_semaphores) { + DCHECK(read_begin_semaphores->empty()); + std::swap(*read_begin_semaphores, read_begin_semaphores_); + + DCHECK(read_end_semaphores->empty()); + std::swap(*read_end_semaphores, read_end_semaphores_); +} + +} // namespace + +OutputPresenterFuchsia::PendingFrame::PendingFrame() = default; +OutputPresenterFuchsia::PendingFrame::~PendingFrame() = default; + +OutputPresenterFuchsia::PendingFrame::PendingFrame(PendingFrame&&) = default; +OutputPresenterFuchsia::PendingFrame& +OutputPresenterFuchsia::PendingFrame::operator=(PendingFrame&&) = default; + +// static +std::unique_ptr<OutputPresenterFuchsia> OutputPresenterFuchsia::Create( + ui::PlatformWindowSurface* window_surface, + SkiaOutputSurfaceDependency* deps, + gpu::MemoryTracker* memory_tracker) { + auto* inspector = base::ComponentInspectorForProcess(); + + if (!base::FeatureList::IsEnabled( + features::kUseSkiaOutputDeviceBufferQueue)) { + inspector->root().CreateString("output_presenter", "swapchain", inspector); + return {}; + } + + inspector->root().CreateString("output_presenter", + "SkiaOutputDeviceBufferQueue", inspector); + + // SetTextureToNewImagePipe() will call ScenicSession::Present() to send + // CreateImagePipe2Cmd creation command, but it will be processed only after + // vsync, which will delay buffer allocation of buffers in AllocateImages(), + // but that shouldn't cause any issues. + fuchsia::images::ImagePipe2Ptr image_pipe; + if (!window_surface->SetTextureToNewImagePipe(image_pipe.NewRequest())) + return {}; + + return std::make_unique<OutputPresenterFuchsia>(std::move(image_pipe), deps, + memory_tracker); +} + +OutputPresenterFuchsia::OutputPresenterFuchsia( + fuchsia::images::ImagePipe2Ptr image_pipe, + SkiaOutputSurfaceDependency* deps, + gpu::MemoryTracker* memory_tracker) + : image_pipe_(std::move(image_pipe)), + dependency_(deps), + shared_image_factory_(deps->GetGpuPreferences(), + deps->GetGpuDriverBugWorkarounds(), + deps->GetGpuFeatureInfo(), + deps->GetSharedContextState().get(), + deps->GetMailboxManager(), + deps->GetSharedImageManager(), + deps->GetGpuImageFactory(), + memory_tracker, + true), + shared_image_representation_factory_(deps->GetSharedImageManager(), + memory_tracker) { + sysmem_allocator_ = base::ComponentContextForProcess() + ->svc() + ->Connect<fuchsia::sysmem::Allocator>(); + + image_pipe_.set_error_handler([this](zx_status_t status) { + ZX_LOG(ERROR, status) << "ImagePipe disconnected"; + + for (auto& frame : pending_frames_) { + std::move(frame.completion_callback) + .Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_FAILED)); + } + pending_frames_.clear(); + }); +} + +OutputPresenterFuchsia::~OutputPresenterFuchsia() {} + +void OutputPresenterFuchsia::InitializeCapabilities( + OutputSurface::Capabilities* capabilities) { + // We expect origin of buffers is at top left. + capabilities->output_surface_origin = gfx::SurfaceOrigin::kTopLeft; + capabilities->supports_post_sub_buffer = false; + capabilities->supports_commit_overlay_planes = false; + + capabilities->sk_color_type = kRGBA_8888_SkColorType; + capabilities->gr_backend_format = + dependency_->GetSharedContextState()->gr_context()->defaultBackendFormat( + capabilities->sk_color_type, GrRenderable::kYes); +} + +bool OutputPresenterFuchsia::Reshape(const gfx::Size& size, + float device_scale_factor, + const gfx::ColorSpace& color_space, + gfx::BufferFormat format, + gfx::OverlayTransform transform) { + if (!image_pipe_) + return false; + + frame_size_ = size; + + return true; +} + +std::vector<std::unique_ptr<OutputPresenter::Image>> +OutputPresenterFuchsia::AllocateImages(gfx::ColorSpace color_space, + gfx::Size image_size, + size_t num_images) { + if (!image_pipe_) + return {}; + + // If we already allocated buffer collection then it needs to be released. + if (last_buffer_collection_id_) { + // If there are pending frames for the old buffer collection then remove the + // collection only after that frame is presented. Otherwise remove it now. + if (!pending_frames_.empty() && + pending_frames_.back().buffer_collection_id == + last_buffer_collection_id_) { + DCHECK(!pending_frames_.back().remove_buffer_collection); + pending_frames_.back().remove_buffer_collection = true; + } else { + image_pipe_->RemoveBufferCollection(last_buffer_collection_id_); + } + } + + buffer_collection_.reset(); + + // Create buffer collection with 2 extra tokens: one for Vulkan and one for + // the ImagePipe. + fuchsia::sysmem::BufferCollectionTokenPtr collection_token; + sysmem_allocator_->AllocateSharedCollection(collection_token.NewRequest()); + + fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> + token_for_scenic; + collection_token->Duplicate(ZX_RIGHT_SAME_RIGHTS, + token_for_scenic.NewRequest()); + + fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> + token_for_vulkan; + collection_token->Duplicate(ZX_RIGHT_SAME_RIGHTS, + token_for_vulkan.NewRequest()); + + fuchsia::sysmem::BufferCollectionSyncPtr collection; + sysmem_allocator_->BindSharedCollection(std::move(collection_token), + collection.NewRequest()); + + zx_status_t status = collection->Sync(); + if (status != ZX_OK) { + ZX_DLOG(ERROR, status) << "fuchsia.sysmem.BufferCollection.Sync()"; + return {}; + } + + auto* vulkan = + dependency_->GetVulkanContextProvider()->GetVulkanImplementation(); + + // Set constraints for the new collection. + fuchsia::sysmem::BufferCollectionConstraints constraints; + constraints.min_buffer_count = num_images; + constraints.usage.none = fuchsia::sysmem::noneUsage; + constraints.image_format_constraints_count = 1; + constraints.image_format_constraints[0].pixel_format.type = + fuchsia::sysmem::PixelFormatType::R8G8B8A8; + constraints.image_format_constraints[0].min_coded_width = frame_size_.width(); + constraints.image_format_constraints[0].min_coded_height = + frame_size_.height(); + constraints.image_format_constraints[0].color_spaces_count = 1; + constraints.image_format_constraints[0].color_space[0].type = + fuchsia::sysmem::ColorSpaceType::SRGB; + collection->SetConstraints(true, constraints); + + // Register the new buffer collection with the ImagePipe. + last_buffer_collection_id_++; + image_pipe_->AddBufferCollection(last_buffer_collection_id_, + std::move(token_for_scenic)); + + // Register the new buffer collection with Vulkan. + gfx::SysmemBufferCollectionId buffer_collection_id = + gfx::SysmemBufferCollectionId::Create(); + + VkDevice vk_device = dependency_->GetVulkanContextProvider() + ->GetDeviceQueue() + ->GetVulkanDevice(); + buffer_collection_ = vulkan->RegisterSysmemBufferCollection( + vk_device, buffer_collection_id, token_for_vulkan.TakeChannel(), + buffer_format_, gfx::BufferUsage::SCANOUT); + + // Wait for the images to be allocated. + zx_status_t wait_status; + fuchsia::sysmem::BufferCollectionInfo_2 buffers_info; + status = collection->WaitForBuffersAllocated(&wait_status, &buffers_info); + if (status != ZX_OK) { + ZX_DLOG(ERROR, status) << "fuchsia.sysmem.BufferCollection failed"; + return {}; + } + + if (wait_status != ZX_OK) { + ZX_DLOG(ERROR, wait_status) + << "Sysmem buffer collection allocation failed."; + return {}; + } + + DCHECK_GE(buffers_info.buffer_count, num_images); + + // We no longer need the BufferCollection connection. Close it to ensure + // ImagePipe can still use the collection after BufferCollection connection + // is dropped below. + collection->Close(); + + // Create PresenterImageFuchsia for each buffer in the collection. + uint32_t image_usage = gpu::SHARED_IMAGE_USAGE_RASTER; + if (vulkan->enforce_protected_memory()) + image_usage |= gpu::SHARED_IMAGE_USAGE_PROTECTED; + + std::vector<std::unique_ptr<OutputPresenter::Image>> images; + images.reserve(num_images); + + fuchsia::sysmem::ImageFormat_2 image_format; + image_format.coded_width = frame_size_.width(); + image_format.coded_height = frame_size_.height(); + + // Create an image for each buffer in the collection. + for (size_t i = 0; i < num_images; ++i) { + last_image_id_++; + image_pipe_->AddImage(last_image_id_, last_buffer_collection_id_, i, + image_format); + + gfx::GpuMemoryBufferHandle gmb_handle; + gmb_handle.type = gfx::GpuMemoryBufferType::NATIVE_PIXMAP; + gmb_handle.native_pixmap_handle.buffer_collection_id = buffer_collection_id; + gmb_handle.native_pixmap_handle.buffer_index = i; + + auto mailbox = gpu::Mailbox::GenerateForSharedImage(); + if (!shared_image_factory_.CreateSharedImage( + mailbox, gpu::kInProcessCommandBufferClientId, + std::move(gmb_handle), buffer_format_, gpu::kNullSurfaceHandle, + frame_size_, color_space, image_usage)) { + return {}; + } + + auto image = std::make_unique<PresenterImageFuchsia>(last_image_id_); + if (!image->Initialize(&shared_image_factory_, + &shared_image_representation_factory_, mailbox, + dependency_)) { + return {}; + } + images.push_back(std::move(image)); + } + + return images; +} + +void OutputPresenterFuchsia::SwapBuffers( + SwapCompletionCallback completion_callback, + BufferPresentedCallback presentation_callback) { + if (!image_pipe_) { + std::move(completion_callback) + .Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_FAILED)); + return; + } + + // SwapBuffer() should be called only after SchedulePrimaryPlane(). + DCHECK(next_frame_); + + TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( + "viz", "OutputPresenterFuchsia::PresentQueue", TRACE_ID_LOCAL(this), + "image_id", next_frame_->image_id); + + next_frame_->completion_callback = std::move(completion_callback); + next_frame_->presentation_callback = std::move(presentation_callback); + + pending_frames_.push_back(std::move(next_frame_.value())); + next_frame_.reset(); + + if (!present_is_pending_) + PresentNextFrame(); +} + +void OutputPresenterFuchsia::PostSubBuffer( + const gfx::Rect& rect, + SwapCompletionCallback completion_callback, + BufferPresentedCallback presentation_callback) { + // Sub buffer presentation is not supported. + NOTREACHED(); +} + +void OutputPresenterFuchsia::CommitOverlayPlanes( + SwapCompletionCallback completion_callback, + BufferPresentedCallback presentation_callback) { + // Overlays are not supported yet. + NOTREACHED(); +} + +void OutputPresenterFuchsia::SchedulePrimaryPlane( + const OverlayProcessorInterface::OutputSurfaceOverlayPlane& plane, + Image* image, + bool is_submitted) { + auto* image_fuchsia = static_cast<PresenterImageFuchsia*>(image); + + DCHECK(!next_frame_); + next_frame_ = PendingFrame(); + next_frame_->image_id = image_fuchsia->image_id(); + next_frame_->buffer_collection_id = last_buffer_collection_id_; + + // Take semaphores for the image and covert them to zx::events that are later + // passed to ImagePipe::PresentImage(). + std::vector<GrBackendSemaphore> read_begin_semaphores; + std::vector<GrBackendSemaphore> read_end_semaphores; + image_fuchsia->TakeSemaphores(&read_begin_semaphores, &read_end_semaphores); + + auto* vulkan_context_provider = dependency_->GetVulkanContextProvider(); + auto* vulkan_implementation = + vulkan_context_provider->GetVulkanImplementation(); + VkDevice vk_device = + vulkan_context_provider->GetDeviceQueue()->GetVulkanDevice(); + + GrSemaphoresToZxEvents(vulkan_implementation, vk_device, + read_begin_semaphores, &(next_frame_->acquire_fences)); + GrSemaphoresToZxEvents(vulkan_implementation, vk_device, read_end_semaphores, + &(next_frame_->release_fences)); + + // Destroy |read_begin_semaphores|, but not |read_end_semaphores|, since + // SharedImageRepresentationSkia::BeginScopedReadAccess() keeps ownership of + // the end_semaphores. + for (auto& semaphore : read_begin_semaphores) { + vkDestroySemaphore(vk_device, semaphore.vkSemaphore(), nullptr); + } +} + +std::vector<OutputPresenter::OverlayData> +OutputPresenterFuchsia::ScheduleOverlays( + SkiaOutputSurface::OverlayList overlays) { + // Overlays are not supported yet. + NOTREACHED(); + return {}; +} + +void OutputPresenterFuchsia::PresentNextFrame() { + DCHECK(!present_is_pending_); + DCHECK(!pending_frames_.empty()); + + TRACE_EVENT_NESTABLE_ASYNC_END1("viz", "OutputPresenterFuchsia::PresentQueue", + TRACE_ID_LOCAL(this), "image_id", + pending_frames_.front().image_id); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( + "viz", "OutputPresenterFuchsia::PresentFrame", TRACE_ID_LOCAL(this), + "image_id", pending_frames_.front().image_id); + + present_is_pending_ = true; + uint64_t target_presentation_time = zx_clock_get_monotonic(); + image_pipe_->PresentImage( + pending_frames_.front().image_id, target_presentation_time, + std::move(pending_frames_.front().acquire_fences), + std::move(pending_frames_.front().release_fences), + fit::bind_member(this, &OutputPresenterFuchsia::OnPresentComplete)); +} + +void OutputPresenterFuchsia::OnPresentComplete( + fuchsia::images::PresentationInfo presentation_info) { + DCHECK(present_is_pending_); + present_is_pending_ = false; + + TRACE_EVENT_NESTABLE_ASYNC_END1("viz", "OutputPresenterFuchsia::PresentFrame", + TRACE_ID_LOCAL(this), "image_id", + pending_frames_.front().image_id); + + std::move(pending_frames_.front().completion_callback) + .Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK)); + std::move(pending_frames_.front().presentation_callback) + .Run(gfx::PresentationFeedback( + base::TimeTicks::FromZxTime(presentation_info.presentation_time), + base::TimeDelta::FromZxDuration( + presentation_info.presentation_interval), + gfx::PresentationFeedback::kVSync)); + + if (pending_frames_.front().remove_buffer_collection) { + image_pipe_->RemoveBufferCollection( + pending_frames_.front().buffer_collection_id); + } + + pending_frames_.pop_front(); + if (!pending_frames_.empty()) + PresentNextFrame(); +} + +} // namespace viz diff --git a/chromium/components/viz/service/display_embedder/output_presenter_fuchsia.h b/chromium/components/viz/service/display_embedder/output_presenter_fuchsia.h new file mode 100644 index 00000000000..bfee93dae49 --- /dev/null +++ b/chromium/components/viz/service/display_embedder/output_presenter_fuchsia.h @@ -0,0 +1,115 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_PRESENTER_FUCHSIA_H_ +#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_PRESENTER_FUCHSIA_H_ + +#include <fuchsia/images/cpp/fidl.h> + +#include <memory> +#include <vector> + +#include "base/containers/circular_deque.h" +#include "components/viz/service/display_embedder/output_presenter.h" +#include "components/viz/service/viz_service_export.h" +#include "gpu/command_buffer/common/shared_image_usage.h" +#include "gpu/command_buffer/service/shared_image_factory.h" + +namespace ui { +class PlatformWindowSurface; +} // namespace ui + +namespace viz { + +class VIZ_SERVICE_EXPORT OutputPresenterFuchsia : public OutputPresenter { + public: + static std::unique_ptr<OutputPresenterFuchsia> Create( + ui::PlatformWindowSurface* window_surface, + SkiaOutputSurfaceDependency* deps, + gpu::MemoryTracker* memory_tracker); + + OutputPresenterFuchsia(fuchsia::images::ImagePipe2Ptr image_pipe, + SkiaOutputSurfaceDependency* deps, + gpu::MemoryTracker* memory_tracker); + ~OutputPresenterFuchsia() override; + + // OutputPresenter implementation: + void InitializeCapabilities(OutputSurface::Capabilities* capabilities) final; + bool Reshape(const gfx::Size& size, + float device_scale_factor, + const gfx::ColorSpace& color_space, + gfx::BufferFormat format, + gfx::OverlayTransform transform) final; + std::vector<std::unique_ptr<Image>> AllocateImages( + gfx::ColorSpace color_space, + gfx::Size image_size, + size_t num_images) final; + void SwapBuffers(SwapCompletionCallback completion_callback, + BufferPresentedCallback presentation_callback) final; + void PostSubBuffer(const gfx::Rect& rect, + SwapCompletionCallback completion_callback, + BufferPresentedCallback presentation_callback) final; + void CommitOverlayPlanes(SwapCompletionCallback completion_callback, + BufferPresentedCallback presentation_callback) final; + void SchedulePrimaryPlane( + const OverlayProcessorInterface::OutputSurfaceOverlayPlane& plane, + Image* image, + bool is_submitted) final; + std::vector<OverlayData> ScheduleOverlays( + SkiaOutputSurface::OverlayList overlays) final; + + private: + struct PendingFrame { + PendingFrame(); + ~PendingFrame(); + + PendingFrame(PendingFrame&&); + PendingFrame& operator=(PendingFrame&&); + + uint32_t buffer_collection_id; + uint32_t image_id; + + std::vector<zx::event> acquire_fences; + std::vector<zx::event> release_fences; + + SwapCompletionCallback completion_callback; + BufferPresentedCallback presentation_callback; + + // Indicates that this is the last frame for this buffer collection and that + // the collection can be removed after the frame is presented. + bool remove_buffer_collection = false; + }; + + void PresentNextFrame(); + void OnPresentComplete(fuchsia::images::PresentationInfo presentation_info); + + fuchsia::sysmem::AllocatorPtr sysmem_allocator_; + fuchsia::images::ImagePipe2Ptr image_pipe_; + SkiaOutputSurfaceDependency* const dependency_; + gpu::SharedImageFactory shared_image_factory_; + gpu::SharedImageRepresentationFactory shared_image_representation_factory_; + + gfx::Size frame_size_; + gfx::BufferFormat buffer_format_ = gfx::BufferFormat::RGBA_8888; + + // Last buffer collection ID for the ImagePipe. Incremented every time buffers + // are reallocated. + uint32_t last_buffer_collection_id_ = 0; + + // Counter to generate image IDs for the ImagePipe. + uint32_t last_image_id_ = 0; + + std::unique_ptr<gpu::SysmemBufferCollection> buffer_collection_; + + // The next frame to be submitted by SwapBuffers(). + base::Optional<PendingFrame> next_frame_; + + base::circular_deque<PendingFrame> pending_frames_; + + bool present_is_pending_ = false; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_PRESENTER_FUCHSIA_H_ diff --git a/chromium/components/viz/service/display_embedder/output_presenter_gl.cc b/chromium/components/viz/service/display_embedder/output_presenter_gl.cc new file mode 100644 index 00000000000..289ab589939 --- /dev/null +++ b/chromium/components/viz/service/display_embedder/output_presenter_gl.cc @@ -0,0 +1,408 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/viz/service/display_embedder/output_presenter_gl.h" + +#include <memory> +#include <utility> +#include <vector> + +#include "base/threading/thread_task_runner_handle.h" +#include "build/build_config.h" +#include "components/viz/common/resources/resource_format_utils.h" +#include "components/viz/service/display_embedder/skia_output_surface_dependency.h" +#include "gpu/command_buffer/service/shared_context_state.h" +#include "gpu/ipc/common/gpu_surface_lookup.h" +#include "ui/display/types/display_snapshot.h" +#include "ui/gfx/buffer_format_util.h" +#include "ui/gfx/geometry/rect_conversions.h" +#include "ui/gl/gl_fence.h" +#include "ui/gl/gl_surface.h" + +#if defined(OS_ANDROID) +#include "ui/gl/gl_surface_egl_surface_control.h" +#endif + +#if defined(USE_OZONE) +#include "ui/base/ui_base_features.h" +#endif + +namespace viz { + +namespace { + +class PresenterImageGL : public OutputPresenter::Image { + public: + PresenterImageGL() = default; + ~PresenterImageGL() override = default; + + bool Initialize(gpu::SharedImageFactory* factory, + gpu::SharedImageRepresentationFactory* representation_factory, + const gfx::Size& size, + const gfx::ColorSpace& color_space, + ResourceFormat format, + SkiaOutputSurfaceDependency* deps, + uint32_t shared_image_usage); + + void BeginPresent() final; + void EndPresent() final; + int present_count() const final; + + gl::GLImage* GetGLImage(std::unique_ptr<gfx::GpuFence>* fence); + + private: + std::unique_ptr<gpu::SharedImageRepresentationOverlay> + overlay_representation_; + std::unique_ptr<gpu::SharedImageRepresentationGLTexture> gl_representation_; + std::unique_ptr<gpu::SharedImageRepresentationOverlay::ScopedReadAccess> + scoped_overlay_read_access_; + std::unique_ptr<gpu::SharedImageRepresentationGLTexture::ScopedAccess> + scoped_gl_read_access_; + + int present_count_ = 0; +}; + +bool PresenterImageGL::Initialize( + gpu::SharedImageFactory* factory, + gpu::SharedImageRepresentationFactory* representation_factory, + const gfx::Size& size, + const gfx::ColorSpace& color_space, + ResourceFormat format, + SkiaOutputSurfaceDependency* deps, + uint32_t shared_image_usage) { + auto mailbox = gpu::Mailbox::GenerateForSharedImage(); + + if (!factory->CreateSharedImage(mailbox, format, size, color_space, + deps->GetSurfaceHandle(), + shared_image_usage)) { + DLOG(ERROR) << "CreateSharedImage failed."; + return false; + } + + if (!Image::Initialize(factory, representation_factory, mailbox, deps)) + return false; + + overlay_representation_ = representation_factory->ProduceOverlay(mailbox); + + // If the backing doesn't support overlay, then fallback to GL. + if (!overlay_representation_) + gl_representation_ = representation_factory->ProduceGLTexture(mailbox); + + if (!overlay_representation_ && !gl_representation_) { + DLOG(ERROR) << "ProduceOverlay() and ProduceGLTexture() failed."; + return false; + } + + return true; +} + +void PresenterImageGL::BeginPresent() { + if (++present_count_ != 1) { + DCHECK(scoped_overlay_read_access_ || scoped_gl_read_access_); + return; + } + + DCHECK(!sk_surface()); + DCHECK(!scoped_overlay_read_access_); + + if (overlay_representation_) { + scoped_overlay_read_access_ = + overlay_representation_->BeginScopedReadAccess( + true /* need_gl_image */); + DCHECK(scoped_overlay_read_access_); + return; + } + + scoped_gl_read_access_ = gl_representation_->BeginScopedAccess( + GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM, + gpu::SharedImageRepresentation::AllowUnclearedAccess::kNo); + DCHECK(scoped_gl_read_access_); +} + +void PresenterImageGL::EndPresent() { + DCHECK(present_count_); + if (--present_count_) + return; + scoped_overlay_read_access_.reset(); + scoped_gl_read_access_.reset(); +} + +int PresenterImageGL::present_count() const { + return present_count_; +} + +gl::GLImage* PresenterImageGL::GetGLImage( + std::unique_ptr<gfx::GpuFence>* fence) { + if (scoped_overlay_read_access_) + return scoped_overlay_read_access_->gl_image(); + + DCHECK(scoped_gl_read_access_); + + if (gl::GLFence::IsGpuFenceSupported() && fence) { + if (auto gl_fence = gl::GLFence::CreateForGpuFence()) + *fence = gl_fence->GetGpuFence(); + } + auto* texture = gl_representation_->GetTexture(); + return texture->GetLevelImage(texture->target(), 0); +} + +} // namespace + +// static +const uint32_t OutputPresenterGL::kDefaultSharedImageUsage = + gpu::SHARED_IMAGE_USAGE_SCANOUT | gpu::SHARED_IMAGE_USAGE_DISPLAY | + gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT; + +// static +std::unique_ptr<OutputPresenterGL> OutputPresenterGL::Create( + SkiaOutputSurfaceDependency* deps, + gpu::MemoryTracker* memory_tracker) { +#if defined(OS_ANDROID) + if (deps->GetGpuFeatureInfo() + .status_values[gpu::GPU_FEATURE_TYPE_ANDROID_SURFACE_CONTROL] != + gpu::kGpuFeatureStatusEnabled) { + return nullptr; + } + + bool can_be_used_with_surface_control = false; + ANativeWindow* window = + gpu::GpuSurfaceLookup::GetInstance()->AcquireNativeWidget( + deps->GetSurfaceHandle(), &can_be_used_with_surface_control); + if (!window || !can_be_used_with_surface_control) + return nullptr; + // TODO(https://crbug.com/1012401): don't depend on GL. + auto gl_surface = base::MakeRefCounted<gl::GLSurfaceEGLSurfaceControl>( + window, base::ThreadTaskRunnerHandle::Get()); + if (!gl_surface->Initialize(gl::GLSurfaceFormat())) { + LOG(ERROR) << "Failed to initialize GLSurfaceEGLSurfaceControl."; + return nullptr; + } + + if (!deps->GetSharedContextState()->MakeCurrent(gl_surface.get(), + true /* needs_gl*/)) { + LOG(ERROR) << "MakeCurrent failed."; + return nullptr; + } + + return std::make_unique<OutputPresenterGL>( + std::move(gl_surface), deps, memory_tracker, kDefaultSharedImageUsage); +#else + return nullptr; +#endif +} + +OutputPresenterGL::OutputPresenterGL(scoped_refptr<gl::GLSurface> gl_surface, + SkiaOutputSurfaceDependency* deps, + gpu::MemoryTracker* memory_tracker, + uint32_t shared_image_usage) + : gl_surface_(gl_surface), + dependency_(deps), + supports_async_swap_(gl_surface_->SupportsAsyncSwap()), + shared_image_factory_(deps->GetGpuPreferences(), + deps->GetGpuDriverBugWorkarounds(), + deps->GetGpuFeatureInfo(), + deps->GetSharedContextState().get(), + deps->GetMailboxManager(), + deps->GetSharedImageManager(), + deps->GetGpuImageFactory(), + memory_tracker, + true), + shared_image_representation_factory_(deps->GetSharedImageManager(), + memory_tracker), + shared_image_usage_(shared_image_usage) { + // GL is origin is at bottom left normally, all Surfaceless implementations + // are flipped. + DCHECK_EQ(gl_surface_->GetOrigin(), gfx::SurfaceOrigin::kTopLeft); + + // TODO(https://crbug.com/958166): The initial |image_format_| should not be + // used, and the gfx::BufferFormat specified in Reshape should be used + // instead, because it may be updated to reflect changes in the content being + // displayed (e.g, HDR content appearing on-screen). +#if defined(OS_MACOSX) + image_format_ = BGRA_8888; +#else +#if defined(USE_OZONE) + if (features::IsUsingOzonePlatform()) { + image_format_ = + GetResourceFormat(display::DisplaySnapshot::PrimaryFormat()); + return; + } +#endif + image_format_ = RGBA_8888; +#endif +} + +OutputPresenterGL::~OutputPresenterGL() = default; + +void OutputPresenterGL::InitializeCapabilities( + OutputSurface::Capabilities* capabilities) { + capabilities->android_surface_control_feature_enabled = true; + capabilities->supports_post_sub_buffer = gl_surface_->SupportsPostSubBuffer(); + capabilities->supports_commit_overlay_planes = + gl_surface_->SupportsCommitOverlayPlanes(); + + // Set supports_surfaceless to enable overlays. + capabilities->supports_surfaceless = true; + // We expect origin of buffers is at top left. + capabilities->output_surface_origin = gfx::SurfaceOrigin::kTopLeft; + + // TODO(penghuang): Use defaultBackendFormat() in shared image implementation + // to make sure backend format is consistent. + capabilities->sk_color_type = ResourceFormatToClosestSkColorType( + true /* gpu_compositing */, image_format_); + capabilities->gr_backend_format = + dependency_->GetSharedContextState()->gr_context()->defaultBackendFormat( + capabilities->sk_color_type, GrRenderable::kYes); +} + +bool OutputPresenterGL::Reshape(const gfx::Size& size, + float device_scale_factor, + const gfx::ColorSpace& color_space, + gfx::BufferFormat format, + gfx::OverlayTransform transform) { + return gl_surface_->Resize(size, device_scale_factor, color_space, + gfx::AlphaBitsForBufferFormat(format)); +} + +std::vector<std::unique_ptr<OutputPresenter::Image>> +OutputPresenterGL::AllocateImages(gfx::ColorSpace color_space, + gfx::Size image_size, + size_t num_images) { + std::vector<std::unique_ptr<Image>> images; + for (size_t i = 0; i < num_images; ++i) { + auto image = std::make_unique<PresenterImageGL>(); + if (!image->Initialize(&shared_image_factory_, + &shared_image_representation_factory_, image_size, + color_space, image_format_, dependency_, + shared_image_usage_)) { + DLOG(ERROR) << "Failed to initialize image."; + return {}; + } + images.push_back(std::move(image)); + } + + return images; +} + +void OutputPresenterGL::SwapBuffers( + SwapCompletionCallback completion_callback, + BufferPresentedCallback presentation_callback) { + if (supports_async_swap_) { + gl_surface_->SwapBuffersAsync(std::move(completion_callback), + std::move(presentation_callback)); + } else { + auto result = gl_surface_->SwapBuffers(std::move(presentation_callback)); + std::move(completion_callback).Run(gfx::SwapCompletionResult(result)); + } +} + +void OutputPresenterGL::PostSubBuffer( + const gfx::Rect& rect, + SwapCompletionCallback completion_callback, + BufferPresentedCallback presentation_callback) { + if (supports_async_swap_) { + gl_surface_->PostSubBufferAsync( + rect.x(), rect.y(), rect.width(), rect.height(), + std::move(completion_callback), std::move(presentation_callback)); + } else { + auto result = gl_surface_->PostSubBuffer(rect.x(), rect.y(), rect.width(), + rect.height(), + std::move(presentation_callback)); + std::move(completion_callback).Run(gfx::SwapCompletionResult(result)); + } +} + +void OutputPresenterGL::SchedulePrimaryPlane( + const OverlayProcessorInterface::OutputSurfaceOverlayPlane& plane, + Image* image, + bool is_submitted) { + std::unique_ptr<gfx::GpuFence> fence; + // If the submitted_image() is being scheduled, we don't new a new fence. + auto* gl_image = reinterpret_cast<PresenterImageGL*>(image)->GetGLImage( + is_submitted ? nullptr : &fence); + + // Output surface is also z-order 0. + constexpr int kPlaneZOrder = 0; + // Output surface always uses the full texture. + constexpr gfx::RectF kUVRect(0.f, 0.f, 1.0f, 1.0f); + gl_surface_->ScheduleOverlayPlane(kPlaneZOrder, plane.transform, gl_image, + ToNearestRect(plane.display_rect), kUVRect, + plane.enable_blending, std::move(fence)); +} + +void OutputPresenterGL::CommitOverlayPlanes( + SwapCompletionCallback completion_callback, + BufferPresentedCallback presentation_callback) { + if (supports_async_swap_) { + gl_surface_->CommitOverlayPlanesAsync(std::move(completion_callback), + std::move(presentation_callback)); + } else { + auto result = + gl_surface_->CommitOverlayPlanes(std::move(presentation_callback)); + std::move(completion_callback).Run(gfx::SwapCompletionResult(result)); + } +} + +std::vector<OutputPresenter::OverlayData> OutputPresenterGL::ScheduleOverlays( + SkiaOutputSurface::OverlayList overlays) { + std::vector<OverlayData> pending_overlays; +#if defined(OS_ANDROID) || defined(OS_MACOSX) + // Note while reading through this for-loop that |overlay| has different + // types on different platforms. On Android and Ozone it is an + // OverlayCandidate, on Windows it is a DCLayerOverlay, and on macOS it is + // a CALayerOverlay. + for (auto& overlay : overlays) { + // Extract the shared image and GLImage for the overlay. Note that for + // solid color overlays, this will remain nullptr. + gl::GLImage* gl_image = nullptr; + if (overlay.mailbox.IsSharedImage()) { + auto shared_image = + shared_image_representation_factory_.ProduceOverlay(overlay.mailbox); + // When display is re-opened, the first few frames might not have video + // resource ready. Possible investigation crbug.com/1023971. + if (!shared_image) { + LOG(ERROR) << "Invalid mailbox."; + continue; + } + + auto shared_image_access = + shared_image->BeginScopedReadAccess(true /* needs_gl_image */); + if (!shared_image_access) { + LOG(ERROR) << "Could not access SharedImage for read."; + continue; + } + + gl_image = shared_image_access->gl_image(); + DLOG_IF(ERROR, !gl_image) << "Cannot get GLImage."; + + pending_overlays.emplace_back(std::move(shared_image), + std::move(shared_image_access)); + } + +#if defined(OS_ANDROID) + if (gl_image) { + DCHECK(!overlay.gpu_fence_id); + gl_surface_->ScheduleOverlayPlane( + overlay.plane_z_order, overlay.transform, gl_image, + ToNearestRect(overlay.display_rect), overlay.uv_rect, + !overlay.is_opaque, nullptr /* gpu_fence */); + } +#elif defined(OS_MACOSX) + gl_surface_->ScheduleCALayer(ui::CARendererLayerParams( + overlay.shared_state->is_clipped, + gfx::ToEnclosingRect(overlay.shared_state->clip_rect), + overlay.shared_state->rounded_corner_bounds, + overlay.shared_state->sorting_context_id, + gfx::Transform(overlay.shared_state->transform), gl_image, + overlay.contents_rect, gfx::ToEnclosingRect(overlay.bounds_rect), + overlay.background_color, overlay.edge_aa_mask, + overlay.shared_state->opacity, overlay.filter)); +#endif + } +#endif // defined(OS_ANDROID) || defined(OS_MACOSX) + + return pending_overlays; +} + +} // namespace viz diff --git a/chromium/components/viz/service/display_embedder/output_presenter_gl.h b/chromium/components/viz/service/display_embedder/output_presenter_gl.h new file mode 100644 index 00000000000..bfbf51996d1 --- /dev/null +++ b/chromium/components/viz/service/display_embedder/output_presenter_gl.h @@ -0,0 +1,78 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_PRESENTER_GL_H_ +#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_PRESENTER_GL_H_ + +#include <memory> +#include <vector> + +#include "components/viz/service/display_embedder/output_presenter.h" +#include "components/viz/service/viz_service_export.h" +#include "gpu/command_buffer/common/shared_image_usage.h" +#include "gpu/command_buffer/service/shared_image_factory.h" + +namespace gl { +class GLSurface; +} // namespace gl + +namespace viz { + +class VIZ_SERVICE_EXPORT OutputPresenterGL : public OutputPresenter { + public: + static const uint32_t kDefaultSharedImageUsage; + + static std::unique_ptr<OutputPresenterGL> Create( + SkiaOutputSurfaceDependency* deps, + gpu::MemoryTracker* memory_tracker); + + OutputPresenterGL(scoped_refptr<gl::GLSurface> gl_surface, + SkiaOutputSurfaceDependency* deps, + gpu::MemoryTracker* memory_tracker, + uint32_t shared_image_usage = kDefaultSharedImageUsage); + ~OutputPresenterGL() override; + + gl::GLSurface* gl_surface() { return gl_surface_.get(); } + + // OutputPresenter implementation: + void InitializeCapabilities(OutputSurface::Capabilities* capabilities) final; + bool Reshape(const gfx::Size& size, + float device_scale_factor, + const gfx::ColorSpace& color_space, + gfx::BufferFormat format, + gfx::OverlayTransform transform) final; + std::vector<std::unique_ptr<Image>> AllocateImages( + gfx::ColorSpace color_space, + gfx::Size image_size, + size_t num_images) final; + void SwapBuffers(SwapCompletionCallback completion_callback, + BufferPresentedCallback presentation_callback) final; + void PostSubBuffer(const gfx::Rect& rect, + SwapCompletionCallback completion_callback, + BufferPresentedCallback presentation_callback) final; + void CommitOverlayPlanes(SwapCompletionCallback completion_callback, + BufferPresentedCallback presentation_callback) final; + void SchedulePrimaryPlane( + const OverlayProcessorInterface::OutputSurfaceOverlayPlane& plane, + Image* image, + bool is_submitted) final; + std::vector<OverlayData> ScheduleOverlays( + SkiaOutputSurface::OverlayList overlays) final; + + private: + scoped_refptr<gl::GLSurface> gl_surface_; + SkiaOutputSurfaceDependency* dependency_; + const bool supports_async_swap_; + + ResourceFormat image_format_; + + // Shared Image factories + gpu::SharedImageFactory shared_image_factory_; + gpu::SharedImageRepresentationFactory shared_image_representation_factory_; + uint32_t shared_image_usage_; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_PRESENTER_GL_H_ diff --git a/chromium/components/viz/service/display_embedder/output_surface_provider_impl.cc b/chromium/components/viz/service/display_embedder/output_surface_provider_impl.cc index b4d4b1c1c59..da8936a87cf 100644 --- a/chromium/components/viz/service/display_embedder/output_surface_provider_impl.cc +++ b/chromium/components/viz/service/display_embedder/output_surface_provider_impl.cc @@ -57,6 +57,7 @@ #if defined(USE_OZONE) #include "components/viz/service/display_embedder/software_output_device_ozone.h" +#include "ui/base/ui_base_features.h" #include "ui/display/types/display_snapshot.h" #include "ui/ozone/public/ozone_platform.h" #include "ui/ozone/public/platform_window_surface.h" @@ -115,11 +116,6 @@ std::unique_ptr<OutputSurface> OutputSurfaceProviderImpl::CreateOutputSurface( output_surface = std::make_unique<SoftwareOutputSurface>( CreateSoftwareOutputDeviceForPlatform(surface_handle, display_client)); } else if (renderer_settings.use_skia_renderer) { -#if defined(OS_MACOSX) - // TODO(penghuang): Support SkiaRenderer for all platforms. - NOTIMPLEMENTED(); - return nullptr; -#else { gpu::ScopedAllowScheduleGpuTask allow_schedule_gpu_task; output_surface = SkiaOutputSurfaceImpl::Create( @@ -138,7 +134,6 @@ std::unique_ptr<OutputSurface> OutputSurfaceProviderImpl::CreateOutputSurface( #endif return nullptr; } -#endif } else { DCHECK(task_executor_); @@ -183,6 +178,10 @@ std::unique_ptr<OutputSurface> OutputSurfaceProviderImpl::CreateOutputSurface( std::move(context_provider)); } else if (context_provider->ContextCapabilities().surfaceless) { #if defined(USE_OZONE) || defined(OS_MACOSX) || defined(OS_ANDROID) +#if defined(USE_OZONE) + if (!features::IsUsingOzonePlatform()) + NOTREACHED(); +#endif output_surface = std::make_unique<GLOutputSurfaceBufferQueue>( std::move(context_provider), surface_handle, std::make_unique<BufferQueue>( @@ -234,24 +233,31 @@ OutputSurfaceProviderImpl::CreateSoftwareOutputDeviceForPlatform( NOTREACHED(); return nullptr; #elif defined(USE_OZONE) - ui::SurfaceFactoryOzone* factory = - ui::OzonePlatform::GetInstance()->GetSurfaceFactoryOzone(); - std::unique_ptr<ui::PlatformWindowSurface> platform_window_surface = - factory->CreatePlatformWindowSurface(surface_handle); - bool in_host_process = - !gpu_service_impl_ || gpu_service_impl_->in_host_process(); - std::unique_ptr<ui::SurfaceOzoneCanvas> surface_ozone = - factory->CreateCanvasForWidget( - surface_handle, - in_host_process ? nullptr : gpu_service_impl_->main_runner()); - CHECK(surface_ozone); - return std::make_unique<SoftwareOutputDeviceOzone>( - std::move(platform_window_surface), std::move(surface_ozone)); -#elif defined(USE_X11) + if (features::IsUsingOzonePlatform()) { + ui::SurfaceFactoryOzone* factory = + ui::OzonePlatform::GetInstance()->GetSurfaceFactoryOzone(); + std::unique_ptr<ui::PlatformWindowSurface> platform_window_surface = + factory->CreatePlatformWindowSurface(surface_handle); + bool in_host_process = + !gpu_service_impl_ || gpu_service_impl_->in_host_process(); + std::unique_ptr<ui::SurfaceOzoneCanvas> surface_ozone = + factory->CreateCanvasForWidget( + surface_handle, + in_host_process ? nullptr : gpu_service_impl_->main_runner()); + CHECK(surface_ozone); + return std::make_unique<SoftwareOutputDeviceOzone>( + std::move(platform_window_surface), std::move(surface_ozone)); + } +#endif + +#if defined(USE_X11) return std::make_unique<SoftwareOutputDeviceX11>( surface_handle, gpu_service_impl_->in_host_process() ? nullptr : gpu_service_impl_->main_runner()); +#else + NOTREACHED(); + return nullptr; #endif } diff --git a/chromium/components/viz/service/display_embedder/skia_output_device.cc b/chromium/components/viz/service/display_embedder/skia_output_device.cc index 3bec0806cec..d767dd45e7a 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_device.cc +++ b/chromium/components/viz/service/display_embedder/skia_output_device.cc @@ -6,15 +6,43 @@ #include <utility> +#include "base/bind.h" #include "base/check_op.h" #include "base/notreached.h" +#include "base/task/task_traits.h" +#include "base/task/thread_pool.h" +#include "base/task/thread_pool/thread_pool_instance.h" #include "components/viz/service/display/dc_layer_overlay.h" #include "gpu/command_buffer/service/memory_tracking.h" #include "third_party/skia/include/core/SkSurface.h" #include "ui/gfx/gpu_fence.h" #include "ui/gfx/presentation_feedback.h" +#include "ui/latency/latency_tracker.h" namespace viz { +namespace { + +scoped_refptr<base::SequencedTaskRunner> CreateLatencyTracerRunner() { + if (!base::ThreadPoolInstance::Get()) + return nullptr; + return base::ThreadPool::CreateSequencedTaskRunner( + {base::TaskPriority::BEST_EFFORT, + base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}); +} + +void ReportLatency(const gfx::SwapTimings& timings, + ui::LatencyTracker* tracker, + std::vector<ui::LatencyInfo> latency_info) { + for (auto& latency : latency_info) { + latency.AddLatencyNumberWithTimestamp( + ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, timings.swap_start); + latency.AddLatencyNumberWithTimestamp( + ui::INPUT_EVENT_LATENCY_FRAME_SWAP_COMPONENT, timings.swap_end); + } + tracker->OnGpuSwapBuffersCompleted(std::move(latency_info)); +} + +} // namespace SkiaOutputDevice::ScopedPaint::ScopedPaint(SkiaOutputDevice* device) : device_(device), sk_surface_(device->BeginPaint(&end_semaphores_)) { @@ -31,9 +59,14 @@ SkiaOutputDevice::SkiaOutputDevice( : did_swap_buffer_complete_callback_( std::move(did_swap_buffer_complete_callback)), memory_type_tracker_( - std::make_unique<gpu::MemoryTypeTracker>(memory_tracker)) {} + std::make_unique<gpu::MemoryTypeTracker>(memory_tracker)), + latency_tracker_(std::make_unique<ui::LatencyTracker>()), + latency_tracker_runner_(CreateLatencyTracerRunner()) {} -SkiaOutputDevice::~SkiaOutputDevice() = default; +SkiaOutputDevice::~SkiaOutputDevice() { + if (latency_tracker_runner_) + latency_tracker_runner_->DeleteSoon(FROM_HERE, std::move(latency_tracker_)); +} void SkiaOutputDevice::CommitOverlayPlanes( BufferPresentedCallback feedback, @@ -48,7 +81,9 @@ void SkiaOutputDevice::PostSubBuffer( NOTREACHED(); } -void SkiaOutputDevice::SetDrawRectangle(const gfx::Rect& draw_rectangle) {} +bool SkiaOutputDevice::SetDrawRectangle(const gfx::Rect& draw_rectangle) { + return false; +} void SkiaOutputDevice::SetGpuVSyncEnabled(bool enabled) { NOTIMPLEMENTED(); @@ -74,6 +109,9 @@ void SkiaOutputDevice::SetEnableDCLayers(bool enable) { } #endif +void SkiaOutputDevice::EnsureBackbuffer() {} +void SkiaOutputDevice::DiscardBackbuffer() {} + void SkiaOutputDevice::StartSwapBuffers(BufferPresentedCallback feedback) { DCHECK_LT(static_cast<int>(pending_swaps_.size()), capabilities_.max_frames_pending); @@ -82,34 +120,33 @@ void SkiaOutputDevice::StartSwapBuffers(BufferPresentedCallback feedback) { } void SkiaOutputDevice::FinishSwapBuffers( - gfx::SwapResult result, + gfx::SwapCompletionResult result, const gfx::Size& size, - std::vector<ui::LatencyInfo> latency_info) { + std::vector<ui::LatencyInfo> latency_info, + const base::Optional<gfx::Rect>& damage_area) { DCHECK(!pending_swaps_.empty()); const gpu::SwapBuffersCompleteParams& params = - pending_swaps_.front().Complete(result); + pending_swaps_.front().Complete(std::move(result), damage_area); did_swap_buffer_complete_callback_.Run(params, size); pending_swaps_.front().CallFeedback(); - for (auto& latency : latency_info) { - latency.AddLatencyNumberWithTimestamp( - ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, - params.swap_response.timings.swap_start); - latency.AddLatencyNumberWithTimestamp( - ui::INPUT_EVENT_LATENCY_FRAME_SWAP_COMPONENT, - params.swap_response.timings.swap_end); + if (latency_tracker_runner_) { + // Report latency off GPU main thread. + latency_tracker_runner_->PostTask( + FROM_HERE, + base::BindOnce(&ReportLatency, params.swap_response.timings, + latency_tracker_.get(), std::move(latency_info))); + } else { + ReportLatency(params.swap_response.timings, latency_tracker_.get(), + std::move(latency_info)); } - latency_tracker_.OnGpuSwapBuffersCompleted(latency_info); pending_swaps_.pop(); } -void SkiaOutputDevice::EnsureBackbuffer() {} -void SkiaOutputDevice::DiscardBackbuffer() {} - SkiaOutputDevice::SwapInfo::SwapInfo( uint64_t swap_id, SkiaOutputDevice::BufferPresentedCallback feedback) @@ -123,10 +160,13 @@ SkiaOutputDevice::SwapInfo::SwapInfo(SwapInfo&& other) = default; SkiaOutputDevice::SwapInfo::~SwapInfo() = default; const gpu::SwapBuffersCompleteParams& SkiaOutputDevice::SwapInfo::Complete( - gfx::SwapResult result) { - params_.swap_response.result = result; + gfx::SwapCompletionResult result, + const base::Optional<gfx::Rect>& damage_rect) { + params_.swap_response.result = result.swap_result; params_.swap_response.timings.swap_end = base::TimeTicks::Now(); - + params_.frame_buffer_damage_area = damage_rect; + if (result.ca_layer_params) + params_.ca_layer_params = *result.ca_layer_params; return params_; } diff --git a/chromium/components/viz/service/display_embedder/skia_output_device.h b/chromium/components/viz/service/display_embedder/skia_output_device.h index 80dab35e5c3..0aa9857cd2c 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_device.h +++ b/chromium/components/viz/service/display_embedder/skia_output_device.h @@ -11,6 +11,7 @@ #include "base/callback.h" #include "base/containers/queue.h" #include "base/macros.h" +#include "base/memory/scoped_refptr.h" #include "base/optional.h" #include "build/build_config.h" #include "components/viz/service/display/output_surface.h" @@ -20,10 +21,13 @@ #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/src/gpu/GrSemaphore.h" #include "ui/gfx/swap_result.h" -#include "ui/latency/latency_tracker.h" class SkSurface; +namespace base { +class SequencedTaskRunner; +} + namespace gfx { class ColorSpace; class Rect; @@ -36,6 +40,10 @@ class MemoryTracker; class MemoryTypeTracker; } // namespace gpu +namespace ui { +class LatencyTracker; +} + namespace viz { class SkiaOutputDevice { @@ -89,7 +97,7 @@ class SkiaOutputDevice { std::vector<ui::LatencyInfo> latency_info); // Set the rectangle that will be drawn into on the surface. - virtual void SetDrawRectangle(const gfx::Rect& draw_rectangle); + virtual bool SetDrawRectangle(const gfx::Rect& draw_rectangle); virtual void SetGpuVSyncEnabled(bool enabled); @@ -129,7 +137,9 @@ class SkiaOutputDevice { SwapInfo(uint64_t swap_id, BufferPresentedCallback feedback); SwapInfo(SwapInfo&& other); ~SwapInfo(); - const gpu::SwapBuffersCompleteParams& Complete(gfx::SwapResult result); + const gpu::SwapBuffersCompleteParams& Complete( + gfx::SwapCompletionResult result, + const base::Optional<gfx::Rect>& damage_area); void CallFeedback(); private: @@ -150,9 +160,11 @@ class SkiaOutputDevice { // Helper method for SwapBuffers() and PostSubBuffer(). It should be called // at the end of SwapBuffers() and PostSubBuffer() implementations - void FinishSwapBuffers(gfx::SwapResult result, - const gfx::Size& size, - std::vector<ui::LatencyInfo> latency_info); + void FinishSwapBuffers( + gfx::SwapCompletionResult result, + const gfx::Size& size, + std::vector<ui::LatencyInfo> latency_info, + const base::Optional<gfx::Rect>& damage_area = base::nullopt); OutputSurface::Capabilities capabilities_; @@ -161,13 +173,16 @@ class SkiaOutputDevice { base::queue<SwapInfo> pending_swaps_; - ui::LatencyTracker latency_tracker_; - // RGBX format is emulated with RGBA. bool is_emulated_rgbx_ = false; std::unique_ptr<gpu::MemoryTypeTracker> memory_type_tracker_; + private: + std::unique_ptr<ui::LatencyTracker> latency_tracker_; + // task runner for latency tracker. + scoped_refptr<base::SequencedTaskRunner> latency_tracker_runner_; + DISALLOW_COPY_AND_ASSIGN(SkiaOutputDevice); }; diff --git a/chromium/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc b/chromium/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc index 1ba33bf3fe3..1969bdfedcc 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc +++ b/chromium/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc @@ -4,284 +4,36 @@ #include "components/viz/service/display_embedder/skia_output_device_buffer_queue.h" +#include <memory> +#include <utility> +#include <vector> + #include "base/command_line.h" -#include "base/threading/thread_task_runner_handle.h" -#include "build/build_config.h" -#include "components/viz/common/resources/resource_format_utils.h" #include "components/viz/common/switches.h" #include "components/viz/service/display_embedder/skia_output_surface_dependency.h" #include "gpu/command_buffer/common/capabilities.h" -#include "gpu/command_buffer/common/shared_image_usage.h" #include "gpu/command_buffer/service/feature_info.h" #include "gpu/command_buffer/service/shared_context_state.h" #include "gpu/command_buffer/service/shared_image_representation.h" #include "gpu/config/gpu_finch_features.h" -#include "gpu/ipc/common/gpu_surface_lookup.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/core/SkSurfaceProps.h" -#include "ui/display/types/display_snapshot.h" -#include "ui/gfx/buffer_format_util.h" -#include "ui/gfx/geometry/rect_conversions.h" -#include "ui/gl/gl_fence.h" #include "ui/gl/gl_surface.h" -#if defined(OS_ANDROID) -#include "ui/gl/gl_surface_egl_surface_control.h" -#endif - namespace viz { -namespace { - -constexpr uint32_t kSharedImageUsage = - gpu::SHARED_IMAGE_USAGE_SCANOUT | gpu::SHARED_IMAGE_USAGE_DISPLAY | - gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT; - -} // namespace - -class SkiaOutputDeviceBufferQueue::Image { - public: - Image(gpu::SharedImageFactory* factory, - gpu::SharedImageRepresentationFactory* representation_factory) - : factory_(factory), representation_factory_(representation_factory) {} - ~Image() { - // TODO(vasilyt): As we are going to delete image anyway we should be able - // to abort write to avoid unnecessary flush to submit semaphores. - if (scoped_skia_write_access_) { - EndWriteSkia(); - } - DCHECK(!scoped_skia_write_access_); - } - - bool Initialize(const gfx::Size& size, - const gfx::ColorSpace& color_space, - ResourceFormat format, - SkiaOutputSurfaceDependency* deps, - uint32_t shared_image_usage) { - auto mailbox = gpu::Mailbox::GenerateForSharedImage(); - // TODO(penghuang): This should pass the surface handle for ChromeOS - if (!factory_->CreateSharedImage(mailbox, format, size, color_space, - gpu::kNullSurfaceHandle, - shared_image_usage)) { - DLOG(ERROR) << "CreateSharedImage failed."; - return false; - } - - // Initialize |shared_image_deletor_| to make sure the shared image backing - // will be released with the Image. - shared_image_deletor_.ReplaceClosure(base::BindOnce( - base::IgnoreResult(&gpu::SharedImageFactory::DestroySharedImage), - base::Unretained(factory_), mailbox)); - - skia_representation_ = representation_factory_->ProduceSkia( - mailbox, deps->GetSharedContextState()); - if (!skia_representation_) { - DLOG(ERROR) << "ProduceSkia() failed."; - return false; - } - - overlay_representation_ = representation_factory_->ProduceOverlay(mailbox); - - // If the backing doesn't support overlay, then fallback to GL. - if (!overlay_representation_) - gl_representation_ = representation_factory_->ProduceGLTexture(mailbox); - - if (!overlay_representation_ && !gl_representation_) { - DLOG(ERROR) << "ProduceOverlay() and ProduceGLTexture() failed."; - return false; - } - - return true; - } - - void BeginWriteSkia() { - DCHECK(!scoped_skia_write_access_); - DCHECK(!scoped_overlay_read_access_); - DCHECK(end_semaphores_.empty()); - - std::vector<GrBackendSemaphore> begin_semaphores; - // LegacyFontHost will get LCD text and skia figures out what type to use. - SkSurfaceProps surface_props(0 /* flags */, - SkSurfaceProps::kLegacyFontHost_InitType); - - // Buffer queue is internal to GPU proc and handles texture initialization, - // so allow uncleared access. - // TODO(vasilyt): Props and MSAA - scoped_skia_write_access_ = skia_representation_->BeginScopedWriteAccess( - 0 /* final_msaa_count */, surface_props, &begin_semaphores, - &end_semaphores_, - gpu::SharedImageRepresentation::AllowUnclearedAccess::kYes); - DCHECK(scoped_skia_write_access_); - if (!begin_semaphores.empty()) { - scoped_skia_write_access_->surface()->wait(begin_semaphores.size(), - begin_semaphores.data()); - } - } - - SkSurface* sk_surface() { - return scoped_skia_write_access_ ? scoped_skia_write_access_->surface() - : nullptr; - } - - std::vector<GrBackendSemaphore> TakeEndWriteSkiaSemaphores() { - std::vector<GrBackendSemaphore> temp_vector; - temp_vector.swap(end_semaphores_); - return temp_vector; - } - - void EndWriteSkia() { - // The Flush now takes place in finishPaintCurrentBuffer on the CPU side. - // check if end_semaphores is not empty then flash here - DCHECK(scoped_skia_write_access_); - if (!end_semaphores_.empty()) { - GrFlushInfo flush_info = { - .fFlags = kNone_GrFlushFlags, - .fNumSemaphores = end_semaphores_.size(), - .fSignalSemaphores = end_semaphores_.data(), - }; - scoped_skia_write_access_->surface()->flush( - SkSurface::BackendSurfaceAccess::kNoAccess, flush_info); - } - scoped_skia_write_access_.reset(); - end_semaphores_.clear(); - - // SkiaRenderer always draws the full frame. - skia_representation_->SetCleared(); - } - - void BeginPresent() { - if (++present_count_ != 1) { - DCHECK(scoped_overlay_read_access_ || scoped_gl_read_access_); - return; - } - - DCHECK(!scoped_skia_write_access_); - DCHECK(!scoped_overlay_read_access_); - - if (overlay_representation_) { - scoped_overlay_read_access_ = - overlay_representation_->BeginScopedReadAccess( - true /* need_gl_image */); - DCHECK(scoped_overlay_read_access_); - return; - } - - scoped_gl_read_access_ = gl_representation_->BeginScopedAccess( - GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM, - gpu::SharedImageRepresentation::AllowUnclearedAccess::kNo); - DCHECK(scoped_gl_read_access_); - } - - void EndPresent() { - DCHECK(present_count_); - if (--present_count_) - return; - scoped_overlay_read_access_.reset(); - scoped_gl_read_access_.reset(); - } - - gl::GLImage* GetGLImage(std::unique_ptr<gfx::GpuFence>* fence) { - if (scoped_overlay_read_access_) - return scoped_overlay_read_access_->gl_image(); - - DCHECK(scoped_gl_read_access_); - - if (gl::GLFence::IsGpuFenceSupported() && fence) { - if (auto gl_fence = gl::GLFence::CreateForGpuFence()) - *fence = gl_fence->GetGpuFence(); - } - auto* texture = gl_representation_->GetTexture(); - return texture->GetLevelImage(texture->target(), 0); - } - - base::WeakPtr<Image> GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } - - int present_count() const { return present_count_; } - gpu::SharedImageRepresentationSkia* skia_representation() { - return skia_representation_.get(); - } - const gfx::Size& size() const { return skia_representation_->size(); } - - private: - gpu::SharedImageFactory* const factory_; - gpu::SharedImageRepresentationFactory* const representation_factory_; - - base::ScopedClosureRunner shared_image_deletor_; - std::unique_ptr<gpu::SharedImageRepresentationSkia> skia_representation_; - std::unique_ptr<gpu::SharedImageRepresentationOverlay> - overlay_representation_; - std::unique_ptr<gpu::SharedImageRepresentationGLTexture> gl_representation_; - std::unique_ptr<gpu::SharedImageRepresentationSkia::ScopedWriteAccess> - scoped_skia_write_access_; - std::unique_ptr<gpu::SharedImageRepresentationOverlay::ScopedReadAccess> - scoped_overlay_read_access_; - std::unique_ptr<gpu::SharedImageRepresentationGLTexture::ScopedAccess> - scoped_gl_read_access_; - std::vector<GrBackendSemaphore> end_semaphores_; - int present_count_ = 0; - base::WeakPtrFactory<Image> weak_ptr_factory_{this}; - - DISALLOW_COPY_AND_ASSIGN(Image); -}; - -class SkiaOutputDeviceBufferQueue::OverlayData { - public: - OverlayData( - std::unique_ptr<gpu::SharedImageRepresentationOverlay> representation, - std::unique_ptr<gpu::SharedImageRepresentationOverlay::ScopedReadAccess> - scoped_read_access) - : representation_(std::move(representation)), - scoped_read_access_(std::move(scoped_read_access)) {} - OverlayData(OverlayData&&) = default; - ~OverlayData() = default; - OverlayData& operator=(OverlayData&&) = default; - - gl::GLImage* gl_image() { return scoped_read_access_->gl_image(); } - - private: - std::unique_ptr<gpu::SharedImageRepresentationOverlay> representation_; - std::unique_ptr<gpu::SharedImageRepresentationOverlay::ScopedReadAccess> - scoped_read_access_; -}; SkiaOutputDeviceBufferQueue::SkiaOutputDeviceBufferQueue( - scoped_refptr<gl::GLSurface> gl_surface, + std::unique_ptr<OutputPresenter> presenter, SkiaOutputSurfaceDependency* deps, gpu::MemoryTracker* memory_tracker, - const DidSwapBufferCompleteCallback& did_swap_buffer_complete_callback, - uint32_t shared_image_usage) + const DidSwapBufferCompleteCallback& did_swap_buffer_complete_callback) : SkiaOutputDevice(memory_tracker, did_swap_buffer_complete_callback), - dependency_(deps), - gl_surface_(std::move(gl_surface)), - supports_async_swap_(gl_surface_->SupportsAsyncSwap()), - shared_image_factory_(deps->GetGpuPreferences(), - deps->GetGpuDriverBugWorkarounds(), - deps->GetGpuFeatureInfo(), - deps->GetSharedContextState().get(), - deps->GetMailboxManager(), - deps->GetSharedImageManager(), - deps->GetGpuImageFactory(), - memory_tracker, - true), - shared_image_usage_(shared_image_usage) { - shared_image_representation_factory_ = - std::make_unique<gpu::SharedImageRepresentationFactory>( - dependency_->GetSharedImageManager(), memory_tracker); - -#if defined(USE_OZONE) - image_format_ = GetResourceFormat(display::DisplaySnapshot::PrimaryFormat()); -#else - image_format_ = RGBA_8888; -#endif - // GL is origin is at bottom left normally, all Surfaceless implementations - // are flipped. - DCHECK_EQ(gl_surface_->GetOrigin(), gfx::SurfaceOrigin::kTopLeft); - + presenter_(std::move(presenter)), + dependency_(deps) { capabilities_.uses_default_gl_framebuffer = false; - capabilities_.android_surface_control_feature_enabled = true; - capabilities_.supports_post_sub_buffer = gl_surface_->SupportsPostSubBuffer(); - capabilities_.supports_commit_overlay_planes = - gl_surface_->SupportsCommitOverlayPlanes(); - capabilities_.max_frames_pending = 2; + capabilities_.preserve_buffer_content = true; + capabilities_.only_invalidates_damage_rect = false; + capabilities_.number_of_buffers = 3; // Force the number of max pending frames to one when the switch // "double-buffer-compositing" is passed. @@ -289,35 +41,12 @@ SkiaOutputDeviceBufferQueue::SkiaOutputDeviceBufferQueue( // allocates at most one additional buffer. base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kDoubleBufferCompositing)) - capabilities_.max_frames_pending = 1; + capabilities_.number_of_buffers = 2; + capabilities_.max_frames_pending = capabilities_.number_of_buffers - 1; - capabilities_.only_invalidates_damage_rect = false; - // Set supports_surfaceless to enable overlays. - capabilities_.supports_surfaceless = true; - capabilities_.preserve_buffer_content = true; - // We expect origin of buffers is at top left. - capabilities_.output_surface_origin = gfx::SurfaceOrigin::kTopLeft; - - // TODO(penghuang): Use defaultBackendFormat() in shared image implementation - // to make sure backend formant is consistent. - capabilities_.sk_color_type = ResourceFormatToClosestSkColorType( - true /* gpu_compositing */, image_format_); - capabilities_.gr_backend_format = - dependency_->GetSharedContextState()->gr_context()->defaultBackendFormat( - capabilities_.sk_color_type, GrRenderable::kYes); + presenter_->InitializeCapabilities(&capabilities_); } -SkiaOutputDeviceBufferQueue::SkiaOutputDeviceBufferQueue( - scoped_refptr<gl::GLSurface> gl_surface, - SkiaOutputSurfaceDependency* deps, - gpu::MemoryTracker* memory_tracker, - const DidSwapBufferCompleteCallback& did_swap_buffer_complete_callback) - : SkiaOutputDeviceBufferQueue(std::move(gl_surface), - deps, - memory_tracker, - did_swap_buffer_complete_callback, - kSharedImageUsage) {} - SkiaOutputDeviceBufferQueue::~SkiaOutputDeviceBufferQueue() { FreeAllSurfaces(); // Clear and cancel swap_completion_callbacks_ to free all resource bind to @@ -325,58 +54,17 @@ SkiaOutputDeviceBufferQueue::~SkiaOutputDeviceBufferQueue() { swap_completion_callbacks_.clear(); } -// static -std::unique_ptr<SkiaOutputDeviceBufferQueue> -SkiaOutputDeviceBufferQueue::Create( - SkiaOutputSurfaceDependency* deps, - gpu::MemoryTracker* memory_tracker, - const DidSwapBufferCompleteCallback& did_swap_buffer_complete_callback) { -#if defined(OS_ANDROID) - if (deps->GetGpuFeatureInfo() - .status_values[gpu::GPU_FEATURE_TYPE_ANDROID_SURFACE_CONTROL] != - gpu::kGpuFeatureStatusEnabled) { - return nullptr; - } - - bool can_be_used_with_surface_control = false; - ANativeWindow* window = - gpu::GpuSurfaceLookup::GetInstance()->AcquireNativeWidget( - deps->GetSurfaceHandle(), &can_be_used_with_surface_control); - if (!window || !can_be_used_with_surface_control) - return nullptr; - // TODO(https://crbug.com/1012401): don't depend on GL. - auto gl_surface = base::MakeRefCounted<gl::GLSurfaceEGLSurfaceControl>( - window, base::ThreadTaskRunnerHandle::Get()); - if (!gl_surface->Initialize(gl::GLSurfaceFormat())) { - LOG(ERROR) << "Failed to initialize GLSurfaceEGLSurfaceControl."; - return nullptr; - } - - if (!deps->GetSharedContextState()->MakeCurrent(gl_surface.get(), - true /* needs_gl*/)) { - LOG(ERROR) << "MakeCurrent failed."; - return nullptr; - } - - return std::make_unique<SkiaOutputDeviceBufferQueue>( - std::move(gl_surface), deps, memory_tracker, - did_swap_buffer_complete_callback); -#else - return nullptr; -#endif -} - -SkiaOutputDeviceBufferQueue::Image* -SkiaOutputDeviceBufferQueue::GetNextImage() { +OutputPresenter::Image* SkiaOutputDeviceBufferQueue::GetNextImage() { DCHECK(!available_images_.empty()); auto* image = available_images_.front(); available_images_.pop_front(); return image; } -void SkiaOutputDeviceBufferQueue::PageFlipComplete(Image* image) { +void SkiaOutputDeviceBufferQueue::PageFlipComplete( + OutputPresenter::Image* image) { if (displayed_image_) { - DCHECK_EQ(displayed_image_->size(), image_size_); + DCHECK_EQ(displayed_image_->skia_representation()->size(), image_size_); DCHECK_EQ(displayed_image_->present_count() > 1, displayed_image_ == image); displayed_image_->EndPresent(); if (!displayed_image_->present_count()) { @@ -417,57 +105,13 @@ void SkiaOutputDeviceBufferQueue::SchedulePrimaryPlane( DCHECK(image); image->BeginPresent(); - - std::unique_ptr<gfx::GpuFence> fence; - // If the submitted_image_ is being scheduled, we don't new a new fence. - auto* gl_image = - image->GetGLImage(image == submitted_image_ ? nullptr : &fence); - - // Output surface is also z-order 0. - constexpr int kPlaneZOrder = 0; - // Output surface always uses the full texture. - constexpr gfx::RectF kUVRect(0.f, 0.f, 1.0f, 1.0f); - gl_surface_->ScheduleOverlayPlane(kPlaneZOrder, plane.transform, gl_image, - ToNearestRect(plane.display_rect), kUVRect, - plane.enable_blending, std::move(fence)); + presenter_->SchedulePrimaryPlane(plane, image, image == submitted_image_); } void SkiaOutputDeviceBufferQueue::ScheduleOverlays( SkiaOutputSurface::OverlayList overlays) { -#if defined(OS_ANDROID) DCHECK(pending_overlays_.empty()); - for (auto& overlay : overlays) { - auto shared_image = - shared_image_representation_factory_->ProduceOverlay(overlay.mailbox); - // When display is re-opened, the first few frames might not have video - // resource ready. Possible investigation crbug.com/1023971. - if (!shared_image) { - LOG(ERROR) << "Invalid mailbox."; - continue; - } - - std::unique_ptr<gpu::SharedImageRepresentationOverlay::ScopedReadAccess> - shared_image_access = - shared_image->BeginScopedReadAccess(true /* needs_gl_image */); - if (!shared_image_access) { - LOG(ERROR) << "Could not access SharedImage for read."; - continue; - } - - pending_overlays_.emplace_back(std::move(shared_image), - std::move(shared_image_access)); - auto* gl_image = pending_overlays_.back().gl_image(); - DLOG_IF(ERROR, !gl_image) << "Cannot get GLImage."; - - if (gl_image) { - DCHECK(!overlay.gpu_fence_id); - gl_surface_->ScheduleOverlayPlane( - overlay.plane_z_order, overlay.transform, gl_image, - ToNearestRect(overlay.display_rect), overlay.uv_rect, - !overlay.is_opaque, nullptr /* gpu_fence */); - } - } -#endif // defined(OS_ANDROID) + pending_overlays_ = presenter_->ScheduleOverlays(std::move(overlays)); } void SkiaOutputDeviceBufferQueue::SwapBuffers( @@ -479,24 +123,17 @@ void SkiaOutputDeviceBufferQueue::SwapBuffers( submitted_image_ = current_image_; current_image_ = nullptr; - if (supports_async_swap_) { - // Cancelable callback uses weak ptr to drop this task upon destruction. - // Thus it is safe to use |base::Unretained(this)|. - // Bind submitted_image_->GetWeakPtr(), since the |submitted_image_| could - // be released due to reshape() or destruction. - swap_completion_callbacks_.emplace_back( - std::make_unique<CancelableSwapCompletionCallback>(base::BindOnce( - &SkiaOutputDeviceBufferQueue::DoFinishSwapBuffers, - base::Unretained(this), image_size_, std::move(latency_info), - submitted_image_->GetWeakPtr(), std::move(committed_overlays_)))); - gl_surface_->SwapBuffersAsync(swap_completion_callbacks_.back()->callback(), - std::move(feedback)); - } else { - DoFinishSwapBuffers(image_size_, std::move(latency_info), - submitted_image_->GetWeakPtr(), - std::move(committed_overlays_), - gl_surface_->SwapBuffers(std::move(feedback)), nullptr); - } + // Cancelable callback uses weak ptr to drop this task upon destruction. + // Thus it is safe to use |base::Unretained(this)|. + // Bind submitted_image_->GetWeakPtr(), since the |submitted_image_| could + // be released due to reshape() or destruction. + swap_completion_callbacks_.emplace_back( + std::make_unique<CancelableSwapCompletionCallback>(base::BindOnce( + &SkiaOutputDeviceBufferQueue::DoFinishSwapBuffers, + base::Unretained(this), image_size_, std::move(latency_info), + submitted_image_->GetWeakPtr(), std::move(committed_overlays_)))); + presenter_->SwapBuffers(swap_completion_callbacks_.back()->callback(), + std::move(feedback)); committed_overlays_.clear(); std::swap(committed_overlays_, pending_overlays_); } @@ -513,27 +150,18 @@ void SkiaOutputDeviceBufferQueue::PostSubBuffer( } DCHECK(submitted_image_); - if (supports_async_swap_) { - // Cancelable callback uses weak ptr to drop this task upon destruction. - // Thus it is safe to use |base::Unretained(this)|. - // Bind submitted_image_->GetWeakPtr(), since the |submitted_image_| could - // be released due to reshape() or destruction. - swap_completion_callbacks_.emplace_back( - std::make_unique<CancelableSwapCompletionCallback>(base::BindOnce( - &SkiaOutputDeviceBufferQueue::DoFinishSwapBuffers, - base::Unretained(this), image_size_, std::move(latency_info), - submitted_image_->GetWeakPtr(), std::move(committed_overlays_)))); - gl_surface_->PostSubBufferAsync( - rect.x(), rect.y(), rect.width(), rect.height(), - swap_completion_callbacks_.back()->callback(), std::move(feedback)); - } else { - DoFinishSwapBuffers( - image_size_, std::move(latency_info), submitted_image_->GetWeakPtr(), - std::move(committed_overlays_), - gl_surface_->PostSubBuffer(rect.x(), rect.y(), rect.width(), - rect.height(), std::move(feedback)), - nullptr); - } + // Cancelable callback uses weak ptr to drop this task upon destruction. + // Thus it is safe to use |base::Unretained(this)|. + // Bind submitted_image_->GetWeakPtr(), since the |submitted_image_| could + // be released due to reshape() or destruction. + swap_completion_callbacks_.emplace_back( + std::make_unique<CancelableSwapCompletionCallback>(base::BindOnce( + &SkiaOutputDeviceBufferQueue::DoFinishSwapBuffers, + base::Unretained(this), image_size_, std::move(latency_info), + submitted_image_->GetWeakPtr(), std::move(committed_overlays_)))); + presenter_->PostSubBuffer(rect, swap_completion_callbacks_.back()->callback(), + std::move(feedback)); + committed_overlays_.clear(); std::swap(committed_overlays_, pending_overlays_); } @@ -548,24 +176,18 @@ void SkiaOutputDeviceBufferQueue::CommitOverlayPlanes( // A main buffer has to be submitted for previous frames. DCHECK(submitted_image_); - if (supports_async_swap_) { - // Cancelable callback uses weak ptr to drop this task upon destruction. - // Thus it is safe to use |base::Unretained(this)|. - // Bind submitted_image_->GetWeakPtr(), since the |submitted_image_| could - // be released due to reshape() or destruction. - swap_completion_callbacks_.emplace_back( - std::make_unique<CancelableSwapCompletionCallback>(base::BindOnce( - &SkiaOutputDeviceBufferQueue::DoFinishSwapBuffers, - base::Unretained(this), image_size_, std::move(latency_info), - submitted_image_->GetWeakPtr(), std::move(committed_overlays_)))); - gl_surface_->CommitOverlayPlanesAsync( - swap_completion_callbacks_.back()->callback(), std::move(feedback)); - } else { - DoFinishSwapBuffers( - image_size_, std::move(latency_info), submitted_image_->GetWeakPtr(), - std::move(committed_overlays_), - gl_surface_->CommitOverlayPlanes(std::move(feedback)), nullptr); - } + // Cancelable callback uses weak ptr to drop this task upon destruction. + // Thus it is safe to use |base::Unretained(this)|. + // Bind submitted_image_->GetWeakPtr(), since the |submitted_image_| could + // be released due to reshape() or destruction. + swap_completion_callbacks_.emplace_back( + std::make_unique<CancelableSwapCompletionCallback>(base::BindOnce( + &SkiaOutputDeviceBufferQueue::DoFinishSwapBuffers, + base::Unretained(this), image_size_, std::move(latency_info), + submitted_image_->GetWeakPtr(), std::move(committed_overlays_)))); + presenter_->CommitOverlayPlanes(swap_completion_callbacks_.back()->callback(), + std::move(feedback)); + committed_overlays_.clear(); std::swap(committed_overlays_, pending_overlays_); } @@ -573,13 +195,11 @@ void SkiaOutputDeviceBufferQueue::CommitOverlayPlanes( void SkiaOutputDeviceBufferQueue::DoFinishSwapBuffers( const gfx::Size& size, std::vector<ui::LatencyInfo> latency_info, - const base::WeakPtr<Image>& image, - std::vector<OverlayData> overlays, - gfx::SwapResult result, - std::unique_ptr<gfx::GpuFence> gpu_fence) { - DCHECK(!gpu_fence); - - FinishSwapBuffers(result, size, latency_info); + const base::WeakPtr<OutputPresenter::Image>& image, + std::vector<OutputPresenter::OverlayData> overlays, + gfx::SwapCompletionResult result) { + DCHECK(!result.gpu_fence); + FinishSwapBuffers(std::move(result), size, latency_info); PageFlipComplete(image.get()); } @@ -588,8 +208,8 @@ bool SkiaOutputDeviceBufferQueue::Reshape(const gfx::Size& size, const gfx::ColorSpace& color_space, gfx::BufferFormat format, gfx::OverlayTransform transform) { - if (!gl_surface_->Resize(size, device_scale_factor, color_space, - gfx::AlphaBitsForBufferFormat(format))) { + if (!presenter_->Reshape(size, device_scale_factor, color_space, format, + transform)) { DLOG(ERROR) << "Failed to resize."; return false; } @@ -598,16 +218,13 @@ bool SkiaOutputDeviceBufferQueue::Reshape(const gfx::Size& size, image_size_ = size; FreeAllSurfaces(); - for (int i = 0; i < capabilities_.max_frames_pending + 1; ++i) { - auto image = std::make_unique<Image>( - &shared_image_factory_, shared_image_representation_factory_.get()); - if (!image->Initialize(image_size_, color_space_, image_format_, - dependency_, shared_image_usage_)) { - DLOG(ERROR) << "Failed to initialize image."; - return false; - } + images_ = presenter_->AllocateImages(color_space_, image_size_, + capabilities_.number_of_buffers); + if (images_.empty()) + return false; + + for (auto& image : images_) { available_images_.push_back(image.get()); - images_.push_back(std::move(image)); } return true; diff --git a/chromium/components/viz/service/display_embedder/skia_output_device_buffer_queue.h b/chromium/components/viz/service/display_embedder/skia_output_device_buffer_queue.h index 77ad81308cf..efb7d9c629a 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_device_buffer_queue.h +++ b/chromium/components/viz/service/display_embedder/skia_output_device_buffer_queue.h @@ -7,39 +7,29 @@ #include "base/cancelable_callback.h" #include "base/macros.h" +#include "components/viz/service/display_embedder/output_presenter.h" #include "components/viz/service/display_embedder/skia_output_device.h" #include "components/viz/service/viz_service_export.h" -#include "gpu/command_buffer/service/shared_image_factory.h" - -namespace gl { -class GLSurface; -} // namespace gl namespace viz { class SkiaOutputSurfaceDependency; -class VIZ_SERVICE_EXPORT SkiaOutputDeviceBufferQueue final - : public SkiaOutputDevice { +class VIZ_SERVICE_EXPORT SkiaOutputDeviceBufferQueue : public SkiaOutputDevice { public: SkiaOutputDeviceBufferQueue( - scoped_refptr<gl::GLSurface> gl_surface, + std::unique_ptr<OutputPresenter> presenter, SkiaOutputSurfaceDependency* deps, gpu::MemoryTracker* memory_tracker, const DidSwapBufferCompleteCallback& did_swap_buffer_complete_callback); - SkiaOutputDeviceBufferQueue( - scoped_refptr<gl::GLSurface> gl_surface, - SkiaOutputSurfaceDependency* deps, - gpu::MemoryTracker* memory_tracker, - const DidSwapBufferCompleteCallback& did_swap_buffer_complete_callback, - uint32_t shared_image_usage); + ~SkiaOutputDeviceBufferQueue() override; - static std::unique_ptr<SkiaOutputDeviceBufferQueue> Create( - SkiaOutputSurfaceDependency* deps, - gpu::MemoryTracker* memory_tracker, - const DidSwapBufferCompleteCallback& did_swap_buffer_complete_callback); + SkiaOutputDeviceBufferQueue(const SkiaOutputDeviceBufferQueue&) = delete; + SkiaOutputDeviceBufferQueue& operator=(const SkiaOutputDeviceBufferQueue&) = + delete; + // SkiaOutputDevice overrides. void SwapBuffers(BufferPresentedCallback feedback, std::vector<ui::LatencyInfo> latency_info) override; void PostSubBuffer(const gfx::Rect& rect, @@ -63,48 +53,41 @@ class VIZ_SERVICE_EXPORT SkiaOutputDeviceBufferQueue final override; void ScheduleOverlays(SkiaOutputSurface::OverlayList overlays) override; - gl::GLSurface* gl_surface() { return gl_surface_.get(); } - private: friend class SkiaOutputDeviceBufferQueueTest; - class Image; - class OverlayData; using CancelableSwapCompletionCallback = - base::CancelableOnceCallback<void(gfx::SwapResult, - std::unique_ptr<gfx::GpuFence>)>; + base::CancelableOnceCallback<void(gfx::SwapCompletionResult)>; - Image* GetNextImage(); - void PageFlipComplete(Image* image); + OutputPresenter::Image* GetNextImage(); + void PageFlipComplete(OutputPresenter::Image* image); void FreeAllSurfaces(); - // Used as callback for SwapBuff ersAsync and PostSubBufferAsync to finish + // Used as callback for SwapBuffersAsync and PostSubBufferAsync to finish // operation void DoFinishSwapBuffers(const gfx::Size& size, std::vector<ui::LatencyInfo> latency_info, - const base::WeakPtr<Image>& image, - std::vector<OverlayData> overlays, - gfx::SwapResult result, - std::unique_ptr<gfx::GpuFence> gpu_fence); + const base::WeakPtr<OutputPresenter::Image>& image, + std::vector<OutputPresenter::OverlayData> overlays, + gfx::SwapCompletionResult result); + + std::unique_ptr<OutputPresenter> presenter_; SkiaOutputSurfaceDependency* const dependency_; - scoped_refptr<gl::GLSurface> gl_surface_; - const bool supports_async_swap_; // Format of images gfx::ColorSpace color_space_; gfx::Size image_size_; - ResourceFormat image_format_; // All allocated images. - std::vector<std::unique_ptr<Image>> images_; + std::vector<std::unique_ptr<OutputPresenter::Image>> images_; // This image is currently used by Skia as RenderTarget. This may be nullptr // if there is no drawing for the current frame or if allocation failed. - Image* current_image_ = nullptr; + OutputPresenter::Image* current_image_ = nullptr; // The last image submitted for presenting. - Image* submitted_image_ = nullptr; + OutputPresenter::Image* submitted_image_ = nullptr; // The image currently on the screen, if any. - Image* displayed_image_ = nullptr; + OutputPresenter::Image* displayed_image_ = nullptr; // These are free for use, and are not nullptr. - base::circular_deque<Image*> available_images_; + base::circular_deque<OutputPresenter::Image*> available_images_; // These cancelable callbacks bind images that have been scheduled to display // but are not displayed yet. This deque will be cleared when represented // frames are destroyed. Use CancelableOnceCallback to prevent resources @@ -112,17 +95,9 @@ class VIZ_SERVICE_EXPORT SkiaOutputDeviceBufferQueue final base::circular_deque<std::unique_ptr<CancelableSwapCompletionCallback>> swap_completion_callbacks_; // Scheduled overlays for the next SwapBuffers call. - std::vector<OverlayData> pending_overlays_; + std::vector<OutputPresenter::OverlayData> pending_overlays_; // Committed overlays for the last SwapBuffers call. - std::vector<OverlayData> committed_overlays_; - - // Shared Image factories - gpu::SharedImageFactory shared_image_factory_; - std::unique_ptr<gpu::SharedImageRepresentationFactory> - shared_image_representation_factory_; - uint32_t shared_image_usage_; - - DISALLOW_COPY_AND_ASSIGN(SkiaOutputDeviceBufferQueue); + std::vector<OutputPresenter::OverlayData> committed_overlays_; }; } // namespace viz diff --git a/chromium/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc b/chromium/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc index 8cc92113d1d..92a51592bf6 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc +++ b/chromium/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc @@ -16,6 +16,7 @@ #include "build/build_config.h" #include "gpu/command_buffer/service/scheduler.h" +#include "components/viz/service/display_embedder/output_presenter_gl.h" #include "components/viz/service/display_embedder/skia_output_surface_dependency_impl.h" #include "components/viz/service/gl/gpu_service_impl.h" #include "components/viz/test/test_gpu_service_holder.h" @@ -157,7 +158,8 @@ class MockGLSurfaceAsync : public gl::GLSurfaceStub { void SwapComplete() { DCHECK(!callbacks_.empty()); - std::move(callbacks_.front()).Run(gfx::SwapResult::SWAP_ACK, nullptr); + std::move(callbacks_.front()) + .Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK)); callbacks_.pop_front(); } @@ -222,15 +224,17 @@ class SkiaOutputDeviceBufferQueueTest : public TestOnGpu { std::unique_ptr<SkiaOutputDeviceBufferQueue> onscreen_device = std::make_unique<SkiaOutputDeviceBufferQueue>( - gl_surface_, dependency_.get(), memory_tracker_.get(), - present_callback, shared_image_usage); + std::make_unique<OutputPresenterGL>(gl_surface_, dependency_.get(), + memory_tracker_.get(), + shared_image_usage), + dependency_.get(), memory_tracker_.get(), present_callback); output_device_ = std::move(onscreen_device); } void TearDownOnGpu() override { output_device_.reset(); } - using Image = SkiaOutputDeviceBufferQueue::Image; + using Image = OutputPresenter::Image; const std::vector<std::unique_ptr<Image>>& images() { return output_device_->images_; @@ -279,11 +283,15 @@ class SkiaOutputDeviceBufferQueueTest : public TestOnGpu { (size_t)CountBuffers()); } - Image* PaintAndSchedulePrimaryPlane() { - // Call Begin/EndPaint to ensusre the image is initialized before use. + Image* PaintPrimaryPlane() { std::vector<GrBackendSemaphore> end_semaphores; output_device_->BeginPaint(&end_semaphores); output_device_->EndPaint(); + return current_image(); + } + + Image* PaintAndSchedulePrimaryPlane() { + PaintPrimaryPlane(); SchedulePrimaryPlane(); return current_image(); } @@ -330,7 +338,7 @@ TEST_F_GPU(SkiaOutputDeviceBufferQueueTest, MultipleGetCurrentBufferCalls) { output_device_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), kDefaultFormat, gfx::OVERLAY_TRANSFORM_NONE); EXPECT_NE(0U, memory_tracker().GetSize()); - EXPECT_NE(PaintAndSchedulePrimaryPlane(), nullptr); + EXPECT_NE(PaintPrimaryPlane(), nullptr); EXPECT_NE(0U, memory_tracker().GetSize()); EXPECT_EQ(3, CountBuffers()); auto* fb = current_image(); diff --git a/chromium/components/viz/service/display_embedder/skia_output_device_dawn.cc b/chromium/components/viz/service/display_embedder/skia_output_device_dawn.cc index a2bb693c020..9790a1b1a03 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_device_dawn.cc +++ b/chromium/components/viz/service/display_embedder/skia_output_device_dawn.cc @@ -6,17 +6,11 @@ #include "base/check_op.h" #include "base/notreached.h" -#include "build/build_config.h" #include "components/viz/common/gpu/dawn_context_provider.h" +#include "third_party/dawn/src/include/dawn_native/D3D12Backend.h" #include "ui/gfx/presentation_feedback.h" #include "ui/gfx/vsync_provider.h" - -#if defined(OS_WIN) -#include "third_party/dawn/src/include/dawn_native/D3D12Backend.h" #include "ui/gl/vsync_provider_win.h" -#elif defined(OS_LINUX) -#include "third_party/dawn/src/include/dawn_native/VulkanBackend.h" -#endif namespace viz { @@ -41,7 +35,7 @@ SkiaOutputDeviceDawn::SkiaOutputDeviceDawn( DidSwapBufferCompleteCallback did_swap_buffer_complete_callback) : SkiaOutputDevice(memory_tracker, did_swap_buffer_complete_callback), context_provider_(context_provider), - widget_(widget) { + child_window_(widget) { capabilities_.output_surface_origin = origin; capabilities_.uses_default_gl_framebuffer = false; capabilities_.supports_post_sub_buffer = false; @@ -51,13 +45,16 @@ SkiaOutputDeviceDawn::SkiaOutputDeviceDawn( context_provider_->GetGrContext()->defaultBackendFormat( kSurfaceColorType, GrRenderable::kYes); -#if defined(OS_WIN) - vsync_provider_ = std::make_unique<gl::VSyncProviderWin>(widget_); -#endif + vsync_provider_ = std::make_unique<gl::VSyncProviderWin>(widget); + child_window_.Initialize(); } SkiaOutputDeviceDawn::~SkiaOutputDeviceDawn() = default; +gpu::SurfaceHandle SkiaOutputDeviceDawn::GetChildSurfaceHandle() const { + return child_window_.window(); +} + bool SkiaOutputDeviceDawn::Reshape(const gfx::Size& size, float device_scale_factor, const gfx::ColorSpace& color_space, @@ -86,7 +83,7 @@ void SkiaOutputDeviceDawn::SwapBuffers( std::vector<ui::LatencyInfo> latency_info) { StartSwapBuffers({}); swap_chain_.Present(); - FinishSwapBuffers(gfx::SwapResult::SWAP_ACK, + FinishSwapBuffers(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK), gfx::Size(size_.width(), size_.height()), std::move(latency_info)); @@ -136,13 +133,8 @@ void SkiaOutputDeviceDawn::EndPaint() { } void SkiaOutputDeviceDawn::CreateSwapChainImplementation() { -#if defined(OS_WIN) swap_chain_implementation_ = dawn_native::d3d12::CreateNativeSwapChainImpl( - context_provider_->GetDevice().Get(), widget_); -#else - NOTREACHED(); - ALLOW_UNUSED_LOCAL(widget_); -#endif + context_provider_->GetDevice().Get(), child_window_.window()); } } // namespace viz diff --git a/chromium/components/viz/service/display_embedder/skia_output_device_dawn.h b/chromium/components/viz/service/display_embedder/skia_output_device_dawn.h index 56ef86fe380..3d6bcf821f0 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_device_dawn.h +++ b/chromium/components/viz/service/display_embedder/skia_output_device_dawn.h @@ -5,6 +5,7 @@ #ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_DEVICE_DAWN_H_ #define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_DEVICE_DAWN_H_ +#include "build/build_config.h" #include "components/viz/service/display_embedder/skia_output_device.h" #include "third_party/dawn/src/include/dawn/dawn_wsi.h" #include "third_party/dawn/src/include/dawn/webgpu.h" @@ -13,6 +14,7 @@ #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "ui/gfx/native_widget_types.h" +#include "ui/gl/child_window_win.h" namespace viz { @@ -28,6 +30,8 @@ class SkiaOutputDeviceDawn : public SkiaOutputDevice { DidSwapBufferCompleteCallback did_swap_buffer_complete_callback); ~SkiaOutputDeviceDawn() override; + gpu::SurfaceHandle GetChildSurfaceHandle() const; + // SkiaOutputDevice implementation: bool Reshape(const gfx::Size& size, float device_scale_factor, @@ -45,7 +49,6 @@ class SkiaOutputDeviceDawn : public SkiaOutputDevice { void CreateSwapChainImplementation(); DawnContextProvider* const context_provider_; - gfx::AcceleratedWidget widget_; DawnSwapChainImplementation swap_chain_implementation_; wgpu::SwapChain swap_chain_; wgpu::Texture texture_; @@ -56,6 +59,13 @@ class SkiaOutputDeviceDawn : public SkiaOutputDevice { sk_sp<SkColorSpace> sk_color_space_; GrBackendTexture backend_texture_; + // D3D12 requires that we use flip model swap chains. Flip swap chains + // require that the swap chain be connected with DWM. DWM requires that + // the rendering windows are owned by the process that's currently doing + // the rendering. gl::ChildWindowWin creates and owns a window which is + // reparented by the browser to be a child of its window. + gl::ChildWindowWin child_window_; + DISALLOW_COPY_AND_ASSIGN(SkiaOutputDeviceDawn); }; diff --git a/chromium/components/viz/service/display_embedder/skia_output_device_gl.cc b/chromium/components/viz/service/display_embedder/skia_output_device_gl.cc index 44ce3b99f68..2f80461da3b 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_device_gl.cc +++ b/chromium/components/viz/service/display_embedder/skia_output_device_gl.cc @@ -7,6 +7,7 @@ #include <utility> #include "base/bind_helpers.h" +#include "components/viz/common/gpu/context_lost_reason.h" #include "components/viz/service/display/dc_layer_overlay.h" #include "gpu/command_buffer/common/swap_buffers_complete_params.h" #include "gpu/command_buffer/service/feature_info.h" @@ -25,6 +26,7 @@ #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_surface.h" +#include "ui/gl/gl_utils.h" #include "ui/gl/gl_version_info.h" namespace viz { @@ -192,8 +194,9 @@ void SkiaOutputDeviceGL::SwapBuffers( std::move(latency_info)); gl_surface_->SwapBuffersAsync(std::move(callback), std::move(feedback)); } else { - FinishSwapBuffers(gl_surface_->SwapBuffers(std::move(feedback)), - surface_size, std::move(latency_info)); + gfx::SwapResult result = gl_surface_->SwapBuffers(std::move(feedback)); + FinishSwapBuffers(gfx::SwapCompletionResult(result), surface_size, + std::move(latency_info)); } } @@ -213,12 +216,11 @@ void SkiaOutputDeviceGL::PostSubBuffer( gl_surface_->PostSubBufferAsync(rect.x(), rect.y(), rect.width(), rect.height(), std::move(callback), std::move(feedback)); - } else { - FinishSwapBuffers( - gl_surface_->PostSubBuffer(rect.x(), rect.y(), rect.width(), - rect.height(), std::move(feedback)), - surface_size, std::move(latency_info)); + gfx::SwapResult result = gl_surface_->PostSubBuffer( + rect.x(), rect.y(), rect.width(), rect.height(), std::move(feedback)); + FinishSwapBuffers(gfx::SwapCompletionResult(result), surface_size, + std::move(latency_info)); } } @@ -237,22 +239,23 @@ void SkiaOutputDeviceGL::CommitOverlayPlanes( gl_surface_->CommitOverlayPlanesAsync(std::move(callback), std::move(feedback)); } else { - FinishSwapBuffers(gl_surface_->CommitOverlayPlanes(std::move(feedback)), - surface_size, std::move(latency_info)); + FinishSwapBuffers( + gfx::SwapCompletionResult( + gl_surface_->CommitOverlayPlanes(std::move(feedback))), + surface_size, std::move(latency_info)); } } void SkiaOutputDeviceGL::DoFinishSwapBuffers( const gfx::Size& size, std::vector<ui::LatencyInfo> latency_info, - gfx::SwapResult result, - std::unique_ptr<gfx::GpuFence> gpu_fence) { - DCHECK(!gpu_fence); - FinishSwapBuffers(result, size, latency_info); + gfx::SwapCompletionResult result) { + DCHECK(!result.gpu_fence); + FinishSwapBuffers(std::move(result), size, latency_info); } -void SkiaOutputDeviceGL::SetDrawRectangle(const gfx::Rect& draw_rectangle) { - gl_surface_->SetDrawRectangle(draw_rectangle); +bool SkiaOutputDeviceGL::SetDrawRectangle(const gfx::Rect& draw_rectangle) { + return gl_surface_->SetDrawRectangle(draw_rectangle); } void SkiaOutputDeviceGL::SetGpuVSyncEnabled(bool enabled) { diff --git a/chromium/components/viz/service/display_embedder/skia_output_device_gl.h b/chromium/components/viz/service/display_embedder/skia_output_device_gl.h index d8491578a4b..a1318dbbaec 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_device_gl.h +++ b/chromium/components/viz/service/display_embedder/skia_output_device_gl.h @@ -20,10 +20,6 @@ class GLImage; class GLSurface; } // namespace gl -namespace gfx { -class GpuFence; -} // namespace gfx - namespace gpu { class MailboxManager; class SharedContextState; @@ -63,7 +59,7 @@ class SkiaOutputDeviceGL final : public SkiaOutputDevice { std::vector<ui::LatencyInfo> latency_info) override; void CommitOverlayPlanes(BufferPresentedCallback feedback, std::vector<ui::LatencyInfo> latency_info) override; - void SetDrawRectangle(const gfx::Rect& draw_rectangle) override; + bool SetDrawRectangle(const gfx::Rect& draw_rectangle) override; void SetGpuVSyncEnabled(bool enabled) override; #if defined(OS_WIN) void SetEnableDCLayers(bool enable) override; @@ -80,8 +76,7 @@ class SkiaOutputDeviceGL final : public SkiaOutputDevice { // operation void DoFinishSwapBuffers(const gfx::Size& size, std::vector<ui::LatencyInfo> latency_info, - gfx::SwapResult result, - std::unique_ptr<gfx::GpuFence>); + gfx::SwapCompletionResult result); scoped_refptr<gl::GLImage> GetGLImageForMailbox(const gpu::Mailbox& mailbox); diff --git a/chromium/components/viz/service/display_embedder/skia_output_device_offscreen.cc b/chromium/components/viz/service/display_embedder/skia_output_device_offscreen.cc index 7b4708d94c9..b846ee6db2a 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_device_offscreen.cc +++ b/chromium/components/viz/service/display_embedder/skia_output_device_offscreen.cc @@ -63,7 +63,7 @@ void SkiaOutputDeviceOffscreen::SwapBuffers( DCHECK(backend_texture_.isValid()); StartSwapBuffers(std::move(feedback)); - FinishSwapBuffers(gfx::SwapResult::SWAP_ACK, + FinishSwapBuffers(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK), gfx::Size(size_.width(), size_.height()), std::move(latency_info)); } diff --git a/chromium/components/viz/service/display_embedder/skia_output_device_vulkan.cc b/chromium/components/viz/service/display_embedder/skia_output_device_vulkan.cc index 930ec126a14..6b89f737bdc 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_device_vulkan.cc +++ b/chromium/components/viz/service/display_embedder/skia_output_device_vulkan.cc @@ -6,6 +6,7 @@ #include <utility> +#include "base/logging.h" #include "build/build_config.h" #include "components/viz/common/gpu/vulkan_context_provider.h" #include "gpu/command_buffer/service/memory_tracking.h" @@ -101,22 +102,37 @@ void SkiaOutputDeviceVulkan::PostSubBuffer( #endif StartSwapBuffers(std::move(feedback)); - auto image_size = vulkan_surface_->image_size(); - gfx::SwapResult result = gfx::SwapResult::SWAP_ACK; - // If the swapchain is new created, but rect doesn't cover the whole buffer, - // we will still present it even it causes a artifact in this frame and - // recovered when the next frame is presented. We do that because the old - // swapchain's present thread is blocked on waiting a reply from xserver, and - // presenting a new image with the new create swapchain will somehow makes - // xserver send a reply to us, and then unblock the old swapchain's present - // thread. So the old swapchain can be destroyed properly. - if (!rect.IsEmpty()) - result = vulkan_surface_->PostSubBuffer(rect); - if (is_new_swapchain_) { - is_new_swapchain_ = false; - result = gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS; + + if (is_new_swap_chain_ && rect == gfx::Rect(vulkan_surface_->image_size())) { + is_new_swap_chain_ = false; + } + + if (!is_new_swap_chain_) { + auto image_index = vulkan_surface_->swap_chain()->current_image_index(); + for (size_t i = 0; i < damage_of_images_.size(); ++i) { + if (i == image_index) { + damage_of_images_[i] = gfx::Rect(); + } else { + damage_of_images_[i].Union(rect); + } + } + } + + if (!rect.IsEmpty()) { + // If the swapchain is new created, but rect doesn't cover the whole buffer, + // we will still present it even it causes a artifact in this frame and + // recovered when the next frame is presented. We do that because the old + // swapchain's present thread is blocked on waiting a reply from xserver, + // and presenting a new image with the new create swapchain will somehow + // makes xserver send a reply to us, and then unblock the old swapchain's + // present thread. So the old swapchain can be destroyed properly. + vulkan_surface_->PostSubBufferAsync( + rect, base::BindOnce(&SkiaOutputDeviceVulkan::OnPostSubBufferFinished, + weak_ptr_factory_.GetWeakPtr(), + std::move(latency_info))); + } else { + OnPostSubBufferFinished(std::move(latency_info), gfx::SwapResult::SWAP_ACK); } - FinishSwapBuffers(result, image_size, std::move(latency_info)); } SkSurface* SkiaOutputDeviceVulkan::BeginPaint( @@ -241,7 +257,7 @@ bool SkiaOutputDeviceVulkan::Initialize() { vulkan_surface_ = std::move(vulkan_surface); capabilities_.uses_default_gl_framebuffer = false; - capabilities_.max_frames_pending = vulkan_surface_->image_count() - 1; + capabilities_.max_frames_pending = 1; // Vulkan FIFO swap chain should return vk images in presenting order, so set // preserve_buffer_content & supports_post_sub_buffer to true to let // SkiaOutputBufferImpl to manager damages. @@ -249,6 +265,12 @@ bool SkiaOutputDeviceVulkan::Initialize() { capabilities_.output_surface_origin = gfx::SurfaceOrigin::kTopLeft; capabilities_.supports_post_sub_buffer = true; capabilities_.supports_pre_transform = true; + // We don't know the number of buffers until the VulkanSwapChain is + // initialized, so set it to 0. Since |damage_area_from_skia_output_device| is + // assigned to true, so |number_of_buffers| will not be used for tracking + // framebuffer damages. + capabilities_.number_of_buffers = 0; + capabilities_.damage_area_from_skia_output_device = true; const auto surface_format = vulkan_surface_->surface_format().format; DCHECK(surface_format == VK_FORMAT_B8G8R8A8_UNORM || @@ -277,15 +299,34 @@ bool SkiaOutputDeviceVulkan::RecreateSwapChain( for (const auto& sk_surface_size_pair : sk_surface_size_pairs_) { memory_type_tracker_->TrackMemFree(sk_surface_size_pair.bytes_allocated); } + auto num_images = vulkan_surface_->swap_chain()->num_images(); sk_surface_size_pairs_.clear(); - sk_surface_size_pairs_.resize(vulkan_surface_->swap_chain()->num_images()); + sk_surface_size_pairs_.resize(num_images); color_space_ = std::move(color_space); - is_new_swapchain_ = true; + damage_of_images_.resize(num_images); + for (auto& damage : damage_of_images_) + damage = gfx::Rect(vulkan_surface_->image_size()); + is_new_swap_chain_ = true; } return true; } +void SkiaOutputDeviceVulkan::OnPostSubBufferFinished( + std::vector<ui::LatencyInfo> latency_info, + gfx::SwapResult result) { + if (result == gfx::SwapResult::SWAP_ACK) { + auto image_index = vulkan_surface_->swap_chain()->current_image_index(); + FinishSwapBuffers(gfx::SwapCompletionResult(result), + vulkan_surface_->image_size(), std::move(latency_info), + damage_of_images_[image_index]); + } else { + FinishSwapBuffers(gfx::SwapCompletionResult(result), + vulkan_surface_->image_size(), std::move(latency_info), + gfx::Rect(vulkan_surface_->image_size())); + } +} + SkiaOutputDeviceVulkan::SkSurfaceSizePair::SkSurfaceSizePair() = default; SkiaOutputDeviceVulkan::SkSurfaceSizePair::SkSurfaceSizePair( const SkSurfaceSizePair& other) = default; diff --git a/chromium/components/viz/service/display_embedder/skia_output_device_vulkan.h b/chromium/components/viz/service/display_embedder/skia_output_device_vulkan.h index af859a52785..4ae2ad690d4 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_device_vulkan.h +++ b/chromium/components/viz/service/display_embedder/skia_output_device_vulkan.h @@ -9,6 +9,7 @@ #include <vector> #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "base/optional.h" #include "base/util/type_safety/pass_key.h" #include "build/build_config.h" @@ -72,6 +73,8 @@ class SkiaOutputDeviceVulkan final : public SkiaOutputDevice { bool RecreateSwapChain(const gfx::Size& size, sk_sp<SkColorSpace> color_space, gfx::OverlayTransform transform); + void OnPostSubBufferFinished(std::vector<ui::LatencyInfo> latency_info, + gfx::SwapResult result); VulkanContextProvider* const context_provider_; @@ -88,7 +91,14 @@ class SkiaOutputDeviceVulkan final : public SkiaOutputDevice { std::vector<SkSurfaceSizePair> sk_surface_size_pairs_; sk_sp<SkColorSpace> color_space_; - bool is_new_swapchain_ = true; + + // The swapchain is new created without a frame which convers the whole area + // of it. + bool is_new_swap_chain_ = true; + + std::vector<gfx::Rect> damage_of_images_; + + base::WeakPtrFactory<SkiaOutputDeviceVulkan> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(SkiaOutputDeviceVulkan); }; diff --git a/chromium/components/viz/service/display_embedder/skia_output_device_webview.cc b/chromium/components/viz/service/display_embedder/skia_output_device_webview.cc index a0526224e33..e8aa380c9f9 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_device_webview.cc +++ b/chromium/components/viz/service/display_embedder/skia_output_device_webview.cc @@ -80,8 +80,9 @@ void SkiaOutputDeviceWebView::SwapBuffers( gfx::Size surface_size = gfx::Size(sk_surface_->width(), sk_surface_->height()); - FinishSwapBuffers(gl_surface_->SwapBuffers(std::move(feedback)), surface_size, - std::move(latency_info)); + FinishSwapBuffers( + gfx::SwapCompletionResult(gl_surface_->SwapBuffers(std::move(feedback))), + surface_size, std::move(latency_info)); } SkSurface* SkiaOutputDeviceWebView::BeginPaint( diff --git a/chromium/components/viz/service/display_embedder/skia_output_device_x11.cc b/chromium/components/viz/service/display_embedder/skia_output_device_x11.cc index 2fc506b3fff..c30693c52c4 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_device_x11.cc +++ b/chromium/components/viz/service/display_embedder/skia_output_device_x11.cc @@ -6,6 +6,7 @@ #include <utility> +#include "base/logging.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/vk/GrVkTypes.h" @@ -27,10 +28,11 @@ SkiaOutputDeviceX11::SkiaOutputDeviceX11( did_swap_buffer_complete_callback), display_(gfx::GetXDisplay()), widget_(widget), - gc_(XCreateGC(display_, widget_, 0, nullptr)) { - int result = XGetWindowAttributes(display_, widget_, &attributes_); + gc_(XCreateGC(display_, static_cast<uint32_t>(widget_), 0, nullptr)) { + int result = XGetWindowAttributes(display_, static_cast<uint32_t>(widget_), + &attributes_); LOG_IF(FATAL, !result) << "XGetWindowAttributes failed for window " - << widget_; + << static_cast<uint32_t>(widget_); bpp_ = gfx::BitsPerPixelForPixmapDepth(display_, attributes_.depth); support_rendr_ = ui::QueryRenderSupport(display_); @@ -82,14 +84,15 @@ void SkiaOutputDeviceX11::PostSubBuffer( if (bpp_ == 32 || bpp_ == 16) { // gfx::PutARGBImage() only supports 16 and 32 bpp. // TODO(penghuang): Switch to XShmPutImage. - gfx::PutARGBImage(display_, attributes_.visual, attributes_.depth, widget_, - gc_, static_cast<const uint8_t*>(sk_pixmap.addr()), + gfx::PutARGBImage(display_, attributes_.visual, attributes_.depth, + static_cast<uint32_t>(widget_), gc_, + static_cast<const uint8_t*>(sk_pixmap.addr()), rect.width(), rect.height(), 0 /* src_x */, 0 /* src_y */, rect.x() /* dst_x */, rect.y() /* dst_y */, rect.width(), rect.height()); } else if (support_rendr_) { - Pixmap pixmap = - XCreatePixmap(display_, widget_, rect.width(), rect.height(), 32); + Pixmap pixmap = XCreatePixmap(display_, static_cast<uint32_t>(widget_), + rect.width(), rect.height(), 32); GC gc = XCreateGC(display_, pixmap, 0, nullptr); XImage image = {}; @@ -97,10 +100,10 @@ void SkiaOutputDeviceX11::PostSubBuffer( image.height = rect.height(); image.depth = 32; image.bits_per_pixel = 32; - image.format = ZPixmap; - image.byte_order = LSBFirst; + image.format = static_cast<int>(x11::ImageFormat::ZPixmap); + image.byte_order = static_cast<int>(x11::ImageOrder::LSBFirst); image.bitmap_unit = 8; - image.bitmap_bit_order = LSBFirst; + image.bitmap_bit_order = static_cast<int>(x11::ImageOrder::LSBFirst); image.bytes_per_line = sk_pixmap.rowBytes(); image.red_mask = 0xff << SK_R32_SHIFT; @@ -115,8 +118,8 @@ void SkiaOutputDeviceX11::PostSubBuffer( display_, pixmap, ui::GetRenderARGB32Format(display_), 0, nullptr); XRenderPictFormat* pictformat = XRenderFindVisualFormat(display_, attributes_.visual); - Picture dest_picture = - XRenderCreatePicture(display_, widget_, pictformat, 0, nullptr); + Picture dest_picture = XRenderCreatePicture( + display_, static_cast<uint32_t>(widget_), pictformat, 0, nullptr); XRenderComposite(display_, PictOpSrc, // op picture, // src @@ -137,7 +140,7 @@ void SkiaOutputDeviceX11::PostSubBuffer( NOTIMPLEMENTED(); } XFlush(display_); - FinishSwapBuffers(gfx::SwapResult::SWAP_ACK, + FinishSwapBuffers(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK), gfx::Size(sk_surface_->width(), sk_surface_->height()), std::move(latency_info)); } diff --git a/chromium/components/viz/service/display_embedder/skia_output_surface_dependency.h b/chromium/components/viz/service/display_embedder/skia_output_surface_dependency.h index d3d814edf9b..a85a0cf5c1c 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_surface_dependency.h +++ b/chromium/components/viz/service/display_embedder/skia_output_surface_dependency.h @@ -120,6 +120,10 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceDependency { bool IsUsingDawn() const { return gr_context_type() == gpu::GrContextType::kDawn; } + + bool IsUsingMetal() const { + return gr_context_type() == gpu::GrContextType::kMetal; + } }; } // namespace viz diff --git a/chromium/components/viz/service/display_embedder/skia_output_surface_impl.cc b/chromium/components/viz/service/display_embedder/skia_output_surface_impl.cc index 36404ea695d..f453eae9c38 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_surface_impl.cc +++ b/chromium/components/viz/service/display_embedder/skia_output_surface_impl.cc @@ -204,10 +204,14 @@ void SkiaOutputSurfaceImpl::Reshape(const gfx::Size& size, // SetDrawRectangle() will need to be called at the new size. has_set_draw_rectangle_for_frame_ = false; - // Reshape will damage all buffers. - current_buffer_ = 0u; - for (auto& damage : damage_of_buffers_) - damage = gfx::Rect(size); + if (use_damage_area_from_skia_output_device_) { + damage_of_current_buffer_ = gfx::Rect(size); + } else { + // Reshape will damage all buffers. + current_buffer_ = 0u; + for (auto& damage : damage_of_buffers_) + damage = gfx::Rect(size); + } // impl_on_gpu_ is released on the GPU thread by a posted task from // SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained. @@ -429,21 +433,15 @@ void SkiaOutputSurfaceImpl::SwapBuffers(OutputSurfaceFrame frame) { } void SkiaOutputSurfaceImpl::SwapBuffersSkipped() { - if (deferred_framebuffer_draw_closure_) { - // Run the task to draw the root RenderPass on the GPU thread. If we aren't - // going to swap buffers and there are no CopyOutputRequests on the root - // RenderPass we don't strictly need to draw. However, we still need to - // PostTask to the GPU thread to deal with freeing resources and running - // callbacks. This is infrequent and all the work is already done in - // FinishPaintCurrentFrame() so use the same path. - auto task = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::SwapBuffersSkipped, - base::Unretained(impl_on_gpu_.get()), - std::move(deferred_framebuffer_draw_closure_)); - ScheduleGpuTask(std::move(task), std::move(resource_sync_tokens_)); - - // TODO(vasilyt): reuse root recorder - RecreateRootRecorder(); - } + // PostTask to the GPU thread to deal with freeing resources and running + // callbacks. + auto task = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::SwapBuffersSkipped, + base::Unretained(impl_on_gpu_.get()), + std::move(deferred_framebuffer_draw_closure_)); + ScheduleGpuTask(std::move(task), std::move(resource_sync_tokens_)); + + // TODO(vasilyt): reuse root recorder + RecreateRootRecorder(); } void SkiaOutputSurfaceImpl::ScheduleOutputSurfaceAsOverlay( @@ -491,7 +489,6 @@ gpu::SyncToken SkiaOutputSurfaceImpl::SubmitPaint( sync_token.SetVerifyFlush(); auto ddl = current_paint_->recorder()->detach(); - DCHECK(ddl); // impl_on_gpu_ is released on the GPU thread by a posted task from // SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained. @@ -562,7 +559,9 @@ sk_sp<SkImage> SkiaOutputSurfaceImpl::MakePromiseSkImageFromRenderPass( image_context->color_space(), Fulfill, DoNothing, DoNothing, image_context.get()), backend_format); - DCHECK(image_context->has_image()); + if (!image_context->has_image()) { + return nullptr; + } } images_in_current_paint_.push_back(image_context.get()); return image_context->image(); @@ -601,14 +600,22 @@ void SkiaOutputSurfaceImpl::CopyOutput( const gfx::ColorSpace& color_space, std::unique_ptr<CopyOutputRequest> request) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - if (!request->has_result_task_runner()) - request->set_result_task_runner(base::ThreadTaskRunnerHandle::Get()); - auto callback = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::CopyOutput, - base::Unretained(impl_on_gpu_.get()), id, - geometry, color_space, std::move(request), - std::move(deferred_framebuffer_draw_closure_)); - ScheduleGpuTask(std::move(callback), std::move(resource_sync_tokens_)); + // Defer CopyOutput for root render pass with draw framebuffer to + // SwapBuffers() or SwapBuffersSkipped(). + if (!id) { + deferred_framebuffer_draw_closure_ = base::BindOnce( + &SkiaOutputSurfaceImplOnGpu::CopyOutput, + base::Unretained(impl_on_gpu_.get()), id, geometry, color_space, + std::move(request), std::move(deferred_framebuffer_draw_closure_)); + } else { + DCHECK(!deferred_framebuffer_draw_closure_); + auto callback = base::BindOnce( + base::IgnoreResult(&SkiaOutputSurfaceImplOnGpu::CopyOutput), + base::Unretained(impl_on_gpu_.get()), id, geometry, color_space, + std::move(request), base::OnceCallback<bool()>()); + ScheduleGpuTask(std::move(callback), std::move(resource_sync_tokens_)); + } } void SkiaOutputSurfaceImpl::ScheduleOverlays( @@ -692,7 +699,17 @@ bool SkiaOutputSurfaceImpl::Initialize() { if (capabilities_.preserve_buffer_content && capabilities_.supports_post_sub_buffer) { capabilities_.only_invalidates_damage_rect = false; - damage_of_buffers_.resize(capabilities_.max_frames_pending + 1); + capabilities_.supports_target_damage = true; + // If there is only one pending frame, then we can use damage area hint from + // SkiaOutputDevice, otherwise we have to track damage area in + // SkiaOutputSurfaceImpl. + if (capabilities_.max_frames_pending == 1 && + capabilities_.damage_area_from_skia_output_device) { + use_damage_area_from_skia_output_device_ = true; + damage_of_current_buffer_ = gfx::Rect(); + } else { + damage_of_buffers_.resize(capabilities_.number_of_buffers); + } } return result; @@ -769,7 +786,24 @@ SkiaOutputSurfaceImpl::CreateSkSurfaceCharacterization( impl_on_gpu_->GetGpuPreferences().enforce_vulkan_protected_memory ? GrProtected::kYes : GrProtected::kNo); - DCHECK(characterization.isValid()); + VkFormat vk_format = VK_FORMAT_UNDEFINED; + LOG_IF(DFATAL, !characterization.isValid()) + << "\n surface_size=" << surface_size.ToString() + << "\n format=" << static_cast<int>(format) + << "\n color_type=" << static_cast<int>(color_type) + << "\n backend_format.isValid()=" << backend_format.isValid() + << "\n backend_format.backend()=" + << static_cast<int>(backend_format.backend()) + << "\n backend_format.asGLFormat()=" + << static_cast<int>(backend_format.asGLFormat()) + << "\n backend_format.asVkFormat()=" + << static_cast<int>(backend_format.asVkFormat(&vk_format)) + << "\n backend_format.asVkFormat() vk_format=" + << static_cast<int>(vk_format) + << "\n surface_origin=" << static_cast<int>(surface_origin) + << "\n willGlFBO0=" << capabilities_.uses_default_gl_framebuffer + << "\n isProtected=" + << impl_on_gpu_->GetGpuPreferences().enforce_vulkan_protected_memory; return characterization; } @@ -806,6 +840,11 @@ void SkiaOutputSurfaceImpl::DidSwapBuffersComplete( damage = gfx::Rect(size_); } + if (use_damage_area_from_skia_output_device_) { + damage_of_current_buffer_ = params.frame_buffer_damage_area; + DCHECK(damage_of_current_buffer_); + } + if (!params.texture_in_use_responses.empty()) client_->DidReceiveTextureInUseResponses(params.texture_in_use_responses); if (!params.ca_layer_params.is_empty) @@ -881,6 +920,10 @@ GrBackendFormat SkiaOutputSurfaceImpl::GetGrBackendFormatForTexture( wgpu::TextureFormat format = ToDawnFormat(resource_format); return GrBackendFormat::MakeDawn(format); #endif + } else if (dependency_->IsUsingMetal()) { +#if defined(OS_MACOSX) + return GrBackendFormat::MakeMtl(ToMTLPixelFormat(resource_format)); +#endif } else { DCHECK(!ycbcr_info); // Convert internal format from GLES2 to platform GL. @@ -988,6 +1031,11 @@ SkiaOutputSurfaceImpl::GetGpuTaskSchedulerHelper() { } gfx::Rect SkiaOutputSurfaceImpl::GetCurrentFramebufferDamage() const { + if (use_damage_area_from_skia_output_device_) { + DCHECK(damage_of_current_buffer_); + return *damage_of_current_buffer_; + } + if (damage_of_buffers_.empty()) return gfx::Rect(); diff --git a/chromium/components/viz/service/display_embedder/skia_output_surface_impl.h b/chromium/components/viz/service/display_embedder/skia_output_surface_impl.h index e7738f180a5..d3369b11a64 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_surface_impl.h +++ b/chromium/components/viz/service/display_embedder/skia_output_surface_impl.h @@ -269,6 +269,9 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface { // to avoid the expense of posting a task and calling MakeCurrent. base::OnceCallback<bool()> deferred_framebuffer_draw_closure_; + bool use_damage_area_from_skia_output_device_ = false; + // Damage area of the current buffer. Differ to the last submit buffer. + base::Optional<gfx::Rect> damage_of_current_buffer_; // Current buffer index. size_t current_buffer_ = 0; // Damage area of the buffer. Differ to the last submit buffer. diff --git a/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc index 1c27142a2d1..7d6ebac68f7 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc +++ b/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc @@ -26,6 +26,7 @@ #include "components/viz/service/display/texture_deleter.h" #include "components/viz/service/display_embedder/direct_context_provider.h" #include "components/viz/service/display_embedder/image_context_impl.h" +#include "components/viz/service/display_embedder/output_presenter_gl.h" #include "components/viz/service/display_embedder/skia_output_device.h" #include "components/viz/service/display_embedder/skia_output_device_buffer_queue.h" #include "components/viz/service/display_embedder/skia_output_device_gl.h" @@ -83,8 +84,18 @@ #if BUILDFLAG(SKIA_USE_DAWN) #include "components/viz/common/gpu/dawn_context_provider.h" +#if defined(OS_WIN) #include "components/viz/service/display_embedder/skia_output_device_dawn.h" #endif +#endif + +#if defined(USE_OZONE) || defined(USE_X11) +#include "ui/base/ui_base_features.h" +#endif + +#if defined(OS_FUCHSIA) +#include "components/viz/service/display_embedder/output_presenter_fuchsia.h" +#endif namespace viz { @@ -278,36 +289,34 @@ void OnRGBAReadbackDone( } // namespace -class SkiaOutputSurfaceImplOnGpu::ScopedPromiseImageAccess { - public: - ScopedPromiseImageAccess(SkiaOutputSurfaceImplOnGpu* impl_on_gpu, - std::vector<ImageContextImpl*> image_contexts) - : impl_on_gpu_(impl_on_gpu), image_contexts_(std::move(image_contexts)) { - begin_semaphores_.reserve(image_contexts_.size()); - // We may need one more space for the swap buffer semaphore. - end_semaphores_.reserve(image_contexts_.size() + 1); - impl_on_gpu_->BeginAccessImages(image_contexts_, &begin_semaphores_, - &end_semaphores_); - } - - ~ScopedPromiseImageAccess() { - impl_on_gpu_->EndAccessImages(image_contexts_); - } - - std::vector<GrBackendSemaphore>& begin_semaphores() { - return begin_semaphores_; - } +SkiaOutputSurfaceImplOnGpu::PromiseImageAccessHelper::PromiseImageAccessHelper( + SkiaOutputSurfaceImplOnGpu* impl_on_gpu) + : impl_on_gpu_(impl_on_gpu) {} - std::vector<GrBackendSemaphore>& end_semaphores() { return end_semaphores_; } +SkiaOutputSurfaceImplOnGpu::PromiseImageAccessHelper:: + ~PromiseImageAccessHelper() { + CHECK(image_contexts_.empty()); +} - private: - SkiaOutputSurfaceImplOnGpu* const impl_on_gpu_; - std::vector<ImageContextImpl*> image_contexts_; - std::vector<GrBackendSemaphore> begin_semaphores_; - std::vector<GrBackendSemaphore> end_semaphores_; +void SkiaOutputSurfaceImplOnGpu::PromiseImageAccessHelper::BeginAccess( + std::vector<ImageContextImpl*> image_contexts, + std::vector<GrBackendSemaphore>* begin_semaphores, + std::vector<GrBackendSemaphore>* end_semaphores) { + DCHECK(begin_semaphores); + DCHECK(end_semaphores); + begin_semaphores->reserve(image_contexts.size()); + // We may need one more space for the swap buffer semaphore. + end_semaphores->reserve(image_contexts.size() + 1); + image_contexts_.reserve(image_contexts.size() + image_contexts_.size()); + image_contexts_.insert(image_contexts.begin(), image_contexts.end()); + impl_on_gpu_->BeginAccessImages(std::move(image_contexts), begin_semaphores, + end_semaphores); +} - DISALLOW_COPY_AND_ASSIGN(ScopedPromiseImageAccess); -}; +void SkiaOutputSurfaceImplOnGpu::PromiseImageAccessHelper::EndAccess() { + impl_on_gpu_->EndAccessImages(image_contexts_); + image_contexts_.clear(); +} // Skia gr_context() and |context_provider_| share an underlying GLContext. // Each of them caches some GL state. Interleaving usage could make cached @@ -599,7 +608,9 @@ class DirectContextProviderDelegateImpl : public DirectContextProviderDelegate, #if defined(OS_FUCHSIA) void RegisterSysmemBufferCollection(gfx::SysmemBufferCollectionId id, - zx::channel token) override { + zx::channel token, + gfx::BufferFormat format, + gfx::BufferUsage usage) override { NOTREACHED(); } @@ -621,6 +632,10 @@ class DirectContextProviderDelegateImpl : public DirectContextProviderDelegate, return sync_token; } + void WaitSyncToken(const gpu::SyncToken& sync_token) override { + NOTREACHED(); + } + void Flush() override { // No need to flush in this implementation. } @@ -810,6 +825,8 @@ SkiaOutputSurfaceImplOnGpu::SkiaOutputSurfaceImplOnGpu( dawn_context_provider_(dependency_->GetDawnContextProvider()), renderer_settings_(renderer_settings), sequence_id_(sequence_id), + did_swap_buffer_complete_callback_( + std::move(did_swap_buffer_complete_callback)), context_lost_callback_(std::move(context_lost_callback)), gpu_vsync_callback_(std::move(gpu_vsync_callback)), gpu_preferences_(dependency_->GetGpuPreferences()), @@ -818,8 +835,6 @@ SkiaOutputSurfaceImplOnGpu::SkiaOutputSurfaceImplOnGpu( DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); weak_ptr_ = weak_ptr_factory_.GetWeakPtr(); - did_swap_buffer_complete_callback_ = CreateSafeRepeatingCallback( - weak_ptr_, std::move(did_swap_buffer_complete_callback)); buffer_presented_callback_ = CreateSafeRepeatingCallback( weak_ptr_, std::move(buffer_presented_callback)); } @@ -833,7 +848,7 @@ SkiaOutputSurfaceImplOnGpu::~SkiaOutputSurfaceImplOnGpu() { gl::ScopedProgressReporter scoped_progress_reporter( context_state_->progress_reporter()); // This ensures any outstanding callbacks for promise images are performed. - gr_context()->flush(); + gr_context()->flushAndSubmit(); release_current_last_.emplace(gl_surface_, context_state_); } @@ -884,14 +899,23 @@ bool SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame( base::Optional<gfx::Rect> draw_rectangle) { TRACE_EVENT0("viz", "SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame"); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DCHECK(ddl); DCHECK(!scoped_output_device_paint_); - if (!MakeCurrent(true /* need_fbo0 */)) + bool need_fbo0 = gl_surface_ && !gl_surface_->IsSurfaceless(); + if (!MakeCurrent(need_fbo0)) + return false; + + if (!ddl) { + MarkContextLost(CONTEXT_LOST_UNKNOWN); return false; + } - if (draw_rectangle) - output_device_->SetDrawRectangle(*draw_rectangle); + if (draw_rectangle) { + if (!output_device_->SetDrawRectangle(*draw_rectangle)) { + MarkContextLost( + ContextLostReason::CONTEXT_LOST_SET_DRAW_RECTANGLE_FAILED); + } + } // We do not reset scoped_output_device_paint_ after drawing the ddl until // SwapBuffers() is called, because we may need access to output_sk_surface() @@ -910,12 +934,13 @@ bool SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame( gpu::kInProcessCommandBufferClientId); } - ScopedPromiseImageAccess scoped_promise_image_access( - this, std::move(image_contexts)); - if (!scoped_promise_image_access.begin_semaphores().empty()) { - auto result = output_sk_surface()->wait( - scoped_promise_image_access.begin_semaphores().size(), - scoped_promise_image_access.begin_semaphores().data()); + std::vector<GrBackendSemaphore> begin_semaphores; + std::vector<GrBackendSemaphore> end_semaphores; + promise_image_access_helper_.BeginAccess( + std::move(image_contexts), &begin_semaphores, &end_semaphores); + if (!begin_semaphores.empty()) { + auto result = output_sk_surface()->wait(begin_semaphores.size(), + begin_semaphores.data()); DCHECK(result); } @@ -941,22 +966,16 @@ bool SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame( &paint); } - GrFlushInfo flush_info; - flush_info.fFlags = kNone_GrFlushFlags; - auto end_paint_semaphores = scoped_output_device_paint_->TakeEndPaintSemaphores(); + end_semaphores.insert(end_semaphores.end(), end_paint_semaphores.begin(), + end_paint_semaphores.end()); - end_paint_semaphores.insert( - end_paint_semaphores.end(), - std::make_move_iterator( - scoped_promise_image_access.end_semaphores().begin()), - std::make_move_iterator( - scoped_promise_image_access.end_semaphores().end())); - - // update the size and data pointer - flush_info.fNumSemaphores = end_paint_semaphores.size(); - flush_info.fSignalSemaphores = end_paint_semaphores.data(); + GrFlushInfo flush_info = { + .fFlags = kNone_GrFlushFlags, + .fNumSemaphores = end_semaphores.size(), + .fSignalSemaphores = end_semaphores.data(), + }; gpu::AddVulkanCleanupTaskForSkiaFlush(vulkan_context_provider_, &flush_info); @@ -972,8 +991,7 @@ bool SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame( } if (result != GrSemaphoresSubmitted::kYes && - !(scoped_promise_image_access.begin_semaphores().empty() && - end_paint_semaphores.empty())) { + !(begin_semaphores.empty() && end_semaphores.empty())) { // TODO(penghuang): handle vulkan device lost. DLOG(ERROR) << "output_sk_surface()->flush() failed."; return false; @@ -1007,6 +1025,8 @@ void SkiaOutputSurfaceImplOnGpu::SwapBuffers( } DCHECK(output_device_); + gr_context()->submit(); + promise_image_access_helper_.EndAccess(); scoped_output_device_paint_.reset(); if (output_surface_plane_) { @@ -1049,12 +1069,15 @@ void SkiaOutputSurfaceImplOnGpu::SwapBuffers( void SkiaOutputSurfaceImplOnGpu::SwapBuffersSkipped( base::OnceCallback<bool()> deferred_framebuffer_draw_closure) { - std::move(deferred_framebuffer_draw_closure).Run(); - + if (deferred_framebuffer_draw_closure) + std::move(deferred_framebuffer_draw_closure).Run(); + gr_context()->submit(); + promise_image_access_helper_.EndAccess(); // Perform cleanup that would have otherwise happened in SwapBuffers(). scoped_output_device_paint_.reset(); context_state_->UpdateSkiaOwnedMemorySize(); destroy_after_swap_.clear(); + #if BUILDFLAG(ENABLE_VULKAN) if (is_using_vulkan()) gpu::ReportQueueSubmitPerSwapBuffers(); @@ -1071,8 +1094,13 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass( DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(ddl); - if (!MakeCurrent(true /* need_fbo0 */)) + if (!MakeCurrent(false /* need_fbo0 */)) + return; + + if (!ddl) { + MarkContextLost(CONTEXT_LOST_UNKNOWN); return; + } PullTextureUpdates(std::move(sync_tokens)); @@ -1089,31 +1117,29 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass( cache_use.emplace(dependency_->GetGrShaderCache(), gpu::kInProcessCommandBufferClientId); } - ScopedPromiseImageAccess scoped_promise_image_access( - this, std::move(image_contexts)); - if (!scoped_promise_image_access.begin_semaphores().empty()) { - auto result = offscreen.surface()->wait( - scoped_promise_image_access.begin_semaphores().size(), - scoped_promise_image_access.begin_semaphores().data()); + std::vector<GrBackendSemaphore> begin_semaphores; + std::vector<GrBackendSemaphore> end_semaphores; + promise_image_access_helper_.BeginAccess( + std::move(image_contexts), &begin_semaphores, &end_semaphores); + if (!begin_semaphores.empty()) { + auto result = offscreen.surface()->wait(begin_semaphores.size(), + begin_semaphores.data()); DCHECK(result); } offscreen.surface()->draw(ddl.get()); destroy_after_swap_.emplace_back(std::move(ddl)); - GrFlushInfo flush_info; - flush_info.fFlags = kNone_GrFlushFlags; - flush_info.fNumSemaphores = - scoped_promise_image_access.end_semaphores().size(); - flush_info.fSignalSemaphores = - scoped_promise_image_access.end_semaphores().data(); - + GrFlushInfo flush_info = { + .fFlags = kNone_GrFlushFlags, + .fNumSemaphores = end_semaphores.size(), + .fSignalSemaphores = end_semaphores.data(), + }; gpu::AddVulkanCleanupTaskForSkiaFlush(vulkan_context_provider_, &flush_info); auto result = offscreen.surface()->flush( SkSurface::BackendSurfaceAccess::kNoAccess, flush_info); if (result != GrSemaphoresSubmitted::kYes && - !(scoped_promise_image_access.begin_semaphores().empty() && - scoped_promise_image_access.end_semaphores().empty())) { + !(begin_semaphores.empty() && end_semaphores.empty())) { // TODO(penghuang): handle vulkan device lost. DLOG(ERROR) << "offscreen.surface()->flush() failed."; return; @@ -1142,7 +1168,7 @@ void SkiaOutputSurfaceImplOnGpu::RemoveRenderPassResource( // |image_contexts| will go out of scope and be destroyed now. } -void SkiaOutputSurfaceImplOnGpu::CopyOutput( +bool SkiaOutputSurfaceImplOnGpu::CopyOutput( RenderPassId id, copy_output::RenderPassGeometry geometry, const gfx::ColorSpace& color_space, @@ -1152,10 +1178,15 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutput( // TODO(crbug.com/898595): Do this on the GPU instead of CPU with Vulkan. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - // Clear |destroy_after_swap_| if we CopyOutput without SwapBuffers. - base::ScopedClosureRunner cleanup( - base::BindOnce([](std::vector<std::unique_ptr<SkDeferredDisplayList>>) {}, - std::move(destroy_after_swap_))); + if (deferred_framebuffer_draw_closure) { + // returns false if context not set to current, i.e lost + if (!std::move(deferred_framebuffer_draw_closure).Run()) + return false; + DCHECK(context_state_->IsCurrent(nullptr /* surface */)); + } else { + if (!MakeCurrent(true /* need_fbo0 */)) + return false; + } if (use_gl_renderer_copier_) gpu::ContextUrl::SetActiveUrl(copier_active_url_); @@ -1163,8 +1194,6 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutput( // Lazy initialize GLRendererCopier before draw because // DirectContextProvider ctor the backbuffer. if (use_gl_renderer_copier_ && !copier_) { - if (!MakeCurrent(true /* need_fbo0 */)) - return; auto client = std::make_unique<DirectContextProviderDelegateImpl>( gpu_preferences_, dependency_->GetGpuDriverBugWorkarounds(), dependency_->GetGpuFeatureInfo(), context_state_.get(), @@ -1178,7 +1207,7 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutput( if (result != gpu::ContextResult::kSuccess) { DLOG(ERROR) << "Couldn't initialize GLRendererCopier"; context_provider_ = nullptr; - return; + return false; } context_current_task_runner_ = base::MakeRefCounted<ContextCurrentTaskRunner>(weak_ptr_); @@ -1193,15 +1222,6 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutput( gr_context()->resetContext(); } - if (deferred_framebuffer_draw_closure) { - // returns false if context not set to current, i.e lost - if (!std::move(deferred_framebuffer_draw_closure).Run()) - return; - DCHECK(context_state_->IsCurrent(nullptr /* surface */)); - } else { - if (!MakeCurrent(true /* need_fbo0 */)) - return; - } bool from_fbo0 = !id; DCHECK(scoped_output_device_paint_ || !from_fbo0); @@ -1227,11 +1247,11 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutput( surface->getCanvas()->drawPaint(paint); gl::ScopedProgressReporter scoped_progress_reporter( context_state_->progress_reporter()); - surface->flush(); + surface->flush(SkSurface::BackendSurfaceAccess::kNoAccess, {}); } if (use_gl_renderer_copier_) { - surface->flush(); + surface->flush(SkSurface::BackendSurfaceAccess::kNoAccess, {}); GLuint gl_id = 0; GLenum internal_format = supports_alpha_ ? GL_RGBA : GL_RGB; @@ -1262,7 +1282,7 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutput( if (decoder()->HasMoreIdleWork() || decoder()->HasPendingQueries()) ScheduleDelayedWork(); - return; + return true; } base::Optional<gpu::raster::GrShaderCache::ScopedCacheUse> cache_use; @@ -1307,7 +1327,6 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutput( SkIRect src_rect = SkIRect::MakeXYWH(source_selection.x(), source_selection.y(), source_selection.width(), source_selection.height()); - if (request->result_format() == CopyOutputRequest::ResultFormat::I420_PLANES) { std::unique_ptr<ReadPixelsContext> context = @@ -1338,6 +1357,7 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutput( NOTIMPLEMENTED(); // ResultFormat::RGBA_TEXTURE } ScheduleCheckReadbackCompletion(); + return true; } gpu::DecoderContext* SkiaOutputSurfaceImplOnGpu::decoder() { @@ -1399,7 +1419,7 @@ void SkiaOutputSurfaceImplOnGpu::BeginAccessImages( } void SkiaOutputSurfaceImplOnGpu::EndAccessImages( - const std::vector<ImageContextImpl*>& image_contexts) { + const base::flat_set<ImageContextImpl*>& image_contexts) { TRACE_EVENT0("viz", "SkiaOutputSurfaceImplOnGpu::EndAccessImages"); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); for (auto* context : image_contexts) @@ -1457,7 +1477,7 @@ void SkiaOutputSurfaceImplOnGpu::SetCapabilitiesForTesting( output_device_ = std::make_unique<SkiaOutputDeviceOffscreen>( context_state_, capabilities.output_surface_origin, renderer_settings_.requires_alpha_channel, memory_tracker_.get(), - did_swap_buffer_complete_callback_); + GetDidSwapBuffersCompleteCallback()); } bool SkiaOutputSurfaceImplOnGpu::Initialize() { @@ -1465,11 +1485,13 @@ bool SkiaOutputSurfaceImplOnGpu::Initialize() { "is_using_vulkan", is_using_vulkan()); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); #if defined(USE_OZONE) - gpu::SurfaceHandle surface_handle = dependency_->GetSurfaceHandle(); - if (surface_handle != gpu::kNullSurfaceHandle) { - window_surface_ = ui::OzonePlatform::GetInstance() - ->GetSurfaceFactoryOzone() - ->CreatePlatformWindowSurface(surface_handle); + if (features::IsUsingOzonePlatform()) { + gpu::SurfaceHandle surface_handle = dependency_->GetSurfaceHandle(); + if (surface_handle != gpu::kNullSurfaceHandle) { + window_surface_ = ui::OzonePlatform::GetInstance() + ->GetSurfaceFactoryOzone() + ->CreatePlatformWindowSurface(surface_handle); + } } #endif @@ -1516,7 +1538,7 @@ bool SkiaOutputSurfaceImplOnGpu::InitializeForGL() { output_device_ = std::make_unique<SkiaOutputDeviceOffscreen>( context_state_, gfx::SurfaceOrigin::kTopLeft, renderer_settings_.requires_alpha_channel, memory_tracker_.get(), - did_swap_buffer_complete_callback_); + GetDidSwapBuffersCompleteCallback()); supports_alpha_ = renderer_settings_.requires_alpha_channel; } else { gl_surface_ = @@ -1529,8 +1551,10 @@ bool SkiaOutputSurfaceImplOnGpu::InitializeForGL() { if (gl_surface_->IsSurfaceless()) { std::unique_ptr<SkiaOutputDeviceBufferQueue> onscreen_device = std::make_unique<SkiaOutputDeviceBufferQueue>( - gl_surface_, dependency_, memory_tracker_.get(), - did_swap_buffer_complete_callback_); + std::make_unique<OutputPresenterGL>(gl_surface_, dependency_, + memory_tracker_.get()), + dependency_, memory_tracker_.get(), + GetDidSwapBuffersCompleteCallback()); supports_alpha_ = onscreen_device->supports_alpha(); output_device_ = std::move(onscreen_device); @@ -1539,7 +1563,7 @@ bool SkiaOutputSurfaceImplOnGpu::InitializeForGL() { std::unique_ptr<SkiaOutputDeviceWebView> onscreen_device = std::make_unique<SkiaOutputDeviceWebView>( context_state_.get(), gl_surface_, memory_tracker_.get(), - did_swap_buffer_complete_callback_); + GetDidSwapBuffersCompleteCallback()); supports_alpha_ = onscreen_device->supports_alpha(); output_device_ = std::move(onscreen_device); } else { @@ -1547,7 +1571,7 @@ bool SkiaOutputSurfaceImplOnGpu::InitializeForGL() { std::make_unique<SkiaOutputDeviceGL>( dependency_->GetMailboxManager(), context_state_.get(), gl_surface_, feature_info_, memory_tracker_.get(), - did_swap_buffer_complete_callback_); + GetDidSwapBuffersCompleteCallback()); supports_alpha_ = onscreen_device->supports_alpha(); output_device_ = std::move(onscreen_device); } @@ -1571,44 +1595,56 @@ bool SkiaOutputSurfaceImplOnGpu::InitializeForVulkan() { output_device_ = std::make_unique<SkiaOutputDeviceOffscreen>( context_state_, gfx::SurfaceOrigin::kBottomLeft, renderer_settings_.requires_alpha_channel, memory_tracker_.get(), - did_swap_buffer_complete_callback_); + GetDidSwapBuffersCompleteCallback()); supports_alpha_ = renderer_settings_.requires_alpha_channel; } else { #if defined(USE_X11) - supports_alpha_ = true; - if (!gpu_preferences_.disable_vulkan_surface) { - output_device_ = SkiaOutputDeviceVulkan::Create( - vulkan_context_provider_, dependency_->GetSurfaceHandle(), - memory_tracker_.get(), did_swap_buffer_complete_callback_); + if (!features::IsUsingOzonePlatform()) { + supports_alpha_ = true; + if (!gpu_preferences_.disable_vulkan_surface) { + output_device_ = SkiaOutputDeviceVulkan::Create( + vulkan_context_provider_, dependency_->GetSurfaceHandle(), + memory_tracker_.get(), GetDidSwapBuffersCompleteCallback()); + } + if (!output_device_) { + output_device_ = std::make_unique<SkiaOutputDeviceX11>( + context_state_, dependency_->GetSurfaceHandle(), + memory_tracker_.get(), GetDidSwapBuffersCompleteCallback()); + } } +#endif if (!output_device_) { - output_device_ = std::make_unique<SkiaOutputDeviceX11>( - context_state_, dependency_->GetSurfaceHandle(), - memory_tracker_.get(), did_swap_buffer_complete_callback_); - } +#if defined(OS_FUCHSIA) + auto output_presenter = OutputPresenterFuchsia::Create( + window_surface_.get(), dependency_, memory_tracker_.get()); #else - auto output_device = SkiaOutputDeviceBufferQueue::Create( - dependency_, memory_tracker_.get(), did_swap_buffer_complete_callback_); - if (output_device) { - // TODO(https://crbug.com/1012401): don't depend on GL. - gl_surface_ = output_device->gl_surface(); - output_device_ = std::move(output_device); - } else { - auto output_device = SkiaOutputDeviceVulkan::Create( - vulkan_context_provider_, dependency_->GetSurfaceHandle(), - memory_tracker_.get(), did_swap_buffer_complete_callback_); -#if defined(OS_WIN) - gpu::SurfaceHandle child_surface = - output_device ? output_device->GetChildSurfaceHandle() - : gpu::kNullSurfaceHandle; - if (child_surface != gpu::kNullSurfaceHandle) { - DidCreateAcceleratedSurfaceChildWindow(dependency_->GetSurfaceHandle(), - child_surface); + auto output_presenter = + OutputPresenterGL::Create(dependency_, memory_tracker_.get()); + if (output_presenter) { + // TODO(https://crbug.com/1012401): don't depend on GL. + gl_surface_ = output_presenter->gl_surface(); } #endif - output_device_ = std::move(output_device); - } + if (output_presenter) { + output_device_ = std::make_unique<SkiaOutputDeviceBufferQueue>( + std::move(output_presenter), dependency_, memory_tracker_.get(), + GetDidSwapBuffersCompleteCallback()); + } else { + auto output_device = SkiaOutputDeviceVulkan::Create( + vulkan_context_provider_, dependency_->GetSurfaceHandle(), + memory_tracker_.get(), GetDidSwapBuffersCompleteCallback()); +#if defined(OS_WIN) + gpu::SurfaceHandle child_surface = + output_device ? output_device->GetChildSurfaceHandle() + : gpu::kNullSurfaceHandle; + if (child_surface != gpu::kNullSurfaceHandle) { + DidCreateAcceleratedSurfaceChildWindow( + dependency_->GetSurfaceHandle(), child_surface); + } #endif + output_device_ = std::move(output_device); + } + } } #endif return !!output_device_; @@ -1622,20 +1658,33 @@ bool SkiaOutputSurfaceImplOnGpu::InitializeForDawn() { output_device_ = std::make_unique<SkiaOutputDeviceOffscreen>( context_state_, gfx::SurfaceOrigin::kBottomLeft, renderer_settings_.requires_alpha_channel, memory_tracker_.get(), - did_swap_buffer_complete_callback_); + GetDidSwapBuffersCompleteCallback()); supports_alpha_ = renderer_settings_.requires_alpha_channel; } else { #if defined(USE_X11) // TODO(sgilhuly): Set up a Vulkan swapchain so that Linux can also use // SkiaOutputDeviceDawn. - output_device_ = std::make_unique<SkiaOutputDeviceX11>( - context_state_, dependency_->GetSurfaceHandle(), memory_tracker_.get(), - did_swap_buffer_complete_callback_); + if (!features::IsUsingOzonePlatform()) { + output_device_ = std::make_unique<SkiaOutputDeviceX11>( + context_state_, dependency_->GetSurfaceHandle(), + memory_tracker_.get(), GetDidSwapBuffersCompleteCallback()); + } else { + return false; + } +#elif defined(OS_WIN) + std::unique_ptr<SkiaOutputDeviceDawn> output_device = + std::make_unique<SkiaOutputDeviceDawn>( + dawn_context_provider_, dependency_->GetSurfaceHandle(), + gfx::SurfaceOrigin::kTopLeft, memory_tracker_.get(), + GetDidSwapBuffersCompleteCallback()); + const gpu::SurfaceHandle child_surface_handle = + output_device->GetChildSurfaceHandle(); + DidCreateAcceleratedSurfaceChildWindow(dependency_->GetSurfaceHandle(), + child_surface_handle); + output_device_ = std::move(output_device); #else - output_device_ = std::make_unique<SkiaOutputDeviceDawn>( - dawn_context_provider_, dependency_->GetSurfaceHandle(), - gfx::SurfaceOrigin::kTopLeft, memory_tracker_.get(), - did_swap_buffer_complete_callback_); + NOTREACHED(); + return false; #endif } #endif @@ -1651,9 +1700,10 @@ bool SkiaOutputSurfaceImplOnGpu::MakeCurrent(bool need_fbo0) { if (!context_state_->MakeCurrent(gl_surface, need_gl)) { LOG(ERROR) << "Failed to make current."; dependency_->DidLoseContext( - gpu::error::kMakeCurrentFailed, + *context_state_->context_lost_reason(), GURL("chrome://gpu/SkiaOutputSurfaceImplOnGpu::MakeCurrent")); - MarkContextLost(CONTEXT_LOST_MAKECURRENT_FAILED); + MarkContextLost(GetContextLostReason( + gpu::error::kLostContext, *context_state_->context_lost_reason())); return false; } context_state_->set_need_context_state_reset(true); @@ -1734,6 +1784,28 @@ void SkiaOutputSurfaceImplOnGpu::BufferPresented( // Handled by SkiaOutputDevice already. } +void SkiaOutputSurfaceImplOnGpu::DidSwapBuffersCompleteInternal( + gpu::SwapBuffersCompleteParams params, + const gfx::Size& pixel_size) { + if (params.swap_response.result == gfx::SwapResult::SWAP_FAILED) { + DLOG(ERROR) << "Context lost on SWAP_FAILED"; + if (!context_state_->IsCurrent(nullptr) || + !context_state_->CheckResetStatus(false)) { + // Mark the context lost if not already lost. + MarkContextLost(ContextLostReason::CONTEXT_LOST_SWAP_FAILED); + } + } + + PostTaskToClientThread( + base::BindOnce(did_swap_buffer_complete_callback_, params, pixel_size)); +} + +SkiaOutputSurfaceImplOnGpu::DidSwapBufferCompleteCallback +SkiaOutputSurfaceImplOnGpu::GetDidSwapBuffersCompleteCallback() { + return base::BindRepeating( + &SkiaOutputSurfaceImplOnGpu::DidSwapBuffersCompleteInternal, weak_ptr_); +} + void SkiaOutputSurfaceImplOnGpu::MarkContextLost(ContextLostReason reason) { // This function potentially can be re-entered during from // SharedContextState::MarkContextLost(). This guards against it. diff --git a/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h index b9ab673914a..c6919214b9c 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h +++ b/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h @@ -9,7 +9,6 @@ #include <utility> #include <vector> -#include "base/containers/circular_deque.h" #include "base/macros.h" #include "base/optional.h" #include "base/threading/thread_checker.h" @@ -153,7 +152,7 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate { void RemoveRenderPassResource( std::vector<RenderPassId> ids, std::vector<std::unique_ptr<ImageContextImpl>> image_contexts); - void CopyOutput(RenderPassId id, + bool CopyOutput(RenderPassId id, copy_output::RenderPassGeometry geometry, const gfx::ColorSpace& color_space, std::unique_ptr<CopyOutputRequest> request, @@ -162,7 +161,7 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate { void BeginAccessImages(const std::vector<ImageContextImpl*>& image_contexts, std::vector<GrBackendSemaphore>* begin_semaphores, std::vector<GrBackendSemaphore>* end_semaphores); - void EndAccessImages(const std::vector<ImageContextImpl*>& image_contexts); + void EndAccessImages(const base::flat_set<ImageContextImpl*>& image_contexts); sk_sp<GrContextThreadSafeProxy> GetGrContextThreadSafeProxy(); const gl::GLVersionInfo* gl_version_info() const { return gl_version_info_; } @@ -211,7 +210,6 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate { gpu::MemoryTracker* GetMemoryTracker() { return memory_tracker_.get(); } private: - class ScopedPromiseImageAccess; class OffscreenSurface; class DisplayContext; @@ -220,6 +218,12 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate { bool InitializeForVulkan(); bool InitializeForDawn(); + // Provided as a callback to |device_|. + void DidSwapBuffersCompleteInternal(gpu::SwapBuffersCompleteParams params, + const gfx::Size& pixel_size); + + DidSwapBufferCompleteCallback GetDidSwapBuffersCompleteCallback(); + // Make context current for GL, and return false if the context is lost. // It will do nothing when Vulkan is used. bool MakeCurrent(bool need_fbo0); @@ -286,6 +290,7 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate { // readback using GLRendererCopier. // TODO(samans): Remove |sequence_id| once readback always uses Skia. const gpu::SequenceId sequence_id_; + // Should only be run on the client thread with PostTaskToClientThread(). DidSwapBufferCompleteCallback did_swap_buffer_complete_callback_; BufferPresentedCallback buffer_presented_callback_; ContextLostCallback context_lost_callback_; @@ -308,6 +313,24 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate { std::unique_ptr<DisplayContext> display_context_; bool context_is_lost_ = false; + class PromiseImageAccessHelper { + public: + explicit PromiseImageAccessHelper(SkiaOutputSurfaceImplOnGpu* impl_on_gpu); + ~PromiseImageAccessHelper(); + + void BeginAccess(std::vector<ImageContextImpl*> image_contexts, + std::vector<GrBackendSemaphore>* begin_semaphores, + std::vector<GrBackendSemaphore>* end_semaphores); + void EndAccess(); + + private: + SkiaOutputSurfaceImplOnGpu* const impl_on_gpu_; + base::flat_set<ImageContextImpl*> image_contexts_; + + DISALLOW_COPY_AND_ASSIGN(PromiseImageAccessHelper); + }; + PromiseImageAccessHelper promise_image_access_helper_{this}; + std::unique_ptr<SkiaOutputDevice> output_device_; base::Optional<SkiaOutputDevice::ScopedPaint> scoped_output_device_paint_; diff --git a/chromium/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc b/chromium/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc index 67800b78170..8f5b7e6e077 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc +++ b/chromium/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc @@ -156,6 +156,7 @@ TEST_F(SkiaOutputSurfaceImplTest, SubmitPaint) { geometry.readback_offset = gfx::Vector2d(0, 0); output_surface_->CopyOutput(0, geometry, color_space, std::move(request)); + output_surface_->SwapBuffersSkipped(); BlockMainThread(); // SubmitPaint draw is deferred until CopyOutput. diff --git a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.cc index dc602776aad..bb23f23c193 100644 --- a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.cc +++ b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.cc @@ -11,6 +11,7 @@ #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" #include "base/time/time.h" +#include "build/build_config.h" #include "components/viz/common/frame_sinks/begin_frame_source.h" #include "components/viz/common/quads/compositor_frame.h" #include "components/viz/common/resources/bitmap_allocation.h" @@ -46,6 +47,20 @@ void RecordShouldSendBeginFrame(SendBeginFrameResult result) { "Compositing.CompositorFrameSinkSupport.ShouldSendBeginFrame", result); } +void AdjustPresentationFeedback(gfx::PresentationFeedback* feedback, + base::TimeTicks swap_start) { + // Swap start to end breakdown is always reported if ready timestamp is + // available. The other timestamps are adjusted to assume 0 delay in those + // stages if the breakdown is not available. + if (feedback->ready_timestamp.is_null()) + return; + + feedback->available_timestamp = + std::max(feedback->available_timestamp, swap_start); + feedback->latch_timestamp = + std::max(feedback->latch_timestamp, feedback->ready_timestamp); +} + } // namespace CompositorFrameSinkSupport::CompositorFrameSinkSupport( @@ -623,6 +638,8 @@ void CompositorFrameSinkSupport::DidPresentCompositorFrame( details.draw_start_timestamp = draw_start_timestamp; details.swap_timings = swap_timings; details.presentation_feedback = feedback; + AdjustPresentationFeedback(&details.presentation_feedback, + swap_timings.swap_start); pending_received_frame_times_.erase(received_frame_timestamp); // We should only ever get one PresentationFeedback per frame_token. diff --git a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc index 04968717787..dacc55a238f 100644 --- a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc +++ b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc @@ -7,6 +7,7 @@ #include "base/bind.h" #include "base/stl_util.h" #include "base/test/simple_test_tick_clock.h" +#include "build/build_config.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/quads/compositor_frame.h" @@ -22,18 +23,19 @@ #include "components/viz/test/fake_external_begin_frame_source.h" #include "components/viz/test/fake_surface_observer.h" #include "components/viz/test/mock_compositor_frame_sink_client.h" +#include "components/viz/test/viz_test_suite.h" #include "services/viz/privileged/mojom/compositing/frame_sink_manager.mojom.h" #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/khronos/GLES2/gl2.h" -using testing::UnorderedElementsAre; -using testing::IsEmpty; -using testing::SizeIs; -using testing::Invoke; using testing::_; using testing::Eq; +using testing::Invoke; +using testing::IsEmpty; +using testing::SizeIs; +using testing::UnorderedElementsAre; namespace viz { namespace { @@ -645,13 +647,17 @@ TEST_F(CompositorFrameSinkSupportTest, ProhibitsUnprivilegedCopyRequests) { false /* not root frame sink */); bool did_receive_aborted_copy_result = false; + base::RunLoop aborted_copy_run_loop; auto request = std::make_unique<CopyOutputRequest>( CopyOutputRequest::ResultFormat::RGBA_BITMAP, base::BindOnce( - [](bool* got_nothing, std::unique_ptr<CopyOutputResult> result) { + [](bool* got_nothing, base::OnceClosure finished, + std::unique_ptr<CopyOutputResult> result) { *got_nothing = result->IsEmpty(); + std::move(finished).Run(); }, - &did_receive_aborted_copy_result)); + &did_receive_aborted_copy_result, + aborted_copy_run_loop.QuitClosure())); auto frame = MakeDefaultCompositorFrame(); ResourceId frame_resource_ids[] = {1, 2, 3}; @@ -660,6 +666,7 @@ TEST_F(CompositorFrameSinkSupportTest, ProhibitsUnprivilegedCopyRequests) { EXPECT_FALSE(SubmitCompositorFrameWithCopyRequest(std::move(frame), std::move(request))); + aborted_copy_run_loop.Run(); EXPECT_TRUE(did_receive_aborted_copy_result); // All the resources in the rejected frame should have been returned. @@ -778,8 +785,10 @@ TEST_F(CompositorFrameSinkSupportTest, EvictOlderSurfaces) { } void CopyRequestTestCallback(bool* called, + base::OnceClosure finished, std::unique_ptr<CopyOutputResult> result) { *called = true; + std::move(finished).Run(); } TEST_F(CompositorFrameSinkSupportTest, DuplicateCopyRequest) { @@ -796,9 +805,11 @@ TEST_F(CompositorFrameSinkSupportTest, DuplicateCopyRequest) { } bool called1 = false; + base::RunLoop called1_run_loop; auto request = std::make_unique<CopyOutputRequest>( CopyOutputRequest::ResultFormat::RGBA_BITMAP, - base::BindOnce(&CopyRequestTestCallback, &called1)); + base::BindOnce(&CopyRequestTestCallback, &called1, + called1_run_loop.QuitClosure())); request->set_source(kArbitrarySourceId1); support_->RequestCopyOfOutput(local_surface_id_, std::move(request)); @@ -806,9 +817,11 @@ TEST_F(CompositorFrameSinkSupportTest, DuplicateCopyRequest) { EXPECT_FALSE(called1); bool called2 = false; + base::RunLoop called2_run_loop; request = std::make_unique<CopyOutputRequest>( CopyOutputRequest::ResultFormat::RGBA_BITMAP, - base::BindOnce(&CopyRequestTestCallback, &called2)); + base::BindOnce(&CopyRequestTestCallback, &called2, + called2_run_loop.QuitClosure())); request->set_source(kArbitrarySourceId2); support_->RequestCopyOfOutput(local_surface_id_, std::move(request)); @@ -818,14 +831,17 @@ TEST_F(CompositorFrameSinkSupportTest, DuplicateCopyRequest) { EXPECT_FALSE(called2); bool called3 = false; + base::RunLoop called3_run_loop; request = std::make_unique<CopyOutputRequest>( CopyOutputRequest::ResultFormat::RGBA_BITMAP, - base::BindOnce(&CopyRequestTestCallback, &called3)); + base::BindOnce(&CopyRequestTestCallback, &called3, + called3_run_loop.QuitClosure())); request->set_source(kArbitrarySourceId1); support_->RequestCopyOfOutput(local_surface_id_, std::move(request)); GetSurfaceForId(surface_id)->TakeCopyOutputRequestsFromClient(); // Two callbacks are from source1, so the first should be called. + called1_run_loop.Run(); EXPECT_TRUE(called1); EXPECT_FALSE(called2); EXPECT_FALSE(called3); @@ -834,6 +850,8 @@ TEST_F(CompositorFrameSinkSupportTest, DuplicateCopyRequest) { ExpireAllTemporaryReferences(); local_surface_id_ = LocalSurfaceId(); manager_.surface_manager()->GarbageCollectSurfaces(); + called2_run_loop.Run(); + called3_run_loop.Run(); EXPECT_TRUE(called1); EXPECT_TRUE(called2); EXPECT_TRUE(called3); @@ -1437,5 +1455,4 @@ TEST_F(CompositorFrameSinkSupportTest, ThrottleUnresponsiveClient) { support->SetNeedsBeginFrame(false); } - } // namespace viz diff --git a/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.h b/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.h index f7623ec4869..80767529f3c 100644 --- a/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.h +++ b/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.h @@ -12,10 +12,10 @@ #include <vector> #include "base/callback_helpers.h" +#include "base/check.h" #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/containers/unique_ptr_adapters.h" -#include "base/logging.h" #include "base/macros.h" #include "base/optional.h" #include "base/single_thread_task_runner.h" diff --git a/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc b/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc index c2b189e0e66..675e05a76a7 100644 --- a/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc +++ b/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc @@ -64,7 +64,7 @@ RootCompositorFrameSinkImpl::Create( bool hw_support_for_multiple_refresh_rates = false; bool wants_vsync_updates = false; - if (params->external_begin_frame_controller.is_pending()) { + if (params->external_begin_frame_controller) { auto owned_external_begin_frame_source_mojo = std::make_unique<ExternalBeginFrameSourceMojo>( frame_sink_manager, diff --git a/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc b/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc index 08338b4153b..037d3cbea16 100644 --- a/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc +++ b/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc @@ -15,6 +15,7 @@ #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" +#include "base/threading/sequenced_task_runner_handle.h" #include "base/time/default_tick_clock.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" @@ -521,29 +522,20 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame( // metadata, and notify the oracle. const int64_t capture_frame_number = next_capture_frame_number_++; VideoFrameMetadata* const metadata = frame->metadata(); - metadata->SetTimeTicks(VideoFrameMetadata::CAPTURE_BEGIN_TIME, - clock_->NowTicks()); - metadata->SetInteger(VideoFrameMetadata::CAPTURE_COUNTER, - capture_frame_number); - metadata->SetTimeDelta(VideoFrameMetadata::FRAME_DURATION, - oracle_->estimated_frame_duration()); - metadata->SetDouble(VideoFrameMetadata::FRAME_RATE, - 1.0 / oracle_->min_capture_period().InSecondsF()); - metadata->SetTimeTicks(VideoFrameMetadata::REFERENCE_TIME, event_time); - metadata->SetDouble(VideoFrameMetadata::DEVICE_SCALE_FACTOR, - frame_metadata.device_scale_factor); - metadata->SetDouble(VideoFrameMetadata::PAGE_SCALE_FACTOR, - frame_metadata.page_scale_factor); - metadata->SetDouble(VideoFrameMetadata::ROOT_SCROLL_OFFSET_X, - frame_metadata.root_scroll_offset.x()); - metadata->SetDouble(VideoFrameMetadata::ROOT_SCROLL_OFFSET_Y, - frame_metadata.root_scroll_offset.y()); + metadata->capture_begin_time = clock_->NowTicks(); + metadata->capture_counter = capture_frame_number; + metadata->frame_duration = oracle_->estimated_frame_duration(); + metadata->frame_rate = 1.0 / oracle_->min_capture_period().InSecondsF(); + metadata->reference_time = event_time; + metadata->device_scale_factor = frame_metadata.device_scale_factor; + metadata->page_scale_factor = frame_metadata.page_scale_factor; + metadata->root_scroll_offset_x = frame_metadata.root_scroll_offset.x(); + metadata->root_scroll_offset_y = frame_metadata.root_scroll_offset.y(); if (frame_metadata.top_controls_visible_height.has_value()) { last_top_controls_visible_height_ = *frame_metadata.top_controls_visible_height; } - metadata->SetDouble(VideoFrameMetadata::TOP_CONTROLS_VISIBLE_HEIGHT, - last_top_controls_visible_height_); + metadata->top_controls_visible_height = last_top_controls_visible_height_; oracle_->RecordCapture(utilization); TRACE_EVENT_ASYNC_BEGIN2("gpu.capture", "Capture", oracle_frame_number, @@ -578,8 +570,8 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame( if (pixel_format_ == media::PIXEL_FORMAT_I420) update_rect = ExpandRectToI420SubsampleBoundaries(update_rect); } - metadata->SetRect(media::VideoFrameMetadata::CAPTURE_UPDATE_RECT, - update_rect); + metadata->capture_update_rect = update_rect; + // Extreme edge-case: If somehow the source size is so tiny that the content // region becomes empty, just deliver a frame filled with black. if (content_rect.IsEmpty()) { @@ -639,6 +631,7 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame( VideoCaptureOverlay::MakeCombinedRenderer( GetOverlaysInOrder(), content_rect, frame->format()), std::move(frame), base::TimeTicks::Now()))); + request->set_result_task_runner(base::SequencedTaskRunnerHandle::Get()); request->set_source(copy_request_source_); request->set_area(gfx::Rect(source_size)); request->SetScaleRatio( @@ -803,10 +796,8 @@ void FrameSinkVideoCapturerImpl::OnFrameReadyForDelivery( DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_GE(capture_frame_number, next_delivery_frame_number_); - if (frame) { - frame->metadata()->SetTimeTicks(VideoFrameMetadata::CAPTURE_END_TIME, - clock_->NowTicks()); - } + if (frame) + frame->metadata()->capture_end_time = clock_->NowTicks(); // Ensure frames are delivered in-order by using a min-heap, and only // deliver the next frame(s) in-sequence when they are found at the top. @@ -871,7 +862,7 @@ void FrameSinkVideoCapturerImpl::MaybeDeliverFrame( // the consumer. media::mojom::VideoFrameInfoPtr info = media::mojom::VideoFrameInfo::New(); info->timestamp = frame->timestamp(); - info->metadata = frame->metadata()->GetInternalValues().Clone(); + info->metadata = *(frame->metadata()); info->pixel_format = frame->format(); info->coded_size = frame->coded_size(); info->visible_rect = frame->visible_rect(); diff --git a/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc b/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc index 11b9327289f..21ef53a9b01 100644 --- a/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc +++ b/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc @@ -58,20 +58,16 @@ bool CompareVarsInCompositorFrameMetadata( float device_scale_factor, float page_scale_factor, const gfx::Vector2dF& root_scroll_offset) { - double dsf, psf, rso_x, rso_y; - bool valid = true; - - valid &= frame.metadata()->GetDouble( - media::VideoFrameMetadata::DEVICE_SCALE_FACTOR, &dsf); - valid &= frame.metadata()->GetDouble( - media::VideoFrameMetadata::PAGE_SCALE_FACTOR, &psf); - valid &= frame.metadata()->GetDouble( - media::VideoFrameMetadata::ROOT_SCROLL_OFFSET_X, &rso_x); - valid &= frame.metadata()->GetDouble( - media::VideoFrameMetadata::ROOT_SCROLL_OFFSET_Y, &rso_y); - - return valid && dsf == device_scale_factor && psf == page_scale_factor && - gfx::Vector2dF(rso_x, rso_y) == root_scroll_offset; + auto dsf = frame.metadata()->device_scale_factor; + auto psf = frame.metadata()->page_scale_factor; + auto rso_x = frame.metadata()->root_scroll_offset_x; + auto rso_y = frame.metadata()->root_scroll_offset_y; + + bool valid = dsf.has_value() && psf.has_value() && rso_x.has_value() && + rso_y.has_value(); + + return valid && *dsf == device_scale_factor && *psf == page_scale_factor && + gfx::Vector2dF(*rso_x, *rso_y) == root_scroll_offset; } // Dummy frame sink ID. @@ -107,7 +103,11 @@ struct YUVColor { // Forces any pending Mojo method calls between the capturer and consumer to be // made. -void PropagateMojoTasks() { +void PropagateMojoTasks( + scoped_refptr<base::TestMockTimeTaskRunner> runner = nullptr) { + if (runner) { + runner->RunUntilIdle(); + } base::RunLoop().RunUntilIdle(); } @@ -171,7 +171,7 @@ class MockConsumer : public mojom::FrameSinkVideoConsumer { const_cast<uint8_t*>(static_cast<const uint8_t*>(mapping.memory())), mapping.size(), info->timestamp); ASSERT_TRUE(frame); - frame->metadata()->MergeInternalValuesFrom(info->metadata); + frame->set_metadata(info->metadata); if (info->color_space.has_value()) frame->set_color_space(info->color_space.value()); @@ -288,7 +288,11 @@ class FakeCapturableFrameSink : public CapturableFrameSink { void SendCopyOutputResult(int offset) { auto it = results_.begin() + offset; std::move(*it).Run(); - PropagateMojoTasks(); + PropagateMojoTasks(task_runner_); + } + + void set_task_runner(scoped_refptr<base::TestMockTimeTaskRunner> runner) { + task_runner_ = std::move(runner); } private: @@ -296,6 +300,7 @@ class FakeCapturableFrameSink : public CapturableFrameSink { YUVColor color_ = {0xde, 0xad, 0xbf}; SizeSet size_set_; CompositorFrameMetadata metadata_; + scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; std::vector<base::OnceClosure> results_; }; @@ -393,6 +398,10 @@ class FrameSinkVideoCapturerTest : public testing::Test { start_time_ = task_runner_->NowTicks(); capturer_->clock_ = task_runner_->GetMockTickClock(); + // Ensure any posted tasks for CopyOutputResults will be handled when + // PropagateMojoTasks() is called + frame_sink_.set_task_runner(task_runner_); + // Replace the retry timer with one that uses this test's fake clock and // task runner. capturer_->refresh_frame_retry_timer_.emplace( @@ -642,26 +651,15 @@ TEST_F(FrameSinkVideoCapturerTest, CapturesCompositedFrames) { EXPECT_LT(last_timestamp, frame->timestamp()); last_timestamp = frame->timestamp(); const VideoFrameMetadata* metadata = frame->metadata(); - base::TimeTicks capture_begin_time; - EXPECT_TRUE(metadata->GetTimeTicks(VideoFrameMetadata::CAPTURE_BEGIN_TIME, - &capture_begin_time)); - EXPECT_EQ(expected_capture_begin_time, capture_begin_time); - base::TimeTicks capture_end_time; - EXPECT_TRUE(metadata->GetTimeTicks(VideoFrameMetadata::CAPTURE_END_TIME, - &capture_end_time)); - EXPECT_EQ(expected_capture_end_time, capture_end_time); + EXPECT_EQ(expected_capture_begin_time, *metadata->capture_begin_time); + EXPECT_EQ(expected_capture_end_time, *metadata->capture_end_time); EXPECT_EQ(gfx::ColorSpace::CreateREC709(), frame->ColorSpace()); - EXPECT_TRUE(metadata->HasKey(VideoFrameMetadata::FRAME_DURATION)); - // FRAME_DURATION is an estimate computed by the VideoCaptureOracle, so it + // frame_duration is an estimate computed by the VideoCaptureOracle, so it // its exact value is not being checked here. - double frame_rate = 0.0; - EXPECT_TRUE( - metadata->GetDouble(VideoFrameMetadata::FRAME_RATE, &frame_rate)); - EXPECT_NEAR(media::limits::kMaxFramesPerSecond, frame_rate, 0.001); - base::TimeTicks reference_time; - EXPECT_TRUE(metadata->GetTimeTicks(VideoFrameMetadata::REFERENCE_TIME, - &reference_time)); - EXPECT_EQ(expected_reference_time, reference_time); + EXPECT_TRUE(metadata->frame_duration.has_value()); + EXPECT_NEAR(media::limits::kMaxFramesPerSecond, *metadata->frame_rate, + 0.001); + EXPECT_EQ(expected_reference_time, *metadata->reference_time); // Notify the capturer that the consumer is done with the frame. consumer.SendDoneNotification(i); @@ -1150,14 +1148,10 @@ TEST_F(FrameSinkVideoCapturerTest, DeliversUpdateRectAndCaptureCounter) { EXPECT_EQ(expected_frames_count, consumer.num_frames_received()); { auto received_frame = consumer.TakeFrame(cur_frame_index); - gfx::Rect received_update_rect; - int received_capture_counter = 0; - ASSERT_TRUE(received_frame->metadata()->GetInteger( - media::VideoFrameMetadata::CAPTURE_COUNTER, &received_capture_counter)); - ASSERT_TRUE(received_frame->metadata()->GetRect( - media::VideoFrameMetadata::CAPTURE_UPDATE_RECT, &received_update_rect)); - EXPECT_EQ(gfx::Rect(size_set().capture_size), received_update_rect); - previous_capture_counter_received = received_capture_counter; + EXPECT_EQ(gfx::Rect(size_set().capture_size), + received_frame->metadata()->capture_update_rect); + previous_capture_counter_received = + *received_frame->metadata()->capture_counter; } consumer.SendDoneNotification(cur_frame_index); @@ -1184,13 +1178,9 @@ TEST_F(FrameSinkVideoCapturerTest, DeliversUpdateRectAndCaptureCounter) { EXPECT_EQ(expected_frames_count, consumer.num_frames_received()); { auto received_frame = consumer.TakeFrame(++cur_frame_index); - int received_capture_counter = 0; - gfx::Rect received_update_rect; - ASSERT_TRUE(received_frame->metadata()->GetInteger( - media::VideoFrameMetadata::CAPTURE_COUNTER, &received_capture_counter)); - ASSERT_TRUE(received_frame->metadata()->GetRect( - media::VideoFrameMetadata::CAPTURE_UPDATE_RECT, &received_update_rect)); - EXPECT_EQ(expected_frame_update_rect, received_update_rect); + int received_capture_counter = *received_frame->metadata()->capture_counter; + EXPECT_EQ(expected_frame_update_rect, + *received_frame->metadata()->capture_update_rect); EXPECT_EQ(previous_capture_counter_received + 1, received_capture_counter); previous_capture_counter_received = received_capture_counter; } @@ -1206,13 +1196,8 @@ TEST_F(FrameSinkVideoCapturerTest, DeliversUpdateRectAndCaptureCounter) { EXPECT_EQ(expected_frames_count, consumer.num_frames_received()); { auto received_frame = consumer.TakeFrame(++cur_frame_index); - int received_capture_counter = 0; - gfx::Rect received_update_rect; - ASSERT_TRUE(received_frame->metadata()->GetInteger( - media::VideoFrameMetadata::CAPTURE_COUNTER, &received_capture_counter)); - ASSERT_TRUE(received_frame->metadata()->GetRect( - media::VideoFrameMetadata::CAPTURE_UPDATE_RECT, &received_update_rect)); - EXPECT_TRUE(received_update_rect.IsEmpty()); + int received_capture_counter = *received_frame->metadata()->capture_counter; + EXPECT_TRUE(received_frame->metadata()->capture_update_rect->IsEmpty()); EXPECT_EQ(previous_capture_counter_received + 1, received_capture_counter); previous_capture_counter_received = received_capture_counter; } @@ -1229,13 +1214,9 @@ TEST_F(FrameSinkVideoCapturerTest, DeliversUpdateRectAndCaptureCounter) { EXPECT_EQ(expected_frames_count, consumer.num_frames_received()); { auto received_frame = consumer.TakeFrame(++cur_frame_index); - int received_capture_counter = 0; - gfx::Rect received_update_rect; - ASSERT_TRUE(received_frame->metadata()->GetInteger( - media::VideoFrameMetadata::CAPTURE_COUNTER, &received_capture_counter)); - ASSERT_TRUE(received_frame->metadata()->GetRect( - media::VideoFrameMetadata::CAPTURE_UPDATE_RECT, &received_update_rect)); - EXPECT_EQ(gfx::Rect(size_set().capture_size), received_update_rect); + int received_capture_counter = *received_frame->metadata()->capture_counter; + EXPECT_EQ(gfx::Rect(size_set().capture_size), + *received_frame->metadata()->capture_update_rect); EXPECT_EQ(previous_capture_counter_received + 1, received_capture_counter); previous_capture_counter_received = received_capture_counter; } @@ -1252,13 +1233,9 @@ TEST_F(FrameSinkVideoCapturerTest, DeliversUpdateRectAndCaptureCounter) { EXPECT_EQ(expected_frames_count, consumer.num_frames_received()); { auto received_frame = consumer.TakeFrame(++cur_frame_index); - int received_capture_counter = 0; - gfx::Rect received_update_rect; - ASSERT_TRUE(received_frame->metadata()->GetInteger( - media::VideoFrameMetadata::CAPTURE_COUNTER, &received_capture_counter)); - ASSERT_TRUE(received_frame->metadata()->GetRect( - media::VideoFrameMetadata::CAPTURE_UPDATE_RECT, &received_update_rect)); - EXPECT_EQ(gfx::Rect(size_set().capture_size), received_update_rect); + int received_capture_counter = *received_frame->metadata()->capture_counter; + EXPECT_EQ(gfx::Rect(size_set().capture_size), + *received_frame->metadata()->capture_update_rect); EXPECT_EQ(previous_capture_counter_received + 1, received_capture_counter); previous_capture_counter_received = received_capture_counter; } @@ -1286,14 +1263,10 @@ TEST_F(FrameSinkVideoCapturerTest, CaptureCounterSkipsWhenFramesAreDropped) { EXPECT_EQ(expected_frames_count, consumer.num_frames_received()); { auto received_frame = consumer.TakeFrame(cur_receive_frame_index); - int received_capture_counter = 0; - gfx::Rect received_update_rect; - ASSERT_TRUE(received_frame->metadata()->GetInteger( - media::VideoFrameMetadata::CAPTURE_COUNTER, &received_capture_counter)); - ASSERT_TRUE(received_frame->metadata()->GetRect( - media::VideoFrameMetadata::CAPTURE_UPDATE_RECT, &received_update_rect)); - EXPECT_EQ(gfx::Rect(size_set().capture_size), received_update_rect); - previous_capture_counter_received = received_capture_counter; + EXPECT_EQ(gfx::Rect(size_set().capture_size), + *received_frame->metadata()->capture_update_rect); + previous_capture_counter_received = + *received_frame->metadata()->capture_counter; } consumer.SendDoneNotification(cur_receive_frame_index); @@ -1318,10 +1291,8 @@ TEST_F(FrameSinkVideoCapturerTest, CaptureCounterSkipsWhenFramesAreDropped) { EXPECT_EQ(expected_frames_count, consumer.num_frames_received()); { auto received_frame = consumer.TakeFrame(++cur_receive_frame_index); - int received_capture_counter = 0; - ASSERT_TRUE(received_frame->metadata()->GetInteger( - media::VideoFrameMetadata::CAPTURE_COUNTER, &received_capture_counter)); - EXPECT_NE(previous_capture_counter_received + 1, received_capture_counter); + EXPECT_NE(previous_capture_counter_received + 1, + *received_frame->metadata()->capture_counter); } StopCapture(); } diff --git a/chromium/components/viz/service/frame_sinks/video_capture/interprocess_frame_pool.cc b/chromium/components/viz/service/frame_sinks/video_capture/interprocess_frame_pool.cc index 04d16521310..419d97d9f39 100644 --- a/chromium/components/viz/service/frame_sinks/video_capture/interprocess_frame_pool.cc +++ b/chromium/components/viz/service/frame_sinks/video_capture/interprocess_frame_pool.cc @@ -8,6 +8,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/logging.h" using media::VideoFrame; using media::VideoPixelFormat; diff --git a/chromium/components/viz/service/gl/gpu_service_impl.cc b/chromium/components/viz/service/gl/gpu_service_impl.cc index df961f12be1..98cdbff9bf0 100644 --- a/chromium/components/viz/service/gl/gpu_service_impl.cc +++ b/chromium/components/viz/service/gl/gpu_service_impl.cc @@ -405,6 +405,13 @@ GpuServiceImpl::GpuServiceImpl( } #endif +#if defined(OS_WIN) + auto info_callback = base::BindRepeating( + &GpuServiceImpl::UpdateOverlayAndHDRInfo, weak_ptr_factory_.GetWeakPtr()); + gl::DirectCompositionSurfaceWin::SetOverlayHDRGpuInfoUpdateCallback( + info_callback); +#endif + gpu_memory_buffer_factory_ = gpu::GpuMemoryBufferFactory::CreateNativeType(vulkan_context_provider()); @@ -768,12 +775,12 @@ void GpuServiceImpl::RequestHDRStatus(RequestHDRStatusCallback callback) { void GpuServiceImpl::RequestHDRStatusOnMainThread( RequestHDRStatusCallback callback) { DCHECK(main_runner_->BelongsToCurrentThread()); - bool hdr_enabled = false; + #if defined(OS_WIN) - hdr_enabled = gl::DirectCompositionSurfaceWin::IsHDRSupported(); + hdr_enabled_ = gl::DirectCompositionSurfaceWin::IsHDRSupported(); #endif io_runner_->PostTask(FROM_HERE, - base::BindOnce(std::move(callback), hdr_enabled)); + base::BindOnce(std::move(callback), hdr_enabled_)); } void GpuServiceImpl::RegisterDisplayContext( @@ -837,6 +844,10 @@ void GpuServiceImpl::DidUpdateOverlayInfo( const gpu::OverlayInfo& overlay_info) { gpu_host_->DidUpdateOverlayInfo(gpu_info_.overlay_info); } + +void GpuServiceImpl::DidUpdateHDRStatus(bool hdr_enabled) { + gpu_host_->DidUpdateHDRStatus(hdr_enabled); +} #endif void GpuServiceImpl::StoreShaderToDisk(int client_id, @@ -968,12 +979,6 @@ void GpuServiceImpl::DisplayAdded() { if (!in_host_process()) ui::GpuSwitchingManager::GetInstance()->NotifyDisplayAdded(); - -#if defined(OS_WIN) - // Update overlay info in the GPU process and send the updated data back to - // the GPU host in the Browser process through mojom if the info has changed. - UpdateOverlayInfo(); -#endif } void GpuServiceImpl::DisplayRemoved() { @@ -986,12 +991,6 @@ void GpuServiceImpl::DisplayRemoved() { if (!in_host_process()) ui::GpuSwitchingManager::GetInstance()->NotifyDisplayRemoved(); - -#if defined(OS_WIN) - // Update overlay info in the GPU process and send the updated data back to - // the GPU host in the Browser process through mojom if the info has changed. - UpdateOverlayInfo(); -#endif } void GpuServiceImpl::DestroyAllChannels() { @@ -1141,12 +1140,20 @@ gpu::Scheduler* GpuServiceImpl::GetGpuScheduler() { } #if defined(OS_WIN) -void GpuServiceImpl::UpdateOverlayInfo() { +void GpuServiceImpl::UpdateOverlayAndHDRInfo() { gpu::OverlayInfo old_overlay_info = gpu_info_.overlay_info; gpu::CollectHardwareOverlayInfo(&gpu_info_.overlay_info); + // Update overlay info in the GPU process and send the updated data back to + // the GPU host in the Browser process through mojom if the info has changed. if (old_overlay_info != gpu_info_.overlay_info) DidUpdateOverlayInfo(gpu_info_.overlay_info); + + // Update HDR status in the GPU process through the GPU host mojom. + bool old_hdr_enabled_status = hdr_enabled_; + hdr_enabled_ = gl::DirectCompositionSurfaceWin::IsHDRSupported(); + if (old_hdr_enabled_status != hdr_enabled_) + DidUpdateHDRStatus(hdr_enabled_); } #endif diff --git a/chromium/components/viz/service/gl/gpu_service_impl.h b/chromium/components/viz/service/gl/gpu_service_impl.h index 018be6ac2cb..90d68b30ce3 100644 --- a/chromium/components/viz/service/gl/gpu_service_impl.h +++ b/chromium/components/viz/service/gl/gpu_service_impl.h @@ -210,6 +210,7 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate, const GURL& active_url) override; #if defined(OS_WIN) void DidUpdateOverlayInfo(const gpu::OverlayInfo& overlay_info) override; + void DidUpdateHDRStatus(bool hdr_enabled) override; #endif void StoreShaderToDisk(int client_id, const std::string& key, @@ -342,10 +343,10 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate, // process. If |for_context_loss| is true an error message will be logged. void MaybeExit(bool for_context_loss); - // Update overlay info on the GPU process and send the updated info back - // to the browser process if there is a change. + // Update overlay info and HDR status on the GPU process and send the updated + // info back to the browser process if there is a change. #if defined(OS_WIN) - void UpdateOverlayInfo(); + void UpdateOverlayAndHDRInfo(); #endif scoped_refptr<base::SingleThreadTaskRunner> main_runner_; @@ -363,6 +364,8 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate, // Information about general chrome feature support for the GPU. gpu::GpuFeatureInfo gpu_feature_info_; + bool hdr_enabled_ = false; + // What we would have gotten if we haven't fallen back to SwiftShader or // pure software (in the viz case). base::Optional<gpu::GPUInfo> gpu_info_for_hardware_gpu_; diff --git a/chromium/components/viz/service/gl/info_collection_gpu_service_impl.cc b/chromium/components/viz/service/gl/info_collection_gpu_service_impl.cc index ef5bf16f6bd..f72d981d39c 100644 --- a/chromium/components/viz/service/gl/info_collection_gpu_service_impl.cc +++ b/chromium/components/viz/service/gl/info_collection_gpu_service_impl.cc @@ -4,6 +4,7 @@ #include "components/viz/service/gl/info_collection_gpu_service_impl.h" +#include <utility> #include "base/task/post_task.h" #include "base/task_runner_util.h" #include "gpu/config/dx_diag_node.h" @@ -15,10 +16,12 @@ InfoCollectionGpuServiceImpl::InfoCollectionGpuServiceImpl( scoped_refptr<base::SingleThreadTaskRunner> main_runner, scoped_refptr<base::SingleThreadTaskRunner> io_runner, const gpu::DevicePerfInfo& device_perf_info, + const gpu::GPUInfo::GPUDevice& gpu_device, mojo::PendingReceiver<mojom::InfoCollectionGpuService> pending_receiver) : main_runner_(std::move(main_runner)), io_runner_(std::move(io_runner)), - device_perf_info_(device_perf_info) { + device_perf_info_(device_perf_info), + gpu_device_(gpu_device) { DCHECK(!io_runner_->BelongsToCurrentThread()); DCHECK(main_runner_->BelongsToCurrentThread()); @@ -41,29 +44,48 @@ void InfoCollectionGpuServiceImpl::BindOnIO( receiver_.Bind(std::move(pending_receiver)); } -void InfoCollectionGpuServiceImpl:: - GetGpuSupportedRuntimeVersionAndDevicePerfInfo( - GetGpuSupportedRuntimeVersionAndDevicePerfInfoCallback callback) { +void InfoCollectionGpuServiceImpl::GetGpuSupportedDx12VersionAndDevicePerfInfo( + GetGpuSupportedDx12VersionAndDevicePerfInfoCallback callback) { DCHECK(io_runner_->BelongsToCurrentThread()); main_runner_->PostTask( FROM_HERE, base::BindOnce(&InfoCollectionGpuServiceImpl:: - GetGpuSupportedRuntimeVersionAndDevicePerfInfoOnMain, + GetGpuSupportedDx12VersionAndDevicePerfInfoOnMain, base::Unretained(this), std::move(callback))); } void InfoCollectionGpuServiceImpl:: - GetGpuSupportedRuntimeVersionAndDevicePerfInfoOnMain( - GetGpuSupportedRuntimeVersionAndDevicePerfInfoCallback callback) { + GetGpuSupportedDx12VersionAndDevicePerfInfoOnMain( + GetGpuSupportedDx12VersionAndDevicePerfInfoCallback callback) { DCHECK(main_runner_->BelongsToCurrentThread()); - gpu::Dx12VulkanVersionInfo dx12_vulkan_version_info; - gpu::RecordGpuSupportedRuntimeVersionHistograms(&dx12_vulkan_version_info); + uint32_t d3d12_feature_level = gpu::GetGpuSupportedD3D12Version(); + gpu::RecordGpuSupportedDx12VersionHistograms(d3d12_feature_level); - io_runner_->PostTask( - FROM_HERE, base::BindOnce(std::move(callback), dx12_vulkan_version_info, - device_perf_info_)); + io_runner_->PostTask(FROM_HERE, + base::BindOnce(std::move(callback), d3d12_feature_level, + device_perf_info_)); +} + +void InfoCollectionGpuServiceImpl::GetGpuSupportedVulkanVersionInfo( + GetGpuSupportedVulkanVersionInfoCallback callback) { + DCHECK(io_runner_->BelongsToCurrentThread()); + + main_runner_->PostTask( + FROM_HERE, + base::BindOnce( + &InfoCollectionGpuServiceImpl::GetGpuSupportedVulkanVersionInfoOnMain, + base::Unretained(this), std::move(callback))); +} + +void InfoCollectionGpuServiceImpl::GetGpuSupportedVulkanVersionInfoOnMain( + GetGpuSupportedVulkanVersionInfoCallback callback) { + DCHECK(main_runner_->BelongsToCurrentThread()); + + uint32_t vulkan_version = gpu::GetGpuSupportedVulkanVersion(gpu_device_); + io_runner_->PostTask(FROM_HERE, + base::BindOnce(std::move(callback), vulkan_version)); } void InfoCollectionGpuServiceImpl::RequestDxDiagNodeInfo( diff --git a/chromium/components/viz/service/gl/info_collection_gpu_service_impl.h b/chromium/components/viz/service/gl/info_collection_gpu_service_impl.h index 82fbd8a190c..52800cabd5a 100644 --- a/chromium/components/viz/service/gl/info_collection_gpu_service_impl.h +++ b/chromium/components/viz/service/gl/info_collection_gpu_service_impl.h @@ -28,14 +28,18 @@ class VIZ_SERVICE_EXPORT InfoCollectionGpuServiceImpl scoped_refptr<base::SingleThreadTaskRunner> main_runner, scoped_refptr<base::SingleThreadTaskRunner> io_runner, const gpu::DevicePerfInfo& device_perf_info, + const gpu::GPUInfo::GPUDevice& gpu_device, mojo::PendingReceiver<mojom::InfoCollectionGpuService> pending_receiver); ~InfoCollectionGpuServiceImpl() override; void RequestDxDiagNodeInfo(RequestDxDiagNodeInfoCallback callback) override; - void GetGpuSupportedRuntimeVersionAndDevicePerfInfo( - GetGpuSupportedRuntimeVersionAndDevicePerfInfoCallback callback) override; + void GetGpuSupportedDx12VersionAndDevicePerfInfo( + GetGpuSupportedDx12VersionAndDevicePerfInfoCallback callback) override; + + void GetGpuSupportedVulkanVersionInfo( + GetGpuSupportedVulkanVersionInfoCallback callback) override; private: void BindOnIO( @@ -43,8 +47,11 @@ class VIZ_SERVICE_EXPORT InfoCollectionGpuServiceImpl void RequestDxDiagNodeInfoOnMain(RequestDxDiagNodeInfoCallback callback); - void GetGpuSupportedRuntimeVersionAndDevicePerfInfoOnMain( - GetGpuSupportedRuntimeVersionAndDevicePerfInfoCallback callback); + void GetGpuSupportedDx12VersionAndDevicePerfInfoOnMain( + GetGpuSupportedDx12VersionAndDevicePerfInfoCallback callback); + + void GetGpuSupportedVulkanVersionInfoOnMain( + GetGpuSupportedVulkanVersionInfoCallback callback); scoped_refptr<base::SingleThreadTaskRunner> main_runner_; scoped_refptr<base::SingleThreadTaskRunner> io_runner_; @@ -53,6 +60,10 @@ class VIZ_SERVICE_EXPORT InfoCollectionGpuServiceImpl // unsandboxed GPU process. const gpu::DevicePerfInfo device_perf_info_; + // The GPU ids and the driver version that was passed down from the browser + // process + const gpu::GPUInfo::GPUDevice gpu_device_; + // Should only be accessed on the IO thread after creation. mojo::Receiver<mojom::InfoCollectionGpuService> receiver_{this}; diff --git a/chromium/components/viz/service/main/viz_main_impl.cc b/chromium/components/viz/service/main/viz_main_impl.cc index 13c8e2a1482..f74b6cd4ea8 100644 --- a/chromium/components/viz/service/main/viz_main_impl.cc +++ b/chromium/components/viz/service/main/viz_main_impl.cc @@ -199,7 +199,8 @@ void VizMainImpl::CreateInfoCollectionGpuService( info_collection_gpu_service_ = std::make_unique<InfoCollectionGpuServiceImpl>( gpu_thread_task_runner_, io_task_runner(), - gpu_init_->device_perf_info().value(), std::move(pending_receiver)); + gpu_init_->device_perf_info().value(), gpu_init_->gpu_info().active_gpu(), + std::move(pending_receiver)); } #endif diff --git a/chromium/components/viz/service/surfaces/surface_manager.h b/chromium/components/viz/service/surfaces/surface_manager.h index 75a7c60158c..a2a774769f7 100644 --- a/chromium/components/viz/service/surfaces/surface_manager.h +++ b/chromium/components/viz/service/surfaces/surface_manager.h @@ -12,9 +12,9 @@ #include <unordered_set> #include <vector> +#include "base/check_op.h" #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" -#include "base/logging.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" diff --git a/chromium/components/viz/service/surfaces/surface_unittest.cc b/chromium/components/viz/service/surfaces/surface_unittest.cc index 1054b5fa130..0ccffb3ffac 100644 --- a/chromium/components/viz/service/surfaces/surface_unittest.cc +++ b/chromium/components/viz/service/surfaces/surface_unittest.cc @@ -4,6 +4,7 @@ #include "components/viz/service/surfaces/surface.h" #include "base/bind.h" +#include "base/run_loop.h" #include "cc/test/scheduler_test_common.h" #include "components/viz/common/frame_sinks/copy_output_result.h" #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h" @@ -76,8 +77,10 @@ TEST(SurfaceTest, SurfaceIds) { } void TestCopyResultCallback(bool* called, + base::OnceClosure finished, std::unique_ptr<CopyOutputResult> result) { *called = true; + std::move(finished).Run(); } // Test that CopyOutputRequests can outlive the current frame and be @@ -94,14 +97,16 @@ TEST(SurfaceTest, CopyRequestLifetime) { CompositorFrame frame = MakeDefaultCompositorFrame(); support->SubmitCompositorFrame(local_surface_id, std::move(frame)); Surface* surface = surface_manager->GetSurfaceForId(surface_id); - ASSERT_TRUE(!!surface); + ASSERT_TRUE(surface); bool copy_called = false; + base::RunLoop copy_runloop; support->RequestCopyOfOutput( local_surface_id, std::make_unique<CopyOutputRequest>( CopyOutputRequest::ResultFormat::RGBA_BITMAP, - base::BindOnce(&TestCopyResultCallback, ©_called))); + base::BindOnce(&TestCopyResultCallback, ©_called, + copy_runloop.QuitClosure()))); surface->TakeCopyOutputRequestsFromClient(); EXPECT_TRUE(surface_manager->GetSurfaceForId(surface_id)); EXPECT_FALSE(copy_called); @@ -135,6 +140,7 @@ TEST(SurfaceTest, CopyRequestLifetime) { ASSERT_EQ(1u, copy_requests.count(last_pass_id)); EXPECT_FALSE(copy_called); copy_requests.clear(); // Deleted requests will auto-send an empty result. + copy_runloop.Run(); EXPECT_TRUE(copy_called); } diff --git a/chromium/components/viz/viz.gni b/chromium/components/viz/viz.gni index 89ee3c3f1f8..5f9f5501430 100644 --- a/chromium/components/viz/viz.gni +++ b/chromium/components/viz/viz.gni @@ -8,7 +8,7 @@ import("//testing/test.gni") viz_remove_configs = [] viz_add_configs = [ "//build/config:precompiled_headers" ] -if (!is_debug && (is_win || is_android)) { +if (!is_debug) { viz_remove_configs += [ "//build/config/compiler:default_optimization" ] viz_add_configs += [ "//build/config/compiler:optimize_max" ] } |