diff options
Diffstat (limited to 'chromium/components/viz')
228 files changed, 4476 insertions, 29985 deletions
diff --git a/chromium/components/viz/BUILD.gn b/chromium/components/viz/BUILD.gn index db7f49872ec..a1d5c682e06 100644 --- a/chromium/components/viz/BUILD.gn +++ b/chromium/components/viz/BUILD.gn @@ -41,13 +41,14 @@ viz_test("viz_unittests") { } if (is_fuchsia) { - use_cfv2 = false - additional_manifest_fragments = [ - # TODO(crbug.com/1185811): Figure out why jit_capabilities is needed. - "//build/config/fuchsia/test/jit_capabilities.test-cmx", + use_cfv1 = false - "//build/config/fuchsia/test/vulkan_capabilities.test-cmx", - ] + # TODO(https://crbug.com/1185811): Investigate removing the requirement for + # job_policy_ambient_mark_vmo_exec for the sake of V8's allocator in tests. + test_runner_shard = "//build/config/fuchsia/test/elf_test_ambient_exec_runner.shard.test-cml" + + additional_manifest_fragments = + [ "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml" ] } } diff --git a/chromium/components/viz/OWNERS b/chromium/components/viz/OWNERS index ee2085c35b7..d5881ea1a6b 100644 --- a/chromium/components/viz/OWNERS +++ b/chromium/components/viz/OWNERS @@ -7,10 +7,8 @@ # display / resources / quads / passes vmpstr@chromium.org -weiliangc@chromium.org # renderers (GL/Skia/Software) -weiliangc@chromium.org rjkroege@chromium.org ccameron@chromium.org penghuang@chromium.org diff --git a/chromium/components/viz/README.md b/chromium/components/viz/README.md index d6a63a63702..1170d457693 100644 --- a/chromium/components/viz/README.md +++ b/chromium/components/viz/README.md @@ -131,13 +131,6 @@ Code here supports presentation of the backing store drawn by the display compositor (typically thought of as SwapBuffers), as well as the use of overlays. -The source code is split into two build targets, -``components/viz/service:service`` and -``components/viz/service:gpu_service_dependencies``. The latter is -code that requires being run in the gpu process, thus could not work -if the viz service is located elsewhere. This is forward-looking code -as the viz service is moving into the gpu process always. - | Can depend on: | |:--------------------------------------| | viz/common/* | @@ -187,12 +180,6 @@ method. deallocating) gpu memory buffers, setting up a channel for the command buffer, etc. -Similar to ``service/display_embedder`` this is split into two targets in -the build system. Code that must run in the gpu process is compiled in -the ``components/viz/service:gpu_service_dependencies`` build target, -and the rest is compiled in ``components/viz/service:service``. As all -code moves to the gpu process, these two build targets will merge. - | Can depend on: | |:----------------------| | viz/common/* | diff --git a/chromium/components/viz/common/BUILD.gn b/chromium/components/viz/common/BUILD.gn index 7abb58d2112..9dbc3d66be3 100644 --- a/chromium/components/viz/common/BUILD.gn +++ b/chromium/components/viz/common/BUILD.gn @@ -180,12 +180,6 @@ viz_component("common") { "frame_sinks/delay_based_time_source.h", "frame_timing_details.h", "frame_timing_details_map.h", - "gl_i420_converter.cc", - "gl_i420_converter.h", - "gl_nv12_converter.cc", - "gl_nv12_converter.h", - "gl_scaler.cc", - "gl_scaler.h", "gpu/context_cache_controller.cc", "gpu/context_cache_controller.h", "gpu/context_lost_observer.h", @@ -340,6 +334,10 @@ viz_component("common") { sources += [ "display/use_layered_window.cc", "display/use_layered_window.h", + "overlay_state/win/overlay_state_aggregator.cc", + "overlay_state/win/overlay_state_aggregator.h", + "overlay_state/win/overlay_state_service.cc", + "overlay_state/win/overlay_state_service.h", ] deps += [ "//ui/base" ] @@ -395,18 +393,6 @@ viz_source_set("unit_tests") { "yuv_readback_unittest.cc", ] - if (enable_gl_renderer_tests) { - sources += [ - "gl_i420_converter_pixeltest.cc", - "gl_i420_converter_unittest.cc", - "gl_nv12_converter_pixeltest.cc", - "gl_scaler_overscan_pixeltest.cc", - "gl_scaler_pixeltest.cc", - "gl_scaler_shader_pixeltest.cc", - "gl_scaler_unittest.cc", - ] - } - if (enable_vulkan) { sources += [ "gpu/vulkan_in_process_context_provider_unittest.cc" ] } @@ -428,6 +414,14 @@ viz_source_set("unit_tests") { "//ui/gfx/geometry", "//ui/latency", ] + + if (is_win) { + sources += [ "overlay_state/win/overlay_state_service_unittest.cc" ] + deps += [ + "//components/viz/test:test_suite", + "//media/mojo/mojom", + ] + } } viz_source_set("perf_tests") { @@ -446,7 +440,11 @@ viz_source_set("perf_tests") { if (is_android) { android_library("common_java") { - deps = [ "//base:base_java" ] + deps = [ + "//base:base_java", + "//base:jni_java", + "//build/android:build_java", + ] sources = [ "java/src/org/chromium/components/viz/common/VizSwitches.java", "java/src/org/chromium/components/viz/common/display/DeJellyUtils.java", diff --git a/chromium/components/viz/common/DEPS b/chromium/components/viz/common/DEPS index bff0504c2df..398e9db0ce5 100644 --- a/chromium/components/viz/common/DEPS +++ b/chromium/components/viz/common/DEPS @@ -42,5 +42,5 @@ specific_include_rules = { "bitmap_allocation.cc" : [ # Only used to pass Mojo handles, not to communicate with the viz service. "+mojo/public/cpp/base/shared_memory_utils.h", - ], + ] } diff --git a/chromium/components/viz/common/display/renderer_settings.h b/chromium/components/viz/common/display/renderer_settings.h index 8b0b5eda59c..bc48a50a766 100644 --- a/chromium/components/viz/common/display/renderer_settings.h +++ b/chromium/components/viz/common/display/renderer_settings.h @@ -30,7 +30,6 @@ class VIZ_COMMON_EXPORT RendererSettings { bool partial_swap_enabled = false; bool should_clear_root_render_pass = true; bool release_overlay_resources_after_gpu_query = false; - bool use_skia_renderer = true; bool dont_round_texture_sizes_for_pixel_tests = false; int highp_threshold_min = 0; bool auto_resize_output_surface = true; diff --git a/chromium/components/viz/common/features.cc b/chromium/components/viz/common/features.cc index fa5f838ac37..808900f9bc9 100644 --- a/chromium/components/viz/common/features.cc +++ b/chromium/components/viz/common/features.cc @@ -37,12 +37,13 @@ namespace features { const base::Feature kAdpf{"Adpf", base::FEATURE_DISABLED_BY_DEFAULT}; // Target duration used for power hint on Android. +// `0` indicates use hard coded default. const base::FeatureParam<int> kAdpfTargetDurationMs{&kAdpf, - "AdpfTargetDurationMs", 12}; + "AdpfTargetDurationMs", 0}; const base::Feature kEnableOverlayPrioritization { "EnableOverlayPrioritization", -#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(IS_CHROMEOS_ASH) base::FEATURE_ENABLED_BY_DEFAULT #else base::FEATURE_DISABLED_BY_DEFAULT @@ -68,10 +69,6 @@ const base::Feature kVideoDetectorIgnoreNonVideos{ const base::Feature kSimpleFrameRateThrottling{ "SimpleFrameRateThrottling", base::FEATURE_DISABLED_BY_DEFAULT}; -// Use the SkiaRenderer. -const base::Feature kUseSkiaRenderer{"UseSkiaRenderer", - base::FEATURE_ENABLED_BY_DEFAULT}; - // Kill-switch to disable de-jelly, even if flags/properties indicate it should // be enabled. const base::Feature kDisableDeJelly{"DisableDeJelly", @@ -84,10 +81,6 @@ const base::Feature kDynamicColorGamut{"DynamicColorGamut", base::FEATURE_DISABLED_BY_DEFAULT}; #endif -// Uses glClear to composite solid color quads whenever possible. -const base::Feature kFastSolidColorDraw{"FastSolidColorDraw", - base::FEATURE_DISABLED_BY_DEFAULT}; - // Submit CompositorFrame from SynchronousLayerTreeFrameSink directly to viz in // WebView. const base::Feature kVizFrameSubmissionForWebView{ @@ -190,15 +183,13 @@ bool IsAdpfEnabled() { return base::FeatureList::IsEnabled(kAdpf); } -bool IsClipPrewalkDamageEnabled() { - static constexpr base::Feature kClipPrewalkDamage{ - "ClipPrewalkDamage", base::FEATURE_ENABLED_BY_DEFAULT}; - - return base::FeatureList::IsEnabled(kClipPrewalkDamage); -} - bool IsOverlayPrioritizationEnabled() { +#if BUILDFLAG(IS_CHROMEOS_LACROS) + // DelegatedCompositing in Lacros makes this feature a no-op. + return false; +#else return base::FeatureList::IsEnabled(kEnableOverlayPrioritization); +#endif } bool IsDelegatedCompositingEnabled() { @@ -218,11 +209,6 @@ bool IsSimpleFrameRateThrottlingEnabled() { return base::FeatureList::IsEnabled(kSimpleFrameRateThrottling); } -bool IsUsingSkiaRenderer() { - return base::FeatureList::IsEnabled(kUseSkiaRenderer) || - features::IsUsingVulkan(); -} - #if BUILDFLAG(IS_ANDROID) bool IsDynamicColorGamutEnabled() { if (viz::AlwaysUseWideColorGamut()) @@ -234,10 +220,6 @@ bool IsDynamicColorGamutEnabled() { } #endif -bool IsUsingFastPathForSolidColorQuad() { - return base::FeatureList::IsEnabled(kFastSolidColorDraw); -} - bool IsUsingVizFrameSubmissionForWebView() { return base::FeatureList::IsEnabled(kVizFrameSubmissionForWebView); } @@ -290,8 +272,8 @@ bool ShouldUsePlatformDelegatedInk() { return base::FeatureList::IsEnabled(kUsePlatformDelegatedInk); } -#if BUILDFLAG(IS_ANDROID) bool UseSurfaceLayerForVideo() { +#if BUILDFLAG(IS_ANDROID) // SurfaceLayer video should work fine with new heuristic. if (base::FeatureList::IsEnabled(kWebViewNewInvalidateHeuristic)) return true; @@ -301,8 +283,12 @@ bool UseSurfaceLayerForVideo() { return true; } return base::FeatureList::IsEnabled(kUseSurfaceLayerForVideoDefault); +#else + return true; +#endif } +#if BUILDFLAG(IS_ANDROID) bool UseRealVideoColorSpaceForDisplay() { // We need Android S for proper color space support in SurfaceControl. if (base::android::BuildInfo::GetInstance()->sdk_int() < diff --git a/chromium/components/viz/common/features.h b/chromium/components/viz/common/features.h index 139f9430c10..aab8f02e427 100644 --- a/chromium/components/viz/common/features.h +++ b/chromium/components/viz/common/features.h @@ -22,7 +22,6 @@ namespace features { VIZ_COMMON_EXPORT extern const base::Feature kAdpf; VIZ_COMMON_EXPORT extern const base::FeatureParam<int> kAdpfTargetDurationMs; VIZ_COMMON_EXPORT extern const base::Feature kEnableOverlayPrioritization; -VIZ_COMMON_EXPORT extern const base::Feature kUseSkiaRenderer; VIZ_COMMON_EXPORT extern const base::Feature kDelegatedCompositing; VIZ_COMMON_EXPORT extern const base::Feature kRecordSkPicture; VIZ_COMMON_EXPORT extern const base::Feature kDisableDeJelly; @@ -32,7 +31,6 @@ VIZ_COMMON_EXPORT extern const base::Feature kVideoDetectorIgnoreNonVideos; #if BUILDFLAG(IS_ANDROID) VIZ_COMMON_EXPORT extern const base::Feature kDynamicColorGamut; #endif -VIZ_COMMON_EXPORT extern const base::Feature kFastSolidColorDraw; VIZ_COMMON_EXPORT extern const base::Feature kVizFrameSubmissionForWebView; VIZ_COMMON_EXPORT extern const base::Feature kUsePreferredIntervalForVideo; VIZ_COMMON_EXPORT extern const base::Feature kUseRealBuffersForPageFlipTest; @@ -69,7 +67,6 @@ VIZ_COMMON_EXPORT extern const char kPredictorLinear2[]; VIZ_COMMON_EXPORT extern const char kPredictorLsq[]; VIZ_COMMON_EXPORT bool IsAdpfEnabled(); -VIZ_COMMON_EXPORT bool IsClipPrewalkDamageEnabled(); VIZ_COMMON_EXPORT bool IsSimpleFrameRateThrottlingEnabled(); #if BUILDFLAG(IS_ANDROID) VIZ_COMMON_EXPORT bool IsDynamicColorGamutEnabled(); @@ -77,8 +74,6 @@ VIZ_COMMON_EXPORT bool IsDynamicColorGamutEnabled(); VIZ_COMMON_EXPORT bool IsOverlayPrioritizationEnabled(); VIZ_COMMON_EXPORT bool IsDelegatedCompositingEnabled(); VIZ_COMMON_EXPORT bool IsSyncWindowDestructionEnabled(); -VIZ_COMMON_EXPORT bool IsUsingFastPathForSolidColorQuad(); -VIZ_COMMON_EXPORT bool IsUsingSkiaRenderer(); VIZ_COMMON_EXPORT bool IsUsingVizFrameSubmissionForWebView(); VIZ_COMMON_EXPORT bool IsUsingPreferredIntervalForVideo(); VIZ_COMMON_EXPORT bool ShouldUseRealBuffersForPageFlipTest(); @@ -89,8 +84,8 @@ VIZ_COMMON_EXPORT bool ShouldUseSetPresentDuration(); VIZ_COMMON_EXPORT absl::optional<int> ShouldDrawPredictedInkPoints(); VIZ_COMMON_EXPORT std::string InkPredictor(); VIZ_COMMON_EXPORT bool ShouldUsePlatformDelegatedInk(); -#if BUILDFLAG(IS_ANDROID) VIZ_COMMON_EXPORT bool UseSurfaceLayerForVideo(); +#if BUILDFLAG(IS_ANDROID) VIZ_COMMON_EXPORT bool UseRealVideoColorSpaceForDisplay(); #endif VIZ_COMMON_EXPORT bool IsSurfaceSyncThrottling(); diff --git a/chromium/components/viz/common/frame_sinks/README.md b/chromium/components/viz/common/frame_sinks/README.md index fc04942b7cf..f134b4e142e 100644 --- a/chromium/components/viz/common/frame_sinks/README.md +++ b/chromium/components/viz/common/frame_sinks/README.md @@ -56,8 +56,8 @@ properties: Note that all coordinates are constrained to be integer values, to avoid introducing alignment, rounding or other "fuzz" issues. - * Result format: An RGBA-interleaved bitmap (SkBitmap) or I420 Y+U+V image - planes. + * Result format: An RGBA-interleaved bitmap (SkBitmap), I420 Y+U+V image + planes, or NV12 Y+UV image planes. For efficient video capture, the above are used as follows: An issuer of CopyOutputRequests "locks into" a target area within the Surface (usually the diff --git a/chromium/components/viz/common/frame_sinks/blit_request.cc b/chromium/components/viz/common/frame_sinks/blit_request.cc index 29bbb2d2fb5..b55f2a216fd 100644 --- a/chromium/components/viz/common/frame_sinks/blit_request.cc +++ b/chromium/components/viz/common/frame_sinks/blit_request.cc @@ -31,10 +31,14 @@ std::string BlendBitmap::ToString() const { BlitRequest::BlitRequest( const gfx::Point& destination_region_offset, + LetterboxingBehavior letterboxing_behavior, const std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes>& - mailboxes) + mailboxes, + bool populates_gpu_memory_buffer) : destination_region_offset_(destination_region_offset), - mailboxes_(mailboxes) {} + letterboxing_behavior_(letterboxing_behavior), + mailboxes_(mailboxes), + populates_gpu_memory_buffer_(populates_gpu_memory_buffer) {} BlitRequest::BlitRequest(BlitRequest&& other) = default; BlitRequest& BlitRequest::operator=(BlitRequest&& other) = default; @@ -42,9 +46,10 @@ BlitRequest& BlitRequest::operator=(BlitRequest&& other) = default; BlitRequest::~BlitRequest() = default; std::string BlitRequest::ToString() const { - return base::StringPrintf("blit to %s, blend %u bitmaps", + return base::StringPrintf("blit to %s, blend %u bitmaps, populates GMB? %d", destination_region_offset_.ToString().c_str(), - static_cast<uint32_t>(blend_bitmaps_.size())); + static_cast<uint32_t>(blend_bitmaps_.size()), + populates_gpu_memory_buffer_); } } // namespace viz diff --git a/chromium/components/viz/common/frame_sinks/blit_request.h b/chromium/components/viz/common/frame_sinks/blit_request.h index b236231c611..ee4656b2332 100644 --- a/chromium/components/viz/common/frame_sinks/blit_request.h +++ b/chromium/components/viz/common/frame_sinks/blit_request.h @@ -53,6 +53,16 @@ class VIZ_COMMON_EXPORT BlendBitmap { sk_sp<SkImage> image_; }; +// Enum used to specify letteboxing behavior for a BlitRequest. +enum class LetterboxingBehavior { + // No letterboxing is needed - only the destination region will be written + // into by the handler of CopyOutputRequest. + kDoNotLetterbox, + // Letterboxing is needed - everything outside of the destination region + // will be filled with black by the handler of CopyOutputRequest. + kLetterbox +}; + // Structure describing a blit operation that can be appended to // `CopyOutputRequest` if the callers want to place the results of the operation // in textures that they own. @@ -60,8 +70,10 @@ class VIZ_COMMON_EXPORT BlitRequest { public: explicit BlitRequest( const gfx::Point& destination_region_offset, + LetterboxingBehavior letterboxing_behavior, const std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes>& - mailboxes); + mailboxes, + bool populates_gpu_memory_buffer); BlitRequest(BlitRequest&& other); BlitRequest& operator=(BlitRequest&& other); @@ -74,6 +86,10 @@ class VIZ_COMMON_EXPORT BlitRequest { return destination_region_offset_; } + LetterboxingBehavior letterboxing_behavior() const { + return letterboxing_behavior_; + } + const std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes>& mailboxes() const { return mailboxes_; @@ -84,6 +100,10 @@ class VIZ_COMMON_EXPORT BlitRequest { return mailboxes_[i]; } + bool populates_gpu_memory_buffer() const { + return populates_gpu_memory_buffer_; + } + // Appends a new `BlendBitmap` request to this blit request. // |source_region| is expressed in |image|'s coordinate system // (i.e. 0,0 width x height rectangle). |destination_region| is expressed @@ -106,12 +126,20 @@ class VIZ_COMMON_EXPORT BlitRequest { // images. gfx::Point destination_region_offset_; + // Specifies the letterboxing behavior of this request. + LetterboxingBehavior letterboxing_behavior_; + // Mailboxes with planes that will be populated. // The textures can (but don't have to be) backed by // a GpuMemoryBuffer. The pixel format of the request determines // how many planes need to be present. std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes> mailboxes_; + // True if `mailboxes_` describe shared images that have been created from + // a GpuMemoryBuffer. In this case, the CopyOutputResult needs to be sent out + // only after it's safe to map the GpuMemoryBuffer to system memory. + bool populates_gpu_memory_buffer_; + // Collection of bitmaps that will be blended onto the textures. // They will be blended in order (so if i < j, bitmap at offset i will // be blended before bitmap at offset j), using SrcOver blend mode. 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 648a6fa7f0a..63f2ff3fd1f 100644 --- a/chromium/components/viz/common/frame_sinks/copy_output_request.cc +++ b/chromium/components/viz/common/frame_sinks/copy_output_request.cc @@ -107,6 +107,7 @@ void CopyOutputRequest::set_blit_request(BlitRequest blit_request) { DCHECK(!blit_request_); DCHECK_EQ(result_destination(), ResultDestination::kNativeTextures); DCHECK_EQ(result_format(), ResultFormat::NV12_PLANES); + DCHECK(has_result_selection()); // Destination region must start at an even offset for NV12 results: DCHECK_EQ(blit_request.destination_region_offset().x() % 2, 0); diff --git a/chromium/components/viz/common/frame_sinks/copy_output_request.h b/chromium/components/viz/common/frame_sinks/copy_output_request.h index fa9a5ddd42a..28fef1f9b69 100644 --- a/chromium/components/viz/common/frame_sinks/copy_output_request.h +++ b/chromium/components/viz/common/frame_sinks/copy_output_request.h @@ -116,12 +116,14 @@ class VIZ_COMMON_EXPORT CopyOutputRequest { // Optionally specify that only a portion of the result be generated. The // selection rect will be clamped to the result bounds, which always starts at // 0,0 and spans the post-scaling size of the copy area (see set_area() - // above). Only RGBA format supports odd-sized result selection. + // above). Only RGBA format supports odd-sized result selection. Can only be + // called before blit request was set on the copy request. void set_result_selection(const gfx::Rect& selection) { DCHECK(result_format_ == ResultFormat::RGBA || (selection.width() % 2 == 0 && selection.height() % 2 == 0)) << "CopyOutputRequest supports odd-sized result_selection() only for " "RGBA!"; + DCHECK(!has_blit_request()); result_selection_ = selection; } bool has_result_selection() const { return result_selection_.has_value(); } @@ -129,7 +131,15 @@ class VIZ_COMMON_EXPORT CopyOutputRequest { // Requests that the region copied by the CopyOutputRequest be blitted into // the caller's textures. Can be called only for CopyOutputRequests that - // target native textures. + // target native textures. Requires that result selection was set, in which + // case the caller's textures will be populated with the results of the + // copy request. The region in the caller's textures that will be populated + // is specified by `gfx::Rect(blit_request.destination_region_offset(), + // result_selection().size())`. If blit request is configured to perform + // letterboxing, all contents outside of that region will be overwritten with + // black, otherwise they will be unchanged. If the copy request's result would + // be smaller than `result_selection().size()`, the request will fail (i.e. + // empty result will be sent). void set_blit_request(BlitRequest blit_request); bool has_blit_request() const { return blit_request_.has_value(); } const BlitRequest& blit_request() const { return *blit_request_; } diff --git a/chromium/components/viz/common/gl_i420_converter.cc b/chromium/components/viz/common/gl_i420_converter.cc deleted file mode 100644 index c00e202c351..00000000000 --- a/chromium/components/viz/common/gl_i420_converter.cc +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2018 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/gl_i420_converter.h" - -#include <utility> - -#include "components/viz/common/gpu/context_provider.h" - -namespace viz { - -GLI420Converter::GLI420Converter(ContextProvider* context_provider) - : GLI420Converter(context_provider, true) { - DCHECK(context_provider_); -} - -GLI420Converter::GLI420Converter(ContextProvider* context_provider, - bool allow_mrt_path) - : context_provider_(context_provider), - step1_(context_provider_), - step2_(context_provider_) { - DCHECK(context_provider_); - context_provider_->AddObserver(this); - if (!allow_mrt_path || step1_.GetMaxDrawBuffersSupported() < 2) { - step3_ = std::make_unique<GLScaler>(context_provider_); - step4_ = std::make_unique<GLScaler>(context_provider_); - } -} - -GLI420Converter::~GLI420Converter() { - OnContextLost(); // Free context-related resources. -} - -bool GLI420Converter::Configure(const Parameters& params) { - Parameters step1_params = params; - if (!step1_params.output_color_space.IsValid()) { - step1_params.output_color_space = gfx::ColorSpace::CreateREC709(); - } - - // Configure the "step 1" scaler. - if (is_using_mrt_path()) { - step1_params.export_format = Parameters::ExportFormat::NV61; - DCHECK_EQ(step1_params.swizzle[0], params.swizzle[0]); - step1_params.swizzle[1] = GL_RGBA; // Don't swizzle 2nd rendering target. - } else { - step1_params.export_format = Parameters::ExportFormat::INTERLEAVED_QUADS; - step1_params.swizzle[0] = GL_RGBA; // Will swizzle in steps 2-4. - } - if (!step1_.Configure(step1_params)) { - return false; - } - - // Configure the "step 2" scaler (and steps 3 and 4 for the non-MRT path) that - // further transform the output from the "step 1" scaler to produce the final - // outputs. - Parameters step2_params; - step2_params.scale_to = gfx::Vector2d(1, 1); - step2_params.source_color_space = step1_params.output_color_space; - step2_params.output_color_space = step1_params.output_color_space; - // Use FAST quality, a single bilinear pass, because there will either be no - // scaling or exactly 50% scaling. - step2_params.quality = Parameters::Quality::FAST; - step2_params.swizzle[0] = params.swizzle[0]; - if (is_using_mrt_path()) { - // NV61 provides half-width and full-height U/V. I420 U/V planes are - // half-width and half-height. So, scale Y by 50%. - step2_params.scale_from = gfx::Vector2d(1, 2); - step2_params.export_format = - Parameters::ExportFormat::DEINTERLEAVE_PAIRWISE; - step2_params.swizzle[1] = step2_params.swizzle[0]; - if (!step2_.Configure(step2_params)) { - return false; - } - } else { - // Extract a full-size Y plane from the interleaved YUVA from step 1. - step2_params.scale_from = gfx::Vector2d(1, 1); - step2_params.export_format = Parameters::ExportFormat::CHANNEL_0; - if (!step2_.Configure(step2_params)) { - return false; - } - // Extract half-size U/V planes from the interleaved YUVA from step 1. - step2_params.scale_from = gfx::Vector2d(2, 2); - step2_params.export_format = Parameters::ExportFormat::CHANNEL_1; - if (!step3_->Configure(step2_params)) { - return false; - } - step2_params.export_format = Parameters::ExportFormat::CHANNEL_2; - if (!step4_->Configure(step2_params)) { - return false; - } - } - - params_ = params; - return true; -} - -bool GLI420Converter::Convert(GLuint src_texture, - const gfx::Size& src_texture_size, - const gfx::Vector2d& src_offset, - const gfx::Rect& output_rect, - const GLuint yuv_textures[3]) { - DCHECK_EQ(output_rect.x() % 8, 0); - DCHECK_EQ(output_rect.width() % 8, 0); - DCHECK_EQ(output_rect.y() % 2, 0); - DCHECK_EQ(output_rect.height() % 2, 0); - - if (!context_provider_) { - return false; - } - - if (is_using_mrt_path()) { - const gfx::Rect luma_output_rect(output_rect.x() / 4, output_rect.y(), - output_rect.width() / 4, - output_rect.height()); - EnsureIntermediateTextureDefined(luma_output_rect.size()); - const gfx::Rect chroma_output_rect( - gfx::Size(luma_output_rect.width() / 2, luma_output_rect.height() / 2)); - return (step1_.ScaleToMultipleOutputs( - src_texture, src_texture_size, src_offset, yuv_textures[0], - intermediate_texture_, luma_output_rect) && - step2_.ScaleToMultipleOutputs(intermediate_texture_, - intermediate_texture_size_, - gfx::Vector2d(), yuv_textures[1], - yuv_textures[2], chroma_output_rect)); - } - - // Non-MRT path: - EnsureIntermediateTextureDefined(output_rect.size()); - const gfx::Rect luma_output_rect(0, 0, output_rect.width() / 4, - output_rect.height()); - const gfx::Rect chroma_output_rect(0, 0, luma_output_rect.width() / 2, - luma_output_rect.height() / 2); - return (step1_.Scale(src_texture, src_texture_size, src_offset, - intermediate_texture_, output_rect) && - step2_.Scale(intermediate_texture_, intermediate_texture_size_, - gfx::Vector2d(), yuv_textures[0], luma_output_rect) && - step3_->Scale(intermediate_texture_, intermediate_texture_size_, - gfx::Vector2d(), yuv_textures[1], chroma_output_rect) && - step4_->Scale(intermediate_texture_, intermediate_texture_size_, - gfx::Vector2d(), yuv_textures[2], chroma_output_rect)); -} - -// static -gfx::Rect GLI420Converter::ToAlignedRect(const gfx::Rect& rect) { - // Origin coordinates: FLOOR(...) - const int aligned_x = - ((rect.x() < 0) ? ((rect.x() - 7) / 8) : (rect.x() / 8)) * 8; - const int aligned_y = - ((rect.y() < 0) ? ((rect.y() - 1) / 2) : (rect.y() / 2)) * 2; - // Span coordinates: CEIL(...) - const int aligned_right = - ((rect.right() < 0) ? (rect.right() / 8) : ((rect.right() + 7) / 8)) * 8; - const int aligned_bottom = - ((rect.bottom() < 0) ? (rect.bottom() / 2) : ((rect.bottom() + 1) / 2)) * - 2; - return gfx::Rect(aligned_x, aligned_y, aligned_right - aligned_x, - aligned_bottom - aligned_y); -} - -// static -bool GLI420Converter::ParametersAreEquivalent(const Parameters& a, - const Parameters& b) { - const auto Resolve = [](Parameters params) { - // Per header comments, if an invalid output_color_space is specified, use - // REC709. - if (!params.output_color_space.IsValid()) { - params.output_color_space = gfx::ColorSpace::CreateREC709(); - } - // Both of these fields are overwritten, in Configure(), whether the MRT - // path is going to be used or not. So, for the purposes of "equivalence," - // just set these like the MRT path would. - params.export_format = Parameters::ExportFormat::NV61; - params.swizzle[1] = GL_RGBA; - return params; - }; - return GLScaler::ParametersAreEquivalent(Resolve(a), Resolve(b)); -} - -void GLI420Converter::EnsureIntermediateTextureDefined( - const gfx::Size& required) { - if (intermediate_texture_size_ == required) { - return; - } - auto* const gl = context_provider_->ContextGL(); - if (intermediate_texture_ == 0) { - gl->GenTextures(1, &intermediate_texture_); - } - gl->BindTexture(GL_TEXTURE_2D, intermediate_texture_); - gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, required.width(), required.height(), - 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - intermediate_texture_size_ = required; -} - -void GLI420Converter::OnContextLost() { - if (intermediate_texture_ != 0) { - if (auto* gl = context_provider_->ContextGL()) { - gl->DeleteTextures(1, &intermediate_texture_); - } - intermediate_texture_ = 0; - intermediate_texture_size_ = gfx::Size(); - } - if (context_provider_) { - context_provider_->RemoveObserver(this); - context_provider_ = nullptr; - } -} - -} // namespace viz diff --git a/chromium/components/viz/common/gl_i420_converter.h b/chromium/components/viz/common/gl_i420_converter.h deleted file mode 100644 index 6792ca8fdc8..00000000000 --- a/chromium/components/viz/common/gl_i420_converter.h +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2018 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_GL_I420_CONVERTER_H_ -#define COMPONENTS_VIZ_COMMON_GL_I420_CONVERTER_H_ - -#include <memory> - -#include "base/memory/raw_ptr.h" -#include "base/memory/scoped_refptr.h" -#include "components/viz/common/gl_scaler.h" -#include "components/viz/common/gpu/context_lost_observer.h" -#include "components/viz/common/viz_common_export.h" -#include "ui/gfx/color_space.h" -#include "ui/gfx/geometry/size.h" - -namespace gfx { -class Rect; -class Vector2d; -} // namespace gfx - -namespace viz { - -class ContextProvider; - -// A convenience wrapper around GLScaler that also reformats the scaler's output -// from interleaved RGBA to I420 planes. The I420 format consists of three -// planes of image data: the Y (luma) plane at full size, plus U and V (chroma) -// planes at half-width and half-height. There are two possible modes of -// operation (auto-detected at runtime): -// -// The faster, multiple rendering target (MRT) path: If the platform supports -// MRTs (most of the GPUs in use today), scaling and conversion is a two step -// process: -// -// Step 1: Produce NV61 format output, a luma plane and a UV-interleaved -// image. The luma plane is the same as the desired I420 luma plane. Note, -// that the UV image is of half-width but not yet half-height. -// -// (interleaved quads) -// RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA -// RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA -// RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA -// RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA -// | -// | (luma plane) (chroma, interleaved) -// | YYYY YYYY UVUV UVUV -// +---> { YYYY YYYY + UVUV UVUV } -// YYYY YYYY UVUV UVUV -// YYYY YYYY UVUV UVUV -// -// Step 2: Derives the two I420 chroma planes from the UV-interleaved image -// from Step 1. This step separates the U and V pixels into separate planes, -// and also scales the height by half. This produces the desired I420 chroma -// planes. -// -// (chroma, interleaved) (two chroma planes) -// UVUV UVUV -// UVUV UVUV --> { UUUU + VVVV } -// UVUV UVUV UUUU VVVV -// UVUV UVUV -// -// The non-MRT path: For platforms that can only render to a single target at a -// time. This first scales the source to its final size and color-converts, -// transforming an RGBA input into a YUVA output. Then, it scans the YUVA image -// three times to generate each of the Y+U+V planes. -// -// Texture packing: OpenGLES2 treats all of the input and output textures as -// RGBA format. See comments for the Convert() method, which explains how the -// planar image data is packed into GL_RGBA textures, how the output textures -// should be sized, and why there are alignment requirements when specifying the -// output rect. -class VIZ_COMMON_EXPORT GLI420Converter final : public ContextLostObserver { - public: - // GLI420Converter uses the exact same parameters as GLScaler. - using Parameters = GLScaler::Parameters; - - explicit GLI420Converter(ContextProvider* context_provider); - - GLI420Converter(const GLI420Converter&) = delete; - GLI420Converter& operator=(const GLI420Converter&) = delete; - - ~GLI420Converter() final; - - // Returns true if the GL context provides the necessary support for enabling - // precise color management (see Parameters::enable_precise_color_management). - bool SupportsPreciseColorManagement() const { - return step1_.SupportsPreciseColorManagement(); - } - - // [Re]Configure the converter with the given |new_params|. Returns true on - // success, or false on failure. If |new_params| does not specify an - // |output_color_space|, it will be default to REC709. - [[nodiscard]] bool Configure(const Parameters& new_params); - - // Returns the currently-configured and resolved Parameters. Results are - // undefined if Configure() has never been called successfully. - const Parameters& params() const { return params_; } - - // Scales a portion of |src_texture|, then format-converts it to three I420 - // planes, placing the results into |yuv_textures| at offset (0, 0). Returns - // true to indicate success, or false if this GLI420Converter is not valid. - // - // |src_texture_size| is the full, allocated size of the |src_texture|. This - // is required for computing texture coordinate transforms (and only because - // the OpenGL ES 2.0 API lacks the ability to query this info). - // - // |src_offset| is the offset in the source texture corresponding to point - // (0,0) in the source/output coordinate spaces. This prevents the need for - // extra texture copies just to re-position the source coordinate system. - // - // |aligned_output_rect| selects the region to draw (in the scaled, not the - // source, coordinate space). This is used to save work in cases where only a - // portion needs to be re-scaled. Because of the way the planar image data is - // packed in the output textures, the output rect's coordinates must be - // aligned (see ToAlignedRect() below). - // - // The |yuv_textures| are packed with planar data, meaning that each RGBA quad - // contains four pixel values: R is pixel 0, G is pixel 1, and so on. This - // makes it trivial to read-back the textures from a pixel buffer as a - // sequence of unsigned bytes. Thus, the output texture for the Y plane should - // be defined as GL_RGBA and be at least 1/4 the width of that specified in - // |aligned_output_rect|. Similarly, the output textures for the U and V - // planes should be defined as GL_RGBA and have at least 1/8 the width and 1/2 - // the height of |aligned_output_rect|. - // - // WARNING: The output will always be placed at (0, 0) in the output textures, - // and not at |aligned_output_rect.origin()|. - // - // Note that the |src_texture| will have the min/mag filter set to GL_LINEAR - // and wrap_s/t set to CLAMP_TO_EDGE in this call. - bool Convert(GLuint src_texture, - const gfx::Size& src_texture_size, - const gfx::Vector2d& src_offset, - const gfx::Rect& aligned_output_rect, - const GLuint yuv_textures[3]); - - // Returns the smallest Rect that encloses |rect| and lays on aligned - // boundaries, as required by the |aligned_output_rect| argument passed to - // Convert(). The horizontal coordinates will always be a multiple of 8, and - // the vertical coordinates a multiple of 2. - static gfx::Rect ToAlignedRect(const gfx::Rect& rect); - - // Returns true if configuring a GLI420Converter with either |a| or |b| will - // produce identical behaviors and results. - static bool ParametersAreEquivalent(const Parameters& a, const Parameters& b); - - private: - friend class GLI420ConverterPixelTest; - - GLI420Converter(ContextProvider* context_provider, bool allow_mrt_path); - - bool is_using_mrt_path() const { return !step3_; } - - // Creates or re-defines the intermediate texture, to ensure a texture of the - // given |required| size is defined. - void EnsureIntermediateTextureDefined(const gfx::Size& required); - - // ContextLostObserver implementation. - void OnContextLost() final; - - // The provider of the GL context. This is non-null while the GL context is - // valid and GLI420Converter is observing for context loss. - raw_ptr<ContextProvider> context_provider_; - - // Scales the source content and produces either: - // * MRT path: NV61-format output in two textures. - // * Non-MRT path: YUVA interleaved output in one texture. - GLScaler step1_; - - // Holds the results from executing the first-stage |scaler_|, and is read by - // the other scalers: - // * MRT path: This holds the UV-interleaved data (2nd rendering target). - // * Non-MRT path: The scaled YUVA interleaved data. - GLuint intermediate_texture_ = 0; - gfx::Size intermediate_texture_size_; - - // Step 2 operation using the |intermediate_texture_| as input: - // * MRT path: Separates-out the U and V planes (and scales height by half). - // * Non-MRT path: Extracts the luma plane. - GLScaler step2_; - - // Steps 3 and 4 are used by the non-MRT path only, to extract the two chroma - // planes from |intermediate_texture_|. - std::unique_ptr<GLScaler> step3_; - std::unique_ptr<GLScaler> step4_; - - // The Parameters that were provided to the last successful Configure() call. - Parameters params_; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_COMMON_GL_I420_CONVERTER_H_ diff --git a/chromium/components/viz/common/gl_i420_converter_pixeltest.cc b/chromium/components/viz/common/gl_i420_converter_pixeltest.cc deleted file mode 100644 index 1cf61bbd691..00000000000 --- a/chromium/components/viz/common/gl_i420_converter_pixeltest.cc +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2018 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/gl_i420_converter.h" - -#include <GLES2/gl2ext.h> - -#include "cc/test/pixel_test.h" -#include "cc/test/pixel_test_utils.h" -#include "components/viz/test/gl_scaler_test_util.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkColor.h" - -namespace viz { - -class GLI420ConverterPixelTest : public cc::PixelTest, - public GLScalerTestUtil, - public testing::WithParamInterface<bool> { - public: - bool allow_mrt_path() const { return GetParam(); } - GLI420Converter* converter() const { return converter_.get(); } - - GLuint CreateTexture(const gfx::Size& size) { - return texture_helper_->CreateTexture(size); - } - - GLuint UploadTexture(const SkBitmap& bitmap) { - return texture_helper_->UploadTexture(bitmap); - } - - SkBitmap DownloadTexture(GLuint texture, const gfx::Size& size) { - return texture_helper_->DownloadTexture(texture, size); - } - - protected: - void SetUp() final { - cc::PixelTest::SetUpGLWithoutRenderer(gfx::SurfaceOrigin::kBottomLeft); - converter_.reset(new GLI420Converter(context_provider(), allow_mrt_path())); - texture_helper_ = std::make_unique<GLScalerTestTextureHelper>( - context_provider()->ContextGL()); - } - - void TearDown() final { - texture_helper_.reset(); - converter_.reset(); - cc::PixelTest::TearDown(); - } - - private: - std::unique_ptr<GLI420Converter> converter_; - std::unique_ptr<GLScalerTestTextureHelper> texture_helper_; -}; - -// Note: This test is pretty much the same as -// GLScalerPixelTest.Example_ScaleAndExportForScreenVideoCapture. The goal of -// this pixel test is to just confirm that everything internal to -// GLI420Converter has been plumbed-through correctly. -TEST_P(GLI420ConverterPixelTest, ScaleAndConvert) { - // These parameters have been chosen based on: 1) overriding defaults, to - // confirm Parameters plumbing; and 2) typical operation on most platforms - // (e.g., flipped source textures, the need to swizzle outputs, etc.). - GLI420Converter::Parameters params; - params.scale_from = gfx::Vector2d(2160, 1440); - params.scale_to = gfx::Vector2d(1280, 720); - params.source_color_space = DefaultRGBColorSpace(); - params.output_color_space = DefaultYUVColorSpace(); - params.enable_precise_color_management = - converter()->SupportsPreciseColorManagement(); - params.quality = GLScaler::Parameters::Quality::GOOD; - params.is_flipped_source = true; - params.flip_output = true; - params.swizzle[0] = GL_BGRA_EXT; // Swizzle for readback. - ASSERT_TRUE(converter()->Configure(params)); - - constexpr gfx::Size kSourceSize = gfx::Size(2160, 1440); - const GLuint src_texture = UploadTexture( - CreateVerticallyFlippedBitmap(CreateSMPTETestImage(kSourceSize))); - constexpr gfx::Rect kOutputRect = gfx::Rect(0, 0, 1280, 720); - ASSERT_EQ(kOutputRect, GLI420Converter::ToAlignedRect(kOutputRect)); - SkBitmap expected = CreateSMPTETestImage(kOutputRect.size()); - ConvertRGBABitmapToYUV(&expected); - - // While the output size is 1280x720, the packing of 4 pixels into one RGBA - // quad means that the texture width must be divided by 4 (for the Y - // plane). Then, the other two planes are half the size of the Y plane in both - // dimensions. - const gfx::Size y_plane_size(kOutputRect.width() / 4, kOutputRect.height()); - const gfx::Size chroma_plane_size(y_plane_size.width() / 2, - y_plane_size.height() / 2); - const GLuint yuv_textures[3] = {CreateTexture(y_plane_size), - CreateTexture(chroma_plane_size), - CreateTexture(chroma_plane_size)}; - - ASSERT_TRUE(converter()->Convert(src_texture, kSourceSize, gfx::Vector2d(), - kOutputRect, yuv_textures)); - - // Download the textures, and unpack them into an interleaved YUV bitmap, for - // comparison against the |expected| rendition. - SkBitmap actual = AllocateRGBABitmap(kOutputRect.size()); - actual.eraseColor(SkColorSetARGB(0xff, 0x00, 0x80, 0x80)); - SkBitmap y_plane = DownloadTexture(yuv_textures[0], y_plane_size); - SwizzleBitmap(&y_plane); - UnpackPlanarBitmap(y_plane, 0, &actual); - SkBitmap u_plane = DownloadTexture(yuv_textures[1], chroma_plane_size); - SwizzleBitmap(&u_plane); - UnpackPlanarBitmap(u_plane, 1, &actual); - SkBitmap v_plane = DownloadTexture(yuv_textures[2], chroma_plane_size); - SwizzleBitmap(&v_plane); - UnpackPlanarBitmap(v_plane, 2, &actual); - - // Provide generous error limits to account for the chroma subsampling in the - // |actual| result when compared to the perfect |expected| rendition. - constexpr float kAvgAbsoluteErrorLimit = 16.f; - constexpr int kMaxAbsoluteErrorLimit = 0x80; - EXPECT_TRUE(cc::FuzzyPixelComparator(false, 100.f, 0.f, - kAvgAbsoluteErrorLimit, - kMaxAbsoluteErrorLimit, 0) - .Compare(expected, actual)) - << "\nActual: " << cc::GetPNGDataUrl(actual) - << "\nExpected: " << cc::GetPNGDataUrl(expected); -} - -// Run the tests twice, once disallowing use of the MRT path, and once allowing -// its use (auto-detecting whether the current platform supports it). -INSTANTIATE_TEST_SUITE_P(All, GLI420ConverterPixelTest, testing::Bool()); - -} // namespace viz diff --git a/chromium/components/viz/common/gl_i420_converter_unittest.cc b/chromium/components/viz/common/gl_i420_converter_unittest.cc deleted file mode 100644 index 9fd42188e99..00000000000 --- a/chromium/components/viz/common/gl_i420_converter_unittest.cc +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2018 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/gl_i420_converter.h" - -#include <string> - -#include "base/strings/stringprintf.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/skia/include/core/SkRect.h" -#include "ui/gfx/geometry/rect.h" - -namespace viz { -namespace { - -// Syntactic convenience: It's clearer to express the tests in terms of -// left+top+right+bottom coordinates, rather than gfx::Rect's x+y+width+height -// scheme. -SkIRect ToAlignedRect(const SkIRect& rect) { - const gfx::Rect& result = GLI420Converter::ToAlignedRect( - gfx::Rect(rect.fLeft, rect.fTop, rect.fRight - rect.fLeft, - rect.fBottom - rect.fTop)); - return SkIRect{result.x(), result.y(), result.right(), result.bottom()}; -} - -// Logging convenience. -std::string ToString(const SkIRect& rect) { - return base::StringPrintf("%d,%d~%d%d", rect.fLeft, rect.fTop, rect.fRight, - rect.fBottom); -} - -TEST(GLI420ConverterTest, AlignsOutputRects) { - struct { - SkIRect expected; - SkIRect input; - } kTestCases[] = { - {SkIRect{0, 0, 0, 0}, SkIRect{0, 0, 0, 0}}, - - {SkIRect{-16, 0, 16, 4}, SkIRect{-9, 0, 16, 4}}, - {SkIRect{-8, 0, 16, 4}, SkIRect{-8, 0, 16, 4}}, - {SkIRect{-8, 0, 16, 4}, SkIRect{-7, 0, 16, 4}}, - {SkIRect{-8, 0, 16, 4}, SkIRect{-1, 0, 16, 4}}, - {SkIRect{0, 0, 16, 4}, SkIRect{0, 0, 16, 4}}, - {SkIRect{0, 0, 16, 4}, SkIRect{1, 0, 16, 4}}, - {SkIRect{0, 0, 16, 4}, SkIRect{7, 0, 16, 4}}, - {SkIRect{8, 0, 16, 4}, SkIRect{8, 0, 16, 4}}, - {SkIRect{8, 0, 16, 4}, SkIRect{9, 0, 16, 4}}, - - {SkIRect{0, -4, 16, 4}, SkIRect{0, -3, 16, 4}}, - {SkIRect{0, -2, 16, 4}, SkIRect{0, -2, 16, 4}}, - {SkIRect{0, -2, 16, 4}, SkIRect{0, -1, 16, 4}}, - {SkIRect{0, 0, 16, 4}, SkIRect{0, 0, 16, 4}}, - {SkIRect{0, 0, 16, 4}, SkIRect{0, 1, 16, 4}}, - {SkIRect{0, 2, 16, 4}, SkIRect{0, 2, 16, 4}}, - {SkIRect{0, 2, 16, 4}, SkIRect{0, 3, 16, 4}}, - - {SkIRect{0, 0, 8, 2}, SkIRect{0, 0, 1, 2}}, - {SkIRect{0, 0, 8, 2}, SkIRect{0, 0, 7, 2}}, - {SkIRect{0, 0, 8, 2}, SkIRect{0, 0, 8, 2}}, - {SkIRect{0, 0, 16, 2}, SkIRect{0, 0, 9, 2}}, - - {SkIRect{0, 0, 8, 2}, SkIRect{0, 0, 8, 1}}, - {SkIRect{0, 0, 8, 2}, SkIRect{0, 0, 8, 2}}, - {SkIRect{0, 0, 8, 4}, SkIRect{0, 0, 8, 3}}, - {SkIRect{0, 0, 8, 4}, SkIRect{0, 0, 8, 4}}, - }; - - for (const auto& test_case : kTestCases) { - EXPECT_EQ(test_case.expected, ToAlignedRect(test_case.input)) - << "ToAlignedRect(" << ToString(test_case.input) << ") should be " - << ToString(test_case.expected); - } -} - -} // namespace -} // namespace viz diff --git a/chromium/components/viz/common/gl_nv12_converter.cc b/chromium/components/viz/common/gl_nv12_converter.cc deleted file mode 100644 index 2c737130a6b..00000000000 --- a/chromium/components/viz/common/gl_nv12_converter.cc +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2021 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/gl_nv12_converter.h" - -#include "base/memory/ptr_util.h" -#include "components/viz/common/gl_i420_converter.h" -#include "components/viz/common/gpu/context_provider.h" - -namespace viz { - -// static -std::unique_ptr<GLNV12Converter> GLNV12Converter::CreateConverterForTest( - ContextProvider* context_provider, - bool allow_mrt_path) { - return base::WrapUnique( - new GLNV12Converter(context_provider, allow_mrt_path)); -} - -GLNV12Converter::GLNV12Converter(ContextProvider* context_provider) - : GLNV12Converter(context_provider, true) {} - -GLNV12Converter::GLNV12Converter(ContextProvider* context_provider, - bool allow_mrt_path) - : context_provider_(context_provider), - step1_(context_provider_), - step2_(context_provider_) { - DCHECK(context_provider_); - context_provider_->AddObserver(this); - if (!allow_mrt_path || step1_.GetMaxDrawBuffersSupported() < 2) { - step3_ = std::make_unique<GLScaler>(context_provider_); - } -} - -GLNV12Converter::~GLNV12Converter() { - OnContextLost(); // Free context-related resources. -} - -// static -gfx::Rect GLNV12Converter::ToAlignedRect(const gfx::Rect& rect) { - // Origin coordinates: FLOOR(...) - const int aligned_x = - ((rect.x() < 0) ? ((rect.x() - 3) / 4) : (rect.x() / 4)) * 4; - const int aligned_y = - ((rect.y() < 0) ? ((rect.y() - 1) / 2) : (rect.y() / 2)) * 2; - // Span coordinates: CEIL(...) - const int aligned_right = - ((rect.right() < 0) ? (rect.right() / 4) : ((rect.right() + 3) / 4)) * 4; - const int aligned_bottom = - ((rect.bottom() < 0) ? (rect.bottom() / 2) : ((rect.bottom() + 1) / 2)) * - 2; - return gfx::Rect(aligned_x, aligned_y, aligned_right - aligned_x, - aligned_bottom - aligned_y); -} - -// static -bool GLNV12Converter::ParametersAreEquivalent(const Parameters& a, - const Parameters& b) { - // Implemented in terms of GLI420Converter: - return GLI420Converter::ParametersAreEquivalent(a, b); -} - -void GLNV12Converter::EnsureIntermediateTextureDefined( - const gfx::Size& required) { - if (intermediate_texture_size_ == required) { - return; - } - auto* const gl = context_provider_->ContextGL(); - if (intermediate_texture_ == 0) { - gl->GenTextures(1, &intermediate_texture_); - } - gl->BindTexture(GL_TEXTURE_2D, intermediate_texture_); - gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, required.width(), required.height(), - 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - intermediate_texture_size_ = required; -} - -bool GLNV12Converter::Configure(const Parameters& params) { - Parameters step1_params = params; - if (!step1_params.output_color_space.IsValid()) { - step1_params.output_color_space = gfx::ColorSpace::CreateREC709(); - } - - // Configure the "step 1" scaler. - if (is_using_mrt_path()) { - step1_params.export_format = Parameters::ExportFormat::NV61; - DCHECK_EQ(step1_params.swizzle[0], params.swizzle[0]); - step1_params.swizzle[1] = GL_RGBA; // Don't swizzle 2nd rendering target. - } else { - step1_params.export_format = Parameters::ExportFormat::INTERLEAVED_QUADS; - step1_params.swizzle[0] = GL_RGBA; // Will swizzle in steps 2-3. - } - if (!step1_.Configure(step1_params)) { - return false; - } - - // Configure the "step 2" scaler (and step 3 for the non-MRT path) that - // further transform the output from the "step 1" scaler to produce the final - // outputs. - Parameters step2_params; - step2_params.scale_to = gfx::Vector2d(1, 1); - step2_params.source_color_space = step1_params.output_color_space; - step2_params.output_color_space = step1_params.output_color_space; - // Use FAST quality, a single bilinear pass, because there will either be no - // scaling or exactly 50% scaling. - step2_params.quality = Parameters::Quality::FAST; - step2_params.swizzle[0] = params.swizzle[0]; - if (is_using_mrt_path()) { - // NV61 provides half-width and full-height U/V. NV12 UV planes are - // half-width and half-height. So, scale just the Y by 50%. - step2_params.scale_from = gfx::Vector2d(1, 2); - step2_params.export_format = Parameters::ExportFormat::INTERLEAVED_QUADS; - step2_params.swizzle[1] = step2_params.swizzle[0]; - if (!step2_.Configure(step2_params)) { - return false; - } - } else { - // Extract a full-size Y plane from the interleaved YUVA from step 1. - step2_params.scale_from = gfx::Vector2d(1, 1); - step2_params.export_format = Parameters::ExportFormat::CHANNEL_0; - if (!step2_.Configure(step2_params)) { - return false; - } - // Extract a half-size UV plane from the interleaved YUVA from step 1. - // UV_CHANNELS provides half-width and full-height UV plane. NV12 UV planes - // are half-wifth and half-height. So, scale just the Y by 50%. - step2_params.scale_from = gfx::Vector2d(1, 2); - step2_params.export_format = Parameters::ExportFormat::UV_CHANNELS; - if (!step3_->Configure(step2_params)) { - return false; - } - } - - params_ = params; - return true; -} - -bool GLNV12Converter::Convert(GLuint src_texture, - const gfx::Size& src_texture_size, - const gfx::Vector2d& src_offset, - const gfx::Rect& aligned_output_rect, - const GLuint yuv_textures[2]) { - DCHECK_EQ(aligned_output_rect.x() % 4, 0); - DCHECK_EQ(aligned_output_rect.width() % 4, 0); - DCHECK_EQ(aligned_output_rect.y() % 2, 0); - DCHECK_EQ(aligned_output_rect.height() % 2, 0); - - if (!context_provider_) { - return false; - } - - if (is_using_mrt_path()) { - const gfx::Rect luma_output_rect( - aligned_output_rect.x() / 4, aligned_output_rect.y(), - aligned_output_rect.width() / 4, aligned_output_rect.height()); - EnsureIntermediateTextureDefined(luma_output_rect.size()); - const gfx::Rect chroma_output_rect( - gfx::Size(luma_output_rect.width(), luma_output_rect.height() / 2)); - return (step1_.ScaleToMultipleOutputs( - src_texture, src_texture_size, src_offset, yuv_textures[0], - intermediate_texture_, luma_output_rect) && - step2_.Scale(intermediate_texture_, intermediate_texture_size_, - gfx::Vector2d(), yuv_textures[1], chroma_output_rect)); - } - - // Non-MRT path: - EnsureIntermediateTextureDefined(aligned_output_rect.size()); - const gfx::Rect luma_output_rect(0, 0, aligned_output_rect.width() / 4, - aligned_output_rect.height()); - const gfx::Rect chroma_output_rect(0, 0, luma_output_rect.width(), - luma_output_rect.height() / 2); - return (step1_.Scale(src_texture, src_texture_size, src_offset, - intermediate_texture_, aligned_output_rect) && - step2_.Scale(intermediate_texture_, intermediate_texture_size_, - gfx::Vector2d(), yuv_textures[0], luma_output_rect) && - step3_->Scale(intermediate_texture_, intermediate_texture_size_, - gfx::Vector2d(), yuv_textures[1], chroma_output_rect)); -} - -void GLNV12Converter::OnContextLost() { - if (intermediate_texture_ != 0) { - if (auto* gl = context_provider_->ContextGL()) { - gl->DeleteTextures(1, &intermediate_texture_); - } - intermediate_texture_ = 0; - intermediate_texture_size_ = gfx::Size(); - } - if (context_provider_) { - context_provider_->RemoveObserver(this); - context_provider_ = nullptr; - } -} - -} // namespace viz diff --git a/chromium/components/viz/common/gl_nv12_converter.h b/chromium/components/viz/common/gl_nv12_converter.h deleted file mode 100644 index 15c6944270f..00000000000 --- a/chromium/components/viz/common/gl_nv12_converter.h +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2021 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_GL_NV12_CONVERTER_H_ -#define COMPONENTS_VIZ_COMMON_GL_NV12_CONVERTER_H_ - -#include <memory> - -#include "base/memory/raw_ptr.h" -#include "base/memory/scoped_refptr.h" -#include "components/viz/common/gl_scaler.h" -#include "components/viz/common/gpu/context_lost_observer.h" -#include "components/viz/common/viz_common_export.h" -#include "ui/gfx/color_space.h" -#include "ui/gfx/geometry/size.h" - -namespace gfx { -class Rect; -class Vector2d; -} // namespace gfx - -namespace viz { - -class ContextProvider; - -// A convenience wrapper around GLScaler that also reformats the scaler's output -// from interleaved RGBA to NV12 planes. The NV12 format consists of two -// planes of image data: the Y (luma) plane at full size, plus interleaved UV -// (chroma) plane at half-width and half-height. There are two possible modes of -// operation (auto-detected at runtime): -// -// The faster, multiple rendering target (MRT) path: If the platform supports -// MRTs (most of the GPUs in use today), scaling and conversion is a two step -// process: -// -// Step 1: Produce NV61 format output, a luma plane and a UV-interleaved -// image. The luma plane is the same as the desired NV12 luma plane. Note, -// that the UV image is of half-width but not yet half-height. -// -// (interleaved quads) -// RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA -// RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA -// RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA -// RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA -// | -// | (luma plane) (chroma, interleaved) -// | YYYY YYYY UVUV UVUV -// +---> { YYYY YYYY + UVUV UVUV } -// YYYY YYYY UVUV UVUV -// YYYY YYYY UVUV UVUV -// -// Step 2: Downscales the chroma plane. -// -// (chroma, interleaved) (chroma, interleaved) -// UVUV UVUV -// UVUV UVUV --> UVUV UVUV -// UVUV UVUV UVUV UVUV -// UVUV UVUV -// -// The non-MRT path: For platforms that can only render to a single target at a -// time. This first scales the source to its final size and color-converts, -// transforming an RGBA input into a YUVx output. Then, it scans the YUVA image -// two times to generate each of the Y+UV planes. -// -// Texture packing: OpenGLES2 treats all of the input and output textures as -// RGBA format. See comments for the Convert() method, which explains how the -// planar image data is packed into GL_RGBA textures, how the output textures -// should be sized, and why there are alignment requirements when specifying the -// output rect. -class VIZ_COMMON_EXPORT GLNV12Converter final : public ContextLostObserver { - public: - // GLNV12Converter uses the exact same parameters as GLScaler. - using Parameters = GLScaler::Parameters; - - explicit GLNV12Converter(ContextProvider* context_provider); - ~GLNV12Converter() final; - - // Returns true if the GL context provides the necessary support for enabling - // precise color management (see Parameters::enable_precise_color_management). - bool SupportsPreciseColorManagement() const { - return step1_.SupportsPreciseColorManagement(); - } - - // [Re]Configure the converter with the given |new_params|. Returns true on - // success, or false on failure. If |new_params| does not specify an - // |output_color_space|, it will be default to REC709. - [[nodiscard]] bool Configure(const Parameters& new_params); - - // Returns the currently-configured and resolved Parameters. Results are - // undefined if Configure() has never been called successfully. - const Parameters& params() const { return params_; } - - // Scales a portion of |src_texture|, then format-converts it to two NV12 - // planes, placing the results into |yuv_textures| at offset (0, 0). Returns - // true to indicate success, or false if this GLNV12Converter is not valid. - // - // |src_texture_size| is the full, allocated size of the |src_texture|. This - // is required for computing texture coordinate transforms (and only because - // the OpenGL ES 2.0 API lacks the ability to query this info). - // - // |src_offset| is the offset in the source texture corresponding to point - // (0,0) in the source/output coordinate spaces. This prevents the need for - // extra texture copies just to re-position the source coordinate system. - // - // |aligned_output_rect| selects the region to draw (in the scaled, not the - // source, coordinate space). This is used to save work in cases where only a - // portion needs to be re-scaled. Because of the way the planar image data is - // packed in the output textures, the output rect's coordinates must be - // aligned (see ToAlignedRect() below). - // - // The |yuv_textures| are packed with planar data. Depending on the plane, - // the packing is as follows: - // - for Y plane, each RGBA quad contains four pixel values: R is pixel 0, - // G is pixel 1, and so on. - // - for UV plane, each RGBA quad contains 2 pixel values: RG are UV values - // for pixel 0, BA are UV values for pixel 1, and so on. - // This makes it trivial to read-back the textures from a pixel buffer as a - // sequence of unsigned bytes. Thus, the output texture for the Y plane should - // be defined as GL_RGBA and be at least 1/4 the width of that specified in - // |aligned_output_rect|. Similarly, the output texture for the UV plane - // should be defined as GL_RGBA and have at least 1/4 the width [1] - // and 1/2 the height [2] of |aligned_output_rect|. - // - // [1] 1/4 width = 1/4 // we pack 4 values per pixel - // * 1/2 // chroma planes are subsampled - // * 2 // we pack 2 chroma planes - // * width - // [2] 1/2 height = 1/2 // chroma planes are subsampled - // * height - // - // WARNING: The output will always be placed at (0, 0) in the output textures, - // and not at |aligned_output_rect.origin()|. - // - // Note that the |src_texture| will have the min/mag filter set to GL_LINEAR - // and wrap_s/t set to CLAMP_TO_EDGE in this call. - bool Convert(GLuint src_texture, - const gfx::Size& src_texture_size, - const gfx::Vector2d& src_offset, - const gfx::Rect& aligned_output_rect, - const GLuint yuv_textures[2]); - - // Returns the smallest Rect that encloses |rect| and lays on aligned - // boundaries, as required by the |aligned_output_rect| argument passed to - // Convert(). The horizontal coordinates will always be a multiple of 4, and - // the vertical coordinates a multiple of 2. - static gfx::Rect ToAlignedRect(const gfx::Rect& rect); - - // Returns true if configuring a GLNV12Converter with either |a| or |b| will - // produce identical behaviors and results. - static bool ParametersAreEquivalent(const Parameters& a, const Parameters& b); - - static std::unique_ptr<GLNV12Converter> CreateConverterForTest( - ContextProvider* context_provider, - bool allow_mrt_path); - - private: - GLNV12Converter(ContextProvider* context_provider, bool allow_mrt_path); - - bool is_using_mrt_path() const { return !step3_; } - - // Creates or re-defines the intermediate texture, to ensure a texture of the - // given |required| size is defined. - void EnsureIntermediateTextureDefined(const gfx::Size& required); - - // ContextLostObserver implementation. - void OnContextLost() final; - - // The provider of the GL context. This is non-null while the GL context is - // valid and GLNV12Converter is observing for context loss. - raw_ptr<ContextProvider> context_provider_; - - // Scales the source content and produces either: - // * MRT path: NV61-format output in two textures. - // * Non-MRT path: YUVA interleaved output in one texture. - GLScaler step1_; - - // Holds the results from executing the first-stage |scaler_|, and is read by - // the other scalers: - // * MRT path: This holds the UV-interleaved data (2nd rendering target). - // * Non-MRT path: The scaled YUVA interleaved data. - GLuint intermediate_texture_ = 0; - gfx::Size intermediate_texture_size_; - - // Step 2 operation using the |intermediate_texture_| as input: - // * MRT path: Scales the height by half. - // * Non-MRT path: Extracts the luma plane. - GLScaler step2_; - - // Steps 3 is used by the non-MRT path only, to extract the interleaved chroma - // planes from |intermediate_texture_|. - std::unique_ptr<GLScaler> step3_; - - // The Parameters that were provided to the last successful Configure() call. - Parameters params_; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_COMMON_GL_NV12_CONVERTER_H_ diff --git a/chromium/components/viz/common/gl_nv12_converter_pixeltest.cc b/chromium/components/viz/common/gl_nv12_converter_pixeltest.cc deleted file mode 100644 index ad3ec480837..00000000000 --- a/chromium/components/viz/common/gl_nv12_converter_pixeltest.cc +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2021 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/gl_nv12_converter.h" - -#include <GLES2/gl2ext.h> - -#include "cc/test/pixel_test.h" -#include "cc/test/pixel_test_utils.h" -#include "components/viz/test/gl_scaler_test_util.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkColor.h" - -namespace viz { - -class GLNV12ConverterPixelTest - : public cc::PixelTest, - public GLScalerTestUtil, - public testing::WithParamInterface< - std::tuple<bool, bool, gfx::Vector2d, gfx::Vector2d>> { - public: - bool allow_mrt_path() const { return std::get<0>(GetParam()); } - bool is_rgba() const { return std::get<1>(GetParam()); } - gfx::Vector2d scale_from() const { return std::get<2>(GetParam()); } - gfx::Vector2d scale_to() const { return std::get<3>(GetParam()); } - - GLNV12Converter* converter() const { return converter_.get(); } - - GLuint CreateTexture(const gfx::Size& size) { - return texture_helper_->CreateTexture(size); - } - - GLuint UploadTexture(const SkBitmap& bitmap) { - return texture_helper_->UploadTexture(bitmap); - } - - SkBitmap DownloadTexture(GLuint texture, const gfx::Size& size) { - return texture_helper_->DownloadTexture(texture, size); - } - - protected: - void SetUp() final { - cc::PixelTest::SetUpGLWithoutRenderer(gfx::SurfaceOrigin::kBottomLeft); - converter_ = GLNV12Converter::CreateConverterForTest(context_provider(), - allow_mrt_path()); - texture_helper_ = std::make_unique<GLScalerTestTextureHelper>( - context_provider()->ContextGL()); - } - - void TearDown() final { - texture_helper_.reset(); - converter_.reset(); - cc::PixelTest::TearDown(); - } - - private: - std::unique_ptr<GLNV12Converter> converter_; - std::unique_ptr<GLScalerTestTextureHelper> texture_helper_; -}; - -// Note: This test is pretty much the same as -// GLScalerPixelTest.Example_ScaleAndExportForScreenVideoCapture. The goal of -// this pixel test is to just confirm that everything internal to -// GLNV12Converter has been plumbed-through correctly. -TEST_P(GLNV12ConverterPixelTest, ScaleAndConvert) { - GLNV12Converter::Parameters params; - params.scale_from = scale_from(); - params.scale_to = scale_to(); - params.source_color_space = DefaultRGBColorSpace(); - params.output_color_space = DefaultYUVColorSpace(); - params.enable_precise_color_management = - converter()->SupportsPreciseColorManagement(); - params.quality = GLScaler::Parameters::Quality::GOOD; - params.is_flipped_source = true; - params.flip_output = true; - params.swizzle[0] = - is_rgba() ? GL_RGBA : GL_BGRA_EXT; // Swizzle for readback. - ASSERT_TRUE(converter()->Configure(params)); - - const gfx::Size kSourceSize = gfx::Size(scale_from().x(), scale_from().y()); - const GLuint src_texture = UploadTexture( - CreateVerticallyFlippedBitmap(CreateSMPTETestImage(kSourceSize))); - const gfx::Rect kOutputRect = gfx::Rect(0, 0, scale_to().x(), scale_to().y()); - ASSERT_EQ(kOutputRect, GLNV12Converter::ToAlignedRect(kOutputRect)); - SkBitmap expected = CreateSMPTETestImage(kOutputRect.size()); - ConvertRGBABitmapToYUV(&expected); - - // While the output size is `kOutputRect.Size()`, the packing of 4 pixels into - // one RGBA quad means that the texture width must be divided by 4 (for the Y - // plane). Then, the chroma plane is half the size of the Y plane in both - // dimensions, but the width is actually double that since we pack 2 values, - // so `y_plane_size.width() * 1/2 * 2` term is simplified to just - // `y_plane_size.width()`. - const gfx::Size y_plane_size(kOutputRect.width() / 4, kOutputRect.height()); - const gfx::Size chroma_plane_size(y_plane_size.width(), - y_plane_size.height() / 2); - - const GLuint yuv_textures[2] = {CreateTexture(y_plane_size), - CreateTexture(chroma_plane_size)}; - - ASSERT_TRUE(converter()->Convert(src_texture, kSourceSize, gfx::Vector2d(), - kOutputRect, yuv_textures)); - - // Download the textures, and unpack them into an interleaved YUV bitmap, for - // comparison against the |expected| rendition. - SkBitmap actual = AllocateRGBABitmap(kOutputRect.size()); - actual.eraseColor(SkColorSetARGB(0xff, 0x00, 0x00, 0x00)); - - SkBitmap y_plane = DownloadTexture(yuv_textures[0], y_plane_size); - SkBitmap uv_plane = DownloadTexture(yuv_textures[1], chroma_plane_size); - - if (!is_rgba()) { - // We've asked the converter to produce output in BGRA, & downloaded it to - // RGBA SkBitmap, so swizzle it. - SwizzleBitmap(&y_plane); - SwizzleBitmap(&uv_plane); - } - - UnpackPlanarBitmap(y_plane, 0, &actual); - UnpackUVBitmap(uv_plane, &actual); - - // Provide generous error limits to account for the chroma subsampling in the - // |actual| result when compared to the perfect |expected| rendition. - constexpr float kAvgAbsoluteErrorLimit = 16.f; - constexpr int kMaxAbsoluteErrorLimit = 0x80; - EXPECT_TRUE(cc::FuzzyPixelComparator(false, 100.f, 0.f, - kAvgAbsoluteErrorLimit, - kMaxAbsoluteErrorLimit, 0) - .Compare(expected, actual)) - << "\nActual: " << cc::GetPNGDataUrl(actual) - << "\nExpected: " << cc::GetPNGDataUrl(expected); -} - -// Run the tests, first parameter controls whether the MRT path is allowed, -// the second controls whether the converter will use RGBA format vs BGRA (true -// for RGBA, i.e. no swizzling done by the converter, false for BGRA, i.e. the -// converter will swizzle the results when writing to texture). -// These parameters have been chosen based on: 1) overriding defaults, to -// confirm Parameters plumbing; and 2) typical operation on most platforms -// (e.g., flipped source textures, the need to swizzle outputs, etc.). In -// addition, the `scale_to()` is made to return width that is divisible by 4, -// but not by 8, to test alignment requirements. -INSTANTIATE_TEST_SUITE_P( - All, - GLNV12ConverterPixelTest, - testing::Combine(testing::Bool(), - testing::Bool(), - testing::Values(gfx::Vector2d(2160, 1440)), - testing::Values(gfx::Vector2d(1280, 720), - gfx::Vector2d(900, 600)))); - -} // namespace viz diff --git a/chromium/components/viz/common/gl_scaler.cc b/chromium/components/viz/common/gl_scaler.cc deleted file mode 100644 index 2393ebc8d09..00000000000 --- a/chromium/components/viz/common/gl_scaler.cc +++ /dev/null @@ -1,1661 +0,0 @@ -// Copyright 2018 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/gl_scaler.h" - -#include <algorithm> -#include <array> -#include <sstream> -#include <string> - -#include "base/logging.h" -#include "components/viz/common/gpu/context_provider.h" -#include "gpu/GLES2/gl2chromium.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "third_party/skia/include/third_party/skcms/skcms.h" -#include "ui/gfx/color_transform.h" -#include "ui/gfx/geometry/rect_conversions.h" - -namespace viz { - -namespace { - -// The code in GLScaler that computes the ScalerStages is greatly simplified by -// being able to access the X and Y components by index (instead of -// Vector2d::x() or Vector2d::y()). Thus, define a helper class to represent the -// relative size as a 2-element std::array and convert to/from Vector2d. -struct RelativeSize : public std::array<int, 2> { - using std::array<int, 2>::operator[]; - - RelativeSize(int width, int height) : std::array<int, 2>{{width, height}} {} - explicit RelativeSize(const gfx::Vector2d& v) - : std::array<int, 2>{{v.x(), v.y()}} {} - - gfx::Vector2d AsVector2d() const { - return gfx::Vector2d((*this)[0], (*this)[1]); - } -}; - -std::ostream& operator<<(std::ostream& out, const RelativeSize& size) { - return (out << size[0] << 'x' << size[1]); -} - -} // namespace - -GLScaler::GLScaler(ContextProvider* context_provider) - : context_provider_(context_provider) { - if (context_provider_) { - DCHECK(context_provider_->ContextGL()); - context_provider_->AddObserver(this); - } -} - -GLScaler::~GLScaler() { - OnContextLost(); // Ensures destruction in dependency order. -} - -bool GLScaler::SupportsPreciseColorManagement() const { - if (!context_provider_) { - return false; - } - if (!supports_half_floats_.has_value()) { - supports_half_floats_ = AreAllGLExtensionsPresent( - context_provider_->ContextGL(), - {"GL_EXT_color_buffer_half_float", "GL_OES_texture_half_float_linear"}); - } - return supports_half_floats_.value(); -} - -int GLScaler::GetMaxDrawBuffersSupported() const { - if (!context_provider_) { - return 0; - } - - if (max_draw_buffers_ < 0) { - // Query the GL context for the multiple draw buffers extension and, if - // present, the actual platform-supported maximum. - GLES2Interface* const gl = context_provider_->ContextGL(); - DCHECK(gl); - if (AreAllGLExtensionsPresent(gl, {"GL_EXT_draw_buffers"})) { - gl->GetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &max_draw_buffers_); - } else { - // The extension is not present & OpenGL ES 2.0 does not support - // glDrawBuffers function without it. - max_draw_buffers_ = 0; - } - } - - return max_draw_buffers_; -} - -bool GLScaler::Configure(const Parameters& new_params) { - chain_.reset(); - shader_programs_.clear(); - - if (!context_provider_) { - return false; - } - GLES2Interface* const gl = context_provider_->ContextGL(); - DCHECK(gl); - - params_ = new_params; - - // Ensure the client has provided valid scaling vectors. - if (params_.scale_from.x() == 0 || params_.scale_from.y() == 0 || - params_.scale_to.x() == 0 || params_.scale_to.y() == 0) { - // The caller computed invalid scale_from and/or scale_to values. - DVLOG(1) << __func__ << ": Invalid scaling vectors: scale_from=" - << params_.scale_from.ToString() - << ", scale_to=" << params_.scale_to.ToString(); - return false; - } - - // Resolve the color spaces according to the rules described in the header - // file. - if (!params_.source_color_space.IsValid()) { - params_.source_color_space = gfx::ColorSpace::CreateSRGB(); - } - if (!params_.output_color_space.IsValid()) { - params_.output_color_space = params_.source_color_space; - } - - // Check that 16-bit half floats are supported if precise color management is - // being requested. - if (params_.enable_precise_color_management) { - if (!SupportsPreciseColorManagement()) { - DVLOG(1) << __func__ - << ": GL context does not support the half-floats " - "required for precise color management."; - return false; - } - } - - // Check that MRT support is available if certain export formats were - // specified in the Parameters. - if (params_.export_format == Parameters::ExportFormat::NV61 || - params_.export_format == - Parameters::ExportFormat::DEINTERLEAVE_PAIRWISE) { - if (GetMaxDrawBuffersSupported() < 2) { - DVLOG(1) << __func__ << ": GL context does not support 2+ draw buffers."; - return false; - } - } - - // Color space transformation is meaningless when using the deinterleaver - // because it only deals with two color channels. This also means precise - // color management must be disabled. - if (params_.export_format == - Parameters::ExportFormat::DEINTERLEAVE_PAIRWISE && - (params_.source_color_space != params_.output_color_space || - params_.enable_precise_color_management)) { - NOTIMPLEMENTED(); - return false; - } - - // Check that one of the two implemented output swizzles has been specified. - for (GLenum s : params_.swizzle) { - if (s != GL_RGBA && s != GL_BGRA_EXT) { - NOTIMPLEMENTED(); - return false; - } - } - - // Create the chain of ScalerStages. If the quality setting is FAST or there - // is no scaling to be done, just create a single stage. - std::unique_ptr<ScalerStage> chain; - if (params_.quality == Parameters::Quality::FAST || - params_.scale_from == params_.scale_to) { - chain = std::make_unique<ScalerStage>(gl, Shader::BILINEAR, HORIZONTAL, - params_.scale_from, params_.scale_to); - } else if (params_.quality == Parameters::Quality::GOOD) { - chain = CreateAGoodScalingChain(gl, params_.scale_from, params_.scale_to); - } else if (params_.quality == Parameters::Quality::BEST) { - chain = CreateTheBestScalingChain(gl, params_.scale_from, params_.scale_to); - } else { - NOTREACHED(); - } - chain = MaybeAppendExportStage(gl, std::move(chain), params_.export_format); - - // Determine the color space and the data type of the pixels in the - // intermediate textures, depending on whether precise color management is - // enabled. Note that nothing special need be done here if no scaling will be - // performed. - GLenum intermediate_texture_type; - if (params_.enable_precise_color_management && - params_.scale_from != params_.scale_to) { - // Ensure the scaling color space is using a linear transfer function. - constexpr auto kLinearFunction = std::make_tuple(1, 0, 1, 0, 0, 0, 1); - skcms_TransferFunction fn; - if (params_.source_color_space.GetTransferFunction(&fn) && - std::make_tuple(fn.a, fn.b, fn.c, fn.d, fn.e, fn.f, fn.g) == - kLinearFunction) { - scaling_color_space_ = params_.source_color_space; - } else { - // Use the source color space, but with a linear transfer function. - skcms_Matrix3x3 to_XYZD50; - params_.source_color_space.GetPrimaryMatrix(&to_XYZD50); - std::tie(fn.a, fn.b, fn.c, fn.d, fn.e, fn.f, fn.g) = kLinearFunction; - scaling_color_space_ = gfx::ColorSpace::CreateCustom(to_XYZD50, fn); - } - intermediate_texture_type = GL_HALF_FLOAT_OES; - } else { - scaling_color_space_ = params_.source_color_space; - intermediate_texture_type = GL_UNSIGNED_BYTE; - } - - // Set the shader program on the final stage. Include color space - // transformation and swizzling, if necessary. - std::unique_ptr<gfx::ColorTransform> transform; - if (scaling_color_space_ != params_.output_color_space) { - transform = gfx::ColorTransform::NewColorTransform( - scaling_color_space_, params_.output_color_space); - } - ScalerStage* const final_stage = chain.get(); - final_stage->set_shader_program( - GetShaderProgram(final_stage->shader(), intermediate_texture_type, - transform.get(), params_.swizzle)); - - // Set the shader program on all prior stages. These stages are all operating - // in the same color space, |scaling_color_space_|. - static const GLenum kNoSwizzle[2] = {GL_RGBA, GL_RGBA}; - ScalerStage* input_stage = final_stage; - while (input_stage->input_stage()) { - input_stage = input_stage->input_stage(); - input_stage->set_shader_program(GetShaderProgram( - input_stage->shader(), intermediate_texture_type, nullptr, kNoSwizzle)); - } - // From this point, |input_stage| points to the first ScalerStage (i.e., the - // one that will be reading from the source). - - // If necessary, prepend an extra "import stage" that color-converts the input - // before any scaling occurs. It's important not to merge color space - // conversion of the source with any other steps because the texture sampler - // must not linearly interpolate until after the colors have been mapped to a - // linear color space. - if (params_.source_color_space != scaling_color_space_) { - input_stage->set_input_stage(std::make_unique<ScalerStage>( - gl, Shader::BILINEAR, HORIZONTAL, input_stage->scale_from(), - input_stage->scale_from())); - input_stage = input_stage->input_stage(); - transform = gfx::ColorTransform::NewColorTransform( - params_.source_color_space, scaling_color_space_); - input_stage->set_shader_program( - GetShaderProgram(input_stage->shader(), intermediate_texture_type, - transform.get(), kNoSwizzle)); - } - - // If the source content is Y-flipped, the input scaler stage will perform - // math to account for this. It also will flip the content during scaling so - // that all following stages may assume the content is not flipped. Then, the - // final stage must ensure the final output is correctly flipped-back (or not) - // based on what the first stage did PLUS what is being requested by the - // client code. - if (params_.is_flipped_source) { - input_stage->set_is_flipped_source(true); - input_stage->set_flip_output(true); - } - if (input_stage->flip_output() != params_.flip_output) { - final_stage->set_flip_output(!final_stage->flip_output()); - } - - chain_ = std::move(chain); - VLOG(2) << __func__ << " built this: " << *this; - return true; -} - -bool GLScaler::ScaleToMultipleOutputs(GLuint src_texture, - const gfx::Size& src_texture_size, - const gfx::Vector2d& src_offset, - GLuint dest_texture_0, - GLuint dest_texture_1, - const gfx::Rect& output_rect) { - if (!chain_) { - return false; - } - - // Bind the vertex attributes used to sweep the entire source area when - // executing the shader programs. - GLES2Interface* const gl = context_provider_->ContextGL(); - DCHECK(gl); - if (vertex_attributes_buffer_) { - gl->BindBuffer(GL_ARRAY_BUFFER, vertex_attributes_buffer_); - } else { - gl->GenBuffers(1, &vertex_attributes_buffer_); - gl->BindBuffer(GL_ARRAY_BUFFER, vertex_attributes_buffer_); - gl->BufferData(GL_ARRAY_BUFFER, sizeof(ShaderProgram::kVertexAttributes), - ShaderProgram::kVertexAttributes, GL_STATIC_DRAW); - } - - // Disable GL clipping/blending features that interfere with assumptions made - // by the implementation. Only those known to possibly be enabled elsewhere in - // Chromium code are disabled here, while the remainder are sanity-DCHECK'ed. - gl->Disable(GL_SCISSOR_TEST); - gl->Disable(GL_STENCIL_TEST); - gl->Disable(GL_BLEND); - DCHECK_NE(gl->IsEnabled(GL_CULL_FACE), GL_TRUE); - DCHECK_NE(gl->IsEnabled(GL_DEPTH_TEST), GL_TRUE); - DCHECK_NE(gl->IsEnabled(GL_POLYGON_OFFSET_FILL), GL_TRUE); - DCHECK_NE(gl->IsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE), GL_TRUE); - DCHECK_NE(gl->IsEnabled(GL_SAMPLE_COVERAGE), GL_TRUE); - DCHECK_NE(gl->IsEnabled(GL_SCISSOR_TEST), GL_TRUE); - DCHECK_NE(gl->IsEnabled(GL_STENCIL_TEST), GL_TRUE); - - chain_->ScaleToMultipleOutputs(src_texture, src_texture_size, src_offset, - dest_texture_0, dest_texture_1, output_rect); - - gl->BindBuffer(GL_ARRAY_BUFFER, 0); - return true; -} - -// static -bool GLScaler::ParametersHasSameScaleRatio(const GLScaler::Parameters& params, - const gfx::Vector2d& from, - const gfx::Vector2d& to) { - // Returns true iff a_num/a_denom == b_num/b_denom. - const auto AreRatiosEqual = [](int32_t a_num, int32_t a_denom, int32_t b_num, - int32_t b_denom) -> bool { - // The math (for each dimension): - // If: a_num/a_denom == b_num/b_denom - // Then: a_num*b_denom == b_num*a_denom - // - // ...and cast to int64_t to guarantee no overflow from the multiplications. - return (static_cast<int64_t>(a_num) * b_denom) == - (static_cast<int64_t>(b_num) * a_denom); - }; - - return AreRatiosEqual(params.scale_from.x(), params.scale_to.x(), from.x(), - to.x()) && - AreRatiosEqual(params.scale_from.y(), params.scale_to.y(), from.y(), - to.y()); -} - -// static -bool GLScaler::ParametersAreEquivalent(const Parameters& a, - const Parameters& b) { - if (!ParametersHasSameScaleRatio(a, b.scale_from, b.scale_to) || - a.enable_precise_color_management != b.enable_precise_color_management || - a.quality != b.quality || a.is_flipped_source != b.is_flipped_source || - a.flip_output != b.flip_output || a.export_format != b.export_format || - a.swizzle[0] != b.swizzle[0] || a.swizzle[1] != b.swizzle[1]) { - return false; - } - - const gfx::ColorSpace source_color_space_a = - a.source_color_space.IsValid() ? a.source_color_space - : gfx::ColorSpace::CreateSRGB(); - const gfx::ColorSpace source_color_space_b = - b.source_color_space.IsValid() ? b.source_color_space - : gfx::ColorSpace::CreateSRGB(); - if (source_color_space_a != source_color_space_b) { - return false; - } - - const gfx::ColorSpace output_color_space_a = a.output_color_space.IsValid() - ? a.output_color_space - : source_color_space_a; - const gfx::ColorSpace output_color_space_b = b.output_color_space.IsValid() - ? b.output_color_space - : source_color_space_b; - return output_color_space_a == output_color_space_b; -} - -void GLScaler::OnContextLost() { - // The destruction order here is important due to data dependencies. - chain_.reset(); - shader_programs_.clear(); - if (vertex_attributes_buffer_) { - if (auto* gl = context_provider_->ContextGL()) { - gl->DeleteBuffers(1, &vertex_attributes_buffer_); - } - vertex_attributes_buffer_ = 0; - } - if (context_provider_) { - context_provider_->RemoveObserver(this); - context_provider_ = nullptr; - } -} - -GLScaler::ShaderProgram* GLScaler::GetShaderProgram( - Shader shader, - GLenum texture_type, - const gfx::ColorTransform* color_transform, - const GLenum swizzle[2]) { - const ShaderCacheKey key{ - shader, - texture_type, - color_transform ? color_transform->GetSrcColorSpace() : gfx::ColorSpace(), - color_transform ? color_transform->GetDstColorSpace() : gfx::ColorSpace(), - swizzle[0], - swizzle[1]}; - auto it = shader_programs_.find(key); - if (it == shader_programs_.end()) { - GLES2Interface* const gl = context_provider_->ContextGL(); - DCHECK(gl); - it = shader_programs_ - .emplace(std::piecewise_construct, std::forward_as_tuple(key), - std::forward_as_tuple(gl, shader, texture_type, - color_transform, swizzle)) - .first; - } - return &it->second; -} - -// static -std::unique_ptr<GLScaler::ScalerStage> GLScaler::CreateAGoodScalingChain( - gpu::gles2::GLES2Interface* gl, - const gfx::Vector2d& scale_from, - const gfx::Vector2d& scale_to) { - DCHECK(scale_from.x() != 0 && scale_from.y() != 0) - << "Bad scale_from: " << scale_from.ToString(); - DCHECK(scale_to.x() != 0 && scale_to.y() != 0) - << "Bad scale_to: " << scale_to.ToString(); - DCHECK(scale_from != scale_to); - - // The GOOD quality chain performs one bilinear upscale followed by N bilinear - // halvings, and does this is both directions. Exception: No upscale is needed - // when |scale_from| is a power of two multiple of |scale_to|. - // - // Since all shaders use bilinear filtering, the heuristics below attempt to - // greedily merge steps wherever possible to minimize GPU memory usage and - // processing time. This also means that it will be extremely rare for the - // stage doing the initial upscale to actually require a larger output texture - // than the source texture (a downscale will be merged into the same stage). - - // Determine the initial upscaled-to size, as the minimum number of doublings - // to make |scale_to| greater than |scale_from|. - const RelativeSize from(scale_from); - const RelativeSize to(scale_to); - RelativeSize upscale_to = to; - for (Axis x_or_y : std::array<Axis, 2>{HORIZONTAL, VERTICAL}) { - while (upscale_to[x_or_y] < from[x_or_y]) { - upscale_to[x_or_y] *= 2; - } - } - - // Create the stages in order from first-to-last, taking the greediest path - // each time. Something like an A* algorithm would be better for discovering - // an optimal sequence of operations, and would allow using the BILINEAR3 - // shader as well, but the run-time performance to compute the stages would be - // too prohibitive. - std::unique_ptr<ScalerStage> chain; - struct CandidateOp { - Shader shader; - Axis primary_axis; - RelativeSize output_size; - }; - std::vector<CandidateOp> candidates; - for (RelativeSize cur = from; cur != to; - cur = RelativeSize(chain->scale_to())) { - candidates.clear(); - - // Determine whether it's possible to do exactly 2 bilinear passes in both - // directions. - RelativeSize output_size_2x2 = {0, 0}; - for (Axis x_or_y : std::array<Axis, 2>{VERTICAL, HORIZONTAL}) { - if (cur[x_or_y] == from[x_or_y]) { - // For the first stage, the 2 bilinear passes must be the initial - // upscale followed by one downscale. If there is no initial upscale, - // then the 2 passes must both be downscales. - if (upscale_to[x_or_y] != from[x_or_y] && - upscale_to[x_or_y] / 2 >= to[x_or_y]) { - output_size_2x2[x_or_y] = upscale_to[x_or_y] / 2; - } else if (upscale_to[x_or_y] == from[x_or_y] && - upscale_to[x_or_y] / 4 >= to[x_or_y]) { - output_size_2x2[x_or_y] = cur[x_or_y] / 4; - } - } else { - // For all later stages, the 2 bilinear passes must be 2 halvings. - if (cur[x_or_y] / 4 >= to[x_or_y]) { - output_size_2x2[x_or_y] = cur[x_or_y] / 4; - } - } - } - if (output_size_2x2[HORIZONTAL] != 0 && output_size_2x2[VERTICAL] != 0) { - candidates.push_back( - CandidateOp{Shader::BILINEAR2X2, HORIZONTAL, output_size_2x2}); - } - - // Determine the valid set of Ops that do 1 to 3 bilinear passes in one - // direction and 0 or 1 pass in the other direction. - for (Axis x_or_y : std::array<Axis, 2>{VERTICAL, HORIZONTAL}) { - // The first bilinear pass in x_or_y must be an upscale or a halving. - Shader shader = Shader::BILINEAR; - RelativeSize output_size = cur; - if (cur[x_or_y] == from[x_or_y] && upscale_to[x_or_y] != from[x_or_y]) { - output_size[x_or_y] = upscale_to[x_or_y]; - } else if (cur[x_or_y] / 2 >= to[x_or_y]) { - output_size[x_or_y] /= 2; - } else { - DCHECK_EQ(cur[x_or_y], to[x_or_y]); - continue; - } - - // Determine whether 1 or 2 additional passes can be made in the same - // direction. - if (output_size[x_or_y] / 4 >= to[x_or_y]) { - shader = Shader::BILINEAR4; // 2 more passes == 3 total. - output_size[x_or_y] /= 4; - } else if (output_size[x_or_y] / 2 >= to[x_or_y]) { - shader = Shader::BILINEAR2; // 1 more pass == 2 total. - output_size[x_or_y] /= 2; - } else { - DCHECK_EQ(output_size[x_or_y], to[x_or_y]); - } - - // Determine whether 0 or 1 bilinear passes can be made in the other - // direction at the same time. - const Axis y_or_x = TheOtherAxis(x_or_y); - if (cur[y_or_x] == from[y_or_x] && upscale_to[y_or_x] != from[y_or_x]) { - output_size[y_or_x] = upscale_to[y_or_x]; - } else if (cur[y_or_x] / 2 >= to[y_or_x]) { - output_size[y_or_x] /= 2; - } else { - DCHECK_EQ(cur[y_or_x], to[y_or_x]); - } - - candidates.push_back(CandidateOp{shader, x_or_y, output_size}); - } - - // From the candidates, pick the one that produces the fewest number of - // output pixels, and append a new ScalerStage. There are opportunities to - // improve the "cost function" here (e.g., pixels in the Y direction - // probably cost more to process than pixels in the X direction), but that - // would require more research. - const auto best_candidate = std::min_element( - candidates.begin(), candidates.end(), - [](const CandidateOp& a, const CandidateOp& b) { - static_assert(sizeof(a.output_size[0]) <= sizeof(int32_t), - "Overflow issue in the math here."); - const int64_t cost_of_a = - int64_t{a.output_size[HORIZONTAL]} * a.output_size[VERTICAL]; - const int64_t cost_of_b = - int64_t{b.output_size[HORIZONTAL]} * b.output_size[VERTICAL]; - return cost_of_a < cost_of_b; - }); - DCHECK(best_candidate != candidates.end()); - DCHECK(cur != best_candidate->output_size) - << "Best candidate's output size (" << best_candidate->output_size - << ") should not equal the input size."; - auto next_stage = std::make_unique<ScalerStage>( - gl, best_candidate->shader, best_candidate->primary_axis, - cur.AsVector2d(), best_candidate->output_size.AsVector2d()); - next_stage->set_input_stage(std::move(chain)); - chain = std::move(next_stage); - } - - return chain; -} - -// static -std::unique_ptr<GLScaler::ScalerStage> GLScaler::CreateTheBestScalingChain( - gpu::gles2::GLES2Interface* gl, - const gfx::Vector2d& scale_from, - const gfx::Vector2d& scale_to) { - // The BEST quality chain performs one bicubic upscale followed by N bicubic - // halvings, and does this is both directions. Exception: No upscale is needed - // when |scale_from| is a power of two multiple of |scale_to|. - - // Determine the initial upscaled-to size, as the minimum number of doublings - // to make |scale_to| greater than |scale_from|. - const RelativeSize from(scale_from); - const RelativeSize to(scale_to); - RelativeSize upscale_to = to; - for (Axis x_or_y : std::array<Axis, 2>{HORIZONTAL, VERTICAL}) { - while (upscale_to[x_or_y] < from[x_or_y]) { - upscale_to[x_or_y] *= 2; - } - } - - // Create the stages in order from first-to-last. - RelativeSize cur = from; - std::unique_ptr<ScalerStage> chain; - for (Axis x_or_y : std::array<Axis, 2>{VERTICAL, HORIZONTAL}) { - if (upscale_to[x_or_y] != from[x_or_y]) { - RelativeSize next = cur; - next[x_or_y] = upscale_to[x_or_y]; - auto upscale_stage = - std::make_unique<ScalerStage>(gl, Shader::BICUBIC_UPSCALE, x_or_y, - cur.AsVector2d(), next.AsVector2d()); - upscale_stage->set_input_stage(std::move(chain)); - chain = std::move(upscale_stage); - cur = next; - } - while (cur[x_or_y] > to[x_or_y]) { - RelativeSize next = cur; - next[x_or_y] /= 2; - auto next_stage = - std::make_unique<ScalerStage>(gl, Shader::BICUBIC_HALF_1D, x_or_y, - cur.AsVector2d(), next.AsVector2d()); - next_stage->set_input_stage(std::move(chain)); - chain = std::move(next_stage); - cur = next; - } - } - DCHECK_EQ(cur, to); - - return chain; -} - -// static -std::unique_ptr<GLScaler::ScalerStage> GLScaler::MaybeAppendExportStage( - gpu::gles2::GLES2Interface* gl, - std::unique_ptr<GLScaler::ScalerStage> chain, - GLScaler::Parameters::ExportFormat export_format) { - DCHECK(chain); - - if (export_format == Parameters::ExportFormat::INTERLEAVED_QUADS) { - return chain; // No format change. - } - - // If the final stage uses the BILINEAR shader that is not upscaling, the - // export stage can replace it with no change in the results. Otherwise, a - // separate export stage will be appended. - gfx::Vector2d scale_from = chain->scale_from(); - const gfx::Vector2d scale_to = chain->scale_to(); - if (chain->shader() == Shader::BILINEAR && scale_from.x() >= scale_to.x() && - scale_from.y() >= scale_to.y()) { - chain = chain->take_input_stage(); - } else { - scale_from = scale_to; - } - - Shader shader = Shader::BILINEAR; - scale_from.set_x(scale_from.x() * 4); - switch (export_format) { - case Parameters::ExportFormat::INTERLEAVED_QUADS: - NOTREACHED(); - break; - case Parameters::ExportFormat::CHANNEL_0: - shader = Shader::PLANAR_CHANNEL_0; - break; - case Parameters::ExportFormat::CHANNEL_1: - shader = Shader::PLANAR_CHANNEL_1; - break; - case Parameters::ExportFormat::CHANNEL_2: - shader = Shader::PLANAR_CHANNEL_2; - break; - case Parameters::ExportFormat::CHANNEL_3: - shader = Shader::PLANAR_CHANNEL_3; - break; - case Parameters::ExportFormat::NV61: - shader = Shader::I422_NV61_MRT; - break; - case Parameters::ExportFormat::DEINTERLEAVE_PAIRWISE: - shader = Shader::DEINTERLEAVE_PAIRWISE_MRT; - // Horizontal scale is only 0.5X, not 0.25X like all the others. - scale_from.set_x(scale_from.x() / 2); - break; - case Parameters::ExportFormat::UV_CHANNELS: - shader = Shader::PLANAR_CHANNELS_1_2; - break; - } - - auto export_stage = std::make_unique<ScalerStage>(gl, shader, HORIZONTAL, - scale_from, scale_to); - export_stage->set_input_stage(std::move(chain)); - return export_stage; -} - -// static -GLScaler::Axis GLScaler::TheOtherAxis(GLScaler::Axis x_or_y) { - return x_or_y == HORIZONTAL ? VERTICAL : HORIZONTAL; -} - -// static -const char* GLScaler::GetShaderName(GLScaler::Shader shader) { - switch (shader) { -#define CASE_RETURN_SHADER_STR(x) \ - case Shader::x: \ - return #x - CASE_RETURN_SHADER_STR(BILINEAR); - CASE_RETURN_SHADER_STR(BILINEAR2); - CASE_RETURN_SHADER_STR(BILINEAR3); - CASE_RETURN_SHADER_STR(BILINEAR4); - CASE_RETURN_SHADER_STR(BILINEAR2X2); - CASE_RETURN_SHADER_STR(BICUBIC_UPSCALE); - CASE_RETURN_SHADER_STR(BICUBIC_HALF_1D); - CASE_RETURN_SHADER_STR(PLANAR_CHANNEL_0); - CASE_RETURN_SHADER_STR(PLANAR_CHANNEL_1); - CASE_RETURN_SHADER_STR(PLANAR_CHANNEL_2); - CASE_RETURN_SHADER_STR(PLANAR_CHANNEL_3); - CASE_RETURN_SHADER_STR(I422_NV61_MRT); - CASE_RETURN_SHADER_STR(DEINTERLEAVE_PAIRWISE_MRT); - CASE_RETURN_SHADER_STR(PLANAR_CHANNELS_1_2); -#undef CASE_RETURN_SHADER_STR - } -} - -// static -bool GLScaler::AreAllGLExtensionsPresent( - gpu::gles2::GLES2Interface* gl, - const std::vector<std::string>& names) { - DCHECK(gl); - if (const auto* extensions = gl->GetString(GL_EXTENSIONS)) { - const std::string extensions_string = - " " + std::string(reinterpret_cast<const char*>(extensions)) + " "; - for (const std::string& name : names) { - if (extensions_string.find(" " + name + " ") == std::string::npos) { - return false; - } - } - return true; - } - return false; -} - -GLScaler::Parameters::Parameters() = default; -GLScaler::Parameters::Parameters(const Parameters& other) = default; -GLScaler::Parameters::~Parameters() = default; - -// static -const GLfloat GLScaler::ShaderProgram::kVertexAttributes[16] = { - -1.0f, -1.0f, 0.0f, 0.0f, // vertex 0 - 1.0f, -1.0f, 1.0f, 0.0f, // vertex 1 - -1.0f, 1.0f, 0.0f, 1.0f, // vertex 2 - 1.0f, 1.0f, 1.0f, 1.0f, // vertex 3 -}; - -GLScaler::ShaderProgram::ShaderProgram( - gpu::gles2::GLES2Interface* gl, - GLScaler::Shader shader, - GLenum texture_type, - const gfx::ColorTransform* color_transform, - const GLenum swizzle[2]) - : gl_(gl), - shader_(shader), - texture_type_(texture_type), - program_(gl_->CreateProgram()) { - DCHECK(program_); - - std::basic_ostringstream<GLchar> vertex_header; - std::basic_ostringstream<GLchar> fragment_directives; - std::basic_ostringstream<GLchar> fragment_header; - std::basic_ostringstream<GLchar> shared_variables; - std::basic_ostringstream<GLchar> vertex_main; - std::basic_ostringstream<GLchar> fragment_main; - - vertex_header - << ("precision highp float;\n" - "attribute vec2 a_position;\n" - "attribute vec2 a_texcoord;\n" - "uniform vec4 src_rect;\n"); - - fragment_header << "precision mediump float;\n"; - switch (texture_type_) { - case GL_FLOAT: - fragment_header << "precision highp sampler2D;\n"; - break; - case GL_HALF_FLOAT_OES: - fragment_header << "precision mediump sampler2D;\n"; - break; - default: - fragment_header << "precision lowp sampler2D;\n"; - break; - } - fragment_header << "uniform sampler2D s_texture;\n"; - - if (color_transform && shader_ != Shader::PLANAR_CHANNEL_3) { - const std::string& source = color_transform->GetShaderSource(); - // Assumption: gfx::ColorTransform::GetShaderSource() should provide a - // function named DoColorConversion() that takes a vec3 argument and returns - // a vec3. - DCHECK_NE(source.find("DoColorConversion"), std::string::npos); - fragment_header << source; - } - - vertex_main - << (" gl_Position = vec4(a_position, 0.0, 1.0);\n" - " vec2 texcoord = src_rect.xy + a_texcoord * src_rect.zw;\n"); - - switch (shader_) { - case Shader::BILINEAR: - shared_variables << "varying highp vec2 v_texcoord;\n"; - vertex_main << " v_texcoord = texcoord;\n"; - fragment_main << " vec4 sample = texture2D(s_texture, v_texcoord);\n"; - if (color_transform) { - fragment_main << " sample.rgb = DoColorConversion(sample.rgb);\n"; - } - if (swizzle[0] == GL_BGRA_EXT) { - fragment_main << " sample.rb = sample.br;\n"; - } - fragment_main << " gl_FragColor = sample;\n"; - break; - - case Shader::BILINEAR2: - // This is equivialent to two passes of the BILINEAR shader above. It can - // be used to scale an image down 1.0x-2.0x in either dimension, or - // exactly 4x. - shared_variables << "varying highp vec4 v_texcoords;\n"; - vertex_header << "uniform vec2 scaling_vector;\n"; - vertex_main - << (" vec2 step = scaling_vector / 4.0;\n" - " v_texcoords.xy = texcoord + step;\n" - " v_texcoords.zw = texcoord - step;\n"); - fragment_main - << (" vec4 blended = (texture2D(s_texture, v_texcoords.xy) +\n" - " texture2D(s_texture, v_texcoords.zw)) /\n" - " 2.0;\n"); - if (color_transform) { - fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n"; - } - if (swizzle[0] == GL_BGRA_EXT) { - fragment_main << " blended.rb = blended.br;\n"; - } - fragment_main << " gl_FragColor = blended;\n"; - break; - - case Shader::BILINEAR3: - // This is kind of like doing 1.5 passes of the BILINEAR shader. It can be - // used to scale an image down 1.5x-3.0x, or exactly 6x. - shared_variables - << ("varying highp vec4 v_texcoords0;\n" - "varying highp vec2 v_texcoords1;\n"); - vertex_header << "uniform vec2 scaling_vector;\n"; - vertex_main - << (" vec2 step = scaling_vector / 3.0;\n" - " v_texcoords0.xy = texcoord + step;\n" - " v_texcoords0.zw = texcoord;\n" - " v_texcoords1 = texcoord - step;\n"); - fragment_main - << (" vec4 blended = (texture2D(s_texture, v_texcoords0.xy) +\n" - " texture2D(s_texture, v_texcoords0.zw) +\n" - " texture2D(s_texture, v_texcoords1)) / 3.0;\n"); - if (color_transform) { - fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n"; - } - if (swizzle[0] == GL_BGRA_EXT) { - fragment_main << " blended.rb = blended.br;\n"; - } - fragment_main << " gl_FragColor = blended;\n"; - break; - - case Shader::BILINEAR4: - // This is equivialent to three passes of the BILINEAR shader above. It - // can be used to scale an image down 2.0x-4.0x or exactly 8x. - shared_variables << "varying highp vec4 v_texcoords[2];\n"; - vertex_header << "uniform vec2 scaling_vector;\n"; - vertex_main - << (" vec2 step = scaling_vector / 8.0;\n" - " v_texcoords[0].xy = texcoord - step * 3.0;\n" - " v_texcoords[0].zw = texcoord - step;\n" - " v_texcoords[1].xy = texcoord + step;\n" - " v_texcoords[1].zw = texcoord + step * 3.0;\n"); - fragment_main - << (" vec4 blended = (\n" - " texture2D(s_texture, v_texcoords[0].xy) +\n" - " texture2D(s_texture, v_texcoords[0].zw) +\n" - " texture2D(s_texture, v_texcoords[1].xy) +\n" - " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n"); - if (color_transform) { - fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n"; - } - if (swizzle[0] == GL_BGRA_EXT) { - fragment_main << " blended.rb = blended.br;\n"; - } - fragment_main << " gl_FragColor = blended;\n"; - break; - - case Shader::BILINEAR2X2: - // This is equivialent to four passes of the BILINEAR shader above, two in - // each dimension. It can be used to scale an image down 1.0x-2.0x in both - // X and Y directions. Or, it could be used to scale an image down by - // exactly 4x in both dimensions. - shared_variables << "varying highp vec4 v_texcoords[2];\n"; - vertex_header << "uniform vec2 scaling_vector;\n"; - vertex_main - << (" vec2 step = scaling_vector / 4.0;\n" - " v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n" - " v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n" - " v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n" - " v_texcoords[1].zw = texcoord + vec2(-step.x, -step.y);\n"); - fragment_main - << (" vec4 blended = (\n" - " texture2D(s_texture, v_texcoords[0].xy) +\n" - " texture2D(s_texture, v_texcoords[0].zw) +\n" - " texture2D(s_texture, v_texcoords[1].xy) +\n" - " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n"); - if (color_transform) { - fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n"; - } - if (swizzle[0] == GL_BGRA_EXT) { - fragment_main << " blended.rb = blended.br;\n"; - } - fragment_main << " gl_FragColor = blended;\n"; - break; - - case Shader::BICUBIC_UPSCALE: - // When scaling up, 4 texture reads are necessary. However, some - // instructions can be saved because the parameter passed to the bicubic - // function will be in a known range. Also, when sampling the bicubic - // function like this, the sum is always exactly one, so normalization can - // be skipped as well. - shared_variables << "varying highp vec2 v_texcoord;\n"; - vertex_main << " v_texcoord = texcoord;\n"; - fragment_header - << ("uniform highp vec2 src_pixelsize;\n" - "uniform highp vec2 scaling_vector;\n" - "const float a = -0.5;\n" - // This function is equivialent to calling the bicubic - // function with x-1, x, 1-x and 2-x (assuming - // 0 <= x < 1). The following is the Catmull-Rom spline. - // See: http://wikipedia.org/wiki/Cubic_Hermite_spline - "vec4 filt4(float x) {\n" - " return vec4(x * x * x, x * x, x, 1) *\n" - " mat4( a, -2.0 * a, a, 0.0,\n" - " a + 2.0, -a - 3.0, 0.0, 1.0,\n" - " -a - 2.0, 3.0 + 2.0 * a, -a, 0.0,\n" - " -a, a, 0.0, 0.0);\n" - "}\n" - "mat4 pixels_x(highp vec2 pos, highp vec2 step) {\n" - " return mat4(texture2D(s_texture, pos - step),\n" - " texture2D(s_texture, pos),\n" - " texture2D(s_texture, pos + step),\n" - " texture2D(s_texture, pos + step * 2.0));\n" - "}\n"); - fragment_main - << (" highp vec2 pixel_pos = v_texcoord * src_pixelsize - \n" - " scaling_vector / 2.0;\n" - " highp float frac = fract(dot(pixel_pos, scaling_vector));\n" - " highp vec2 base = \n" - " (floor(pixel_pos) + vec2(0.5)) / src_pixelsize;\n" - " highp vec2 step = scaling_vector / src_pixelsize;\n" - " vec4 blended = pixels_x(base, step) * filt4(frac);\n"); - if (color_transform) { - fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n"; - } - if (swizzle[0] == GL_BGRA_EXT) { - fragment_main << " blended.rb = blended.br;\n"; - } - fragment_main << " gl_FragColor = blended;\n"; - break; - - case Shader::BICUBIC_HALF_1D: - // This scales down an image by exactly half in one dimension. The - // bilinear lookup reduces the number of texture reads from 8 to 4. - shared_variables << "varying highp vec4 v_texcoords[2];\n"; - vertex_header - << ("uniform vec2 scaling_vector;\n" - "const float CenterDist = 99.0 / 140.0;\n" - "const float LobeDist = 11.0 / 4.0;\n"); - vertex_main - << (" vec2 step = scaling_vector / 2.0;\n" - " v_texcoords[0].xy = texcoord - LobeDist * step;\n" - " v_texcoords[0].zw = texcoord - CenterDist * step;\n" - " v_texcoords[1].xy = texcoord + CenterDist * step;\n" - " v_texcoords[1].zw = texcoord + LobeDist * step;\n"); - fragment_header - << ("const float CenterWeight = 35.0 / 64.0;\n" - "const float LobeWeight = -3.0 / 64.0;\n"); - fragment_main - << (" vec4 blended = \n" - // Lobe pixels - " (texture2D(s_texture, v_texcoords[0].xy) +\n" - " texture2D(s_texture, v_texcoords[1].zw)) *\n" - " LobeWeight +\n" - // Center pixels - " (texture2D(s_texture, v_texcoords[0].zw) +\n" - " texture2D(s_texture, v_texcoords[1].xy)) *\n" - " CenterWeight;\n"); - if (color_transform) { - fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n"; - } - if (swizzle[0] == GL_BGRA_EXT) { - fragment_main << " blended.rb = blended.br;\n"; - } - fragment_main << " gl_FragColor = blended;\n"; - break; - - case Shader::PLANAR_CHANNEL_0: - case Shader::PLANAR_CHANNEL_1: - case Shader::PLANAR_CHANNEL_2: - case Shader::PLANAR_CHANNEL_3: { - // Select one color channel, and pack 4 pixels into one output quad. See - // header file for diagram. - shared_variables << "varying highp vec4 v_texcoords[2];\n"; - vertex_header << "uniform vec2 scaling_vector;\n"; - vertex_main - << (" vec2 step = scaling_vector / 4.0;\n" - " v_texcoords[0].xy = texcoord - step * 1.5;\n" - " v_texcoords[0].zw = texcoord - step * 0.5;\n" - " v_texcoords[1].xy = texcoord + step * 0.5;\n" - " v_texcoords[1].zw = texcoord + step * 1.5;\n"); - std::basic_string<GLchar> convert_open; - std::basic_string<GLchar> convert_close; - if (color_transform && shader_ != Shader::PLANAR_CHANNEL_3) { - convert_open = "DoColorConversion("; - convert_close = ".rgb)"; - } - const char selector = "rgba"[static_cast<int>(shader_) - - static_cast<int>(Shader::PLANAR_CHANNEL_0)]; - fragment_main << " vec4 packed_quad = vec4(\n" - << " " << convert_open - << "texture2D(s_texture, v_texcoords[0].xy)" - << convert_close << '.' << selector << ",\n" - << " " << convert_open - << "texture2D(s_texture, v_texcoords[0].zw)" - << convert_close << '.' << selector << ",\n" - << " " << convert_open - << "texture2D(s_texture, v_texcoords[1].xy)" - << convert_close << '.' << selector << ",\n" - << " " << convert_open - << "texture2D(s_texture, v_texcoords[1].zw)" - << convert_close << '.' << selector << ");\n"; - if (swizzle[0] == GL_BGRA_EXT) { - fragment_main << " packed_quad.rb = packed_quad.br;\n"; - } - fragment_main << " gl_FragColor = packed_quad;\n"; - break; - } - - case Shader::I422_NV61_MRT: - // I422 sampling, delivered via two output textures (NV61 format). See - // header file for diagram. - shared_variables << "varying highp vec4 v_texcoords[2];\n"; - vertex_header << "uniform vec2 scaling_vector;\n"; - vertex_main - << (" vec2 step = scaling_vector / 4.0;\n" - " v_texcoords[0].xy = texcoord - step * 1.5;\n" - " v_texcoords[0].zw = texcoord - step * 0.5;\n" - " v_texcoords[1].xy = texcoord + step * 0.5;\n" - " v_texcoords[1].zw = texcoord + step * 1.5;\n"); - fragment_directives << "#extension GL_EXT_draw_buffers : enable\n"; - fragment_main - << (" vec3 pixel0 = texture2D(s_texture, v_texcoords[0].xy).rgb;\n" - " vec3 pixel1 = texture2D(s_texture, v_texcoords[0].zw).rgb;\n" - " vec3 pixel01 = (pixel0 + pixel1) / 2.0;\n" - " vec3 pixel2 = texture2D(s_texture, v_texcoords[1].xy).rgb;\n" - " vec3 pixel3 = texture2D(s_texture, v_texcoords[1].zw).rgb;\n" - " vec3 pixel23 = (pixel2 + pixel3) / 2.0;\n"); - if (color_transform) { - fragment_main - << (" pixel0 = DoColorConversion(pixel0);\n" - " pixel1 = DoColorConversion(pixel1);\n" - " pixel01 = DoColorConversion(pixel01);\n" - " pixel2 = DoColorConversion(pixel2);\n" - " pixel3 = DoColorConversion(pixel3);\n" - " pixel23 = DoColorConversion(pixel23);\n"); - } - if (swizzle[0] == GL_BGRA_EXT) { - fragment_main - << (" gl_FragData[0] = \n" - " vec4(pixel2.r, pixel1.r, pixel0.r, pixel3.r);\n"); - } else { - fragment_main - << (" gl_FragData[0] = \n" - " vec4(pixel0.r, pixel1.r, pixel2.r, pixel3.r);\n"); - } - if (swizzle[1] == GL_BGRA_EXT) { - fragment_main - << (" gl_FragData[1] = \n" - " vec4(pixel23.g, pixel01.b, pixel01.g, pixel23.b);\n"); - } else { - fragment_main - << (" gl_FragData[1] = \n" - " vec4(pixel01.g, pixel01.b, pixel23.g, pixel23.b);\n"); - } - break; - - case Shader::DEINTERLEAVE_PAIRWISE_MRT: - // Sample two pixels and unswizzle them. There's no need to do vertical - // scaling with math, since the bilinear interpolation in the sampler - // takes care of that. - shared_variables << "varying highp vec4 v_texcoords;\n"; - vertex_header << "uniform vec2 scaling_vector;\n"; - vertex_main - << (" vec2 step = scaling_vector / 2.0;\n" - " v_texcoords.xy = texcoord - step * 0.5;\n" - " v_texcoords.zw = texcoord + step * 0.5;\n"); - fragment_directives << "#extension GL_EXT_draw_buffers : enable\n"; - DCHECK(!color_transform); - fragment_main - << (" vec4 lo_uvuv = texture2D(s_texture, v_texcoords.xy);\n" - " vec4 hi_uvuv = texture2D(s_texture, v_texcoords.zw);\n" - " vec4 uuuu = vec4(lo_uvuv.rb, hi_uvuv.rb);\n" - " vec4 vvvv = vec4(lo_uvuv.ga, hi_uvuv.ga);\n"); - if (swizzle[0] == GL_BGRA_EXT) { - fragment_main << " uuuu.rb = uuuu.br;\n"; - } - fragment_main << " gl_FragData[0] = uuuu;\n"; - if (swizzle[1] == GL_BGRA_EXT) { - fragment_main << " vvvv.rb = vvvv.br;\n"; - } - fragment_main << " gl_FragData[1] = vvvv;\n"; - break; - - case Shader::PLANAR_CHANNELS_1_2: { - // Select two color channels, and pack 2 pairs of pixels into one output - // quad. See header file for diagram. - // This shader performs the same work that is being done by - // Shader::I422_NV61_MRT (see above) for its second render target. - shared_variables << "varying highp vec4 v_texcoords[2];\n"; - vertex_header << "uniform vec2 scaling_vector;\n"; - vertex_main - << (" vec2 step = scaling_vector / 4.0;\n" - " v_texcoords[0].xy = texcoord - step * 1.5;\n" - " v_texcoords[0].zw = texcoord - step * 0.5;\n" - " v_texcoords[1].xy = texcoord + step * 0.5;\n" - " v_texcoords[1].zw = texcoord + step * 1.5;\n"); - fragment_main - << (" vec3 pixel0 = texture2D(s_texture, v_texcoords[0].xy).rgb;\n" - " vec3 pixel1 = texture2D(s_texture, v_texcoords[0].zw).rgb;\n" - " vec3 pixel01 = (pixel0 + pixel1) / 2.0;\n" - " vec3 pixel2 = texture2D(s_texture, v_texcoords[1].xy).rgb;\n" - " vec3 pixel3 = texture2D(s_texture, v_texcoords[1].zw).rgb;\n" - " vec3 pixel23 = (pixel2 + pixel3) / 2.0;\n"); - - if (color_transform) { - fragment_main - << (" pixel01 = DoColorConversion(pixel01);\n" - " pixel23 = DoColorConversion(pixel23);\n"); - } - - fragment_main - << (" vec4 packed_quad = vec4(\n" - " pixel01.g, pixel01.b, pixel23.g, pixel23.b);\n"); - - if (swizzle[0] == GL_BGRA_EXT) { - fragment_main << " packed_quad.rb = packed_quad.br;\n"; - } - - fragment_main << " gl_FragColor = packed_quad;\n"; - break; - } - } - - // Helper function to compile the shader source and log the GLSL compiler's - // results. - const char* shader_name = GLScaler::GetShaderName(shader_); - const auto CompileShaderFromSource = - [shader_name](GLES2Interface* gl, const std::basic_string<GLchar>& source, - GLenum type) -> GLuint { - VLOG(2) << __func__ << ": Compiling shader " << type - << " with source:" << std::endl - << source << ", for GLScaler::Shader=" << shader_name; - const GLuint shader = gl->CreateShader(type); - const GLchar* source_data = source.data(); - const GLint length = base::checked_cast<GLint>(source.size()); - gl->ShaderSource(shader, 1, &source_data, &length); - gl->CompileShader(shader); - GLint compile_status = GL_FALSE; - gl->GetShaderiv(shader, GL_COMPILE_STATUS, &compile_status); - - // Fetch logs and forward them to the system logger. If compilation failed, - // clean-up and return 0 for error. - if (compile_status != GL_TRUE || VLOG_IS_ON(2)) { - GLint log_length = 0; - gl->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); - std::string log; - if (log_length > 0) { - std::unique_ptr<GLchar[]> tmp(new GLchar[log_length]); - GLsizei returned_log_length = 0; - gl->GetShaderInfoLog(shader, log_length, &returned_log_length, - tmp.get()); - log.assign(tmp.get(), returned_log_length); - } - if (log.empty()) { - log = "<<NO LOG>>"; - } - if (compile_status != GL_TRUE) { - LOG(ERROR) << __func__ << ": Compilation of shader " << type - << " failed:" << std::endl - << log; - gl->DeleteShader(shader); - return 0; - } - VLOG(2) << __func__ << ": Compilation of shader " << type - << " succeeded:" << std::endl - << log; - } - return shader; - }; - - // Compile the vertex shader and attach it to the program. - const std::string shared_variables_str = shared_variables.str(); - const GLuint vertex_shader = - CompileShaderFromSource(gl_, - vertex_header.str() + shared_variables_str + - "void main() {\n" + vertex_main.str() + "}\n", - GL_VERTEX_SHADER); - if (vertex_shader == 0) { - return; - } - gl_->AttachShader(program_, vertex_shader); - gl_->DeleteShader(vertex_shader); - - // Compile the fragment shader and attach to |program_|. - const GLuint fragment_shader = CompileShaderFromSource( - gl_, - fragment_directives.str() + fragment_header.str() + shared_variables_str + - "void main() {\n" + fragment_main.str() + "}\n", - GL_FRAGMENT_SHADER); - if (fragment_shader == 0) { - return; - } - gl_->AttachShader(program_, fragment_shader); - gl_->DeleteShader(fragment_shader); - - // Link the program. - gl_->LinkProgram(program_); - GLint link_status = GL_FALSE; - gl_->GetProgramiv(program_, GL_LINK_STATUS, &link_status); - if (link_status != GL_TRUE) { - LOG(ERROR) << "Failed to link shader program."; - return; - } - -#define DCHECK_RESOLVED_LOCATION(member) \ - DCHECK(member != -1 || gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR) \ - << "Failed to get " #member " in program, or GPU process crashed." - - // Resolve the locations of the global variables. - position_location_ = gl_->GetAttribLocation(program_, "a_position"); - DCHECK_RESOLVED_LOCATION(position_location_); - texcoord_location_ = gl_->GetAttribLocation(program_, "a_texcoord"); - DCHECK_RESOLVED_LOCATION(texcoord_location_); - texture_location_ = gl_->GetUniformLocation(program_, "s_texture"); - DCHECK_RESOLVED_LOCATION(texture_location_); - src_rect_location_ = gl_->GetUniformLocation(program_, "src_rect"); - DCHECK_RESOLVED_LOCATION(src_rect_location_); - switch (shader_) { - case Shader::BILINEAR: - break; - - case Shader::BILINEAR2: - case Shader::BILINEAR3: - case Shader::BILINEAR4: - case Shader::BILINEAR2X2: - case Shader::BICUBIC_HALF_1D: - case Shader::PLANAR_CHANNEL_0: - case Shader::PLANAR_CHANNEL_1: - case Shader::PLANAR_CHANNEL_2: - case Shader::PLANAR_CHANNEL_3: - case Shader::I422_NV61_MRT: - case Shader::DEINTERLEAVE_PAIRWISE_MRT: - case Shader::PLANAR_CHANNELS_1_2: - scaling_vector_location_ = - gl_->GetUniformLocation(program_, "scaling_vector"); - DCHECK_RESOLVED_LOCATION(scaling_vector_location_); - break; - - case Shader::BICUBIC_UPSCALE: - src_pixelsize_location_ = - gl_->GetUniformLocation(program_, "src_pixelsize"); - DCHECK_RESOLVED_LOCATION(src_pixelsize_location_); - scaling_vector_location_ = - gl_->GetUniformLocation(program_, "scaling_vector"); - DCHECK_RESOLVED_LOCATION(scaling_vector_location_); - break; - } - -#undef DCHECK_RESOLVED_LOCATION -} - -GLScaler::ShaderProgram::~ShaderProgram() { - gl_->DeleteProgram(program_); -} - -void GLScaler::ShaderProgram::UseProgram(const gfx::Size& src_texture_size, - const gfx::RectF& src_rect, - const gfx::Size& dst_size, - GLScaler::Axis primary_axis, - bool flip_y) { - gl_->UseProgram(program_); - - // OpenGL defines the last parameter to VertexAttribPointer as type - // "const GLvoid*" even though it is actually an offset into the buffer - // object's data store and not a pointer to the client's address space. - const void* offsets[2] = {nullptr, - reinterpret_cast<const void*>(2 * sizeof(GLfloat))}; - - gl_->VertexAttribPointer(position_location_, 2, GL_FLOAT, GL_FALSE, - 4 * sizeof(GLfloat), offsets[0]); - gl_->EnableVertexAttribArray(position_location_); - - gl_->VertexAttribPointer(texcoord_location_, 2, GL_FLOAT, GL_FALSE, - 4 * sizeof(GLfloat), offsets[1]); - gl_->EnableVertexAttribArray(texcoord_location_); - - // Always sample from the first texture unit. - gl_->Uniform1i(texture_location_, 0); - - // Convert |src_rect| from pixel coordinates to texture coordinates. The - // source texture coordinates are in the range [0.0,1.0] for each dimension, - // but the sampling rect may slightly "spill" outside that range (e.g., for - // scaler overscan). - GLfloat src_rect_texcoord[4] = { - src_rect.x() / src_texture_size.width(), - src_rect.y() / src_texture_size.height(), - src_rect.width() / src_texture_size.width(), - src_rect.height() / src_texture_size.height(), - }; - if (flip_y) { - src_rect_texcoord[1] += src_rect_texcoord[3]; - src_rect_texcoord[3] *= -1.0f; - } - gl_->Uniform4fv(src_rect_location_, 1, src_rect_texcoord); - - // Set shader-specific uniform inputs. The |scaling_vector| is the ratio of - // the number of source pixels sampled per dest pixels output. It is used by - // the shader programs to locate distinct texels from the source texture, and - // sample them at the appropriate offset to produce each output texel. - switch (shader_) { - case Shader::BILINEAR: - break; - - case Shader::BILINEAR2: - case Shader::BILINEAR3: - case Shader::BILINEAR4: - case Shader::BICUBIC_HALF_1D: - case Shader::PLANAR_CHANNEL_0: - case Shader::PLANAR_CHANNEL_1: - case Shader::PLANAR_CHANNEL_2: - case Shader::PLANAR_CHANNEL_3: - case Shader::I422_NV61_MRT: - case Shader::DEINTERLEAVE_PAIRWISE_MRT: - case Shader::PLANAR_CHANNELS_1_2: - switch (primary_axis) { - case HORIZONTAL: - gl_->Uniform2f(scaling_vector_location_, - src_rect_texcoord[2] / dst_size.width(), 0.0); - break; - case VERTICAL: - gl_->Uniform2f(scaling_vector_location_, 0.0, - src_rect_texcoord[3] / dst_size.height()); - break; - } - break; - - case Shader::BILINEAR2X2: - gl_->Uniform2f(scaling_vector_location_, - src_rect_texcoord[2] / dst_size.width(), - src_rect_texcoord[3] / dst_size.height()); - break; - - case Shader::BICUBIC_UPSCALE: - gl_->Uniform2f(src_pixelsize_location_, src_texture_size.width(), - src_texture_size.height()); - // For this shader program, the |scaling_vector| has an alternate meaning: - // It is only being used to select whether bicubic sampling is stepped in - // the X or the Y direction. - gl_->Uniform2f(scaling_vector_location_, - primary_axis == HORIZONTAL ? 1.0 : 0.0, - primary_axis == VERTICAL ? 1.0 : 0.0); - break; - } -} - -GLScaler::ScalerStage::ScalerStage(gpu::gles2::GLES2Interface* gl, - GLScaler::Shader shader, - GLScaler::Axis primary_axis, - const gfx::Vector2d& scale_from, - const gfx::Vector2d& scale_to) - : gl_(gl), - shader_(shader), - primary_axis_(primary_axis), - scale_from_(scale_from), - scale_to_(scale_to) { - DCHECK(gl_); -} - -GLScaler::ScalerStage::~ScalerStage() { - if (dest_framebuffer_) { - gl_->DeleteFramebuffers(1, &dest_framebuffer_); - } - if (intermediate_texture_) { - gl_->DeleteTextures(1, &intermediate_texture_); - } -} - -void GLScaler::ScalerStage::ScaleToMultipleOutputs( - GLuint src_texture, - gfx::Size src_texture_size, - const gfx::Vector2d& src_offset, - GLuint dest_texture_0, - GLuint dest_texture_1, - const gfx::Rect& output_rect) { - if (output_rect.IsEmpty()) - return; // No work to do. - - // Make a recursive call to the "input" ScalerStage to produce an intermediate - // texture for this stage to source from. Adjust src_* variables to use the - // intermediate texture as input. - // - // If there is no input stage, simply modify |src_rect| to account for the - // overall |src_offset| and Y-flip. - gfx::RectF src_rect = ToSourceRect(output_rect); - if (input_stage_) { - const gfx::Rect input_rect = ToInputRect(src_rect); - EnsureIntermediateTextureDefined(input_rect.size()); - input_stage_->ScaleToMultipleOutputs(src_texture, src_texture_size, - src_offset, intermediate_texture_, 0, - input_rect); - src_texture = intermediate_texture_; - src_texture_size = intermediate_texture_size_; - DCHECK(!is_flipped_source_); - src_rect -= input_rect.OffsetFromOrigin(); - } else { - if (is_flipped_source_) { - src_rect.set_x(src_rect.x() + src_offset.x()); - src_rect.set_y(src_texture_size.height() - src_rect.bottom() - - src_offset.y()); - } else { - src_rect += src_offset; - } - } - - // Attach the output texture(s) to the framebuffer. - if (!dest_framebuffer_) { - gl_->GenFramebuffers(1, &dest_framebuffer_); - } - gl_->BindFramebuffer(GL_FRAMEBUFFER, dest_framebuffer_); - gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - dest_texture_0, 0); - if (dest_texture_1 > 0) { - gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 1, - GL_TEXTURE_2D, dest_texture_1, 0); - } - - // Bind to the source texture and set the texture sampler to use bilinear - // filtering and clamp-to-edge, as required by all shader programs. - // - // It would be better to stash the existing parameter values, and restore them - // back later. However, glGetTexParameteriv() currently requires a blocking - // call to the GPU service, which is extremely costly performance-wise. - gl_->ActiveTexture(GL_TEXTURE0); - gl_->BindTexture(GL_TEXTURE_2D, src_texture); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - // Prepare the shader program for drawing. - DCHECK(program_); - program_->UseProgram(src_texture_size, src_rect, output_rect.size(), - primary_axis_, flip_output_); - - // Execute the draw. - gl_->Viewport(0, 0, output_rect.width(), output_rect.height()); - const GLenum buffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT0 + 1}; - if (dest_texture_1 > 0) { - gl_->DrawBuffersEXT(2, buffers); - } - gl_->DrawArrays(GL_TRIANGLE_STRIP, 0, 4); - if (dest_texture_1 > 0) { - // Set the draw buffers back, to not disrupt external operations. - gl_->DrawBuffersEXT(1, buffers); - } - - gl_->BindTexture(GL_TEXTURE_2D, 0); - gl_->BindFramebuffer(GL_FRAMEBUFFER, 0); -} - -gfx::RectF GLScaler::ScalerStage::ToSourceRect( - const gfx::Rect& output_rect) const { - return gfx::ScaleRect(gfx::RectF(output_rect), - static_cast<float>(scale_from_.x()) / scale_to_.x(), - static_cast<float>(scale_from_.y()) / scale_to_.y()); -} - -gfx::Rect GLScaler::ScalerStage::ToInputRect(gfx::RectF source_rect) const { - int overscan_x = 0; - int overscan_y = 0; - switch (shader_) { - case Shader::BILINEAR: - case Shader::BILINEAR2: - case Shader::BILINEAR3: - case Shader::BILINEAR4: { - // These shaders sample 1 or more points along the primary axis, and only - // 1 point in the other direction, in order to produce each output pixel. - // The amount of overscan is always 0 or 1 pixel along the primary axis, - // and this can be determined by looking at the upper-left-most source - // texture sampling point: If this point is to the left of the middle of - // the upper-left-most source pixel, the texture sampler will also read - // the pixel to the left of that (for linear interpolation). Similar - // behavior can occur towards the right, upwards, and downwards at the - // source boundaries. - int threshold; - switch (shader_) { - default: - threshold = 1; - break; - case Shader::BILINEAR2: - threshold = 2; - break; - case Shader::BILINEAR3: - threshold = 3; - break; - case Shader::BILINEAR4: - threshold = 4; - break; - } - switch (primary_axis_) { - case HORIZONTAL: - if (scale_from_.x() < threshold * scale_to_.x()) { - overscan_x = 1; - } - if (scale_from_.y() < scale_to_.y()) { - overscan_y = 1; - } - break; - case VERTICAL: - if (scale_from_.x() < scale_to_.x()) { - overscan_x = 1; - } - if (scale_from_.y() < threshold * scale_to_.y()) { - overscan_y = 1; - } - break; - } - break; - } - - case Shader::BILINEAR2X2: - // This shader samples 2 points along both axes, and the overscan is 0 or - // 1 pixel in both directions (same explanation as for the other BILINEAR - // shaders). - if (scale_from_.x() < 2 * scale_to_.x()) { - overscan_x = 1; - } - if (scale_from_.y() < 2 * scale_to_.y()) { - overscan_y = 1; - } - break; - - case Shader::BICUBIC_UPSCALE: - // For each output pixel, this shader always reads 2 pixels about the - // source position in one dimension, and has no overscan in the other - // dimension. - if (scale_from_.x() < scale_to_.x()) { - DCHECK_EQ(HORIZONTAL, primary_axis_); - overscan_x = 2; - } else if (scale_from_.y() < scale_to_.y()) { - DCHECK_EQ(VERTICAL, primary_axis_); - overscan_y = 2; - } else if (scale_from_ == scale_to_) { - // Special case: When not scaling, the math in the shader will resolve - // to just outputting the value for a single source pixel. The shader - // will sample surrounding pixels, but then apply a zero weight to them - // during convolution. Thus, there is effectively no overscan. - NOTREACHED(); // This is a crazy-expensive way to do a 1:1 copy! - } else { - NOTREACHED(); // Downscaling is meaningless. - } - break; - - case Shader::BICUBIC_HALF_1D: { - // For each output pixel, this shader always reads 4 pixels about the - // source position in one dimension, and has no overscan in the other - // dimension. However, since the source position always has a distance - // >= 1 inside the "logical" bounds, there can never be more than 3 pixels - // of overscan. - if (scale_from_.x() == 2 * scale_to_.x()) { - DCHECK_EQ(HORIZONTAL, primary_axis_); - overscan_x = 3; - } else if (scale_from_.y() == 2 * scale_to_.y()) { - DCHECK_EQ(VERTICAL, primary_axis_); - overscan_y = 3; - } else { - // Anything but a half-downscale in one dimension is meaningless. - NOTREACHED(); - } - break; - } - - case Shader::PLANAR_CHANNEL_0: - case Shader::PLANAR_CHANNEL_1: - case Shader::PLANAR_CHANNEL_2: - case Shader::PLANAR_CHANNEL_3: - case Shader::I422_NV61_MRT: - case Shader::PLANAR_CHANNELS_1_2: - // All of these sample 4x1 source pixels to produce each output "pixel". - // There is no overscan. They can also be combined with a bilinear - // downscale, but not an upscale. - DCHECK_GE(scale_from_.x(), 4 * scale_to_.x()); - DCHECK_EQ(HORIZONTAL, primary_axis_); - break; - - case Shader::DEINTERLEAVE_PAIRWISE_MRT: - // This shader samples 2x1 source pixels to produce each output "pixel". - // There is no overscan. It can also be combined with a bilinear - // downscale, but not an upscale. - DCHECK_GE(scale_from_.x(), 2 * scale_to_.x()); - DCHECK_EQ(HORIZONTAL, primary_axis_); - break; - } - - source_rect.Inset(gfx::InsetsF::VH(-overscan_y, -overscan_x)); - return gfx::ToEnclosingRect(source_rect); -} - -void GLScaler::ScalerStage::EnsureIntermediateTextureDefined( - const gfx::Size& size) { - // Reallocate a new texture, if needed. - if (!intermediate_texture_) { - gl_->GenTextures(1, &intermediate_texture_); - } - if (intermediate_texture_size_ != size) { - gl_->BindTexture(GL_TEXTURE_2D, intermediate_texture_); - // Note: Not setting the filter or wrap parameters on the texture here - // because that will be done in ScaleToMultipleOutputs() anyway. - gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, - GL_RGBA, program_->texture_type(), nullptr); - intermediate_texture_size_ = size; - } -} - -std::ostream& operator<<(std::ostream& out, const GLScaler& scaler) { - if (!scaler.chain_) { - return (out << "[GLScaler NOT configured]"); - } - - out << "Output"; - const GLScaler::ScalerStage* const final_stage = scaler.chain_.get(); - for (auto* stage = final_stage; stage; stage = stage->input_stage()) { - out << u8" ← {" << GLScaler::GetShaderName(stage->shader()); - if (stage->shader_program()) { - switch (stage->shader_program()->texture_type()) { - case GL_FLOAT: - out << "/highp"; - break; - case GL_HALF_FLOAT_OES: - out << "/mediump"; - break; - default: - out << "/lowp"; - break; - } - } - if (stage->flip_output()) { - out << "+flip_y"; - } - if (stage->scale_from() == stage->scale_to()) { - out << " copy"; - } else { - out << ' ' << stage->scale_from().ToString() << " to " - << stage->scale_to().ToString(); - } - if (!stage->input_stage() && - scaler.params_.source_color_space != scaler.scaling_color_space_) { - out << ", with color x-form " - << scaler.params_.source_color_space.ToString() << " to " - << scaler.scaling_color_space_.ToString(); - } - if (stage == final_stage) { - if (scaler.params_.output_color_space != scaler.scaling_color_space_) { - out << ", with color x-form to " - << scaler.params_.output_color_space.ToString(); - } - for (int i = 0; i < 2; ++i) { - if (scaler.params_.swizzle[i] != GL_RGBA) { - out << ", with swizzle(" << i << ')'; - } - } - } - out << '}'; - } - out << u8" ← Source"; - return out; -} - -} // namespace viz diff --git a/chromium/components/viz/common/gl_scaler.h b/chromium/components/viz/common/gl_scaler.h deleted file mode 100644 index 99013e8cadf..00000000000 --- a/chromium/components/viz/common/gl_scaler.h +++ /dev/null @@ -1,538 +0,0 @@ -// Copyright 2018 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_GL_SCALER_H_ -#define COMPONENTS_VIZ_COMMON_GL_SCALER_H_ - -#include <stdint.h> - -#include <map> -#include <memory> -#include <ostream> -#include <string> -#include <tuple> -#include <utility> -#include <vector> - -#include "base/memory/raw_ptr.h" -#include "base/memory/scoped_refptr.h" -#include "components/viz/common/gpu/context_lost_observer.h" -#include "components/viz/common/viz_common_export.h" -#include "gpu/command_buffer/client/gles2_interface.h" -#include "third_party/abseil-cpp/absl/types/optional.h" -#include "ui/gfx/color_space.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/geometry/rect_f.h" -#include "ui/gfx/geometry/size.h" -#include "ui/gfx/geometry/vector2d.h" - -namespace gfx { -class ColorTransform; -} // namespace gfx - -namespace viz { - -class ContextProvider; - -// A high-performance texture scaler for use with an OpenGL ES 2.0 context. It -// can be configured to operate at different quality levels, manages/converts -// color spaces, and optionally re-arranges/formats data in output textures for -// use with more-efficient texture readback pipelines. -class VIZ_COMMON_EXPORT GLScaler final : public ContextLostObserver { - public: - struct VIZ_COMMON_EXPORT Parameters { - // Relative scale from/to factors. Both of these must be non-zero. - gfx::Vector2d scale_from = gfx::Vector2d(1, 1); - gfx::Vector2d scale_to = gfx::Vector2d(1, 1); - - // The color space of the source texture and the desired color space of the - // output texture. If |source_color_space| is not set (or invalid), sRGB is - // assumed. If |output_color_space| is not set (or invalid), the source - // color space is assumed. - gfx::ColorSpace source_color_space; - gfx::ColorSpace output_color_space; - - // Enable color management heuristics, using higher precision texture and - // gamma-aware scaling? - // - // When disabled, the gamma of the source color space and other concerns are - // ignored and 8-bit precision is used. - // - // When enabled, scaling occurs in a linear color space with 16-bit floats. - // This produces excellent results for virtually all color spaces while - // typically requiring twice the memory and execution resources. The caller - // must ensure the GL context supports the use of GL_RGBA16F format - // textures. - // - // Relevant reading: http://www.ericbrasseur.org/gamma.html - bool enable_precise_color_management = false; - - // Selects the trade-off between quality and speed. - enum class Quality : int8_t { - // Bilinear single pass. Fastest possible. Do not use this unless the GL - // implementation is so slow that the other quality options won't work. - FAST, - - // Bilinear upscale + N * 50% bilinear downscales. This is still fast - // enough for general-purpose use, and image quality is nearly as good as - // BEST when downscaling. - GOOD, - - // Bicubic upscale + N * 50% bicubic downscales. Produces very good - // quality scaled images, but it's 2-8x slower than the "GOOD" quality. - BEST, - } quality = Quality::GOOD; - - // Is the source texture Y-flipped (i.e., the origin is the lower-left - // corner and not the upper-left corner)? Most GL textures are Y-flipped. - // This information is required so that the scaler can correctly compute the - // sampling region. - bool is_flipped_source = true; - - // Should the output be vertically flipped? Usually, this is used when the - // source is not Y-flipped, but the destination texture needs to be. Or, it - // can be used to draw the final output upside-down to avoid having to copy - // the rows in reverse order after a glReadPixels(). - bool flip_output = false; - - // Optionally rearrange the image data for export. Generally, this is used - // to make later readback steps more efficient (e.g., using glReadPixels() - // will produce the raw bytes in their correct locations). - // - // Output textures are assumed to be using one of the 4-channel RGBA - // formats. While it may be more "proper" to use a single-component texture - // format for the planar-oriented image data, not all GL implementations - // support the use of those formats. However, all must support at least - // GL_RGBA. Therefore, each RGBA pixel is treated as a generic "vec4" (a - // quad of values). - // - // When using this feature, it is usually necessary to adjust the - // |output_rect| passed to Scale() or ScaleToMultipleOutputs(). See notes - // below. - enum class ExportFormat : int8_t { - // Do not rearrange the image data: - // - // (interleaved quads) (interleaved quads) - // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA - // RGBA RGBA RGBA RGBA --> RGBA RGBA RGBA RGBA - // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA - INTERLEAVED_QUADS, - - // Select one color channel, packing each of 4 pixels' values into the 4 - // elements of one output quad. - // - // For example, for CHANNEL_0: - // - // (interleaved quads) (channel 0) - // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA RRRR RRRR - // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA --> RRRR RRRR - // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA RRRR RRRR - // - // Note: Because of this packing, the horizontal coordinates of the - // |output_rect| used with Scale() should be divided by 4. - CHANNEL_0, - CHANNEL_1, - CHANNEL_2, - CHANNEL_3, - - // I422 sampling, delivered via two output textures (NV61 format): The - // first texture is produced the same as CHANNEL_0, while the second - // texture contains CHANNEL_1 and CHANNEL_2 at half-width interleaved and - // full-height. For example, if this is combined with RGB→YUV color space - // conversion: - // - // (interleaved quads) - // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA - // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA - // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA - // | - // | (luma plane) (chroma, interleaved) - // | YYYY YYYY UVUV UVUV - // +---> { YYYY YYYY + UVUV UVUV } - // YYYY YYYY UVUV UVUV - // - // Note: Because of this packing, the horizontal coordinates of the - // |output_rect| used with ScaleToMultipleOutputs() should be divided by - // 4. - // Note 2: This requires a GL context that supports multiple render - // targets with at least two draw buffers. - NV61, - - // Deinterleave into two output textures. - // - // UVUV UVUV UUUU VVVV - // UVUV UVUV --> { UUUU + VVVV } - // UVUV UVUV UUUU VVVV - // - // Note: Because of this packing, the horizontal coordinates of the - // |output_rect| used with ScaleToMultipleOutputs() should be divided by - // 2. - // Note 2: This requires a GL context that supports multiple render - // targets with at least two draw buffers. - DEINTERLEAVE_PAIRWISE, - - // Select CHANNEL_1 and CHANNEL_2, packing each 2-channel pair from - // 4-pixel values into the 2 elements of one output quad. The channels - // will be selected at half-width and full height. This should be - // equivalent to the second texture produced by ExportFormat::NV61 (see - // above). If used on textures after they have gone through RGB→YUV color - // space conversion, the transformation is: - // - // (interleaved quads) (channels 1 & 2) - // YUVx YUVx YUVx YUVx YUVx YUVx YUVx YUVx UVUV UVUV - // YUVx YUVx YUVx YUVx YUVx YUVx YUVx YUVx --> UVUV UVUV - // YUVx YUVx YUVx YUVx YUVx YUVx YUVx YUVx UVUV UVUV - // - // Note: Because of this packing, the horizontal coordinates of the - // |output_rect| used with Scale() should be divided by 4. - UV_CHANNELS, - } export_format = ExportFormat::INTERLEAVED_QUADS; - - // Optionally swizzle the ordering of the values in each output quad. If the - // output of the scaler is not going to be read back (e.g., used with - // glReadPixels()), simply leave these unchanged. Otherwise, changing this - // allows a read-back pipeline to use the native format of the platform to - // avoid having to perform extra "BGRA⇄RGBA swizzle" memcpy's. Usually, this - // should match the format to be used with glReadPixels(), and that should - // match the GL_IMPLEMENTATION_COLOR_READ_FORMAT. - GLenum swizzle[2] = { - GL_RGBA, // For |dest_texture_0|. - GL_RGBA, // For |dest_texture_1|. - }; - - Parameters(); - Parameters(const Parameters& other); - ~Parameters(); - }; - - explicit GLScaler(ContextProvider* context_provider); - - GLScaler(const GLScaler&) = delete; - GLScaler& operator=(const GLScaler&) = delete; - - ~GLScaler() final; - - // Returns true if the GL context provides the necessary support for enabling - // precise color management (see Parameters::enable_precise_color_management). - bool SupportsPreciseColorManagement() const; - - // Returns the maximum number of simultaneous drawing buffers supported by the - // GL context. Certain Parameters can only be used when this is more than 1. - int GetMaxDrawBuffersSupported() const; - - // [Re]Configure the scaler with the given |new_params|. Returns true on - // success, or false on failure. - [[nodiscard]] bool Configure(const Parameters& new_params); - - // Returns the currently-configured and resolved Parameters. Note that these - // Parameters might not be exactly the same as those that were passed to - // Configure() because some properties (e.g., color spaces) are auto-resolved; - // however, ParametersAreEquivalent() will still return true. Results are - // undefined if Configure() has never been called successfully. - const Parameters& params() const { return params_; } - - // Scales a portion of |src_texture| and draws the result into |dest_texture| - // at offset (0, 0). Returns true to indicate success, or false if this - // GLScaler is not valid. - // - // |src_texture_size| is the full, allocated size of the |src_texture|. This - // is required for computing texture coordinate transforms (and only because - // the OpenGL ES 2.0 API lacks the ability to query this info). - // - // |src_offset| is the offset in the source texture corresponding to point - // (0,0) in the source/output coordinate spaces. This prevents the need for - // extra texture copies just to re-position the source coordinate system. - // - // |output_rect| selects the region to draw (in the scaled, not the source, - // coordinate space). This is used to save work in cases where only a portion - // needs to be re-scaled. The implementation will back-compute, internally, to - // determine the region of the |src_texture| to sample. - // - // WARNING: The output will always be placed at (0, 0) in the |dest_texture|, - // and not at |output_rect.origin()|. - // - // Note that the |src_texture| will have the min/mag filter set to GL_LINEAR - // and wrap_s/t set to CLAMP_TO_EDGE in this call. - [[nodiscard]] bool Scale(GLuint src_texture, - const gfx::Size& src_texture_size, - const gfx::Vector2d& src_offset, - GLuint dest_texture, - const gfx::Rect& output_rect) { - return ScaleToMultipleOutputs(src_texture, src_texture_size, src_offset, - dest_texture, 0, output_rect); - } - - // Same as above, but for use cases where there are two output textures drawn - // (see Parameters::ExportFormat). - [[nodiscard]] bool ScaleToMultipleOutputs(GLuint src_texture, - const gfx::Size& src_texture_size, - const gfx::Vector2d& src_offset, - GLuint dest_texture_0, - GLuint dest_texture_1, - const gfx::Rect& output_rect); - - // Returns true if from:to represent the same scale ratio as that specified in - // |params|. - static bool ParametersHasSameScaleRatio(const Parameters& params, - const gfx::Vector2d& from, - const gfx::Vector2d& to); - - // Returns true if configuring a GLScaler with either |a| or |b| will produce - // identical behaviors and results. - static bool ParametersAreEquivalent(const Parameters& a, const Parameters& b); - - private: - friend class GLScalerOverscanPixelTest; - friend class GLScalerShaderPixelTest; - friend VIZ_COMMON_EXPORT std::ostream& operator<<(std::ostream&, - const GLScaler&); - - using GLES2Interface = gpu::gles2::GLES2Interface; - - enum Axis { HORIZONTAL = 0, VERTICAL = 1 }; - - // The shaders used by each stage in the scaling pipeline. - enum class Shader : int8_t { - BILINEAR, - BILINEAR2, - BILINEAR3, - BILINEAR4, - BILINEAR2X2, - BICUBIC_UPSCALE, - BICUBIC_HALF_1D, - PLANAR_CHANNEL_0, - PLANAR_CHANNEL_1, - PLANAR_CHANNEL_2, - PLANAR_CHANNEL_3, - PLANAR_CHANNELS_1_2, - I422_NV61_MRT, - DEINTERLEAVE_PAIRWISE_MRT, - }; - - // A cached, re-usable shader program that performs one step in the scaling - // pipeline. - class VIZ_COMMON_EXPORT ShaderProgram { - public: - ShaderProgram(GLES2Interface* gl, - Shader shader, - GLenum texture_type, - const gfx::ColorTransform* color_transform, - const GLenum swizzle[2]); - - ShaderProgram(const ShaderProgram&) = delete; - ShaderProgram& operator=(const ShaderProgram&) = delete; - - ~ShaderProgram(); - - Shader shader() const { return shader_; } - GLenum texture_type() const { return texture_type_; } - - // UseProgram must be called with GL_ARRAY_BUFFER bound to a vertex - // attribute buffer. |src_texture_size| is the size of the entire source - // texture, regardless of which region is to be sampled. |src_rect| is the - // source region, not including overscan pixels past the edges. - // |primary_axis| determines whether multiple texture samplings occur in one - // direction or the other (for some shaders). Note that this cannot - // necessarily be determined by just comparing the src and dst sizes. - // |flip_y| causes the |src_rect| to be scanned upside-down, to produce a - // vertically-flipped result. - void UseProgram(const gfx::Size& src_texture_size, - const gfx::RectF& src_rect, - const gfx::Size& dst_size, - Axis primary_axis, - bool flip_y); - - // GL_ARRAY_BUFFER data that must be bound when drawing with a - // ShaderProgram. These are the vertex attributes that will sweep the entire - // source area when executing the program. They represent triangle strip - // coordinates: The first two columns are (x,y) values interpolated to - // produce the vertex coordinates in object space, while the latter two - // columns are (s,t) values interpolated to produce the texture coordinates - // that correspond to the vertex coordinates. - static const GLfloat kVertexAttributes[16]; - - private: - const raw_ptr<GLES2Interface> gl_; - const Shader shader_; - const GLenum texture_type_; - - // A program for copying a source texture into a destination texture. - const GLuint program_; - - // The location of the position in the program. - GLint position_location_ = -1; - // The location of the texture coordinate in the program. - GLint texcoord_location_ = -1; - // The location of the source texture in the program. - GLint texture_location_ = -1; - // The location of the texture coordinate of the source rectangle in the - // program. - GLint src_rect_location_ = -1; - // Location of size of source image in pixels. - GLint src_pixelsize_location_ = -1; - // Location of vector for scaling ratio between source and dest textures. - GLint scaling_vector_location_ = -1; - }; - - // One scaling stage in a chain of scaler pipeline stages. Each ScalerStage - // owns the previous ScalerStage in the chain: At execution time, a "working - // backwards" approach is used: The previous "input" stage renders an - // intermediate result that will be used as input for the current stage. - // - // Each ScalerStage caches textures and framebuffers to avoid reallocating - // them for each separate image scaling, which can be expensive on some - // platforms/drivers. - class VIZ_COMMON_EXPORT ScalerStage { - public: - ScalerStage(GLES2Interface* gl, - Shader shader, - Axis primary_axis, - const gfx::Vector2d& scale_from, - const gfx::Vector2d& scale_to); - - ScalerStage(const ScalerStage&) = delete; - ScalerStage& operator=(const ScalerStage&) = delete; - - ~ScalerStage(); - - Shader shader() const { return shader_; } - const gfx::Vector2d& scale_from() const { return scale_from_; } - const gfx::Vector2d& scale_to() const { return scale_to_; } - - ScalerStage* input_stage() const { return input_stage_.get(); } - void set_input_stage(std::unique_ptr<ScalerStage> stage) { - input_stage_ = std::move(stage); - } - std::unique_ptr<ScalerStage> take_input_stage() { - return std::move(input_stage_); - } - - ShaderProgram* shader_program() const { return program_; } - void set_shader_program(ShaderProgram* program) { program_ = program; } - - bool is_flipped_source() const { return is_flipped_source_; } - void set_is_flipped_source(bool flipped) { is_flipped_source_ = flipped; } - - bool flip_output() const { return flip_output_; } - void set_flip_output(bool flip) { flip_output_ = flip; } - - void ScaleToMultipleOutputs(GLuint src_texture, - gfx::Size src_texture_size, - const gfx::Vector2d& src_offset, - GLuint dest_texture_0, - GLuint dest_texture_1, - const gfx::Rect& output_rect); - - private: - friend class GLScalerOverscanPixelTest; - - // Returns the given |output_rect| mapped to the input stage's coordinate - // system. - gfx::RectF ToSourceRect(const gfx::Rect& output_rect) const; - - // Returns the given |source_rect| padded to include the overscan pixels the - // shader program will access. - gfx::Rect ToInputRect(gfx::RectF source_rect) const; - - // Generates the intermediate texture and/or re-defines it if its size has - // changed. - void EnsureIntermediateTextureDefined(const gfx::Size& size); - - const raw_ptr<GLES2Interface> gl_; - const Shader shader_; - const Axis primary_axis_; - const gfx::Vector2d scale_from_; - const gfx::Vector2d scale_to_; - - std::unique_ptr<ScalerStage> input_stage_; - raw_ptr<ShaderProgram> program_ = nullptr; - bool is_flipped_source_ = false; - bool flip_output_ = false; - - GLuint intermediate_texture_ = 0; - gfx::Size intermediate_texture_size_; - GLuint dest_framebuffer_ = 0; - }; - - // ContextLostObserver implementation. - void OnContextLost() final; - - // Returns a cached ShaderProgram, creating one on-demand if necessary. - ShaderProgram* GetShaderProgram(Shader shader, - GLenum texture_type, - const gfx::ColorTransform* color_transform, - const GLenum swizzle[2]); - - // Create a scaling chain using the bilinear shaders. - static std::unique_ptr<ScalerStage> CreateAGoodScalingChain( - gpu::gles2::GLES2Interface* gl, - const gfx::Vector2d& scale_from, - const gfx::Vector2d& scale_to); - - // Create a scaling chain using the bicubic shaders. - static std::unique_ptr<ScalerStage> CreateTheBestScalingChain( - gpu::gles2::GLES2Interface* gl, - const gfx::Vector2d& scale_from, - const gfx::Vector2d& scale_to); - - // Modifies |chain| by appending an export stage, to rearrange the image data - // according to the requested |export_format|. In some cases, this will delete - // the final stage in |chain| before appending the export stage. - static std::unique_ptr<ScalerStage> MaybeAppendExportStage( - gpu::gles2::GLES2Interface* gl, - std::unique_ptr<ScalerStage> chain, - Parameters::ExportFormat export_format); - - // Returns the other of the two axes. - static Axis TheOtherAxis(Axis axis); - - // Returns the name of the |shader| in string form, for logging purposes. - static const char* GetShaderName(Shader shader); - - // Returns true if the given |gl| context mentions all of |names| in its - // extensions string. - static bool AreAllGLExtensionsPresent(gpu::gles2::GLES2Interface* gl, - const std::vector<std::string>& names); - - // The provider of the GL context. This is non-null while the GL context is - // valid and GLScaler is observing for context loss. - raw_ptr<ContextProvider> context_provider_; - - // Set by Configure() to the resolved set of Parameters. - Parameters params_; - - // If set to true, half-float textures are supported. This is lazy-initialized - // by SupportsPreciseColorManagement(). - mutable absl::optional<bool> supports_half_floats_; - - // The maximum number of simultaneous draw buffers, lazy-initialized by - // GetMaxDrawBuffersSupported(). -1 means "not yet known." - mutable int max_draw_buffers_ = -1; - - // Cache of ShaderPrograms. The cache key consists of fields that correspond - // to the arguments of GetShaderProgram(): the shader, the texture format, the - // source and output color spaces (color transform), and the two swizzles. - using ShaderCacheKey = std:: - tuple<Shader, GLenum, gfx::ColorSpace, gfx::ColorSpace, GLenum, GLenum>; - std::map<ShaderCacheKey, ShaderProgram> shader_programs_; - - // The GL_ARRAY_BUFFER that holds the vertices and the texture coordinates - // data for sweeping the source area when a ScalerStage draws a quad (to - // execute its shader program). - GLuint vertex_attributes_buffer_ = 0; - - // The chain of ScalerStages. - std::unique_ptr<ScalerStage> chain_; - - // The color space in which the scaling stages operate. - gfx::ColorSpace scaling_color_space_; -}; - -// For logging. -VIZ_COMMON_EXPORT std::ostream& operator<<(std::ostream& out, - const GLScaler& scaler); - -} // namespace viz - -#endif // COMPONENTS_VIZ_COMMON_GL_SCALER_H_ diff --git a/chromium/components/viz/common/gl_scaler_overscan_pixeltest.cc b/chromium/components/viz/common/gl_scaler_overscan_pixeltest.cc deleted file mode 100644 index 30f4810b0e5..00000000000 --- a/chromium/components/viz/common/gl_scaler_overscan_pixeltest.cc +++ /dev/null @@ -1,532 +0,0 @@ -// Copyright 2018 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/gl_scaler.h" - -#include "build/build_config.h" -#include "cc/test/pixel_test.h" -#include "cc/test/pixel_test_utils.h" -#include "components/viz/common/gpu/context_provider.h" -#include "components/viz/test/gl_scaler_test_util.h" -#include "gpu/GLES2/gl2chromium.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "gpu/command_buffer/client/gles2_implementation.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkRect.h" - -namespace viz { - -class GLScalerOverscanPixelTest : public cc::PixelTest, - public GLScalerTestUtil { - public: - using Axis = GLScaler::Axis; - using ScalerStage = GLScaler::ScalerStage; - using Shader = GLScaler::Shader; - - bool AreMultipleRenderingTargetsSupported() const { - return scaler_->GetMaxDrawBuffersSupported() > 1; - } - - // Creates a ScalerStage chain consisting of a single stage having the given - // configuration. - void UseScaler(Shader shader, - Axis primary_axis, - const gfx::Vector2d& scale_from, - const gfx::Vector2d& scale_to) { - scaler_->chain_ = std::make_unique<ScalerStage>(gl_, shader, primary_axis, - scale_from, scale_to); - scaler_->chain_->set_shader_program(scaler_->GetShaderProgram( - shader, GL_UNSIGNED_BYTE, nullptr, GLScaler::Parameters().swizzle)); - } - - // Converts the given |source_rect| into a possibly-larger one that includes - // all of the pixels that would be sampled by the current scaler (i.e., - // including overscan). This uses the math of the internal implementation to - // compute the values. - gfx::Rect ToInputRect(gfx::Rect source_rect) { - CHECK(scaler_ && scaler_->chain_); - return scaler_->chain_->ToInputRect(gfx::RectF(source_rect)); - } - - // Renders images using the current scaler to auto-detect its overscan. This - // does NOT use the internal implementation to compute the values, but instead - // discovers them experimentally. This is used to confirm that: a) the scaler - // behaves as ToInputRect() expects; and b) the math internal to ToInputRect() - // is correct. - // - // The general approach is to upload a source image containing a blue box in - // the center, surrounded by a red background. The size of the blue box is - // varied: It starts out at a size encompassing more than all of the pixels to - // be sampled by the scaler, and is gradually shrunk until the scaler's output - // begins to include red "bleed-in." At that point, the overscan amount is - // confirmed experimentally. - gfx::Vector2d DetectScalerOverscan(const gfx::Vector2d& scale_from, - const gfx::Vector2d& scale_to) { - // Assume a source size three times the "scale from" width and height. This - // allows for scaling the middle third of a source image, to test possible - // bleed-in on all sides of the output. - const gfx::Size src_size(scale_from.x() * 3, scale_from.y() * 3); - - // The requested output rect is the center third of the image, in the - // destination coordinate space. - const gfx::Rect dst_rect( - src_size.width() / 3 * scale_to.x() / scale_from.x(), - src_size.height() / 3 * scale_to.y() / scale_from.y(), - src_size.width() / 3 * scale_to.x() / scale_from.x(), - src_size.height() / 3 * scale_to.y() / scale_from.y()); - const GLuint dst_texture = texture_helper_->CreateTexture(dst_rect.size()); - - // This is our "basis for comparison" image. If scaled output images match - // this, then there is no bleed-in. - SkBitmap output_without_bleed_in; - { - const bool did_scale = - scaler_->Scale(texture_helper_->UploadTexture(CreateBlueBoxOnRedImage( - src_size, gfx::Rect(src_size))), - src_size, gfx::Vector2d(0, 0), dst_texture, dst_rect); - CHECK(did_scale); - output_without_bleed_in = - texture_helper_->DownloadTexture(dst_texture, dst_rect.size()); - VLOG(2) << scale_from.ToString() << "→" << scale_to.ToString() - << ": Output without bleed-in is " - << cc::GetPNGDataUrl(output_without_bleed_in); - } - - // Perform a linear search for the minimal overscan values that do not cause - // the red bleed-in in the scaled output image. There are actually two - // separate searches here, one horizontally and one vertically. Note that an - // overscan result of -1 indicates a failed search and/or a broken - // implementation. - gfx::Vector2d min_overscan(5, 5); - for (int is_horizontal = 1; is_horizontal >= 0; --is_horizontal) { - while (min_overscan.x() >= 0 && min_overscan.y() >= 0) { - // Decrease the overscan by one pixel (one dimension at a time). - const gfx::Vector2d overscan( - is_horizontal ? (min_overscan.x() - 1) : min_overscan.x(), - is_horizontal ? min_overscan.y() : (min_overscan.y() - 1)); - - // Create the source texture consisting of a centered blue box - // surrounded by red. - const gfx::Rect blue_rect(scale_from.x() - overscan.x(), - scale_from.y() - overscan.y(), - scale_from.x() + 2 * overscan.x(), - scale_from.y() + 2 * overscan.y()); - const SkBitmap source_image = - CreateBlueBoxOnRedImage(src_size, blue_rect); - const bool did_scale = scaler_->Scale( - texture_helper_->UploadTexture(source_image), src_size, - gfx::Vector2d(0, 0), dst_texture, dst_rect); - CHECK(did_scale); - const SkBitmap output = - texture_helper_->DownloadTexture(dst_texture, dst_rect.size()); - - // Compare |output| with |output_without_bleed_in|. If they are - // different, then the blue rect became too small. - bool output_has_bleed_in = false; - for (int y = 0; y < output.height(); ++y) { - for (int x = 0; x < output.width(); ++x) { - if (output.getColor(x, y) != - output_without_bleed_in.getColor(x, y)) { - output_has_bleed_in = true; - break; - } - } - } - - VLOG(2) << scale_from.ToString() << "→" << scale_to.ToString() - << ": Testing overscan=" << overscan.ToString() << std::endl - << "\tSource image is " << cc::GetPNGDataUrl(source_image) - << std::endl - << "\tOutput image is " << cc::GetPNGDataUrl(output); - - if (output_has_bleed_in) { - break; // Search complete: Red bleed-in detected. - } - - min_overscan = overscan; - } - } - - return min_overscan; - } - - static SkBitmap CreateBlueBoxOnRedImage(const gfx::Size& size, - const gfx::Rect& blue_rect) { - SkBitmap result = AllocateRGBABitmap(size); - // Note: None of the color channel values should be close to 0 or 255. This - // is because the bicubic scaler will generate values that overshoot and - // clip, and this will mess-up detection of the number of overscan pixels. - result.eraseColor(SkColorSetRGB(0xc0, 0x40, 0x40)); - result.erase(SkColorSetRGB(0x40, 0x40, 0xc0), - SkIRect{blue_rect.x(), blue_rect.y(), blue_rect.right(), - blue_rect.bottom()}); - return result; - } - - protected: - void SetUp() final { - cc::PixelTest::SetUpGLWithoutRenderer(gfx::SurfaceOrigin::kBottomLeft); - - scaler_ = std::make_unique<GLScaler>(context_provider()); - gl_ = context_provider()->ContextGL(); - CHECK(gl_); - texture_helper_ = std::make_unique<GLScalerTestTextureHelper>(gl_); - } - - void TearDown() final { - texture_helper_.reset(); - gl_ = nullptr; - scaler_.reset(); - - cc::PixelTest::TearDown(); - } - - std::unique_ptr<GLScaler> scaler_; - gpu::gles2::GLES2Interface* gl_ = nullptr; - std::unique_ptr<GLScalerTestTextureHelper> texture_helper_; -}; - -namespace { -constexpr gfx::Rect kTenByTenRect = gfx::Rect(10, 10, 10, 10); -} // namespace - -TEST_F(GLScalerOverscanPixelTest, Bilinear) { - constexpr struct { - gfx::Vector2d scale_from; - gfx::Vector2d scale_to; - gfx::Vector2d expected_overscan; - } kTestCases[] = { - // No scaling. - {gfx::Vector2d(32, 20), gfx::Vector2d(32, 20), gfx::Vector2d(0, 0)}, - - // Scale by 0.5X. - {gfx::Vector2d(32, 20), gfx::Vector2d(16, 20), gfx::Vector2d(0, 0)}, - {gfx::Vector2d(32, 20), gfx::Vector2d(32, 10), gfx::Vector2d(0, 0)}, - {gfx::Vector2d(32, 20), gfx::Vector2d(16, 10), gfx::Vector2d(0, 0)}, - - // Scale by 0.75X. - {gfx::Vector2d(32, 20), gfx::Vector2d(24, 20), gfx::Vector2d(0, 0)}, - {gfx::Vector2d(32, 20), gfx::Vector2d(32, 15), gfx::Vector2d(0, 0)}, - {gfx::Vector2d(32, 20), gfx::Vector2d(24, 15), gfx::Vector2d(0, 0)}, - - // Scale by 1.5X. - {gfx::Vector2d(32, 20), gfx::Vector2d(48, 20), gfx::Vector2d(1, 0)}, - {gfx::Vector2d(32, 20), gfx::Vector2d(32, 30), gfx::Vector2d(0, 1)}, - {gfx::Vector2d(32, 20), gfx::Vector2d(48, 30), gfx::Vector2d(1, 1)}, - - // Scale by 4X. - {gfx::Vector2d(32, 20), gfx::Vector2d(128, 20), gfx::Vector2d(1, 0)}, - {gfx::Vector2d(32, 20), gfx::Vector2d(32, 80), gfx::Vector2d(0, 1)}, - {gfx::Vector2d(32, 20), gfx::Vector2d(128, 80), gfx::Vector2d(1, 1)}, - }; - - for (const auto& tc : kTestCases) { - SCOPED_TRACE(testing::Message() << "scale_from=" << tc.scale_from.ToString() - << ", scale_to=" << tc.scale_to.ToString()); - - // Test the effect on the pixels. - UseScaler(Shader::BILINEAR, Axis::HORIZONTAL, tc.scale_from, tc.scale_to); - EXPECT_EQ(tc.expected_overscan, - DetectScalerOverscan(tc.scale_from, tc.scale_to)); - - // Sanity-check that the internal math estimating the overscan is correct. - gfx::Rect expected_input_rect = kTenByTenRect; - expected_input_rect.Inset( - gfx::Insets::VH(-tc.expected_overscan.y(), -tc.expected_overscan.x())); - EXPECT_EQ(expected_input_rect, ToInputRect(kTenByTenRect)); - } -} - -TEST_F(GLScalerOverscanPixelTest, TwoTapBilinear) { - constexpr struct { - Axis primary_axis; - gfx::Vector2d scale_from; - gfx::Vector2d scale_to; - gfx::Vector2d expected_overscan; - } kTestCases[] = { - // Scale by 0.25X in one direction only. - {Axis::HORIZONTAL, gfx::Vector2d(64, 40), gfx::Vector2d(16, 40), - gfx::Vector2d(0, 0)}, - {Axis::VERTICAL, gfx::Vector2d(64, 40), gfx::Vector2d(64, 10), - gfx::Vector2d(0, 0)}, - - // Scale by 0.25X in one direction, 0.5X in the other. - {Axis::HORIZONTAL, gfx::Vector2d(64, 40), gfx::Vector2d(16, 20), - gfx::Vector2d(0, 0)}, - {Axis::VERTICAL, gfx::Vector2d(64, 40), gfx::Vector2d(32, 10), - gfx::Vector2d(0, 0)}, - - // Scale by 0.75X (1.5X * 0.5X). - {Axis::HORIZONTAL, gfx::Vector2d(64, 40), gfx::Vector2d(48, 40), - gfx::Vector2d(1, 0)}, - {Axis::VERTICAL, gfx::Vector2d(64, 40), gfx::Vector2d(64, 30), - gfx::Vector2d(0, 1)}, - }; - - for (const auto& tc : kTestCases) { - SCOPED_TRACE(testing::Message() << "scale_from=" << tc.scale_from.ToString() - << ", scale_to=" << tc.scale_to.ToString()); - - // Test the effect on the pixels. - UseScaler(Shader::BILINEAR2, tc.primary_axis, tc.scale_from, tc.scale_to); - EXPECT_EQ(tc.expected_overscan, - DetectScalerOverscan(tc.scale_from, tc.scale_to)); - - // Sanity-check that the internal math estimating the overscan is correct. - gfx::Rect expected_input_rect = kTenByTenRect; - expected_input_rect.Inset( - gfx::Insets::VH(-tc.expected_overscan.y(), -tc.expected_overscan.x())); - EXPECT_EQ(expected_input_rect, ToInputRect(kTenByTenRect)); - } -} - -TEST_F(GLScalerOverscanPixelTest, ThreeTapBilinear) { - constexpr struct { - Axis primary_axis; - gfx::Vector2d scale_from; - gfx::Vector2d scale_to; - gfx::Vector2d expected_overscan; - } kTestCases[] = { - // Scale by 0.16...X in one direction only. - {Axis::HORIZONTAL, gfx::Vector2d(66, 40), gfx::Vector2d(11, 40), - gfx::Vector2d(0, 0)}, - {Axis::VERTICAL, gfx::Vector2d(32, 60), gfx::Vector2d(32, 10), - gfx::Vector2d(0, 0)}, - - // Scale by 0.16...X in one direction, 0.5X in the other. - {Axis::HORIZONTAL, gfx::Vector2d(66, 40), gfx::Vector2d(11, 20), - gfx::Vector2d(0, 0)}, - {Axis::VERTICAL, gfx::Vector2d(64, 60), gfx::Vector2d(32, 10), - gfx::Vector2d(0, 0)}, - - // Scale by 0.75X (3.0X * 0.5X * 0.5X). - {Axis::HORIZONTAL, gfx::Vector2d(64, 40), gfx::Vector2d(48, 40), - gfx::Vector2d(1, 0)}, - {Axis::VERTICAL, gfx::Vector2d(64, 40), gfx::Vector2d(64, 30), - gfx::Vector2d(0, 1)}, - }; - - for (const auto& tc : kTestCases) { - SCOPED_TRACE(testing::Message() << "scale_from=" << tc.scale_from.ToString() - << ", scale_to=" << tc.scale_to.ToString()); - - // Test the effect on the pixels. - UseScaler(Shader::BILINEAR3, tc.primary_axis, tc.scale_from, tc.scale_to); - EXPECT_EQ(tc.expected_overscan, - DetectScalerOverscan(tc.scale_from, tc.scale_to)); - - // Sanity-check that the internal math estimating the overscan is correct. - gfx::Rect expected_input_rect = kTenByTenRect; - expected_input_rect.Inset( - gfx::Insets::VH(-tc.expected_overscan.y(), -tc.expected_overscan.x())); - EXPECT_EQ(expected_input_rect, ToInputRect(kTenByTenRect)); - } -} - -TEST_F(GLScalerOverscanPixelTest, FourTapBilinear) { - constexpr struct { - Axis primary_axis; - gfx::Vector2d scale_from; - gfx::Vector2d scale_to; - gfx::Vector2d expected_overscan; - } kTestCases[] = { - // Scale by 0.125X in one direction only. - {Axis::HORIZONTAL, gfx::Vector2d(64, 40), gfx::Vector2d(8, 40), - gfx::Vector2d(0, 0)}, - {Axis::VERTICAL, gfx::Vector2d(64, 40), gfx::Vector2d(64, 5), - gfx::Vector2d(0, 0)}, - - // Scale by 0.125X in one direction, 0.5X in the other. - {Axis::HORIZONTAL, gfx::Vector2d(64, 40), gfx::Vector2d(8, 20), - gfx::Vector2d(0, 0)}, - {Axis::VERTICAL, gfx::Vector2d(64, 40), gfx::Vector2d(32, 5), - gfx::Vector2d(0, 0)}, - - // Scale by 0.75X (6.0X * 0.5X * 0.5X * 0.5X). - {Axis::HORIZONTAL, gfx::Vector2d(64, 40), gfx::Vector2d(48, 40), - gfx::Vector2d(1, 0)}, - {Axis::VERTICAL, gfx::Vector2d(64, 40), gfx::Vector2d(64, 30), - gfx::Vector2d(0, 1)}, - }; - - for (const auto& tc : kTestCases) { - SCOPED_TRACE(testing::Message() << "scale_from=" << tc.scale_from.ToString() - << ", scale_to=" << tc.scale_to.ToString()); - - // Test the effect on the pixels. - UseScaler(Shader::BILINEAR4, tc.primary_axis, tc.scale_from, tc.scale_to); - EXPECT_EQ(tc.expected_overscan, - DetectScalerOverscan(tc.scale_from, tc.scale_to)); - - // Sanity-check that the internal math estimating the overscan is correct. - gfx::Rect expected_input_rect = kTenByTenRect; - expected_input_rect.Inset( - gfx::Insets::VH(-tc.expected_overscan.y(), -tc.expected_overscan.x())); - EXPECT_EQ(expected_input_rect, ToInputRect(kTenByTenRect)); - } -} - -TEST_F(GLScalerOverscanPixelTest, TwoByTwoTapBilinear) { - constexpr struct { - gfx::Vector2d scale_from; - gfx::Vector2d scale_to; - gfx::Vector2d expected_overscan; - } kTestCases[] = { - // Scale by 0.25X in both directions. - {gfx::Vector2d(64, 40), gfx::Vector2d(16, 10), gfx::Vector2d(0, 0)}, - - // Scale by 0.75X (1.5X * 0.5X) in one direction, 0.25X in the other. - {gfx::Vector2d(64, 40), gfx::Vector2d(48, 10), gfx::Vector2d(1, 0)}, - {gfx::Vector2d(64, 40), gfx::Vector2d(16, 30), gfx::Vector2d(0, 1)}, - - // Scale by 0.75X (1.5X * 0.5X) in both directions. - {gfx::Vector2d(64, 40), gfx::Vector2d(48, 30), gfx::Vector2d(1, 1)}, - }; - - for (const auto& tc : kTestCases) { - SCOPED_TRACE(testing::Message() << "scale_from=" << tc.scale_from.ToString() - << ", scale_to=" << tc.scale_to.ToString()); - - // Test the effect on the pixels. - UseScaler(Shader::BILINEAR2X2, Axis::HORIZONTAL, tc.scale_from, - tc.scale_to); - EXPECT_EQ(tc.expected_overscan, - DetectScalerOverscan(tc.scale_from, tc.scale_to)); - - // Sanity-check that the internal math estimating the overscan is correct. - gfx::Rect expected_input_rect = kTenByTenRect; - expected_input_rect.Inset( - gfx::Insets::VH(-tc.expected_overscan.y(), -tc.expected_overscan.x())); - EXPECT_EQ(expected_input_rect, ToInputRect(kTenByTenRect)); - } -} - -TEST_F(GLScalerOverscanPixelTest, BicubicUpscale) { -#if BUILDFLAG(IS_ANDROID) - // Unfortunately, on our current Android bots, there are some inaccuracies - // introduced by the platform that seem to throw-off the pixel testing of the - // bicubic sampler. - constexpr bool kSkipDetectionTest = true; - LOG(WARNING) << "Skipping overscan detection due to platform issues."; -#else - constexpr bool kSkipDetectionTest = false; -#endif - - constexpr struct { - Axis primary_axis; - gfx::Vector2d scale_from; - gfx::Vector2d scale_to; - gfx::Vector2d expected_overscan; - } kTestCases[] = { - // Scale by 1.5X, 2X, and 3.3...X horizontally. - {Axis::HORIZONTAL, gfx::Vector2d(12, 10), gfx::Vector2d(18, 10), - gfx::Vector2d(2, 0)}, - {Axis::HORIZONTAL, gfx::Vector2d(12, 10), gfx::Vector2d(24, 10), - gfx::Vector2d(2, 0)}, - {Axis::HORIZONTAL, gfx::Vector2d(12, 10), gfx::Vector2d(40, 10), - gfx::Vector2d(2, 0)}, - - // Scale by 1.5X, 2X, and 3.3...X vertically. - {Axis::VERTICAL, gfx::Vector2d(12, 10), gfx::Vector2d(12, 15), - gfx::Vector2d(0, 2)}, - {Axis::VERTICAL, gfx::Vector2d(12, 10), gfx::Vector2d(12, 20), - gfx::Vector2d(0, 2)}, - {Axis::VERTICAL, gfx::Vector2d(12, 9), gfx::Vector2d(12, 30), - gfx::Vector2d(0, 2)}, - }; - - for (const auto& tc : kTestCases) { - SCOPED_TRACE(testing::Message() << "scale_from=" << tc.scale_from.ToString() - << ", scale_to=" << tc.scale_to.ToString()); - - // Test the effect on the pixels. - UseScaler(Shader::BICUBIC_UPSCALE, tc.primary_axis, tc.scale_from, - tc.scale_to); - if (!kSkipDetectionTest) { - EXPECT_EQ(tc.expected_overscan, - DetectScalerOverscan(tc.scale_from, tc.scale_to)); - } - - // Sanity-check that the internal math estimating the overscan is correct. - gfx::Rect expected_input_rect = kTenByTenRect; - expected_input_rect.Inset( - gfx::Insets::VH(-tc.expected_overscan.y(), -tc.expected_overscan.x())); - EXPECT_EQ(expected_input_rect, ToInputRect(kTenByTenRect)); - } -} - -TEST_F(GLScalerOverscanPixelTest, BicubicHalving) { - constexpr struct { - Axis primary_axis; - gfx::Vector2d scale_from; - gfx::Vector2d scale_to; - gfx::Vector2d expected_overscan; - } kTestCases[] = { - {Axis::HORIZONTAL, gfx::Vector2d(16, 16), gfx::Vector2d(8, 16), - gfx::Vector2d(3, 0)}, - {Axis::VERTICAL, gfx::Vector2d(16, 16), gfx::Vector2d(16, 8), - gfx::Vector2d(0, 3)}, - }; - - for (const auto& tc : kTestCases) { - SCOPED_TRACE(testing::Message() << "scale_from=" << tc.scale_from.ToString() - << ", scale_to=" << tc.scale_to.ToString()); - - // Test the effect on the pixels. - UseScaler(Shader::BICUBIC_HALF_1D, tc.primary_axis, tc.scale_from, - tc.scale_to); - EXPECT_EQ(tc.expected_overscan, - DetectScalerOverscan(tc.scale_from, tc.scale_to)); - - // Sanity-check that the internal math estimating the overscan is correct. - gfx::Rect expected_input_rect = kTenByTenRect; - expected_input_rect.Inset( - gfx::Insets::VH(-tc.expected_overscan.y(), -tc.expected_overscan.x())); - EXPECT_EQ(expected_input_rect, ToInputRect(kTenByTenRect)); - } -} - -TEST_F(GLScalerOverscanPixelTest, Planerizers) { - if (!AreMultipleRenderingTargetsSupported()) { - LOG(WARNING) << "Skipping test due to lack of MRT support on this machine."; - return; - } - - constexpr struct { - Shader shader; - Axis primary_axis; - gfx::Vector2d scale_from; - gfx::Vector2d scale_to; - } kTestCases[] = { - {Shader::PLANAR_CHANNEL_0, Axis::HORIZONTAL, gfx::Vector2d(16, 16), - gfx::Vector2d(4, 16)}, - // Note: Other PLANAR_CHANNEL_N shaders don't need to be tested since they - // use the same code path. - {Shader::I422_NV61_MRT, Axis::HORIZONTAL, gfx::Vector2d(16, 16), - gfx::Vector2d(4, 16)}, - { - Shader::DEINTERLEAVE_PAIRWISE_MRT, Axis::HORIZONTAL, - gfx::Vector2d(16, 16), gfx::Vector2d(8, 16), - }, - }; - - for (const auto& tc : kTestCases) { - SCOPED_TRACE(testing::Message() - << "shader=" << static_cast<int>(tc.shader) - << ", scale_from=" << tc.scale_from.ToString() - << ", scale_to=" << tc.scale_to.ToString()); - - // Test the effect on the pixels. - UseScaler(tc.shader, tc.primary_axis, tc.scale_from, tc.scale_to); - EXPECT_EQ(gfx::Vector2d(0, 0), - DetectScalerOverscan(tc.scale_from, tc.scale_to)); - - // Sanity-check that the internal math estimating the overscan is correct. - EXPECT_EQ(kTenByTenRect, ToInputRect(kTenByTenRect)); - } -} - -} // namespace viz diff --git a/chromium/components/viz/common/gl_scaler_pixeltest.cc b/chromium/components/viz/common/gl_scaler_pixeltest.cc deleted file mode 100644 index 9c1df00fc82..00000000000 --- a/chromium/components/viz/common/gl_scaler_pixeltest.cc +++ /dev/null @@ -1,628 +0,0 @@ -// Copyright 2018 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/gl_scaler.h" - -#include <sstream> - -#include "base/files/file_path.h" -#include "base/path_service.h" -#include "base/strings/pattern.h" -#include "build/build_config.h" -#include "cc/test/pixel_test.h" -#include "cc/test/pixel_test_utils.h" -#include "components/viz/common/gpu/context_provider.h" -#include "components/viz/test/gl_scaler_test_util.h" -#include "components/viz/test/paths.h" -#include "gpu/GLES2/gl2chromium.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "gpu/command_buffer/client/gles2_implementation.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkColor.h" -#include "ui/gfx/color_space.h" - -#if BUILDFLAG(IS_ANDROID) -#include "base/android/build_info.h" -#endif - -namespace { - -// Loads a PNG test image from the test directory, and converts it to GL_RGBA -// byte order. -SkBitmap LoadPNGTestImage(const std::string& basename) { - base::FilePath test_dir; - if (!base::PathService::Get(viz::Paths::DIR_TEST_DATA, &test_dir)) { - LOG(FATAL) << "Unable to get Paths::DIR_TEST_DATA from base::PathService."; - return SkBitmap(); - } - const auto source_file = test_dir.AppendASCII(basename); - SkBitmap as_n32; - if (!cc::ReadPNGFile(source_file, &as_n32)) { - return SkBitmap(); - } - SkBitmap as_rgba = viz::GLScalerTestUtil::AllocateRGBABitmap( - gfx::Size(as_n32.width(), as_n32.height())); - if (!as_n32.readPixels(SkPixmap(as_rgba.info(), as_rgba.getAddr(0, 0), - as_rgba.rowBytes()))) { - return SkBitmap(); - } - return as_rgba; -} - -} // namespace - -namespace viz { - -#define EXPECT_STRING_MATCHES(expected, actual) \ - if (!base::MatchPattern(actual, expected)) { \ - ADD_FAILURE() << "\nActual: " << (actual) \ - << "\nExpected to match pattern: " << (expected); \ - } - -class GLScalerPixelTest : public cc::PixelTest, public GLScalerTestUtil { - public: - GLScaler* scaler() const { return scaler_.get(); } - - std::string GetScalerString() const { - std::ostringstream oss; - oss << *scaler_; - return oss.str(); - } - - GLuint CreateTexture(const gfx::Size& size) { - return texture_helper_->CreateTexture(size); - } - - GLuint UploadTexture(const SkBitmap& bitmap) { - return texture_helper_->UploadTexture(bitmap); - } - - SkBitmap DownloadTexture(GLuint texture, const gfx::Size& size) { - return texture_helper_->DownloadTexture(texture, size); - } - - // Test convenience to upload |src_bitmap| to the GPU, execute the scaling, - // then download the result from the GPU and return it as a SkBitmap. - SkBitmap Scale(const SkBitmap& src_bitmap, - const gfx::Vector2d& src_offset, - const gfx::Rect& output_rect) { - const GLuint src_texture = UploadTexture(src_bitmap); - const GLuint dest_texture = CreateTexture(output_rect.size()); - if (!scaler()->Scale(src_texture, - gfx::Size(src_bitmap.width(), src_bitmap.height()), - src_offset, dest_texture, output_rect)) { - return SkBitmap(); - } - return DownloadTexture(dest_texture, output_rect.size()); - } - - // Returns the amount of color error expected due to bugs in the current - // platform's bilinear texture sampler. - int GetBaselineColorDifference() const { -#if BUILDFLAG(IS_ANDROID) - // Android seems to have texture sampling problems that are not at all seen - // on any of the desktop platforms. Also, versions before Marshmallow seem - // to have a much larger accuracy issues. - if (base::android::BuildInfo::GetInstance()->sdk_int() < - base::android::SDK_VERSION_MARSHMALLOW) { - return 12; - } - return 2; -#else - return 0; -#endif - } - - protected: - void SetUp() final { - cc::PixelTest::SetUpGLWithoutRenderer(gfx::SurfaceOrigin::kBottomLeft); - - scaler_ = std::make_unique<GLScaler>(context_provider()); - gl_ = context_provider()->ContextGL(); - CHECK(gl_); - texture_helper_ = std::make_unique<GLScalerTestTextureHelper>(gl_); - } - - bool IsAndroidMarshmallow() { -#if BUILDFLAG(IS_ANDROID) - return base::android::BuildInfo::GetInstance()->sdk_int() == - base::android::SDK_VERSION_MARSHMALLOW; -#else - return false; -#endif - } - - void TearDown() final { - texture_helper_.reset(); - gl_ = nullptr; - scaler_.reset(); - - cc::PixelTest::TearDown(); - } - - private: - std::unique_ptr<GLScaler> scaler_; - gpu::gles2::GLES2Interface* gl_ = nullptr; - std::unique_ptr<GLScalerTestTextureHelper> texture_helper_; -}; - -// Tests that the default GLScaler::Parameters produces an unscaled copy. -TEST_F(GLScalerPixelTest, CopiesByDefault) { - // Disabled on Marshmallow. See crbug.com/933080 - if (IsAndroidMarshmallow()) - return; - - ASSERT_TRUE(scaler()->Configure(GLScaler::Parameters())); - EXPECT_EQ(u8"Output ← {BILINEAR/lowp copy} ← Source", GetScalerString()); - const SkBitmap source = CreateSMPTETestImage(kSMPTEFullSize); - const SkBitmap actual = - Scale(source, gfx::Vector2d(), gfx::Rect(kSMPTEFullSize)); - int max_color_diff = GetBaselineColorDifference(); - EXPECT_TRUE(LooksLikeSMPTETestImage( - actual, kSMPTEFullSize, gfx::Rect(kSMPTEFullSize), 0, &max_color_diff)) - << "max_color_diff measured was " << max_color_diff - << "\nActual: " << cc::GetPNGDataUrl(actual); -} - -// Tests a FAST quality scaling of 2→1 in X and 3→2 in Y. -TEST_F(GLScalerPixelTest, ScalesAtFastQuality) { - GLScaler::Parameters params; - params.scale_from = gfx::Vector2d(2, 3); - params.scale_to = gfx::Vector2d(1, 2); - params.quality = GLScaler::Parameters::Quality::FAST; - params.is_flipped_source = false; - ASSERT_TRUE(scaler()->Configure(params)); - EXPECT_EQ(u8"Output ← {BILINEAR/lowp [2 3] to [1 2]} ← Source", - GetScalerString()); - const SkBitmap source = CreateSMPTETestImage(kSMPTEFullSize); - static_assert(kSMPTEFullSize.width() % 2 == 0, "Fix kSMPTEFullSize."); - static_assert(kSMPTEFullSize.height() % 3 == 0, "Fix kSMPTEFullSize."); - const SkBitmap actual = Scale(source, gfx::Vector2d(), - gfx::Rect(0, 0, kSMPTEFullSize.width() / 2, - kSMPTEFullSize.height() * 2 / 3)); - int max_color_diff = GetBaselineColorDifference(); - EXPECT_TRUE(LooksLikeSMPTETestImage( - actual, kSMPTEFullSize, gfx::Rect(kSMPTEFullSize), 2, &max_color_diff)) - << "max_color_diff measured was " << max_color_diff - << "\nActual: " << cc::GetPNGDataUrl(actual); -} - -// Tests a GOOD quality scaling of 1280x720 → 1024x700. -TEST_F(GLScalerPixelTest, ScalesALittleAtGoodQuality) { - GLScaler::Parameters params; - params.scale_from = gfx::Vector2d(1280, 720); - params.scale_to = gfx::Vector2d(1024, 700); - params.quality = GLScaler::Parameters::Quality::GOOD; - params.is_flipped_source = false; - ASSERT_TRUE(scaler()->Configure(params)); - EXPECT_EQ(u8"Output ← {BILINEAR2X2/lowp [1280 720] to [1024 700]} ← Source", - GetScalerString()); - constexpr gfx::Size kSourceSize = gfx::Size(1280, 720); - const SkBitmap source = CreateSMPTETestImage(kSourceSize); - const SkBitmap actual = - Scale(source, gfx::Vector2d(), gfx::Rect(0, 0, 1024, 700)); - int max_color_diff = GetBaselineColorDifference(); - EXPECT_TRUE(LooksLikeSMPTETestImage( - actual, kSourceSize, gfx::Rect(kSourceSize), 2, &max_color_diff)) - << "max_color_diff measured was " << max_color_diff - << "\nActual: " << cc::GetPNGDataUrl(actual); -} - -// Tests a large, skewed reduction at GOOD quality: 3840x720 → 128x256. -TEST_F(GLScalerPixelTest, ScalesALotHorizontallyAtGoodQuality) { - GLScaler::Parameters params; - params.scale_from = gfx::Vector2d(3840, 720); - params.scale_to = gfx::Vector2d(128, 256); - params.quality = GLScaler::Parameters::Quality::GOOD; - params.is_flipped_source = false; - ASSERT_TRUE(scaler()->Configure(params)); - EXPECT_EQ( - u8"Output " - u8"← {BILINEAR/lowp [256 256] to [128 256]} " - u8"← {BILINEAR4/lowp [2048 512] to [256 256]} " - u8"← {BILINEAR2X2/lowp [3840 720] to [2048 512]} " - u8"← Source", - GetScalerString()); - constexpr gfx::Size kSourceSize = gfx::Size(3840, 720); - const SkBitmap source = CreateSMPTETestImage(kSourceSize); - const SkBitmap actual = - Scale(source, gfx::Vector2d(), gfx::Rect(0, 0, 128, 256)); - int max_color_diff = GetBaselineColorDifference(); - EXPECT_TRUE(LooksLikeSMPTETestImage( - actual, kSourceSize, gfx::Rect(kSourceSize), 2, &max_color_diff)) - << "max_color_diff measured was " << max_color_diff - << "\nActual: " << cc::GetPNGDataUrl(actual); -} - -// Tests a large, skewed reduction at GOOD quality: 640x2160 → 256x128. -TEST_F(GLScalerPixelTest, ScalesALotVerticallyAtGoodQuality) { - GLScaler::Parameters params; - params.scale_from = gfx::Vector2d(640, 2160); - params.scale_to = gfx::Vector2d(256, 128); - params.quality = GLScaler::Parameters::Quality::GOOD; - params.is_flipped_source = false; - ASSERT_TRUE(scaler()->Configure(params)); - EXPECT_EQ( - u8"Output " - u8"← {BILINEAR/lowp [256 256] to [256 128]} " - u8"← {BILINEAR4/lowp [512 2048] to [256 256]} " - u8"← {BILINEAR2X2/lowp [640 2160] to [512 2048]} " - u8"← Source", - GetScalerString()); - constexpr gfx::Size kSourceSize = gfx::Size(640, 2160); - const SkBitmap source = CreateSMPTETestImage(kSourceSize); - const SkBitmap actual = - Scale(source, gfx::Vector2d(), gfx::Rect(0, 0, 256, 128)); - int max_color_diff = GetBaselineColorDifference(); - EXPECT_TRUE(LooksLikeSMPTETestImage( - actual, kSourceSize, gfx::Rect(kSourceSize), 2, &max_color_diff)) - << "max_color_diff measured was " << max_color_diff - << "\nActual: " << cc::GetPNGDataUrl(actual); -} - -// Tests a BEST quality scaling of 1280x720 → 1024x700. -TEST_F(GLScalerPixelTest, ScalesAtBestQuality) { - GLScaler::Parameters params; - params.scale_from = gfx::Vector2d(1280, 720); - params.scale_to = gfx::Vector2d(1024, 700); - params.quality = GLScaler::Parameters::Quality::BEST; - params.is_flipped_source = false; - ASSERT_TRUE(scaler()->Configure(params)); - EXPECT_EQ( - u8"Output " - u8"← {BICUBIC_HALF_1D/lowp [2048 700] to [1024 700]} " - u8"← {BICUBIC_UPSCALE/lowp [1280 700] to [2048 700]} " - u8"← {BICUBIC_HALF_1D/lowp [1280 1400] to [1280 700]} " - u8"← {BICUBIC_UPSCALE/lowp [1280 720] to [1280 1400]} " - u8"← Source", - GetScalerString()); - constexpr gfx::Size kSourceSize = gfx::Size(1280, 720); - const SkBitmap source = CreateSMPTETestImage(kSourceSize); - const SkBitmap actual = - Scale(source, gfx::Vector2d(), gfx::Rect(0, 0, 1024, 700)); - int max_color_diff = GetBaselineColorDifference(); - EXPECT_TRUE(LooksLikeSMPTETestImage( - actual, kSourceSize, gfx::Rect(kSourceSize), 4, &max_color_diff)) - << "max_color_diff measured was " << max_color_diff - << "\nActual: " << cc::GetPNGDataUrl(actual); -} - -// Tests that a source offset can be provided to sample the source starting at a -// different location. -TEST_F(GLScalerPixelTest, TranslatesWithSourceOffset) { - // Disabled on Marshmallow. See crbug.com/933080 - if (IsAndroidMarshmallow()) - return; - - GLScaler::Parameters params; - params.is_flipped_source = false; - ASSERT_TRUE(scaler()->Configure(params)); - EXPECT_EQ(u8"Output ← {BILINEAR/lowp copy} ← Source", GetScalerString()); - const SkBitmap source = CreateSMPTETestImage(kSMPTEFullSize); - static_assert(kSMPTEFullSize.width() % 2 == 0, "Fix kSMPTEFullSize."); - static_assert(kSMPTEFullSize.height() % 4 == 0, "Fix kSMPTEFullSize."); - const gfx::Vector2d offset(kSMPTEFullSize.width() / 2, - kSMPTEFullSize.height() / 4); - const gfx::Rect src_rect(offset.x(), offset.y(), - kSMPTEFullSize.width() - offset.x(), - kSMPTEFullSize.height() - offset.y()); - const gfx::Rect output_rect(0, 0, kSMPTEFullSize.width() - offset.x(), - kSMPTEFullSize.height() - offset.y()); - const SkBitmap actual = Scale(source, offset, output_rect); - int max_color_diff = GetBaselineColorDifference(); - EXPECT_TRUE(LooksLikeSMPTETestImage(actual, kSMPTEFullSize, src_rect, 0, - &max_color_diff)) - << "max_color_diff measured was " << max_color_diff - << "\nActual: " << cc::GetPNGDataUrl(actual); -} - -// Tests that the source offset works when the source content is vertically -// flipped. -TEST_F(GLScalerPixelTest, TranslatesVerticallyFlippedSourceWithSourceOffset) { - // Disabled on Marshmallow. See crbug.com/933080 - if (IsAndroidMarshmallow()) - return; - - GLScaler::Parameters params; - params.is_flipped_source = true; - ASSERT_TRUE(scaler()->Configure(params)); - EXPECT_EQ(u8"Output ← {BILINEAR/lowp copy} ← Source", GetScalerString()); - const SkBitmap flipped_source = - CreateVerticallyFlippedBitmap(CreateSMPTETestImage(kSMPTEFullSize)); - const gfx::Vector2d offset(kSMPTEFullSize.width() / 2, - kSMPTEFullSize.height() / 4); - const gfx::Rect src_rect(offset.x(), offset.y(), - kSMPTEFullSize.width() - offset.x(), - kSMPTEFullSize.height() - offset.y()); - const gfx::Rect output_rect(0, 0, kSMPTEFullSize.width() - offset.x(), - kSMPTEFullSize.height() - offset.y()); - const SkBitmap flipped_back_actual = - CreateVerticallyFlippedBitmap(Scale(flipped_source, offset, output_rect)); - int max_color_diff = GetBaselineColorDifference(); - EXPECT_TRUE(LooksLikeSMPTETestImage(flipped_back_actual, kSMPTEFullSize, - src_rect, 0, &max_color_diff)) - << "max_color_diff measured was " << max_color_diff - << "\nActual (flipped-back): " << cc::GetPNGDataUrl(flipped_back_actual); -} - -// Tests that the correct source selection is made when both translating the -// source and then scaling. Scale "from" and "to" values are chosen such that a -// multi-stage scaler will be configured (to test that offsets are correcty -// calculated and passed between multiple stages). -TEST_F(GLScalerPixelTest, ScalesWithTranslatedSourceOffset) { - GLScaler::Parameters params; - params.scale_from = gfx::Vector2d(640, 2160); - params.scale_to = gfx::Vector2d(256, 128); - params.quality = GLScaler::Parameters::Quality::GOOD; - params.is_flipped_source = false; - ASSERT_TRUE(scaler()->Configure(params)); - EXPECT_EQ( - u8"Output " - u8"← {BILINEAR/lowp [256 256] to [256 128]} " - u8"← {BILINEAR4/lowp [512 2048] to [256 256]} " - u8"← {BILINEAR2X2/lowp [640 2160] to [512 2048]} " - u8"← Source", - GetScalerString()); - constexpr gfx::Size kSourceSize = gfx::Size(640, 2160); - const SkBitmap source = CreateSMPTETestImage(kSourceSize); - const gfx::Vector2d offset(kSourceSize.width() / 2, kSourceSize.height() / 4); - const gfx::Rect output_rect(0, 0, 128, 64); - const SkBitmap actual = Scale(source, offset, output_rect); - const gfx::Rect expected_copy_rect( - offset.x(), offset.y(), - output_rect.width() * params.scale_from.x() / params.scale_to.x(), - output_rect.height() * params.scale_from.y() / params.scale_to.y()); - int max_color_diff = GetBaselineColorDifference(); - EXPECT_TRUE(LooksLikeSMPTETestImage(actual, kSourceSize, expected_copy_rect, - 2, &max_color_diff)) - << "max_color_diff measured was " << max_color_diff - << "\nExpected crop region of source: " << expected_copy_rect.ToString() - << "\nFull (uncropped) Source: " << cc::GetPNGDataUrl(source) - << "\nActual: " << cc::GetPNGDataUrl(actual); -} - -// Tests that the output is vertically flipped, if requested in the parameters. -TEST_F(GLScalerPixelTest, VerticallyFlipsOutput) { - // Disabled on Marshmallow. See crbug.com/933080 - if (IsAndroidMarshmallow()) - return; - - GLScaler::Parameters params; - params.is_flipped_source = false; - params.flip_output = true; - ASSERT_TRUE(scaler()->Configure(params)); - EXPECT_EQ(u8"Output ← {BILINEAR/lowp+flip_y copy} ← Source", - GetScalerString()); - const SkBitmap source = CreateSMPTETestImage(kSMPTEFullSize); - const SkBitmap flipped_back_actual = CreateVerticallyFlippedBitmap( - Scale(source, gfx::Vector2d(), gfx::Rect(kSMPTEFullSize))); - int max_color_diff = GetBaselineColorDifference(); - EXPECT_TRUE(LooksLikeSMPTETestImage(flipped_back_actual, kSMPTEFullSize, - gfx::Rect(kSMPTEFullSize), 0, - &max_color_diff)) - << "max_color_diff measured was " << max_color_diff - << "\nActual (flipped-back): " << cc::GetPNGDataUrl(flipped_back_actual); -} - -// Tests that the single-channel export ScalerStage works by executing a red -// channel export. -TEST_F(GLScalerPixelTest, ExportsTheRedColorChannel) { - // Disabled on Marshmallow. See crbug.com/933080 - if (IsAndroidMarshmallow()) - return; - - GLScaler::Parameters params; - params.is_flipped_source = false; - params.export_format = GLScaler::Parameters::ExportFormat::CHANNEL_0; - ASSERT_TRUE(scaler()->Configure(params)); - EXPECT_EQ(u8"Output ← {PLANAR_CHANNEL_0/lowp [4 1] to [1 1]} ← Source", - GetScalerString()); - const SkBitmap source = CreateSMPTETestImage(kSMPTEFullSize); - const SkBitmap expected = CreatePackedPlanarBitmap(source, 0); - const gfx::Size output_size(expected.width(), expected.height()); - const SkBitmap actual = - Scale(source, gfx::Vector2d(), gfx::Rect(output_size)); - constexpr float kAvgAbsoluteErrorLimit = 1.f; - constexpr int kMaxAbsoluteErrorLimit = 2; - EXPECT_TRUE(cc::FuzzyPixelComparator( - false, 100.f, 0.f, - GetBaselineColorDifference() + kAvgAbsoluteErrorLimit, - GetBaselineColorDifference() + kMaxAbsoluteErrorLimit, 0) - .Compare(expected, actual)) - << "\nActual: " << cc::GetPNGDataUrl(actual) - << "\Expected: " << cc::GetPNGDataUrl(expected); -} - -// A test that also stands as an example for how to use the GLScaler to scale a -// screen-sized RGB source (2160x1440, 16:10 aspect ratio) to a typical video -// resolution (720p, 16:9). The end-goal is to produce three textures, which -// contain the three YUV planes in I420 format. -// -// This is a two step process: First, the source is scaled and color space -// converted, with the final result exported as NV61 format (a full size luma -// plane + a half-width interleaved UV image). Second, the interleaved UV image -// is scaled by half in the vertical direction and then separated into one U and -// one V plane. -TEST_F(GLScalerPixelTest, Example_ScaleAndExportForScreenVideoCapture) { - if (scaler()->GetMaxDrawBuffersSupported() < 2) { - LOG(WARNING) << "Skipping test due to lack of MRT support."; - return; - } - - // Step 1: Produce a scaled NV61-format result. - GLScaler::Parameters params; - params.scale_from = gfx::Vector2d(2160, 1440); - params.scale_to = gfx::Vector2d(1280, 720); - params.source_color_space = DefaultRGBColorSpace(); - params.output_color_space = DefaultYUVColorSpace(); - params.enable_precise_color_management = true; - params.quality = GLScaler::Parameters::Quality::GOOD; - params.is_flipped_source = true; - params.flip_output = true; - params.export_format = GLScaler::Parameters::ExportFormat::NV61; - params.swizzle[0] = GL_BGRA_EXT; // Swizzle for readback. - params.swizzle[1] = GL_RGBA; // Don't swizzle output for Step 2. - ASSERT_TRUE(scaler()->Configure(params)); - EXPECT_STRING_MATCHES( - u8"Output " - u8"← {I422_NV61_MRT/mediump [5120 720] to [1280 720], with color x-form " - u8"to *BT709*, with swizzle(0)} " - u8"← {BILINEAR2/mediump [2160 1440] to [1280 720]} " - u8"← {BILINEAR/mediump+flip_y copy, with color x-form *BT709* to " - u8"*transfer:1.0000\\*x*} " - u8"← Source", - GetScalerString()); - - constexpr gfx::Size kSourceSize = gfx::Size(2160, 1440); - const GLuint src_texture = UploadTexture( - CreateVerticallyFlippedBitmap(CreateSMPTETestImage(kSourceSize))); - constexpr gfx::Size kOutputSize = gfx::Size(1280, 720); - SkBitmap expected = CreateSMPTETestImage(kOutputSize); - ConvertRGBABitmapToYUV(&expected); - - // While the output size is 1280x720, the packing of 4 pixels into one RGBA - // quad means that the texture width must be divided by 4, and that size - // passed in the output_rect argument in the call to ScaleToMultipleOutputs(). - const gfx::Size y_plane_size(kOutputSize.width() / 4, kOutputSize.height()); - const GLuint y_plane_texture = CreateTexture(y_plane_size); - const GLuint uv_interleaved_texture = CreateTexture(y_plane_size); - - ASSERT_TRUE(scaler()->ScaleToMultipleOutputs( - src_texture, kSourceSize, gfx::Vector2d(), y_plane_texture, - uv_interleaved_texture, gfx::Rect(y_plane_size))); - - // Step 2: Run the scaler again with the deinterleaver exporter, to produce - // the I420 U and V planes from the NV61 UV interleaved image. - params = GLScaler::Parameters(); // Reset params. - params.scale_from = gfx::Vector2d(1, 2); - params.scale_to = gfx::Vector2d(1, 1); - params.source_color_space = DefaultYUVColorSpace(); - params.quality = GLScaler::Parameters::Quality::GOOD; - params.is_flipped_source = false; // Output was already flipped in Step 1. - params.export_format = - GLScaler::Parameters::ExportFormat::DEINTERLEAVE_PAIRWISE; - params.swizzle[0] = GL_BGRA_EXT; // Swizzle for readback. - params.swizzle[1] = GL_BGRA_EXT; // Swizzle for readback. - ASSERT_TRUE(scaler()->Configure(params)); - EXPECT_EQ( - u8"Output " - u8"← {DEINTERLEAVE_PAIRWISE_MRT/lowp [2 2] to [1 1], with swizzle(0), " - u8"with swizzle(1)} " - u8"← Source", - GetScalerString()); - - const gfx::Size uv_plane_size(y_plane_size.width() / 2, - y_plane_size.height() / 2); - const GLuint u_plane_texture = CreateTexture(uv_plane_size); - const GLuint v_plane_texture = CreateTexture(uv_plane_size); - ASSERT_TRUE(scaler()->ScaleToMultipleOutputs( - uv_interleaved_texture, y_plane_size, gfx::Vector2d(), u_plane_texture, - v_plane_texture, gfx::Rect(uv_plane_size))); - - // Download the textures, and unpack them into an interleaved YUV bitmap, for - // comparison against the |expected| rendition. - SkBitmap actual = AllocateRGBABitmap(kOutputSize); - actual.eraseColor(SkColorSetARGB(0xff, 0x00, 0x80, 0x80)); - SkBitmap y_plane = DownloadTexture(y_plane_texture, y_plane_size); - SwizzleBitmap(&y_plane); - UnpackPlanarBitmap(y_plane, 0, &actual); - SkBitmap u_plane = DownloadTexture(u_plane_texture, uv_plane_size); - SwizzleBitmap(&u_plane); - UnpackPlanarBitmap(u_plane, 1, &actual); - SkBitmap v_plane = DownloadTexture(v_plane_texture, uv_plane_size); - SwizzleBitmap(&v_plane); - UnpackPlanarBitmap(v_plane, 2, &actual); - - // Provide generous error limits to account for the chroma subsampling in the - // |actual| result when compared to the perfect |expected| rendition. - constexpr float kAvgAbsoluteErrorLimit = 16.f; - constexpr int kMaxAbsoluteErrorLimit = 0x80; - EXPECT_TRUE(cc::FuzzyPixelComparator(false, 100.f, 0.f, - kAvgAbsoluteErrorLimit, - kMaxAbsoluteErrorLimit, 0) - .Compare(expected, actual)) - << "\nActual: " << cc::GetPNGDataUrl(actual) - << "\nExpected: " << cc::GetPNGDataUrl(expected); -} - -// Performs a scaling-with-gamma-correction experiment to test GLScaler's -// "precise color management" feature. A 50% scale is executed on the same -// source image, once with color management turned on, and once with it turned -// off. The results, each of which should be different, are then examined. -TEST_F(GLScalerPixelTest, ScalesWithColorManagement) { - if (!scaler()->SupportsPreciseColorManagement()) { - LOG(WARNING) << "Skipping test due to lack of 16-bit float support."; - return; - } - - // An image of a raspberry (source: - // https://commons.wikimedia.org/wiki/File:Framboise_Margy_3.jpg) has been - // transformed in such a way that scaling it by half in both directions will - // reveal whether scaling is occurring on linearized color values. When scaled - // correctly, the output image should contain a visible raspberry blended - // heavily with solid gray. However, if done naively, the output will be a - // solid 50% gray. For details, see: http://www.ericbrasseur.org/gamma.html - // - // Note that the |source| and |expected| images both use the sRGB color space. - const SkBitmap source = LoadPNGTestImage("rasp-grayator.png"); - ASSERT_FALSE(source.isNull()); - const SkBitmap expected = LoadPNGTestImage("rasp-grayator-half.png"); - ASSERT_FALSE(expected.isNull()); - const gfx::Size output_size = - gfx::Size(source.width() / 2, source.height() / 2); - ASSERT_EQ(gfx::Size(expected.width(), expected.height()), output_size); - const SkBitmap expected_naive = AllocateRGBABitmap(output_size); - expected_naive.eraseColor(SkColorSetARGB(0xff, 0x7f, 0x7f, 0x7f)); - - // Scale the right way: With color management enabled, the raspberry should be - // visible in the downscaled result. - GLScaler::Parameters params; - params.scale_from = gfx::Vector2d(2, 2); - params.scale_to = gfx::Vector2d(1, 1); - params.source_color_space = gfx::ColorSpace::CreateSRGB(); - params.enable_precise_color_management = true; - params.quality = GLScaler::Parameters::Quality::GOOD; - params.is_flipped_source = false; - ASSERT_TRUE(scaler()->Configure(params)); - EXPECT_STRING_MATCHES( - u8"Output " - u8"← {BILINEAR/mediump [2 2] to [1 1], with color x-form to *BT709*} " - u8"← {BILINEAR/mediump copy, with color x-form *BT709* to " - u8"*transfer:1.0000\\*x*} " - u8"← Source", - GetScalerString()); - const SkBitmap actual = - Scale(source, gfx::Vector2d(), gfx::Rect(output_size)); - constexpr float kAvgAbsoluteErrorLimit = 1.f; - constexpr int kMaxAbsoluteErrorLimit = 2; - EXPECT_TRUE(cc::FuzzyPixelComparator( - false, 100.f, 0.f, - GetBaselineColorDifference() + kAvgAbsoluteErrorLimit, - GetBaselineColorDifference() + kMaxAbsoluteErrorLimit, 0) - .Compare(expected, actual)) - << "\nActual: " << cc::GetPNGDataUrl(actual) - << "\nExpected (half size): " << cc::GetPNGDataUrl(expected) - << "\nOriginal: " << cc::GetPNGDataUrl(source); - - // Scale the naive way: Without color management, expect a solid gray result. - params.enable_precise_color_management = false; - ASSERT_TRUE(scaler()->Configure(params)); - EXPECT_EQ(u8"Output ← {BILINEAR/lowp [2 2] to [1 1]} ← Source", - GetScalerString()); - const SkBitmap actual_naive = - Scale(source, gfx::Vector2d(), gfx::Rect(output_size)); - EXPECT_TRUE(cc::FuzzyPixelComparator( - false, 100.f, 0.f, - GetBaselineColorDifference() + kAvgAbsoluteErrorLimit, - GetBaselineColorDifference() + kMaxAbsoluteErrorLimit, 0) - .Compare(expected_naive, actual_naive)) - << "\nActual: " << cc::GetPNGDataUrl(actual_naive) - << "\nExpected (half size): " << cc::GetPNGDataUrl(expected_naive) - << "\nOriginal: " << cc::GetPNGDataUrl(source); -} - -#undef EXPECT_STRING_MATCHES - -} // namespace viz diff --git a/chromium/components/viz/common/gl_scaler_shader_pixeltest.cc b/chromium/components/viz/common/gl_scaler_shader_pixeltest.cc deleted file mode 100644 index e1e7b8c226d..00000000000 --- a/chromium/components/viz/common/gl_scaler_shader_pixeltest.cc +++ /dev/null @@ -1,785 +0,0 @@ -// Copyright 2018 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/gl_scaler.h" - -#include <sstream> -#include <tuple> -#include <vector> - -#include "build/build_config.h" -#include "cc/test/pixel_test.h" -#include "cc/test/pixel_test_utils.h" -#include "components/viz/common/gpu/context_provider.h" -#include "components/viz/test/gl_scaler_test_util.h" -#include "gpu/GLES2/gl2chromium.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "gpu/command_buffer/client/gles2_implementation.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkImageInfo.h" -#include "third_party/skia/include/core/SkRect.h" -#include "ui/gfx/color_transform.h" - -#if BUILDFLAG(IS_ANDROID) -#include "base/android/build_info.h" -#endif - -namespace viz { - -namespace { - -// Base size of test images to be operated upon. Both dimensions must be -// divisible by 2, 3, or 4. -constexpr gfx::Size kBaseSize = gfx::Size(16 * 4 * 3, 9 * 4 * 3); - -} // namespace - -class GLScalerShaderPixelTest - : public cc::PixelTest, - public testing::WithParamInterface<std::tuple<bool, bool>>, - public GLScalerTestUtil { - public: - using Axis = GLScaler::Axis; - using Shader = GLScaler::Shader; - using ShaderProgram = GLScaler::ShaderProgram; - - GLScalerShaderPixelTest() - : scoped_trace_( - __FILE__, - __LINE__, - (testing::Message() - << "is_converting_rgb_to_yuv=" << is_converting_rgb_to_yuv() - << ", is_swizzling_output=" << is_swizzling_output())) {} - - bool is_converting_rgb_to_yuv() const { return std::get<0>(GetParam()); } - bool is_swizzling_output() const { return std::get<1>(GetParam()); } - - bool AreMultipleRenderingTargetsSupported() const { - return scaler_->GetMaxDrawBuffersSupported() > 1; - } - - // Returns a cached ShaderProgram, maybe configured to convert RGB→YUV and/or - // swizzle the 1st and 3rd bytes in the output (depending on GetParams()). - ShaderProgram* GetShaderProgram(Shader shader) { - std::unique_ptr<gfx::ColorTransform> transform; - if (is_converting_rgb_to_yuv()) { - transform = gfx::ColorTransform::NewColorTransform( - DefaultRGBColorSpace(), DefaultYUVColorSpace()); - } - const GLenum swizzle[2] = { - static_cast<GLenum>(is_swizzling_output() ? GL_BGRA_EXT : GL_RGBA), - static_cast<GLenum>(is_swizzling_output() ? GL_BGRA_EXT : GL_RGBA), - }; - return scaler_->GetShaderProgram(shader, GL_UNSIGNED_BYTE, transform.get(), - swizzle); - } - - GLuint CreateTexture(const gfx::Size& size) { - return texture_helper_->CreateTexture(size); - } - - GLuint UploadTexture(const SkBitmap& bitmap) { - return texture_helper_->UploadTexture(bitmap); - } - - SkBitmap DownloadTexture(GLuint texture, const gfx::Size& size) { - return texture_helper_->DownloadTexture(texture, size); - } - - GLuint RenderToNewTexture(GLuint src_texture, const gfx::Size& size) { - return RenderToNewTextures(src_texture, size, false).first; - } - - // Using the current shader program, creates new texture(s) of the given - // |size| and draws using |src_texture| as input. If |dual_outputs| is true, - // two new textures are created and drawn-to simultaneously; otherwise, only - // one is created and drawn-to. The caller does not take ownership of the new - // texture(s). - std::pair<GLuint, GLuint> RenderToNewTextures(GLuint src_texture, - const gfx::Size& size, - bool dual_outputs) { - std::pair<GLuint, GLuint> dst_textures( - CreateTexture(size), dual_outputs ? CreateTexture(size) : 0u); - GLuint framebuffer = 0; - gl_->GenFramebuffers(1, &framebuffer); - gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer); - gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, dst_textures.first, 0); - if (dual_outputs) { - gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 1, - GL_TEXTURE_2D, dst_textures.second, 0); - } - - gl_->BindTexture(GL_TEXTURE_2D, src_texture); - - gl_->Viewport(0, 0, size.width(), size.height()); - const GLenum buffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT0 + 1}; - if (dual_outputs) { - gl_->DrawBuffersEXT(2, buffers); - } - // Assumption: The |vertex_attributes_buffer_| created in SetUp() is - // currently bound to GL_ARRAY_BUFFER. - gl_->DrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - gl_->DeleteFramebuffers(1, &framebuffer); - - return dst_textures; - } - - // Returns a texture that converts the input |texture| back to unswizzled RGB, - // if necessary, depending on GetParam(). The caller does not take ownership - // of the returned texture, which could be the same texture as the input - // argument in some cases. - GLuint ConvertBackToUnswizzledRGB(GLuint texture, const gfx::Size& size) { - GLuint result = texture; - if (is_swizzling_output()) { - const GLenum swizzle[2] = {GL_BGRA_EXT, GL_BGRA_EXT}; - scaler_ - ->GetShaderProgram(Shader::BILINEAR, GL_UNSIGNED_BYTE, nullptr, - swizzle) - ->UseProgram(size, gfx::RectF(gfx::Rect(size)), size, - Axis::HORIZONTAL, false); - result = RenderToNewTexture(result, size); - } - if (is_converting_rgb_to_yuv()) { - const auto transform = gfx::ColorTransform::NewColorTransform( - DefaultYUVColorSpace(), DefaultRGBColorSpace()); - const GLenum swizzle[2] = {GL_RGBA, GL_RGBA}; - scaler_ - ->GetShaderProgram(Shader::BILINEAR, GL_UNSIGNED_BYTE, - transform.get(), swizzle) - ->UseProgram(size, gfx::RectF(gfx::Rect(size)), size, - Axis::HORIZONTAL, false); - result = RenderToNewTexture(result, size); - } - return result; - } - - // A test case executed by RunSMPTEScalingTestCases(). - struct SMPTEScalingTestCase { - gfx::Rect src_rect; // Selects a subrect of the source. - int scale_from; // Scale ratio denominator. - int scale_to; // Scale ratio numerator. - int fuzzy_bar_border; // Ignored pixels between color bars, when comparing. - }; - - // Draws with the given shader for each of the provided |test_cases| and adds - // gtest failure(s) if the output does not look like a part of the SMPTE test - // image. - void RunSMPTEScalingTestCases( - Shader shader, - const std::vector<SMPTEScalingTestCase>& test_cases) { - const SkBitmap source = CreateSMPTETestImage(kBaseSize); - const GLuint src_texture = UploadTexture(source); - - for (const auto& tc : test_cases) { - for (int is_horizontal = 0; is_horizontal <= 1; ++is_horizontal) { - gfx::Size dst_size = tc.src_rect.size(); - Axis axis; - if (is_horizontal) { - CHECK_EQ((dst_size.width() * tc.scale_to) % tc.scale_from, 0); - dst_size.set_width(dst_size.width() * tc.scale_to / tc.scale_from); - axis = Axis::HORIZONTAL; - } else { - CHECK_EQ((dst_size.height() * tc.scale_to) % tc.scale_from, 0); - dst_size.set_height(dst_size.height() * tc.scale_to / tc.scale_from); - axis = Axis::VERTICAL; - } - - SCOPED_TRACE(testing::Message() - << "src_rect=" << tc.src_rect.ToString() - << ", scale from→to=" << tc.scale_from << "→" - << tc.scale_to << ", dst_size=" << dst_size.ToString()); - - GetShaderProgram(shader)->UseProgram(kBaseSize, gfx::RectF(tc.src_rect), - dst_size, axis, false); - const SkBitmap actual = DownloadTexture( - ConvertBackToUnswizzledRGB( - RenderToNewTexture(src_texture, dst_size), dst_size), - dst_size); - int max_color_diff = GetMaxAllowedColorDifference(); - if (!LooksLikeSMPTETestImage(actual, kBaseSize, tc.src_rect, - tc.fuzzy_bar_border, &max_color_diff)) { - ADD_FAILURE() << "Scaled image does not look like the correct scaled " - "subrect of the SMPTE test image (max diff measured=" - << max_color_diff - << "):\nActual: " << cc::GetPNGDataUrl(actual); - } - } - } - } - - // Adds test failures if an |actual| image does not match the |expected| - // image. When not doing color space conversion, the images must match - // exactly; otherwise, some minor differences are allowed. - void ExpectAreTheSameImage(const SkBitmap& expected, - const SkBitmap& actual) const { - const int max_color_diff = GetMaxAllowedColorDifference(); - if (!cc::FuzzyPixelComparator(false, 100.0f, 0.0f, max_color_diff, - max_color_diff, 0) - .Compare(expected, actual)) { - ADD_FAILURE() << "Images are not similar enough (max_color_diff=" - << max_color_diff - << "):\nExpected: " << cc::GetPNGDataUrl(expected) - << "\nActual: " << cc::GetPNGDataUrl(actual); - } - } - - // Draws with the given shader to downscale a "striped pattern" image by - // |downscale_factor| in one dimension only, and adds gtest failure(s) if the - // resulting image is not of the |expected_solid_color|. |cycle| specifies the - // colors of the stripes, which should average to |expected_solid_color|. - // - // If the shader program is correct, it should be sampling the texture halfway - // between each pair of stripes and then averaging the result. This means that - // every N pixels in the source will be averaged to one pixel in the output, - // creating a solid color fill as output. If the shader is sampling the - // texture at the wrong points, the result will be tinted and/or contain - // striping. - void RunMultiplePassBilinearTest(Shader shader, - int downscale_factor, - const std::vector<SkColor>& cycle) { - // Compute the expected solid fill color from the colors in |cycle|. - uint32_t sum_red = 0; - uint32_t sum_green = 0; - uint32_t sum_blue = 0; - uint32_t sum_alpha = 0; - for (SkColor c : cycle) { - sum_red += SkColorGetR(c); - sum_green += SkColorGetG(c); - sum_blue += SkColorGetB(c); - sum_alpha += SkColorGetA(c); - } - const float count = cycle.size(); - // Note: Taking the rounded average for each color channel. - const SkColor expected_solid_color = - SkColorSetARGB(sum_alpha / count + 0.5f, sum_red / count + 0.5f, - sum_green / count + 0.5f, sum_blue / count + 0.5f); - - // Run the test for the vertical direction, and again for the horizontal - // direction. - const gfx::Rect src_rect = - gfx::Rect(0, 0, 10 * downscale_factor, 10 * downscale_factor); - for (int is_horizontal = 0; is_horizontal <= 1; ++is_horizontal) { - gfx::Size dst_size = src_rect.size(); - Axis axis; - CyclicalPattern pattern; - if (is_horizontal) { - dst_size.set_width(dst_size.width() / downscale_factor); - axis = Axis::HORIZONTAL; - pattern = VERTICAL_STRIPES; - } else { - dst_size.set_height(dst_size.height() / downscale_factor); - axis = Axis::VERTICAL; - pattern = HORIZONTAL_STRIPES; - } - - // Create the expected output image consisting of a solid fill color. - SkBitmap expected = AllocateRGBABitmap(dst_size); - expected.eraseColor(expected_solid_color); - - // Run the test for each of N possible rotations of the |cycle| of - // stripes. - for (size_t rotation = 0; rotation < cycle.size(); ++rotation) { - SCOPED_TRACE(testing::Message() << "is_horizontal=" << !!is_horizontal - << ", rotation=" << rotation - << ", expected_solid_color=" << std::hex - << expected_solid_color); - - const SkBitmap source = - CreateCyclicalTestImage(src_rect.size(), pattern, cycle, rotation); - const GLuint src_texture = UploadTexture(source); - - // Execute the program, and convert the shader program's drawn result - // back to an unswizzled RGB form, and compare that with the expected - // image. - GetShaderProgram(shader)->UseProgram( - src_rect.size(), gfx::RectF(src_rect), dst_size, axis, false); - const SkBitmap actual = DownloadTexture( - ConvertBackToUnswizzledRGB( - RenderToNewTexture(src_texture, dst_size), dst_size), - dst_size); - ExpectAreTheSameImage(expected, actual); - } - } - } - - protected: - void SetUp() final { - cc::PixelTest::SetUpGLWithoutRenderer(gfx::SurfaceOrigin::kBottomLeft); - - scaler_ = std::make_unique<GLScaler>(context_provider()); - gl_ = context_provider()->ContextGL(); - CHECK(gl_); - - // Set up vertex attributes buffer and its data. - gl_->GenBuffers(1, &vertex_attributes_buffer_); - gl_->BindBuffer(GL_ARRAY_BUFFER, vertex_attributes_buffer_); - gl_->BufferData(GL_ARRAY_BUFFER, sizeof(ShaderProgram::kVertexAttributes), - ShaderProgram::kVertexAttributes, GL_STATIC_DRAW); - - texture_helper_ = std::make_unique<GLScalerTestTextureHelper>(gl_); - } - - void TearDown() final { - texture_helper_.reset(); - - if (vertex_attributes_buffer_) { - gl_->DeleteBuffers(1, &vertex_attributes_buffer_); - vertex_attributes_buffer_ = 0; - } - - gl_ = nullptr; - scaler_.reset(); - - cc::PixelTest::TearDown(); - } - - // Returns the maximum allowed absolute difference between any two color - // values in the expected vs actual image comparisons, given the current test - // parameters and known platform-specific inaccuracy. - int GetMaxAllowedColorDifference() const { -#if BUILDFLAG(IS_ANDROID) - // Android seems to have texture sampling and/or readback accuracy issues - // with these programs that are not at all seen on any of the desktop - // platforms. Also, versions before Marshmallow seem to have a much larger - // accuracy issues with a few of the programs. Thus, use higher thresholds, - // assuming that the programs are correct if they can pass a much lower - // threshold on other platforms. - if (base::android::BuildInfo::GetInstance()->sdk_int() < - base::android::SDK_VERSION_MARSHMALLOW) { - return (is_converting_rgb_to_yuv() || is_swizzling_output()) ? 24 : 12; - } - return (is_converting_rgb_to_yuv() || is_swizzling_output()) ? 4 : 2; -#else - return (is_converting_rgb_to_yuv() || is_swizzling_output()) ? 2 : 0; -#endif - } - - bool IsAndroidMarshmallow() { -#if BUILDFLAG(IS_ANDROID) - return base::android::BuildInfo::GetInstance()->sdk_int() == - base::android::SDK_VERSION_MARSHMALLOW; -#else - return false; -#endif - } - - testing::ScopedTrace scoped_trace_; - std::unique_ptr<GLScaler> scaler_; - gpu::gles2::GLES2Interface* gl_ = nullptr; - GLuint vertex_attributes_buffer_ = 0; - std::unique_ptr<GLScalerTestTextureHelper> texture_helper_; -}; - -// As the BILINEAR shader is used by some of the test helpers, this test is -// necessary to ensure the correctness of the tools used by all the other tests. -TEST_P(GLScalerShaderPixelTest, ValidateTestHelpers) { - // Disabled on Marshmallow. See crbug.com/933080 - if (IsAndroidMarshmallow()) - return; - - // Create/validate a SMPTE color bar test image. - const SkBitmap original = CreateSMPTETestImage(kBaseSize); - int max_color_diff = GetMaxAllowedColorDifference(); - ASSERT_TRUE(LooksLikeSMPTETestImage(original, kBaseSize, gfx::Rect(kBaseSize), - 0, &max_color_diff)) - << "max diff measured=" << max_color_diff; - - // Create and upload a test image that has had RGB→YUV conversion performed - // and/or had its color channels swizzled, depending on the testing params. - SkBitmap image = CreateSMPTETestImage(kBaseSize); - if (is_converting_rgb_to_yuv()) { - ConvertRGBABitmapToYUV(&image); - } - if (is_swizzling_output()) { - SwizzleBitmap(&image); - } - const GLuint uploaded_texture = UploadTexture(image); - - // Use the convert-back helper, which uses the BILINEAR shader to convert the - // |uploaded_texture| back to an unswizzled RGB form. Then, download the - // result and check whether it matches the original. - const gfx::Size size(image.width(), image.height()); - const GLuint converted_back_texture = - ConvertBackToUnswizzledRGB(uploaded_texture, size); - const SkBitmap actual = DownloadTexture(converted_back_texture, size); - ExpectAreTheSameImage(original, actual); -} - -// Tests the default, one-pass bilinear shader which can upscale or downscale by -// up to 2X. -TEST_P(GLScalerShaderPixelTest, Bilinear) { - // Disabled on Marshmallow. See crbug.com/933080 - if (IsAndroidMarshmallow()) - return; - - constexpr gfx::Rect whole = gfx::Rect(kBaseSize); - constexpr gfx::Rect quadrant = - gfx::Rect(kBaseSize.width() / 2, kBaseSize.height() / 2, - kBaseSize.width() / 2, kBaseSize.height() / 2); - const std::vector<SMPTEScalingTestCase> kTestCases = { - // No scaling. - {whole, 1, 1, 0}, - // Downscale by half. - {whole, 2, 1, 1}, - // Upscale by 1.5. - {whole, 2, 3, 1}, - // No scaling; lower-right quadrant only. - {quadrant, 1, 1, 0}, - // Downscale by half; lower-right quadrant only. - {quadrant, 2, 1, 1}, - // Upscale by 1.5; lower-right quadrant only. - {quadrant, 2, 3, 1}, - }; - - RunSMPTEScalingTestCases(Shader::BILINEAR, kTestCases); -} - -// Test the 2-tap bilinear shader, which downscales by 4X in one dimension. -TEST_P(GLScalerShaderPixelTest, TwoTapBilinear) { - RunMultiplePassBilinearTest(Shader::BILINEAR2, 4, - {SkColorSetARGB(0xff, 0xff, 0x00, 0x00), - SkColorSetARGB(0x7f, 0x00, 0x80, 0x00), - SkColorSetARGB(0x7f, 0x00, 0x80, 0x00), - SkColorSetARGB(0xff, 0x00, 0x00, 0xff)}); -} - -// Test the 3-tap bilinear shader, which downscales by 6X in one dimension. -TEST_P(GLScalerShaderPixelTest, ThreeTapBilinear) { - // Disabled on Marshmallow. See crbug.com/933080 - if (IsAndroidMarshmallow()) - return; - - RunMultiplePassBilinearTest(Shader::BILINEAR3, 6, - {SkColorSetARGB(0xff, 0xff, 0x00, 0x00), - SkColorSetARGB(0xbf, 0x00, 0x80, 0xff), - SkColorSetARGB(0x7f, 0x00, 0x80, 0x00), - SkColorSetARGB(0x7f, 0x00, 0x80, 0x00), - SkColorSetARGB(0xbf, 0xff, 0x80, 0x00), - SkColorSetARGB(0xff, 0x00, 0x00, 0xff)}); -} - -// Test the 4-tap bilinear shader, which downscales by 8X in one dimension. -TEST_P(GLScalerShaderPixelTest, FourTapBilinear) { - RunMultiplePassBilinearTest(Shader::BILINEAR4, 8, - {SkColorSetARGB(0xff, 0xff, 0x00, 0x00), - SkColorSetARGB(0x7f, 0x00, 0x80, 0x00), - SkColorSetARGB(0x7f, 0x00, 0x80, 0x00), - SkColorSetARGB(0xff, 0x00, 0x00, 0xff), - SkColorSetARGB(0xff, 0xff, 0xff, 0x00), - SkColorSetARGB(0x7f, 0x00, 0x80, 0x80), - SkColorSetARGB(0x7f, 0x00, 0x80, 0x80), - SkColorSetARGB(0xff, 0xff, 0x00, 0xff)}); -} - -// Test the 2-by-2-tap bilinear shader, which downscales by 4X in both -// dimensions at the same time. -TEST_P(GLScalerShaderPixelTest, TwoByTwoTapBilinear) { - RunMultiplePassBilinearTest(Shader::BILINEAR2X2, 4, - {SkColorSetARGB(0xff, 0xff, 0x00, 0x00), - SkColorSetARGB(0x7f, 0x00, 0x80, 0x00), - SkColorSetARGB(0x7f, 0x00, 0x80, 0x00), - SkColorSetARGB(0xff, 0x00, 0x00, 0xff)}); -} - -// Tests the bicubic upscaler for a variety of scaling factors between 1X and -// 2X, and over the entire source texture versus just its lower-right quadrant. -TEST_P(GLScalerShaderPixelTest, BicubicUpscale) { - // Disabled on Marshmallow. See crbug.com/933080 - if (IsAndroidMarshmallow()) - return; - - constexpr gfx::Rect whole = gfx::Rect(kBaseSize); - constexpr gfx::Rect quadrant = - gfx::Rect(kBaseSize.width() / 2, kBaseSize.height() / 2, - kBaseSize.width() / 2, kBaseSize.height() / 2); - const std::vector<SMPTEScalingTestCase> kTestCases = { - // No scaling. - {whole, 1, 1, 0}, - // Upscale by 4/3. - {whole, 3, 4, 3}, - // Upscale by 3/2. - {whole, 2, 3, 3}, - // Upscale by 2X. - {whole, 1, 2, 3}, - // No scaling; lower-right quadrant only. - {quadrant, 1, 1, 0}, - // Upscale by 4/3. - {quadrant, 3, 4, 3}, - // Upscale by 3/2. - {quadrant, 2, 3, 3}, - // Upscale by 2X. - {quadrant, 1, 2, 3}, - }; - - RunSMPTEScalingTestCases(Shader::BICUBIC_UPSCALE, kTestCases); -} - -// Tests the bicubic half-downscaler, both over an entire source texture and -// over just its lower-right quadrant. -TEST_P(GLScalerShaderPixelTest, BicubicDownscaleByHalf) { - constexpr gfx::Rect whole = gfx::Rect(kBaseSize); - constexpr gfx::Rect quadrant = - gfx::Rect(kBaseSize.width() / 2, kBaseSize.height() / 2, - kBaseSize.width() / 2, kBaseSize.height() / 2); - const std::vector<SMPTEScalingTestCase> kTestCases = { - // Downscale by half. - {whole, 2, 1, 2}, - // Downscale by half; lower-right quadrant only. - {quadrant, 2, 1, 2}, - }; - - RunSMPTEScalingTestCases(Shader::BICUBIC_HALF_1D, kTestCases); -} - -// Tests the shaders that read a normal 4-channel interleaved texture and -// produce a planar texture consisting of just one color channel, packed into -// RGBA quads. -TEST_P(GLScalerShaderPixelTest, Export_Planar) { - // Disabled on Marshmallow. See crbug.com/933080 - if (IsAndroidMarshmallow()) - return; - - const std::vector<SkColor> kCycle = {SkColorSetARGB(0xff, 0xff, 0x00, 0x00), - SkColorSetARGB(0x80, 0x00, 0x80, 0x00), - SkColorSetARGB(0x80, 0x00, 0x80, 0x00), - SkColorSetARGB(0xff, 0x00, 0x00, 0xff)}; - SkBitmap source = CreateCyclicalTestImage(kBaseSize, STAGGERED, kCycle, 0); - const GLuint src_texture = UploadTexture(source); - - // For each channel, create an expected bitmap and compare it to the result - // from drawing with the shader program. - if (is_converting_rgb_to_yuv()) { - ConvertRGBABitmapToYUV(&source); - } - for (int channel = 0; channel <= 3; ++channel) { - SkBitmap expected = CreatePackedPlanarBitmap(source, channel); - if (is_swizzling_output()) { - SwizzleBitmap(&expected); - } - - const Shader shader = static_cast<Shader>( - static_cast<int>(Shader::PLANAR_CHANNEL_0) + channel); - const gfx::Size dst_size(kBaseSize.width() / 4, kBaseSize.height()); - GetShaderProgram(shader)->UseProgram(kBaseSize, - gfx::RectF(gfx::Rect(kBaseSize)), - dst_size, Axis::HORIZONTAL, false); - const SkBitmap actual = - DownloadTexture(RenderToNewTexture(src_texture, dst_size), dst_size); - ExpectAreTheSameImage(expected, actual); - } -} - -// Tests that the I422/NV61 formatter shader program produces a planar texture -// and an interleaved half-width texture from a normal 4-channel interleaved -// texture. See gl_shader.h for more specifics. -TEST_P(GLScalerShaderPixelTest, Export_I422_NV61) { - if (!AreMultipleRenderingTargetsSupported()) { - LOG(WARNING) << "Skipping test due to lack of MRT support on this machine."; - return; - } - - // Use a vertical stripes source image/texture to test that the shader is - // sampling the texture at the correct points and performing - // downscale-blending in the second texture. - const std::vector<SkColor> kCycle = {SkColorSetARGB(0xff, 0xff, 0x00, 0x00), - SkColorSetARGB(0x80, 0x00, 0x80, 0x00), - SkColorSetARGB(0x80, 0x00, 0x80, 0x00), - SkColorSetARGB(0xff, 0x00, 0x00, 0xff)}; - SkBitmap source = - CreateCyclicalTestImage(kBaseSize, VERTICAL_STRIPES, kCycle, 0); - const GLuint src_texture = UploadTexture(source); - - // Create the expected output images: The first (A) is simply the first color - // channel of the source packed into a planar format. The second (BC) consists - // of the second and third color channels downscaled by half and interleaved. - // The following can be considered a reference implementation for what the - // shader program is supposed to do. - if (is_converting_rgb_to_yuv()) { - ConvertRGBABitmapToYUV(&source); - } - SkBitmap expected_a = CreatePackedPlanarBitmap(source, 0); - if (is_swizzling_output()) { - SwizzleBitmap(&expected_a); - } - const gfx::Size dst_size(expected_a.width(), expected_a.height()); - SkBitmap expected_bc = AllocateRGBABitmap(dst_size); - for (int y = 0; y < dst_size.height(); ++y) { - const uint32_t* const src = source.getAddr32(0, y); - uint32_t* const dst_bc = expected_bc.getAddr32(0, y); - for (int x = 0; x < dst_size.width(); ++x) { - // (src[0..3]) (dst_bc) - // RGBA RGBA rgba rgba --> GBgb (e.g, two G's blended into one G) - const uint32_t g01 = ((((src[x * 4 + 0] >> kGreenShift) & 0xff) + - ((src[x * 4 + 1] >> kGreenShift) & 0xff)) / - 2.f + - 0.5f); - const uint32_t b01 = ((((src[x * 4 + 0] >> kBlueShift) & 0xff) + - ((src[x * 4 + 1] >> kBlueShift) & 0xff)) / - 2.f + - 0.5f); - const uint32_t g23 = ((((src[x * 4 + 2] >> kGreenShift) & 0xff) + - ((src[x * 4 + 3] >> kGreenShift) & 0xff)) / - 2.f + - 0.5f); - const uint32_t b23 = ((((src[x * 4 + 2] >> kBlueShift) & 0xff) + - ((src[x * 4 + 3] >> kBlueShift) & 0xff)) / - 2.f + - 0.5f); - dst_bc[x] = ((g01 << kRedShift) | (b01 << kGreenShift) | - (g23 << kBlueShift) | (b23 << kAlphaShift)); - } - } - if (is_swizzling_output()) { - SwizzleBitmap(&expected_bc); - } - - // Execute the program, and compare the shader program's drawn result with the - // expected images. - GetShaderProgram(Shader::I422_NV61_MRT) - ->UseProgram(kBaseSize, gfx::RectF(gfx::Rect(kBaseSize)), dst_size, - Axis::HORIZONTAL, false); - const auto textures = RenderToNewTextures(src_texture, dst_size, true); - const SkBitmap actual_a = DownloadTexture(textures.first, dst_size); - ExpectAreTheSameImage(expected_a, actual_a); - const SkBitmap actual_bc = DownloadTexture(textures.second, dst_size); - ExpectAreTheSameImage(expected_bc, actual_bc); -} - -// Tests that the PLANAR_CHANNELS_1_2 formatter shader program produces an -// interleaved half-width texture from a normal 4-channel interleaved texture. -// See gl_shader.h for more specifics. -TEST_P(GLScalerShaderPixelTest, Export_PLANAR_CHANNELS_1_2) { - // Use a vertical stripes source image/texture to test that the shader is - // sampling the texture at the correct points and performing - // downscale-blending in the second texture. - const std::vector<SkColor> kCycle = {SkColorSetARGB(0xff, 0xff, 0x00, 0x00), - SkColorSetARGB(0x80, 0x00, 0x80, 0x00), - SkColorSetARGB(0x80, 0x00, 0x80, 0x00), - SkColorSetARGB(0xff, 0x00, 0x00, 0xff)}; - SkBitmap source = - CreateCyclicalTestImage(kBaseSize, VERTICAL_STRIPES, kCycle, 0); - const GLuint src_texture = UploadTexture(source); - - // Create the expected output image: it consists of the second and third - // color channels (BC) downscaled by half and interleaved. - // The following can be considered a reference implementation for what the - // shader program is supposed to do. - if (is_converting_rgb_to_yuv()) { - ConvertRGBABitmapToYUV(&source); - } - - // 4x1 pixels get converted to a packed 1x1 pixel. - const gfx::Size dst_size(source.width() / 4, source.height()); - SkBitmap expected_bc = AllocateRGBABitmap(dst_size); - for (int y = 0; y < dst_size.height(); ++y) { - const uint32_t* const src = source.getAddr32(0, y); - uint32_t* const dst_bc = expected_bc.getAddr32(0, y); - for (int x = 0; x < dst_size.width(); ++x) { - // (src[0..3]) (dst_bc) - // RGBA RGBA rgba rgba --> GBgb (e.g, two G's blended into one G) - const uint32_t g01 = ((((src[x * 4 + 0] >> kGreenShift) & 0xff) + - ((src[x * 4 + 1] >> kGreenShift) & 0xff)) / - 2.f + - 0.5f); - const uint32_t b01 = ((((src[x * 4 + 0] >> kBlueShift) & 0xff) + - ((src[x * 4 + 1] >> kBlueShift) & 0xff)) / - 2.f + - 0.5f); - const uint32_t g23 = ((((src[x * 4 + 2] >> kGreenShift) & 0xff) + - ((src[x * 4 + 3] >> kGreenShift) & 0xff)) / - 2.f + - 0.5f); - const uint32_t b23 = ((((src[x * 4 + 2] >> kBlueShift) & 0xff) + - ((src[x * 4 + 3] >> kBlueShift) & 0xff)) / - 2.f + - 0.5f); - dst_bc[x] = ((g01 << kRedShift) | (b01 << kGreenShift) | - (g23 << kBlueShift) | (b23 << kAlphaShift)); - } - } - if (is_swizzling_output()) { - SwizzleBitmap(&expected_bc); - } - - // Execute the program, and compare the shader program's drawn result with the - // expected images. - GetShaderProgram(Shader::PLANAR_CHANNELS_1_2) - ->UseProgram(kBaseSize, gfx::RectF(gfx::Rect(kBaseSize)), dst_size, - Axis::HORIZONTAL, false); - const auto texture = RenderToNewTexture(src_texture, dst_size); - const SkBitmap actual_bc = DownloadTexture(texture, dst_size); - ExpectAreTheSameImage(expected_bc, actual_bc); -} - -// Tests the pairwise-deinterleave shader program that produces two planar -// textures from a single interleaved one. -TEST_P(GLScalerShaderPixelTest, Export_PairwiseDeinterleave) { - if (!AreMultipleRenderingTargetsSupported()) { - LOG(WARNING) << "Skipping test due to lack of MRT support on this machine."; - return; - } - - // This shader does not provide color space conversion. It is just a - // demultiplexer/repackager. - if (is_converting_rgb_to_yuv()) { - return; - } - - // Create a source image/texture with a pattern suitable for ensuring the - // shader is sampling the texture at the correct points. - const std::vector<SkColor> kCycle = {SkColorSetARGB(0xff, 0xff, 0x00, 0x00), - SkColorSetARGB(0xc0, 0x00, 0xc0, 0x00), - SkColorSetARGB(0x80, 0x00, 0x00, 0x80), - SkColorSetARGB(0xff, 0xff, 0xff, 0xff)}; - const SkBitmap source = - CreateCyclicalTestImage(kBaseSize, STAGGERED, kCycle, 0); - const GLuint src_texture = UploadTexture(source); - - // Create the expected pair of planar images. - const gfx::Size dst_size(kBaseSize.width() / 2, kBaseSize.height()); - SkBitmap expected_a = AllocateRGBABitmap(dst_size); - SkBitmap expected_b = AllocateRGBABitmap(dst_size); - for (int y = 0; y < dst_size.height(); ++y) { - const uint32_t* const src = source.getAddr32(0, y); - uint32_t* const dst_a = expected_a.getAddr32(0, y); - uint32_t* const dst_b = expected_b.getAddr32(0, y); - for (int x = 0; x < dst_size.width(); ++x) { - // (src) (dst_a) (dst_b) - // ABAB abab --> { AAaa + BBbb } - dst_a[x] = ((((src[x * 2 + 0] >> kRedShift) & 0xff) << kRedShift) | - (((src[x * 2 + 0] >> kBlueShift) & 0xff) << kGreenShift) | - (((src[x * 2 + 1] >> kRedShift) & 0xff) << kBlueShift) | - (((src[x * 2 + 1] >> kBlueShift) & 0xff) << kAlphaShift)); - dst_b[x] = ((((src[x * 2 + 0] >> kGreenShift) & 0xff) << kRedShift) | - (((src[x * 2 + 0] >> kAlphaShift) & 0xff) << kGreenShift) | - (((src[x * 2 + 1] >> kGreenShift) & 0xff) << kBlueShift) | - (((src[x * 2 + 1] >> kAlphaShift) & 0xff) << kAlphaShift)); - } - } - if (is_swizzling_output()) { - SwizzleBitmap(&expected_a); - SwizzleBitmap(&expected_b); - } - - // Execute the program, and compare the shader program's drawn result with the - // expected images. - GetShaderProgram(Shader::DEINTERLEAVE_PAIRWISE_MRT) - ->UseProgram(kBaseSize, gfx::RectF(gfx::Rect(kBaseSize)), dst_size, - Axis::HORIZONTAL, false); - const auto textures = RenderToNewTextures(src_texture, dst_size, true); - const SkBitmap actual_a = DownloadTexture(textures.first, dst_size); - ExpectAreTheSameImage(expected_a, actual_a); - const SkBitmap actual_b = DownloadTexture(textures.second, dst_size); - ExpectAreTheSameImage(expected_b, actual_b); -} - -INSTANTIATE_TEST_SUITE_P(All, - GLScalerShaderPixelTest, - testing::Combine(testing::Bool(), testing::Bool())); - -} // namespace viz diff --git a/chromium/components/viz/common/gl_scaler_unittest.cc b/chromium/components/viz/common/gl_scaler_unittest.cc deleted file mode 100644 index 67cab05ba02..00000000000 --- a/chromium/components/viz/common/gl_scaler_unittest.cc +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2018 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/gl_scaler.h" - -#include "cc/test/pixel_test.h" -#include "components/viz/common/gpu/context_provider.h" -#include "components/viz/test/gl_scaler_test_util.h" -#include "gpu/GLES2/gl2chromium.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "gpu/command_buffer/client/gles2_implementation.h" -#include "gpu/command_buffer/common/capabilities.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using ::testing::_; -using ::testing::ByRef; -using ::testing::Eq; -using ::testing::Mock; -using ::testing::NiceMock; -using ::testing::NotNull; -using ::testing::Return; -using ::testing::ReturnRef; -using ::testing::SaveArg; -using ::testing::Sequence; - -namespace viz { -namespace { - -class MockContextProvider : public ContextProvider { - public: - MockContextProvider() { - ON_CALL(*this, ContextGL()) - .WillByDefault( - Return(reinterpret_cast<gpu::gles2::GLES2Interface*>(0xdeadbeef))); - ON_CALL(*this, ContextCapabilities()).WillByDefault(ReturnRef(caps_)); - } - - MOCK_METHOD1(AddObserver, void(ContextLostObserver* obs)); - MOCK_METHOD1(RemoveObserver, void(ContextLostObserver* obs)); - MOCK_CONST_METHOD0(ContextCapabilities, const gpu::Capabilities&()); - MOCK_METHOD0(ContextGL, gpu::gles2::GLES2Interface*()); - - // Stubbed-out, because the tests just stack-allocate this object. - void AddRef() const final {} - void Release() const final {} - - private: - gpu::Capabilities caps_; - - // Other ContextProvider methods; but stubbed-out because they are never - // called. - gpu::ContextResult BindToCurrentThread() final { - NOTREACHED(); - return gpu::ContextResult::kSuccess; - } - base::Lock* GetLock() final { - NOTREACHED(); - return nullptr; - } - ContextCacheController* CacheController() final { - NOTREACHED(); - return nullptr; - } - gpu::ContextSupport* ContextSupport() final { - NOTREACHED(); - return nullptr; - } - class GrDirectContext* GrContext() final { - NOTREACHED(); - return nullptr; - } - gpu::SharedImageInterface* SharedImageInterface() final { - NOTREACHED(); - return nullptr; - } - const gpu::GpuFeatureInfo& GetGpuFeatureInfo() const final { - NOTREACHED(); - return *reinterpret_cast<gpu::GpuFeatureInfo*>(0xdeadbeef); - } -}; - -class GLScalerTest : public cc::PixelTest { - protected: - void SetUp() final { - cc::PixelTest::SetUpGLWithoutRenderer(gfx::SurfaceOrigin::kBottomLeft); - } - - void TearDown() final { cc::PixelTest::TearDown(); } -}; - -TEST_F(GLScalerTest, AddAndRemovesSelfAsContextLossObserver) { - NiceMock<MockContextProvider> provider; - ContextLostObserver* registered_observer = nullptr; - Sequence s; - EXPECT_CALL(provider, AddObserver(NotNull())) - .InSequence(s) - .WillOnce(SaveArg<0>(®istered_observer)); - EXPECT_CALL(provider, RemoveObserver(Eq(ByRef(registered_observer)))) - .InSequence(s); - GLScaler scaler(&provider); -} - -TEST_F(GLScalerTest, RemovesObserverWhenContextIsLost) { - NiceMock<MockContextProvider> provider; - ContextLostObserver* registered_observer = nullptr; - Sequence s; - EXPECT_CALL(provider, AddObserver(NotNull())) - .InSequence(s) - .WillOnce(SaveArg<0>(®istered_observer)); - EXPECT_CALL(provider, RemoveObserver(Eq(ByRef(registered_observer)))) - .InSequence(s); - GLScaler scaler(&provider); - static_cast<ContextLostObserver&>(scaler).OnContextLost(); - // Verify RemoveObserver() was called before |scaler| goes out-of-scope. - Mock::VerifyAndClearExpectations(&provider); -} - -TEST_F(GLScalerTest, StopsScalingWhenContextIsLost) { - GLScaler scaler(context_provider()); - - // Configure the scaler with default parameters (1:1 scale ratio). - ASSERT_TRUE(scaler.Configure(GLScaler::Parameters())); - - // Call Scale() and expect it to return true to indicate the operation - // succeeded. - GLScalerTestTextureHelper helper(context_provider()->ContextGL()); - constexpr gfx::Size kSomeSize = gfx::Size(32, 32); - const GLuint src_texture = helper.CreateTexture(kSomeSize); - const GLuint dest_texture = helper.CreateTexture(kSomeSize); - EXPECT_TRUE(scaler.Scale(src_texture, kSomeSize, gfx::Vector2d(), - dest_texture, gfx::Rect(kSomeSize))); - - // After the context is lost, another call to Scale() should return false. - static_cast<ContextLostObserver&>(scaler).OnContextLost(); - EXPECT_FALSE(scaler.Scale(src_texture, kSomeSize, gfx::Vector2d(), - dest_texture, gfx::Rect(kSomeSize))); -} - -TEST_F(GLScalerTest, Configure_RequiresValidScalingVectors) { - GLScaler scaler(context_provider()); - - GLScaler::Parameters params; - EXPECT_TRUE(scaler.Configure(params)); - - for (int i = 0; i < 4; ++i) { - params.scale_from = gfx::Vector2d(i == 0 ? 0 : 1, i == 1 ? 0 : 1); - params.scale_to = gfx::Vector2d(i == 2 ? 0 : 1, i == 3 ? 0 : 1); - EXPECT_FALSE(scaler.Configure(params)); - } -} - -TEST_F(GLScalerTest, Configure_ResolvesUnspecifiedColorSpaces) { - GLScaler scaler(context_provider()); - - // Neither source nor output space specified: Both should resolve to sRGB. - GLScaler::Parameters params; - EXPECT_TRUE(scaler.Configure(params)); - const auto srgb = gfx::ColorSpace::CreateSRGB(); - EXPECT_EQ(srgb, scaler.params().source_color_space); - EXPECT_EQ(srgb, scaler.params().output_color_space); - EXPECT_TRUE(GLScaler::ParametersAreEquivalent(params, scaler.params())); - - // Source space set to XYZD50 with no output space specified: Both should - // resolve to XYZD50. - const auto xyzd50 = gfx::ColorSpace::CreateXYZD50(); - params.source_color_space = xyzd50; - EXPECT_TRUE(scaler.Configure(params)); - EXPECT_EQ(xyzd50, scaler.params().source_color_space); - EXPECT_EQ(xyzd50, scaler.params().output_color_space); - EXPECT_TRUE(GLScaler::ParametersAreEquivalent(params, scaler.params())); - - // Source space set to XYZD50 with output space set to P3D65: Nothing should - // change. - const auto p3d65 = gfx::ColorSpace::CreateDisplayP3D65(); - params.output_color_space = p3d65; - EXPECT_TRUE(scaler.Configure(params)); - EXPECT_EQ(xyzd50, scaler.params().source_color_space); - EXPECT_EQ(p3d65, scaler.params().output_color_space); - EXPECT_TRUE(GLScaler::ParametersAreEquivalent(params, scaler.params())); -} - -TEST_F(GLScalerTest, Configure_RequiresValidSwizzles) { - GLScaler scaler(context_provider()); - GLScaler::Parameters params; - - // Test that all valid combinations work. - for (int i = 0; i < 4; ++i) { - params.swizzle[0] = (i % 2 == 0) ? GL_RGBA : GL_BGRA_EXT; - params.swizzle[1] = (i / 2 == 0) ? GL_RGBA : GL_BGRA_EXT; - EXPECT_TRUE(scaler.Configure(params)) << "i=" << i; - } - - // Test that invalid combinations don't work. - for (int i = 1; i < 4; ++i) { - params.swizzle[0] = (i % 2 == 0) ? GL_RGBA : GL_RGB; - params.swizzle[1] = (i / 2 == 0) ? GL_RGBA : GL_RGB; - EXPECT_FALSE(scaler.Configure(params)) << "i=" << i; - } -} - -TEST_F(GLScalerTest, DetectsEquivalentScaleRatios) { - GLScaler::Parameters params; - EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio(params, gfx::Vector2d(1, 1), - gfx::Vector2d(1, 1))); - EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio( - params, gfx::Vector2d(15, 15), gfx::Vector2d(15, 15))); - - params.scale_from = gfx::Vector2d(2, 1); - EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio(params, gfx::Vector2d(2, 1), - gfx::Vector2d(1, 1))); - EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio( - params, gfx::Vector2d(30, 15), gfx::Vector2d(15, 15))); - - params.scale_from = gfx::Vector2d(1, 2); - EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio(params, gfx::Vector2d(1, 2), - gfx::Vector2d(1, 1))); - EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio( - params, gfx::Vector2d(15, 30), gfx::Vector2d(15, 15))); - - params.scale_from = gfx::Vector2d(2, 1); - EXPECT_FALSE(GLScaler::ParametersHasSameScaleRatio( - params, gfx::Vector2d(1, 2), gfx::Vector2d(1, 1))); - EXPECT_FALSE(GLScaler::ParametersHasSameScaleRatio( - params, gfx::Vector2d(15, 30), gfx::Vector2d(15, 15))); - - params.scale_from = gfx::Vector2d(1, 2); - EXPECT_FALSE(GLScaler::ParametersHasSameScaleRatio( - params, gfx::Vector2d(2, 1), gfx::Vector2d(1, 1))); - EXPECT_FALSE(GLScaler::ParametersHasSameScaleRatio( - params, gfx::Vector2d(30, 15), gfx::Vector2d(15, 15))); -} - -} // namespace -} // namespace viz diff --git a/chromium/components/viz/common/gpu/context_cache_controller.cc b/chromium/components/viz/common/gpu/context_cache_controller.cc index 80425d801e6..9086c6e42cd 100644 --- a/chromium/components/viz/common/gpu/context_cache_controller.cc +++ b/chromium/components/viz/common/gpu/context_cache_controller.cc @@ -11,6 +11,7 @@ #include "base/check_op.h" #include "base/memory/ptr_util.h" #include "base/synchronization/lock.h" +#include "base/time/time.h" #include "gpu/command_buffer/client/context_support.h" #include "third_party/skia/include/gpu/GrDirectContext.h" diff --git a/chromium/components/viz/common/overlay_state/win/DEPS b/chromium/components/viz/common/overlay_state/win/DEPS new file mode 100644 index 00000000000..19fb5f11f6f --- /dev/null +++ b/chromium/components/viz/common/overlay_state/win/DEPS @@ -0,0 +1,8 @@ +# Please consult components/viz/README.md about allowable dependencies. + +include_rules = [ + "+components/viz/test", + "+gpu/command_buffer", + "+gpu/ipc/common", + "+mojo/public/cpp/bindings", +] diff --git a/chromium/components/viz/common/overlay_state/win/overlay_state_aggregator.cc b/chromium/components/viz/common/overlay_state/win/overlay_state_aggregator.cc new file mode 100644 index 00000000000..bebe165d095 --- /dev/null +++ b/chromium/components/viz/common/overlay_state/win/overlay_state_aggregator.cc @@ -0,0 +1,28 @@ +// Copyright 2022 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/overlay_state/win/overlay_state_aggregator.h" + +namespace viz { + +bool OverlayStateAggregator::SetPromotionHint(bool promoted) { + OverlayStateAggregator::PromotionState recommendation = + promoted ? OverlayStateAggregator::PromotionState::kPromoted + : OverlayStateAggregator::PromotionState::kNotPromoted; + + if (recommendation != promotion_state_) { + promotion_state_ = recommendation; + return true; + } + + // No change. + return false; +} + +OverlayStateAggregator::PromotionState +OverlayStateAggregator::GetPromotionState() { + return promotion_state_; +} + +} // namespace viz diff --git a/chromium/components/viz/common/overlay_state/win/overlay_state_aggregator.h b/chromium/components/viz/common/overlay_state/win/overlay_state_aggregator.h new file mode 100644 index 00000000000..7fc87afa568 --- /dev/null +++ b/chromium/components/viz/common/overlay_state/win/overlay_state_aggregator.h @@ -0,0 +1,29 @@ +// Copyright 2022 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_OVERLAY_STATE_WIN_OVERLAY_STATE_AGGREGATOR_H_ +#define COMPONENTS_VIZ_COMMON_OVERLAY_STATE_WIN_OVERLAY_STATE_AGGREGATOR_H_ + +namespace viz { + +class OverlayStateAggregator { + public: + OverlayStateAggregator() = default; + ~OverlayStateAggregator() = default; + + enum class PromotionState { kUnset, kNotPromoted, kPromoted }; + + // Sets a new promotion hint & returns whether the promotion recommendation + // state has changed. + bool SetPromotionHint(bool promoted); + // Gets current promotion recommendation. + PromotionState GetPromotionState(); + + private: + PromotionState promotion_state_ = PromotionState::kUnset; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_OVERLAY_STATE_WIN_OVERLAY_STATE_AGGREGATOR_H_ diff --git a/chromium/components/viz/common/overlay_state/win/overlay_state_service.cc b/chromium/components/viz/common/overlay_state/win/overlay_state_service.cc new file mode 100644 index 00000000000..38edb2d39ff --- /dev/null +++ b/chromium/components/viz/common/overlay_state/win/overlay_state_service.cc @@ -0,0 +1,184 @@ +// Copyright 2022 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/overlay_state/win/overlay_state_service.h" + +#include <utility> + +#include "base/task/single_thread_task_runner.h" +#include "base/threading/thread.h" + +namespace viz { + +OverlayStateService::MailboxState::MailboxState() = default; +OverlayStateService::MailboxState::~MailboxState() = default; +OverlayStateService::OverlayStateService() = default; +OverlayStateService::~OverlayStateService() = default; + +OverlayStateService* OverlayStateService::GetInstance() { + // TODO(wicarr, crbug.com/1316009): Ideally the OverlayStateService should be + // a singleton. Instead the GpuServiceImpl should be responsible for creating + // the OverlayStateService and injecting it into dependent GpuChannel(s) and + // the DCLayerOverlayProcessor. Further the OverlayStateService should live + // in gpu to avoid gpu needing to take a dependency on viz. + static base::NoDestructor<OverlayStateService> service_wrapper; + return service_wrapper.get(); +} + +void OverlayStateService::Initialize( + scoped_refptr<base::SequencedTaskRunner> task_runner) { + // GpuServiceImpl is expected to initialize the OverlayStateService and + // OverlayStateService will operate on the GpuMain sequenced task runner. + // RegisterObserver is expected to be called by GpuChannel and should operate + // on the same GpuMain sequence as GpuServiceImpl. + // SetPromotionHint is called by DCLayerOverlayProcessor which operates on a + // separate task sequence, VizCompositorThread, so calls are posted to + // 'task_runner_' to allow mojo'ing back out to bound PromotionHintObserver + // clients on the proper sequence. + DCHECK(!initialized_); + task_runner_ = std::move(task_runner); + initialized_ = true; +} + +bool OverlayStateService::IsInitialized() { + return initialized_; +} + +void OverlayStateService::RegisterObserver( + mojo::PendingRemote<gpu::mojom::OverlayStateObserver> + overlay_state_observer, + const gpu::Mailbox& mailbox) { + DCHECK(task_runner_->RunsTasksInCurrentSequence()); + + MailboxState*& mailbox_state = mailboxes_[mailbox]; + if (!mailbox_state) { + mailbox_state = new MailboxState(); + // Use of base::Unretained is safe as OverlayStateService is a singleton + // service bound to the lifetime of the GPU process. + mailbox_state->observer_set_.set_disconnect_handler( + base::BindRepeating(&OverlayStateService::OnBoundObserverDisconnect, + base::Unretained(this), mailbox)); + } + + // Add observer to the RemoteSet + mojo::RemoteSetElementId id = + mailbox_state->observer_set_.Add(std::move(overlay_state_observer)); + + // It's possible that the overlay processor has already set promotion hint + // information for this mailbox. If this is the case then we send a Hint + // Changed event to the observer to inform them of the current promotion + // state. + OverlayStateAggregator::PromotionState promotion_state = + mailbox_state->aggregator_.GetPromotionState(); + + // If promotion state is unset there is no further work to do. If it is set + // then inform the new observer of the current state. + if (promotion_state != OverlayStateAggregator::PromotionState::kUnset) { + bool promoted = + promotion_state == OverlayStateAggregator::PromotionState::kPromoted; + mailbox_state->observer_set_.Get(id)->OnStateChanged(promoted); + } +} + +void OverlayStateService::OnBoundObserverDisconnect(const gpu::Mailbox& mailbox, + mojo::RemoteSetElementId) { + auto mailbox_iterator = mailboxes_.find(mailbox); + if (mailbox_iterator != mailboxes_.end() && + mailbox_iterator->second->observer_set_.empty()) { + // When the last observer has been disconnected, stop tracking mailbox. + mailboxes_.erase(mailbox_iterator); + } +} + +void OverlayStateService::OnStateChanged( + const gpu::Mailbox& mailbox, + OverlayStateAggregator::PromotionState promotion_state) { + // If promotion state is unset there is no further work to do. + if (promotion_state == OverlayStateAggregator::PromotionState::kUnset) + return; + + if (!task_runner_->RunsTasksInCurrentSequence()) { + // Use of base::Unretained is safe as OverlayStateService is a singleton + // service bound to the lifetime of the GPU process. + task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&OverlayStateService::OnStateChangedOnTaskRunnerSequence, + base::Unretained(this), mailbox, promotion_state)); + } else { + OnStateChangedOnTaskRunnerSequence(mailbox, promotion_state); + } +} + +void OverlayStateService::OnStateChangedOnTaskRunnerSequence( + const gpu::Mailbox& mailbox, + OverlayStateAggregator::PromotionState promotion_state) { + // Notify all observers of the new hint state. + bool promoted = + promotion_state == OverlayStateAggregator::PromotionState::kPromoted; + auto mailbox_iterator = mailboxes_.find(mailbox); + DCHECK(mailbox_iterator != mailboxes_.end()); + for (auto& observer : mailbox_iterator->second->observer_set_) { + observer->OnStateChanged(promoted); + } +} + +void OverlayStateService::SetPromotionHint(const gpu::Mailbox& mailbox, + bool promoted) { + DCHECK(!task_runner_->RunsTasksInCurrentSequence()); + // Use of base::Unretained is safe as OverlayStateService is a singleton + // service bound to the lifetime of the GPU process. + task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&OverlayStateService::SetPromotionHintOnTaskRunnerSequence, + base::Unretained(this), mailbox, promoted)); +} + +void OverlayStateService::SetPromotionHintOnTaskRunnerSequence( + const gpu::Mailbox& mailbox, + bool promoted) { + // The OverlayStateService is made aware of mailboxes of interest through two + // channels: + // 1.) The registration of an observer for a mailbox. + // 2.) The setting of a promotion hint for a mailbox (the overlay processor + // will only send hints for mailboxes which are tagged as + // 'wants_promotion_hint' on their TransferResource). + // + // If this is the first promotion hint associated with a mailbox which has + // not had an observer registered yet, then we will not have an existing + // entry in 'mailboxes_' tracking it. Since there is no guarantee when we'll + // receive another promotion hint for the mailbox we'll store the result so + // any future observer can be informed of the current promotion state. + auto mailbox_iterator = mailboxes_.find(mailbox); + if (mailbox_iterator != mailboxes_.end()) { + bool state_change = + mailbox_iterator->second->aggregator_.SetPromotionHint(promoted); + if (state_change) { + // Notifying observers requires an IPC so we only send an update when + // the underlying state changes. + OnStateChanged(mailbox, + mailbox_iterator->second->aggregator_.GetPromotionState()); + } + } else { + MailboxState* new_mailbox_state = new MailboxState(); + new_mailbox_state->aggregator_.SetPromotionHint(promoted); + mailboxes_.insert({mailbox, new_mailbox_state}); + } +} + +void OverlayStateService::MailboxDestroyed(const gpu::Mailbox& mailbox) { + DCHECK(!task_runner_->RunsTasksInCurrentSequence()); + // Use of base::Unretained is safe as OverlayStateService is a singleton + // service bound to the lifetime of the GPU process. + task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&OverlayStateService::MailboxDestroyedOnTaskRunnerSequence, + base::Unretained(this), mailbox)); +} + +void OverlayStateService::MailboxDestroyedOnTaskRunnerSequence( + const gpu::Mailbox& mailbox) { + mailboxes_.erase(mailbox); +} + +} // namespace viz diff --git a/chromium/components/viz/common/overlay_state/win/overlay_state_service.h b/chromium/components/viz/common/overlay_state/win/overlay_state_service.h new file mode 100644 index 00000000000..50c95d0f371 --- /dev/null +++ b/chromium/components/viz/common/overlay_state/win/overlay_state_service.h @@ -0,0 +1,83 @@ +// Copyright 2022 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_OVERLAY_STATE_WIN_OVERLAY_STATE_SERVICE_H_ +#define COMPONENTS_VIZ_COMMON_OVERLAY_STATE_WIN_OVERLAY_STATE_SERVICE_H_ + +#include "base/no_destructor.h" +#include "components/viz/common/overlay_state/win/overlay_state_aggregator.h" +#include "components/viz/common/viz_common_export.h" +#include "gpu/command_buffer/common/mailbox.h" +#include "gpu/ipc/common/gpu_channel.mojom.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/cpp/bindings/remote_set.h" + +namespace viz { + +using OverlayStateObserver = gpu::mojom::OverlayStateObserver; + +// The OverlayStateService allows a MediaFoundationRendererClient, running in +// a Renderer process, to understand whether the quads associated with its +// video presentation textures are being promoted to a Direct Composition layer +// by Viz. This allows the MediaFoundationRendererClient to determine the +// appropriate presentation mode to use as the Windowless Swapchain mode +// requires Direct Composition support to work. +// +// The mailbox associated with a texture is used as the common identifier +// between the overlay processor in Viz & the MediaFoundationRendererClient. +// Further the quad associated with the mailbox has it's associated +// TransferResource tagged with 'wants_promotion_hint' to ensure that the +// overlay processor only sends the OverlayStateService hints for mailboxes of +// interest. +// +// The OverlayStateService aggregates hints to help ensure minimal IPC overhead +// in keeping the MediaFoundationRendererClient informed of the current +// promotion state. +class VIZ_COMMON_EXPORT OverlayStateService { + public: + static OverlayStateService* GetInstance(); + void Initialize(scoped_refptr<base::SequencedTaskRunner> task_runner); + bool IsInitialized(); + + void RegisterObserver(mojo::PendingRemote<gpu::mojom::OverlayStateObserver> + promotion_hint_observer, + const gpu::Mailbox& mailbox); + void SetPromotionHint(const gpu::Mailbox& mailbox, bool promoted); + void MailboxDestroyed(const gpu::Mailbox& mailbox); + + private: + friend class base::NoDestructor<OverlayStateService>; + OverlayStateService(); + ~OverlayStateService(); + OverlayStateService(const OverlayStateService&) = delete; + OverlayStateService& operator=(const OverlayStateService&) = delete; + + void OnBoundObserverDisconnect(const gpu::Mailbox& mailbox, + mojo::RemoteSetElementId); + void OnStateChanged(const gpu::Mailbox& mailbox, + OverlayStateAggregator::PromotionState promoted); + void OnStateChangedOnTaskRunnerSequence( + const gpu::Mailbox& mailbox, + OverlayStateAggregator::PromotionState promoted); + void SetPromotionHintOnTaskRunnerSequence(const gpu::Mailbox& mailbox, + bool promoted); + void MailboxDestroyedOnTaskRunnerSequence(const gpu::Mailbox& mailbox); + + struct MailboxState { + MailboxState(); + ~MailboxState(); + OverlayStateAggregator aggregator_; + mojo::RemoteSet<OverlayStateObserver> observer_set_; + }; + + bool initialized_ = false; + base::flat_map<gpu::Mailbox, MailboxState*> mailboxes_; + scoped_refptr<base::SequencedTaskRunner> task_runner_; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_OVERLAY_STATE_WIN_OVERLAY_STATE_SERVICE_H_ diff --git a/chromium/components/viz/common/overlay_state/win/overlay_state_service_unittest.cc b/chromium/components/viz/common/overlay_state/win/overlay_state_service_unittest.cc new file mode 100644 index 00000000000..dcd05e86023 --- /dev/null +++ b/chromium/components/viz/common/overlay_state/win/overlay_state_service_unittest.cc @@ -0,0 +1,195 @@ +// Copyright 2022 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 <utility> + +#include "base/task/single_thread_task_runner.h" +#include "base/test/task_environment.h" +#include "base/threading/thread.h" +#include "components/viz/common/overlay_state/win/overlay_state_service.h" +#include "components/viz/test/viz_test_suite.h" +#include "gpu/command_buffer/common/mailbox.h" +#include "gpu/ipc/common/gpu_channel.mojom.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::Combine; +using testing::DoAll; +using testing::Return; +using testing::SetArgPointee; +using testing::Values; + +namespace viz { + +class OverlayStateServiceUnittest : public testing::Test, + public gpu::mojom::OverlayStateObserver { + public: + OverlayStateServiceUnittest() + : receiver_(this), other_thread_("Other Thread") {} + // PromotionHintObserver + void OnStateChanged(bool promoted) override; + void SetUp() override; + + protected: + void PerformRegistration(const gpu::Mailbox& mailbox); + void SetService(); + + // Helper functions for running SetPromotionHint & MailboxDestroyed calls to + // the OverlayStateService on a separate task sequence. + base::OnceClosure SetPromotionHintClosure(const gpu::Mailbox& mailbox, + bool promoted); + void SetPromotionHint(const gpu::Mailbox& mailbox, bool promoted); + void SetPromotionHintWorker(const gpu::Mailbox& mailbox, bool promoted); + base::OnceClosure DestroyMailboxClosure(const gpu::Mailbox& mailbox); + void DestroyMailbox(const gpu::Mailbox& mailbox); + void DestroyMailboxWorker(const gpu::Mailbox& mailbox); + + // In order to simulate the OverlayStateService running on GpuMain and the + // DCLayerOverlayProcessor running on VizCompositorMain we create a separate + // thread & task runner where SetPromotionHint & DestroyMailbox calls are + // executed from. + scoped_refptr<base::SingleThreadTaskRunner> other_thread_task_runner_; + + raw_ptr<OverlayStateService> service_; + mojo::Receiver<gpu::mojom::OverlayStateObserver> receiver_; + int hint_received_ = 0; + bool last_promote_value_ = false; + + private: + base::Thread other_thread_; +}; + +void OverlayStateServiceUnittest::SetUp() { + other_thread_.StartAndWaitForTesting(); + other_thread_task_runner_ = other_thread_.task_runner(); +} + +void OverlayStateServiceUnittest::OnStateChanged(bool promoted) { + hint_received_++; + last_promote_value_ = promoted; +} + +void OverlayStateServiceUnittest::PerformRegistration( + const gpu::Mailbox& mailbox) { + service_->RegisterObserver(receiver_.BindNewPipeAndPassRemote(), mailbox); +} + +void OverlayStateServiceUnittest::SetService() { + service_ = OverlayStateService::GetInstance(); + if (!service_->IsInitialized()) { + service_->Initialize(base::SequencedTaskRunnerHandle::Get()); + } +} + +void OverlayStateServiceUnittest::SetPromotionHint(const gpu::Mailbox& mailbox, + bool promoted) { + base::RunLoop run_loop; + other_thread_task_runner_->PostTask( + FROM_HERE, SetPromotionHintClosure(std::move(mailbox), promoted)); + other_thread_task_runner_->PostTask(FROM_HERE, + base::BindOnce(run_loop.QuitClosure())); + run_loop.Run(); +} + +base::OnceClosure OverlayStateServiceUnittest::SetPromotionHintClosure( + const gpu::Mailbox& mailbox, + bool promoted) { + return base::BindOnce(&OverlayStateServiceUnittest::SetPromotionHintWorker, + base::Unretained(this), std::move(mailbox), promoted); +} + +void OverlayStateServiceUnittest::SetPromotionHintWorker( + const gpu::Mailbox& mailbox, + bool promoted) { + service_->SetPromotionHint(mailbox, promoted); +} + +void OverlayStateServiceUnittest::DestroyMailbox(const gpu::Mailbox& mailbox) { + base::RunLoop run_loop; + other_thread_task_runner_->PostTask( + FROM_HERE, DestroyMailboxClosure(std::move(mailbox))); + other_thread_task_runner_->PostTask(FROM_HERE, + base::BindOnce(run_loop.QuitClosure())); + run_loop.Run(); +} + +base::OnceClosure OverlayStateServiceUnittest::DestroyMailboxClosure( + const gpu::Mailbox& mailbox) { + return base::BindOnce(&OverlayStateServiceUnittest::DestroyMailboxWorker, + base::Unretained(this), std::move(mailbox)); +} + +void OverlayStateServiceUnittest::DestroyMailboxWorker( + const gpu::Mailbox& mailbox) { + service_->MailboxDestroyed(mailbox); +} + +TEST_F(OverlayStateServiceUnittest, ServiceSingleton) { + OverlayStateService* service = OverlayStateService::GetInstance(); + EXPECT_NE(service, nullptr); + + OverlayStateService* service_instance_2 = OverlayStateService::GetInstance(); + EXPECT_EQ(service, service_instance_2); +} + +TEST_F(OverlayStateServiceUnittest, AddObserver) { + SetService(); + + // Add observer for a new mailbox + gpu::Mailbox mailbox = gpu::Mailbox::Generate(); + PerformRegistration(mailbox); + + // Add observer for existing mailbox with set promotion state + gpu::Mailbox mailbox2 = gpu::Mailbox::Generate(); + SetPromotionHint(mailbox2, true); + VizTestSuite::RunUntilIdle(); + receiver_.reset(); + PerformRegistration(mailbox2); + VizTestSuite::RunUntilIdle(); + EXPECT_EQ(hint_received_, 1); + EXPECT_EQ(last_promote_value_, true); +} + +TEST_F(OverlayStateServiceUnittest, SetHint) { + SetService(); + gpu::Mailbox mailbox = gpu::Mailbox::Generate(); + PerformRegistration(mailbox); + VizTestSuite::RunUntilIdle(); + SetPromotionHint(mailbox, true); + VizTestSuite::RunUntilIdle(); + EXPECT_EQ(hint_received_, 1); + EXPECT_EQ(last_promote_value_, true); + SetPromotionHint(mailbox, false); + VizTestSuite::RunUntilIdle(); + EXPECT_EQ(hint_received_, 2); + EXPECT_EQ(last_promote_value_, false); +} + +TEST_F(OverlayStateServiceUnittest, DeleteMailbox) { + SetService(); + gpu::Mailbox mailbox = gpu::Mailbox::Generate(); + PerformRegistration(mailbox); + VizTestSuite::RunUntilIdle(); + SetPromotionHint(mailbox, true); + VizTestSuite::RunUntilIdle(); + EXPECT_EQ(hint_received_, 1); + EXPECT_EQ(last_promote_value_, true); + // Tell the OverlayStateService the mailbox has been destroyed, but + // don't actually destroy the mailbox for testing purposes as we want to + // ensure another mailbox with the same identifier is treated as a separate + // entity. + DestroyMailbox(mailbox); + // Send another promotion hint for the mailbox - we expect that we will not + // receive a hint changed callback this time because when the mailbox was + // "destroyed" any registered observers should have been removed. + SetPromotionHint(mailbox, false); + VizTestSuite::RunUntilIdle(); + EXPECT_EQ(hint_received_, 1); + EXPECT_EQ(last_promote_value_, true); +} + +} // namespace viz diff --git a/chromium/components/viz/common/quads/compositor_render_pass_unittest.cc b/chromium/components/viz/common/quads/compositor_render_pass_unittest.cc index c6d54156909..dcc1d09ddd6 100644 --- a/chromium/components/viz/common/quads/compositor_render_pass_unittest.cc +++ b/chromium/components/viz/common/quads/compositor_render_pass_unittest.cc @@ -333,7 +333,7 @@ TEST(CompositorRenderPassTest, ReplacedQuadsGetColor) { quad->SetNew(quad_state, quad_rect, quad_rect, SK_ColorRED, false); pass->ReplaceExistingQuadWithSolidColor(pass->quad_list.begin(), SK_ColorGREEN, SkBlendMode::kSrcOver); - EXPECT_EQ(SK_ColorGREEN, quad->color); + EXPECT_EQ(SkColors::kGreen, quad->color); } TEST(CompositorRenderPassTest, ReplacedQuadsGetBlendMode) { diff --git a/chromium/components/viz/common/quads/debug_border_draw_quad.cc b/chromium/components/viz/common/quads/debug_border_draw_quad.cc index f519911e3be..ef991ec8a21 100644 --- a/chromium/components/viz/common/quads/debug_border_draw_quad.cc +++ b/chromium/components/viz/common/quads/debug_border_draw_quad.cc @@ -20,7 +20,8 @@ void DebugBorderDrawQuad::SetNew(const SharedQuadState* shared_quad_state, bool needs_blending = SkColorGetA(c) < 255; DrawQuad::SetAll(shared_quad_state, DrawQuad::Material::kDebugBorder, rect, visible_rect, needs_blending); - color = c; + // TODO(crbug/1308932) remove FromColor and make all SkColor4f + color = SkColor4f::FromColor(c); width = w; } @@ -32,7 +33,8 @@ void DebugBorderDrawQuad::SetAll(const SharedQuadState* shared_quad_state, int w) { DrawQuad::SetAll(shared_quad_state, DrawQuad::Material::kDebugBorder, rect, visible_rect, needs_blending); - color = c; + // TODO(crbug/1308932) remove FromColor and make all SkColor4f + color = SkColor4f::FromColor(c); width = w; } @@ -44,7 +46,8 @@ const DebugBorderDrawQuad* DebugBorderDrawQuad::MaterialCast( void DebugBorderDrawQuad::ExtendValue( base::trace_event::TracedValue* value) const { - value->SetString("color", color_utils::SkColorToRgbaString(color)); + value->SetString("color", + color_utils::SkColorToRgbaString(color.toSkColor())); value->SetInteger("width", width); } diff --git a/chromium/components/viz/common/quads/debug_border_draw_quad.h b/chromium/components/viz/common/quads/debug_border_draw_quad.h index 8772645f855..9d8119e6912 100644 --- a/chromium/components/viz/common/quads/debug_border_draw_quad.h +++ b/chromium/components/viz/common/quads/debug_border_draw_quad.h @@ -28,7 +28,7 @@ class VIZ_COMMON_EXPORT DebugBorderDrawQuad : public DrawQuad { SkColor c, int w); - SkColor color = SK_ColorTRANSPARENT; + SkColor4f color = SkColors::kTransparent; int width = 0; static const DebugBorderDrawQuad* MaterialCast(const DrawQuad*); diff --git a/chromium/components/viz/common/quads/draw_quad_unittest.cc b/chromium/components/viz/common/quads/draw_quad_unittest.cc index 8f0a24327e1..ca9c0f29d06 100644 --- a/chromium/components/viz/common/quads/draw_quad_unittest.cc +++ b/chromium/components/viz/common/quads/draw_quad_unittest.cc @@ -179,12 +179,12 @@ TEST(DrawQuadTest, CopyDebugBorderDrawQuad) { CREATE_QUAD_NEW(DebugBorderDrawQuad, visible_rect, color, width); EXPECT_EQ(DrawQuad::Material::kDebugBorder, copy_quad->material); EXPECT_EQ(visible_rect, copy_quad->visible_rect); - EXPECT_EQ(color, copy_quad->color); + EXPECT_EQ(SkColor4f::FromColor(color), copy_quad->color); EXPECT_EQ(width, copy_quad->width); CREATE_QUAD_ALL(DebugBorderDrawQuad, color, width); EXPECT_EQ(DrawQuad::Material::kDebugBorder, copy_quad->material); - EXPECT_EQ(color, copy_quad->color); + EXPECT_EQ(SkColor4f::FromColor(color), copy_quad->color); EXPECT_EQ(width, copy_quad->width); } @@ -226,6 +226,7 @@ TEST(DrawQuadTest, CopyRenderPassDrawQuad) { TEST(DrawQuadTest, CopySolidColorDrawQuad) { gfx::Rect visible_rect(40, 50, 30, 20); + // TODO(crbug.com/1308932): Use SkColor4f here SkColor color = 0x49494949; bool force_anti_aliasing_off = false; CREATE_SHARED_STATE(); @@ -234,12 +235,12 @@ TEST(DrawQuadTest, CopySolidColorDrawQuad) { force_anti_aliasing_off); EXPECT_EQ(DrawQuad::Material::kSolidColor, copy_quad->material); EXPECT_EQ(visible_rect, copy_quad->visible_rect); - EXPECT_EQ(color, copy_quad->color); + EXPECT_EQ(SkColor4f::FromColor(color), copy_quad->color); EXPECT_EQ(force_anti_aliasing_off, copy_quad->force_anti_aliasing_off); CREATE_QUAD_ALL(SolidColorDrawQuad, color, force_anti_aliasing_off); EXPECT_EQ(DrawQuad::Material::kSolidColor, copy_quad->material); - EXPECT_EQ(color, copy_quad->color); + EXPECT_EQ(SkColor4f::FromColor(color), copy_quad->color); EXPECT_EQ(force_anti_aliasing_off, copy_quad->force_anti_aliasing_off); } diff --git a/chromium/components/viz/common/quads/render_pass_io.cc b/chromium/components/viz/common/quads/render_pass_io.cc index 51f57f46f3c..4a3fa5d6c68 100644 --- a/chromium/components/viz/common/quads/render_pass_io.cc +++ b/chromium/components/viz/common/quads/render_pass_io.cc @@ -160,6 +160,50 @@ bool PointFromDict(const base::Value& dict, gfx::Point* point) { return true; } +base::Value SkColor4fToDict(const SkColor4f color) { + base::Value dict(base::Value::Type::DICTIONARY); + dict.SetDoubleKey("red", color.fR); + dict.SetDoubleKey("green", color.fG); + dict.SetDoubleKey("blue", color.fB); + dict.SetDoubleKey("alpha", color.fA); + return dict; +} + +bool SkColor4fFromDict(const base::Value& dict, SkColor4f* color) { + DCHECK(color); + if (!dict.is_dict()) + return false; + absl::optional<double> red = dict.FindDoubleKey("red"); + absl::optional<double> green = dict.FindDoubleKey("green"); + absl::optional<double> blue = dict.FindDoubleKey("blue"); + absl::optional<double> alpha = dict.FindDoubleKey("alpha"); + if (!red || !green || !blue || !alpha) + return false; + color->fR = static_cast<float>(red.value()); + color->fG = static_cast<float>(green.value()); + color->fB = static_cast<float>(blue.value()); + color->fA = static_cast<float>(alpha.value()); + return true; +} + +// Many quads now store color as an SkColor4f, but older logs will still store +// SkColors (which are ints). For backward compatibility's sake, read either. +bool ColorFromDict(const base::Value& dict, + base::StringPiece key, + SkColor& output_color) { + const base::Value* color_key = dict.FindDictKey(key); + SkColor4f color_4f; + if (!color_key || !SkColor4fFromDict(*color_key, &color_4f)) { + absl::optional<int> color_int = dict.FindIntKey(key); + if (!color_int) + return false; + output_color = static_cast<SkColor>(color_int.value()); + return true; + } + output_color = color_4f.toSkColor(); + return true; +} + base::Value PointFToDict(const gfx::PointF& point) { base::Value dict(base::Value::Type::DICTIONARY); dict.SetDoubleKey("x", point.x()); @@ -467,7 +511,7 @@ base::Value FilterOperationToDict(const cc::FilterOperation& filter) { dict.SetKey("drop_shadow_offset", PointToDict(filter.drop_shadow_offset())); dict.SetIntKey("drop_shadow_color", - bit_cast<int>(filter.drop_shadow_color())); + base::bit_cast<int>(filter.drop_shadow_color())); break; case cc::FilterOperation::REFERENCE: dict.SetStringKey("image_filter", @@ -540,7 +584,7 @@ bool FilterOperationFromDict(const base::Value& dict, } filter.set_drop_shadow_offset(offset); filter.set_drop_shadow_color( - bit_cast<SkColor>(drop_shadow_color.value())); + base::bit_cast<SkColor>(drop_shadow_color.value())); } break; case cc::FilterOperation::REFERENCE: if (!image_filter) @@ -649,6 +693,7 @@ const char* ColorSpaceTransferIdToString(gfx::ColorSpace::TransferID id) { MATCH_ENUM_CASE(TransferID, CUSTOM) MATCH_ENUM_CASE(TransferID, CUSTOM_HDR) MATCH_ENUM_CASE(TransferID, PIECEWISE_HDR) + MATCH_ENUM_CASE(TransferID, SCRGB_LINEAR_80_NITS) } } @@ -729,6 +774,7 @@ uint8_t StringToColorSpaceTransferId(const std::string& token) { MATCH_ENUM_CASE(TransferID, CUSTOM) MATCH_ENUM_CASE(TransferID, CUSTOM_HDR) MATCH_ENUM_CASE(TransferID, PIECEWISE_HDR) + MATCH_ENUM_CASE(TransferID, SCRGB_LINEAR_80_NITS) return -1; } @@ -1131,7 +1177,7 @@ void SolidColorDrawQuadToDict(const SolidColorDrawQuad* draw_quad, base::Value* dict) { DCHECK(draw_quad); DCHECK(dict); - dict->SetIntKey("color", static_cast<int>(draw_quad->color)); + dict->SetKey("color", SkColor4fToDict(draw_quad->color)); dict->SetBoolKey("force_anti_aliasing_off", draw_quad->force_anti_aliasing_off); } @@ -1178,8 +1224,8 @@ void SurfaceDrawQuadToDict(const SurfaceDrawQuad* draw_quad, DCHECK(draw_quad); DCHECK(dict); dict->SetKey("surface_range", SurfaceRangeToDict(draw_quad->surface_range)); - dict->SetIntKey("default_background_color", - bit_cast<int>(draw_quad->default_background_color)); + dict->SetKey("default_background_color", + SkColor4fToDict(draw_quad->default_background_color)); dict->SetBoolKey("stretch_content", draw_quad->stretch_content_to_fill_bounds); dict->SetBoolKey("is_reflection", draw_quad->is_reflection); @@ -1193,8 +1239,8 @@ void TextureDrawQuadToDict(const TextureDrawQuad* draw_quad, dict->SetBoolKey("premultiplied_alpha", draw_quad->premultiplied_alpha); dict->SetKey("uv_top_left", PointFToDict(draw_quad->uv_top_left)); dict->SetKey("uv_bottom_right", PointFToDict(draw_quad->uv_bottom_right)); - dict->SetIntKey("background_color", - static_cast<int>(draw_quad->background_color)); + dict->SetKey("background_color", + SkColor4fToDict(draw_quad->background_color)); dict->SetKey("vertex_opacity", FloatArrayToList(draw_quad->vertex_opacity)); dict->SetBoolKey("y_flipped", draw_quad->y_flipped); dict->SetBoolKey("nearest_neighbor", draw_quad->nearest_neighbor); @@ -1355,13 +1401,17 @@ bool SolidColorDrawQuadFromDict(const base::Value& dict, DCHECK(draw_quad); if (!dict.is_dict()) return false; - absl::optional<int> color = dict.FindIntKey("color"); absl::optional<bool> force_anti_aliasing_off = dict.FindBoolKey("force_anti_aliasing_off"); - if (!color || !force_anti_aliasing_off) + if (!force_anti_aliasing_off) return false; + + SkColor t_color; + if (!ColorFromDict(dict, "color", t_color)) + return false; + draw_quad->SetAll(common.shared_quad_state, common.rect, common.visible_rect, - common.needs_blending, static_cast<SkColor>(color.value()), + common.needs_blending, t_color, force_anti_aliasing_off.value()); return true; } @@ -1413,19 +1463,21 @@ bool SurfaceDrawQuadFromDict(const base::Value& dict, return false; absl::optional<SurfaceRange> surface_range = SurfaceRangeFromDict(*surface_range_dict); - absl::optional<int> default_background_color = - dict.FindIntKey("default_background_color"); absl::optional<bool> stretch_content = dict.FindBoolKey("stretch_content"); absl::optional<bool> is_reflection = dict.FindBoolKey("is_reflection"); absl::optional<bool> allow_merge = dict.FindBoolKey("allow_merge"); - if (!surface_range || !default_background_color || !stretch_content || - !is_reflection || !allow_merge) + if (!surface_range || !stretch_content || !is_reflection || !allow_merge) + return false; + + SkColor t_default_background_color; + if (!ColorFromDict(dict, "default_background_color", + t_default_background_color)) return false; draw_quad->SetAll(common.shared_quad_state, common.rect, common.visible_rect, common.needs_blending, *surface_range, - bit_cast<SkColor>(*default_background_color), - *stretch_content, *is_reflection, *allow_merge); + t_default_background_color, *stretch_content, + *is_reflection, *allow_merge); return true; } @@ -1442,7 +1494,6 @@ bool TextureDrawQuadFromDict(const base::Value& dict, dict.FindBoolKey("premultiplied_alpha"); const base::Value* uv_top_left = dict.FindDictKey("uv_top_left"); const base::Value* uv_bottom_right = dict.FindDictKey("uv_bottom_right"); - absl::optional<int> background_color = dict.FindIntKey("background_color"); const base::Value* vertex_opacity = dict.FindListKey("vertex_opacity"); const base::Value* damage_rect = dict.FindDictKey("damage_rect"); absl::optional<bool> y_flipped = dict.FindBoolKey("y_flipped"); @@ -1455,7 +1506,7 @@ bool TextureDrawQuadFromDict(const base::Value& dict, dict.FindDictKey("resource_size_in_pixels"); if (!premultiplied_alpha || !uv_top_left || !uv_bottom_right || - !background_color || !vertex_opacity || !y_flipped || !nearest_neighbor || + !vertex_opacity || !y_flipped || !nearest_neighbor || !secure_output_only || !protected_video_type || !resource_size_in_pixels) { return false; @@ -1466,9 +1517,11 @@ bool TextureDrawQuadFromDict(const base::Value& dict, return false; gfx::PointF t_uv_top_left, t_uv_bottom_right; gfx::Size t_resource_size_in_pixels; + SkColor t_background_color; if (!PointFFromDict(*uv_top_left, &t_uv_top_left) || !PointFFromDict(*uv_bottom_right, &t_uv_bottom_right) || - !SizeFromDict(*resource_size_in_pixels, &t_resource_size_in_pixels)) { + !SizeFromDict(*resource_size_in_pixels, &t_resource_size_in_pixels) || + !ColorFromDict(dict, "background_color", t_background_color)) { return false; } float t_vertex_opacity[4]; @@ -1480,8 +1533,8 @@ bool TextureDrawQuadFromDict(const base::Value& dict, common.shared_quad_state, common.rect, common.visible_rect, common.needs_blending, resource_id, t_resource_size_in_pixels, premultiplied_alpha.value(), t_uv_top_left, t_uv_bottom_right, - static_cast<SkColor>(background_color.value()), t_vertex_opacity, - y_flipped.value(), nearest_neighbor.value(), secure_output_only.value(), + t_background_color, t_vertex_opacity, y_flipped.value(), + nearest_neighbor.value(), secure_output_only.value(), static_cast<gfx::ProtectedVideoType>(protected_video_type_index)); gfx::Rect t_damage_rect; diff --git a/chromium/components/viz/common/quads/shared_quad_state.cc b/chromium/components/viz/common/quads/shared_quad_state.cc index a96dcaff5cc..b2d89498de6 100644 --- a/chromium/components/viz/common/quads/shared_quad_state.cc +++ b/chromium/components/viz/common/quads/shared_quad_state.cc @@ -48,9 +48,15 @@ void SharedQuadState::AsValueInto(base::trace_event::TracedValue* value) const { visible_quad_layer_rect, value); cc::MathUtil::AddToTracedValue("mask_filter_bounds", mask_filter_info.bounds(), value); - cc::MathUtil::AddCornerRadiiToTracedValue( - "mask_filter_rounded_corners_radii", - mask_filter_info.rounded_corner_bounds(), value); + if (mask_filter_info.HasRoundedCorners()) { + cc::MathUtil::AddCornerRadiiToTracedValue( + "mask_filter_rounded_corners_radii", + mask_filter_info.rounded_corner_bounds(), value); + } + if (mask_filter_info.HasGradientMask()) { + cc::MathUtil::AddToTracedValue("mask_filter_gradient_mask", + mask_filter_info.gradient_mask(), value); + } if (clip_rect) { cc::MathUtil::AddToTracedValue("clip_rect", *clip_rect, value); diff --git a/chromium/components/viz/common/quads/solid_color_draw_quad.cc b/chromium/components/viz/common/quads/solid_color_draw_quad.cc index b69a713edf8..c7029b7ec9d 100644 --- a/chromium/components/viz/common/quads/solid_color_draw_quad.cc +++ b/chromium/components/viz/common/quads/solid_color_draw_quad.cc @@ -11,7 +11,7 @@ namespace viz { SolidColorDrawQuad::SolidColorDrawQuad() - : color(0), force_anti_aliasing_off(false) {} + : color(SkColors::kTransparent), force_anti_aliasing_off(false) {} void SolidColorDrawQuad::SetNew(const SharedQuadState* shared_quad_state, const gfx::Rect& rect, @@ -21,7 +21,7 @@ void SolidColorDrawQuad::SetNew(const SharedQuadState* shared_quad_state, bool needs_blending = SkColorGetA(c) != 255; DrawQuad::SetAll(shared_quad_state, DrawQuad::Material::kSolidColor, rect, visible_rect, needs_blending); - color = c; + color = SkColor4f::FromColor(c); force_anti_aliasing_off = anti_aliasing_off; } @@ -33,7 +33,7 @@ void SolidColorDrawQuad::SetAll(const SharedQuadState* shared_quad_state, bool anti_aliasing_off) { DrawQuad::SetAll(shared_quad_state, DrawQuad::Material::kSolidColor, rect, visible_rect, needs_blending); - color = c; + color = SkColor4f::FromColor(c); force_anti_aliasing_off = anti_aliasing_off; } @@ -45,7 +45,7 @@ const SolidColorDrawQuad* SolidColorDrawQuad::MaterialCast( void SolidColorDrawQuad::ExtendValue( base::trace_event::TracedValue* value) const { - value->SetString("color", color_utils::SkColorToRgbaString(color)); + value->SetString("color", color_utils::SkColor4fToRgbaString(color)); value->SetBoolean("force_anti_aliasing_off", force_anti_aliasing_off); } diff --git a/chromium/components/viz/common/quads/solid_color_draw_quad.h b/chromium/components/viz/common/quads/solid_color_draw_quad.h index 129a06562fd..0d5b3ceedf1 100644 --- a/chromium/components/viz/common/quads/solid_color_draw_quad.h +++ b/chromium/components/viz/common/quads/solid_color_draw_quad.h @@ -28,8 +28,8 @@ class VIZ_COMMON_EXPORT SolidColorDrawQuad : public DrawQuad { SkColor c, bool anti_aliasing_off); - SkColor color; - bool force_anti_aliasing_off; + SkColor4f color; + bool force_anti_aliasing_off = false; static const SolidColorDrawQuad* MaterialCast(const DrawQuad*); diff --git a/chromium/components/viz/common/quads/surface_draw_quad.cc b/chromium/components/viz/common/quads/surface_draw_quad.cc index 3eaf425e7bf..beb51546b9c 100644 --- a/chromium/components/viz/common/quads/surface_draw_quad.cc +++ b/chromium/components/viz/common/quads/surface_draw_quad.cc @@ -30,7 +30,8 @@ void SurfaceDrawQuad::SetNew(const SharedQuadState* shared_quad_state, DrawQuad::SetAll(shared_quad_state, DrawQuad::Material::kSurfaceContent, rect, visible_rect, needs_blending); surface_range = range; - default_background_color = background_color; + // TODO(crbug/1308932) remove FromColor and make all SkColor4f + default_background_color = SkColor4f::FromColor(background_color); stretch_content_to_fill_bounds = stretch_content; } @@ -46,7 +47,8 @@ void SurfaceDrawQuad::SetAll(const SharedQuadState* shared_quad_state, DrawQuad::SetAll(shared_quad_state, DrawQuad::Material::kSurfaceContent, rect, visible_rect, needs_blending); surface_range = range; - default_background_color = background_color; + // TODO(crbug/1308932) remove FromColor and make all SkColor4f + default_background_color = SkColor4f::FromColor(background_color); stretch_content_to_fill_bounds = stretch_content; is_reflection = reflection; allow_merge = merge; diff --git a/chromium/components/viz/common/quads/surface_draw_quad.h b/chromium/components/viz/common/quads/surface_draw_quad.h index 3516469da17..740aa89fd73 100644 --- a/chromium/components/viz/common/quads/surface_draw_quad.h +++ b/chromium/components/viz/common/quads/surface_draw_quad.h @@ -38,7 +38,7 @@ class VIZ_COMMON_EXPORT SurfaceDrawQuad : public DrawQuad { bool merge); SurfaceRange surface_range; - SkColor default_background_color = SK_ColorWHITE; + SkColor4f default_background_color = SkColors::kWhite; bool stretch_content_to_fill_bounds = false; bool is_reflection = false; // If true, allows this surface to be merged into the embedding surface, diff --git a/chromium/components/viz/common/quads/texture_draw_quad.cc b/chromium/components/viz/common/quads/texture_draw_quad.cc index 617acd367a4..3686ed4607c 100644 --- a/chromium/components/viz/common/quads/texture_draw_quad.cc +++ b/chromium/components/viz/common/quads/texture_draw_quad.cc @@ -53,7 +53,7 @@ void TextureDrawQuad::SetNew(const SharedQuadState* shared_quad_state, premultiplied_alpha = premultiplied; uv_top_left = top_left; uv_bottom_right = bottom_right; - background_color = background; + background_color = SkColor4f::FromColor(background); vertex_opacity[0] = opacity[0]; vertex_opacity[1] = opacity[1]; vertex_opacity[2] = opacity[2]; @@ -87,7 +87,7 @@ void TextureDrawQuad::SetAll(const SharedQuadState* shared_quad_state, premultiplied_alpha = premultiplied; uv_top_left = top_left; uv_bottom_right = bottom_right; - background_color = background; + background_color = SkColor4f::FromColor(background); vertex_opacity[0] = opacity[0]; vertex_opacity[1] = opacity[1]; vertex_opacity[2] = opacity[2]; @@ -112,7 +112,7 @@ void TextureDrawQuad::ExtendValue(base::trace_event::TracedValue* value) const { cc::MathUtil::AddToTracedValue("uv_bottom_right", uv_bottom_right, value); value->SetString("background_color", - color_utils::SkColorToRgbaString(background_color)); + color_utils::SkColor4fToRgbaString(background_color)); value->BeginArray("vertex_opacity"); for (size_t i = 0; i < 4; ++i) diff --git a/chromium/components/viz/common/quads/texture_draw_quad.h b/chromium/components/viz/common/quads/texture_draw_quad.h index 3f8ecefa1a4..6a9d0982c0c 100644 --- a/chromium/components/viz/common/quads/texture_draw_quad.h +++ b/chromium/components/viz/common/quads/texture_draw_quad.h @@ -61,7 +61,7 @@ class VIZ_COMMON_EXPORT TextureDrawQuad : public DrawQuad { gfx::PointF uv_top_left; gfx::PointF uv_bottom_right; - SkColor background_color = SK_ColorTRANSPARENT; + SkColor4f background_color = SkColors::kTransparent; float vertex_opacity[4] = {0, 0, 0, 0}; bool y_flipped : 1; bool nearest_neighbor : 1; diff --git a/chromium/components/viz/common/resources/resource_format_utils.cc b/chromium/components/viz/common/resources/resource_format_utils.cc index 52dc3be16a3..7e93dcb77e4 100644 --- a/chromium/components/viz/common/resources/resource_format_utils.cc +++ b/chromium/components/viz/common/resources/resource_format_utils.cc @@ -266,44 +266,6 @@ unsigned int GLInternalFormat(ResourceFormat format) { } } -unsigned int GLCopyTextureInternalFormat(ResourceFormat format) { - // In GLES2, valid formats for glCopyTexImage2D are: GL_ALPHA, GL_LUMINANCE, - // GL_LUMINANCE_ALPHA, GL_RGB, or GL_RGBA. - // Extensions typically used for glTexImage2D do not also work for - // glCopyTexImage2D. For instance GL_BGRA_EXT may not be used for - // anything but gl(Sub)TexImage2D: - // https://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_format_BGRA8888.txt - DCHECK_LE(format, RESOURCE_FORMAT_MAX); - static const GLenum format_gl_data_format[] = { - GL_RGBA, // RGBA_8888 - GL_RGBA, // RGBA_4444 - GL_RGBA, // BGRA_8888 - GL_ALPHA, // ALPHA_8 - GL_LUMINANCE, // LUMINANCE_8 - GL_RGB, // RGB_565 - GL_RGB, // BGR_565 - GL_RGB, // ETC1 - GL_LUMINANCE, // RED_8 - GL_RGBA, // RG_88 - GL_LUMINANCE, // LUMINANCE_F16 - GL_RGBA, // RGBA_F16 - GL_LUMINANCE, // R16_EXT - GL_RGBA, // RG16_EXT - GL_RGB, // RGBX_8888 - GL_RGB, // BGRX_8888 - GL_ZERO, // RGBA_1010102 - GL_ZERO, // BGRA_1010102 - GL_ZERO, // YVU_420 - GL_ZERO, // YUV_420_BIPLANAR - GL_ZERO, // P010 - }; - - static_assert(std::size(format_gl_data_format) == (RESOURCE_FORMAT_MAX + 1), - "format_gl_data_format does not handle all cases."); - - return format_gl_data_format[format]; -} - gfx::BufferFormat BufferFormat(ResourceFormat format) { switch (format) { case BGRA_8888: diff --git a/chromium/components/viz/common/resources/resource_format_utils.h b/chromium/components/viz/common/resources/resource_format_utils.h index fe2ee221a20..ae1328b9e6b 100644 --- a/chromium/components/viz/common/resources/resource_format_utils.h +++ b/chromium/components/viz/common/resources/resource_format_utils.h @@ -36,8 +36,6 @@ SkColorTypeToResourceFormat(SkColorType color_type); VIZ_RESOURCE_FORMAT_EXPORT unsigned int GLDataType(ResourceFormat format); VIZ_RESOURCE_FORMAT_EXPORT unsigned int GLDataFormat(ResourceFormat format); VIZ_RESOURCE_FORMAT_EXPORT unsigned int GLInternalFormat(ResourceFormat format); -VIZ_RESOURCE_FORMAT_EXPORT unsigned int GLCopyTextureInternalFormat( - ResourceFormat format); // Returns the pixel format of the resource when mapped into client-side memory. // Returns a default value when IsGpuMemoryBufferFormatSupported() returns false diff --git a/chromium/components/viz/common/resources/transferable_resource.h b/chromium/components/viz/common/resources/transferable_resource.h index 0a360bc3ade..4f07426d0db 100644 --- a/chromium/components/viz/common/resources/transferable_resource.h +++ b/chromium/components/viz/common/resources/transferable_resource.h @@ -27,6 +27,22 @@ namespace viz { struct ReturnedResource; struct VIZ_COMMON_EXPORT TransferableResource { + enum class SynchronizationType : uint8_t { + // Commands issued (SyncToken) - a resource can be reused as soon as display + // compositor issues the latest command on it and SyncToken will be signaled + // when this happens. + kSyncToken = 0, + // Commands completed (aka read lock fence) - If a gpu resource is backed by + // a GpuMemoryBuffer, then it will be accessed out-of-band, and a gpu fence + // needs to be waited on before the resource is returned and reused. In + // other words, the resource will be returned only when gpu commands are + // completed. + kGpuCommandsCompleted, + // Commands submitted (release fence) - a resource will be returned after + // gpu service submitted commands to the gpu and provide the fence. + kReleaseFence, + }; + TransferableResource(); ~TransferableResource(); @@ -104,10 +120,10 @@ struct VIZ_COMMON_EXPORT TransferableResource { // drawing it. Typically GL_LINEAR, or GL_NEAREST if no anti-aliasing // during scaling is desired. uint32_t filter = 0; - // If a gpu resource is backed by a GpuMemoryBuffer, then it will be accessed - // out-of-band, and a gpu fence needs to be waited on before the resource is - // returned and reused. - bool read_lock_fences_enabled = false; + + // This defines when the display compositor returns resources. Clients may use + // different synchronization types based on their needs. + SynchronizationType synchronization_type = SynchronizationType::kSyncToken; // YCbCr info for resources backed by YCbCr Vulkan images. absl::optional<gpu::VulkanYCbCrInfo> ycbcr_info; @@ -145,7 +161,7 @@ struct VIZ_COMMON_EXPORT TransferableResource { #elif BUILDFLAG(IS_WIN) wants_promotion_hint == o.wants_promotion_hint && #endif - read_lock_fences_enabled == o.read_lock_fences_enabled; + synchronization_type == o.synchronization_type; } bool operator!=(const TransferableResource& o) const { return !(*this == o); } }; diff --git a/chromium/components/viz/common/surfaces/local_surface_id_unittest.cc b/chromium/components/viz/common/surfaces/local_surface_id_unittest.cc index 4a07df62e94..943d4693a86 100644 --- a/chromium/components/viz/common/surfaces/local_surface_id_unittest.cc +++ b/chromium/components/viz/common/surfaces/local_surface_id_unittest.cc @@ -38,6 +38,7 @@ TEST(LocalSurfaceIdTest, VerifyToString) { int previous_log_lvl = logging::GetMinLogLevel(); +#if BUILDFLAG(USE_RUNTIME_VLOG) // When |g_min_log_level| is set to LOG_VERBOSE we expect verbose versions // of local_surface_id::ToString(). logging::SetMinLogLevel(logging::LOG_VERBOSE); @@ -45,6 +46,7 @@ TEST(LocalSurfaceIdTest, VerifyToString) { EXPECT_EQ(verbose_expected, local_surface_id.ToString()); EXPECT_EQ(big_verbose_expected, big_local_surface_id.ToString()); EXPECT_EQ(small_verbose_expected, small_local_surface_id.ToString()); +#endif // BUILDFLAG(USE_RUNTIME_VLOG) // When |g_min_log_level| is set to LOG_INFO we expect less verbose versions // of local_surface_id::ToString(). diff --git a/chromium/components/viz/common/switches.cc b/chromium/components/viz/common/switches.cc index e6d3acbf514..c5cf3aab5df 100644 --- a/chromium/components/viz/common/switches.cc +++ b/chromium/components/viz/common/switches.cc @@ -29,10 +29,6 @@ const char kDeadlineToSynchronizeSurfaces[] = // Also implies --disable-gpu-vsync (see //ui/gl/gl_switches.h). const char kDisableFrameRateLimit[] = "disable-frame-rate-limit"; -// Slows down animations during a DocumentTransition for debugging. -const char kDocumentTransitionSlowdownFactor[] = - "document-transition-slowdown-factor"; - // Sets the number of max pending frames in the GL buffer queue to 1. const char kDoubleBufferCompositing[] = "double-buffer-compositing"; @@ -87,18 +83,4 @@ absl::optional<uint32_t> GetDeadlineToSynchronizeSurfaces() { return activation_deadline_in_frames; } -int GetDocumentTransitionSlowDownFactor() { - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - if (!command_line || - !command_line->HasSwitch(kDocumentTransitionSlowdownFactor)) - return 1; - - auto factor_str = - command_line->GetSwitchValueASCII(kDocumentTransitionSlowdownFactor); - int factor = 0; - LOG_IF(ERROR, !base::StringToInt(factor_str, &factor)) - << "Error parsing document transition slow down factor " << factor_str; - return std::max(1, factor); -} - } // namespace switches diff --git a/chromium/components/viz/common/switches.h b/chromium/components/viz/common/switches.h index b76565e96c5..a602615d40b 100644 --- a/chromium/components/viz/common/switches.h +++ b/chromium/components/viz/common/switches.h @@ -17,7 +17,6 @@ namespace switches { VIZ_COMMON_EXPORT extern const char kDeJellyScreenWidth[]; VIZ_COMMON_EXPORT extern const char kDeadlineToSynchronizeSurfaces[]; VIZ_COMMON_EXPORT extern const char kDisableFrameRateLimit[]; -VIZ_COMMON_EXPORT extern const char kDocumentTransitionSlowdownFactor[]; VIZ_COMMON_EXPORT extern const char kDoubleBufferCompositing[]; VIZ_COMMON_EXPORT extern const char kEnableDeJelly[]; VIZ_COMMON_EXPORT extern const char kEnableHardwareOverlays[]; @@ -33,7 +32,6 @@ VIZ_COMMON_EXPORT extern const char kTintCompositedContentModulate[]; VIZ_COMMON_EXPORT extern const char kShowDCLayerDebugBorders[]; VIZ_COMMON_EXPORT absl::optional<uint32_t> GetDeadlineToSynchronizeSurfaces(); -VIZ_COMMON_EXPORT int GetDocumentTransitionSlowDownFactor(); } // namespace switches diff --git a/chromium/components/viz/common/transition_utils.cc b/chromium/components/viz/common/transition_utils.cc index a59ef9e5eb3..30f611dd4e5 100644 --- a/chromium/components/viz/common/transition_utils.cc +++ b/chromium/components/viz/common/transition_utils.cc @@ -20,6 +20,10 @@ namespace viz { namespace { + +constexpr int kMaxListToProcess = 32; +constexpr int kMaxQuadsPerFrame = 8; + struct StackFrame { StackFrame(int list_index, SharedQuadStateList::ConstIterator sqs_iter, @@ -44,6 +48,10 @@ std::string SkColorToRGBAString(SkColor color) { return str.str(); } +std::string SkColorToRGBAString(SkColor4f color) { + return SkColorToRGBAString(color.toSkColor()); +} + std::unordered_set<uint64_t> ProcessStack( std::ostringstream& str, std::vector<StackFrame>& stack, @@ -78,6 +86,7 @@ std::unordered_set<uint64_t> ProcessStack( str << "(" << quad << ") CompositorRenderPassDrawQuad\n"; }; std::unordered_set<uint64_t> seen_render_pass_ids; + int quads_per_frame_logged = 0; while (!stack.empty()) { auto& frame = stack.back(); auto& pass = list[frame.list_index]; @@ -100,6 +109,14 @@ std::unordered_set<uint64_t> ProcessStack( frame.indent += 2; } else { if (++frame.quad_iter == pass->quad_list.end()) { + quads_per_frame_logged = 0; + stack.pop_back(); + continue; + } + if (++quads_per_frame_logged > kMaxQuadsPerFrame) { + write_indent(frame.indent); + str << "(more quads - orphaned list may not be correct)\n"; + quads_per_frame_logged = 0; stack.pop_back(); continue; } @@ -157,6 +174,12 @@ std::string TransitionUtils::RenderPassListToString( const CompositorRenderPassList& list) { std::ostringstream str; + if (list.size() > kMaxListToProcess) { + str << "RenderPassList too large (" << list.size() + << "), max supported list length " << kMaxListToProcess; + return str.str(); + } + std::vector<StackFrame> stack; stack.emplace_back(list.size() - 1, list.back()->shared_quad_state_list.begin(), diff --git a/chromium/components/viz/common/yuv_readback_unittest.cc b/chromium/components/viz/common/yuv_readback_unittest.cc index 6a0851cf10a..cc228ed0bca 100644 --- a/chromium/components/viz/common/yuv_readback_unittest.cc +++ b/chromium/components/viz/common/yuv_readback_unittest.cc @@ -49,17 +49,9 @@ class YUVReadbackTest : public testing::Test { context_ = std::make_unique<gpu::GLInProcessContext>(); auto result = context_->Initialize( - TestGpuServiceHolder::GetInstance()->task_executor(), - nullptr, /* surface */ - true, /* offscreen */ - gpu::kNullSurfaceHandle, /* window */ - attributes, gpu::SharedMemoryLimits(), - nullptr, /* gpu_memory_buffer_manager */ - nullptr, /* image_factory */ - nullptr, /* gpu::GpuTaskSchedulerHelper */ - nullptr, - /* gpu::DisplayCompositorMemoryAndTaskControllerOnGpu */ - base::ThreadTaskRunnerHandle::Get()); + TestGpuServiceHolder::GetInstance()->task_executor(), attributes, + gpu::SharedMemoryLimits(), + /*image_factory=*/nullptr); DCHECK_EQ(result, gpu::ContextResult::kSuccess); gl_ = context_->GetImplementation(); gpu::ContextSupport* support = context_->GetImplementation(); diff --git a/chromium/components/viz/demo/demo_main.cc b/chromium/components/viz/demo/demo_main.cc index db021f9870f..8cce35cdfe4 100644 --- a/chromium/components/viz/demo/demo_main.cc +++ b/chromium/components/viz/demo/demo_main.cc @@ -148,7 +148,7 @@ class DemoWindow : public ui::PlatformWindowDelegate { // Next, create the host and the service, and pass them the right ends of // the message-pipes. host_ = std::make_unique<demo::DemoHost>( - widget_, platform_window_->GetBounds().size(), + widget_, platform_window_->GetBoundsInPixels().size(), std::move(frame_sink_manager_client_receiver), std::move(frame_sink_manager)); diff --git a/chromium/components/viz/demo/service/demo_service.cc b/chromium/components/viz/demo/service/demo_service.cc index 2d2d153c1ad..ba174324880 100644 --- a/chromium/components/viz/demo/service/demo_service.cc +++ b/chromium/components/viz/demo/service/demo_service.cc @@ -22,7 +22,7 @@ DemoService::DemoService( params->frame_sink_manager = std::move(receiver); params->frame_sink_manager_client = std::move(client); runner_ = std::make_unique<viz::VizCompositorThreadRunnerImpl>(); - runner_->CreateFrameSinkManager(std::move(params)); + runner_->CreateFrameSinkManager(std::move(params), /*gpu_service=*/nullptr); } DemoService::~DemoService() = default; diff --git a/chromium/components/viz/host/client_frame_sink_video_capturer.h b/chromium/components/viz/host/client_frame_sink_video_capturer.h index 6775aea4a58..ce514b0a8b7 100644 --- a/chromium/components/viz/host/client_frame_sink_video_capturer.h +++ b/chromium/components/viz/host/client_frame_sink_video_capturer.h @@ -107,6 +107,10 @@ class VIZ_HOST_EXPORT ClientFrameSinkVideoCapturer // owned pointer to an Overlay. std::unique_ptr<Overlay> CreateOverlay(int32_t stacking_index); + // Getter for `format_`. Returns the pixel format set by the last call to + // `SetFormat()`, or nullopt if the format was not yet set. + absl::optional<media::VideoPixelFormat> GetFormat() const { return format_; } + private: struct ResolutionConstraints { ResolutionConstraints(const gfx::Size& min_size, diff --git a/chromium/components/viz/host/gpu_host_impl.cc b/chromium/components/viz/host/gpu_host_impl.cc index bb0369de155..60e908d2d31 100644 --- a/chromium/components/viz/host/gpu_host_impl.cc +++ b/chromium/components/viz/host/gpu_host_impl.cc @@ -136,7 +136,7 @@ GpuHostImpl::GpuHostImpl(Delegate* delegate, viz_main_->CreateGpuService( gpu_service_remote_.BindNewPipeAndPassReceiver(task_runner), gpu_host_receiver_.BindNewPipeAndPassRemote(task_runner), - std::move(discardable_manager_remote), activity_flags_.CloneHandle(), + std::move(discardable_manager_remote), activity_flags_.CloneRegion(), GetFontRenderParams().Get()->subpixel_rendering); #if defined(USE_OZONE) @@ -194,9 +194,8 @@ void GpuHostImpl::AddConnectionErrorHandler(base::OnceClosure handler) { void GpuHostImpl::BlockLiveOffscreenContexts() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - for (auto iter = urls_with_live_offscreen_contexts_.begin(); - iter != urls_with_live_offscreen_contexts_.end(); ++iter) { - delegate_->BlockDomainFrom3DAPIs(*iter, gpu::DomainGuilt::kUnknown); + for (auto& url : urls_with_live_offscreen_contexts_) { + delegate_->BlockDomainFrom3DAPIs(url, gpu::DomainGuilt::kUnknown); } } @@ -565,8 +564,8 @@ void GpuHostImpl::DidUpdateOverlayInfo(const gpu::OverlayInfo& overlay_info) { delegate_->DidUpdateOverlayInfo(overlay_info); } -void GpuHostImpl::DidUpdateHDRStatus(bool hdr_enabled) { - delegate_->DidUpdateHDRStatus(hdr_enabled); +void GpuHostImpl::DidUpdateDXGIInfo(gfx::mojom::DXGIInfoPtr dxgi_info) { + delegate_->DidUpdateDXGIInfo(std::move(dxgi_info)); } void GpuHostImpl::SetChildSurface(gpu::SurfaceHandle parent, diff --git a/chromium/components/viz/host/gpu_host_impl.h b/chromium/components/viz/host/gpu_host_impl.h index 1253e39ab93..1d77a895981 100644 --- a/chromium/components/viz/host/gpu_host_impl.h +++ b/chromium/components/viz/host/gpu_host_impl.h @@ -42,6 +42,7 @@ #if BUILDFLAG(IS_WIN) #include "services/viz/privileged/mojom/gl/info_collection_gpu_service.mojom.h" +#include "ui/gfx/mojom/dxgi_info.mojom.h" #endif namespace gfx { @@ -79,7 +80,7 @@ class VIZ_HOST_EXPORT GpuHostImpl : public mojom::GpuHost virtual void DidUpdateGPUInfo(const gpu::GPUInfo& gpu_info) = 0; #if BUILDFLAG(IS_WIN) virtual void DidUpdateOverlayInfo(const gpu::OverlayInfo& overlay_info) = 0; - virtual void DidUpdateHDRStatus(bool hdr_enabled) = 0; + virtual void DidUpdateDXGIInfo(gfx::mojom::DXGIInfoPtr dxgi_info) = 0; #endif virtual void BlockDomainFrom3DAPIs(const GURL& url, gpu::DomainGuilt guilt) = 0; @@ -250,7 +251,7 @@ class VIZ_HOST_EXPORT GpuHostImpl : public mojom::GpuHost void DidUpdateGPUInfo(const gpu::GPUInfo& gpu_info) override; #if BUILDFLAG(IS_WIN) void DidUpdateOverlayInfo(const gpu::OverlayInfo& overlay_info) override; - void DidUpdateHDRStatus(bool hdr_enabled) override; + void DidUpdateDXGIInfo(gfx::mojom::DXGIInfoPtr dxgi_info) 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 e703c39804c..e01a3e63109 100644 --- a/chromium/components/viz/host/hit_test/hit_test_query.cc +++ b/chromium/components/viz/host/hit_test/hit_test_query.cc @@ -9,7 +9,6 @@ #include <utility> #include "base/containers/stack.h" -#include "base/metrics/histogram_macros.h" #include "base/strings/string_util.h" #include "components/viz/common/features.h" #include "components/viz/common/hit_test/hit_test_region_list.h" @@ -194,9 +193,6 @@ bool HitTestQuery::FindTargetInRegionForLocation( target->frame_sink_id = hit_test_data_[region_index].frame_sink_id; target->location_in_target = gfx::PointF(); target->flags = HitTestRegionFlags::kHitTestAsk; - RecordSlowPathHitTestReasons( - AsyncHitTestReasons::kPerspectiveTransform | - hit_test_data_[region_index].async_hit_test_reasons); return true; } @@ -243,8 +239,6 @@ bool HitTestQuery::FindTargetInRegionForLocation( target->frame_sink_id = hit_test_data_[region_index].frame_sink_id; target->location_in_target = location_in_target; target->flags = flags; - RecordSlowPathHitTestReasons( - hit_test_data_[region_index].async_hit_test_reasons); return true; } @@ -273,18 +267,14 @@ bool HitTestQuery::FindTargetInRegionForLocation( !(flags & HitTestRegionFlags::kHitTestIgnore)) { target->frame_sink_id = hit_test_data_[region_index].frame_sink_id; target->location_in_target = location_in_target; - uint32_t async_hit_test_reasons = - hit_test_data_[region_index].async_hit_test_reasons; uint32_t target_flags = flags; if (root_view_overlapped) { DCHECK_EQ(hit_test_data_[region_index].async_hit_test_reasons, AsyncHitTestReasons::kOverlappedRegion); target_flags &= ~HitTestRegionFlags::kHitTestAsk; - async_hit_test_reasons = AsyncHitTestReasons::kNotAsyncHitTest; } target->flags = target_flags; // We record fast path hit testing instances with reason kNotAsyncHitTest. - RecordSlowPathHitTestReasons(async_hit_test_reasons); return true; } return false; @@ -365,27 +355,6 @@ bool HitTestQuery::GetTransformToTargetRecursively( return false; } -void HitTestQuery::RecordSlowPathHitTestReasons(uint32_t reasons) const { - static const char* kAsyncHitTestReasonsHistogramName = - "Event.VizHitTest.AsyncHitTestReasons"; - if (reasons == AsyncHitTestReasons::kNotAsyncHitTest) { - UMA_HISTOGRAM_ENUMERATION( - kAsyncHitTestReasonsHistogramName, - AsyncHitTestReasons::kNotAsyncHitTest, - AsyncHitTestReasons::kAsyncHitTestReasonCount + 1); - return; - } - - for (uint32_t i = 0; i < AsyncHitTestReasons::kAsyncHitTestReasonCount; ++i) { - unsigned val = 1 << i; - if (reasons & val) { - UMA_HISTOGRAM_ENUMERATION( - kAsyncHitTestReasonsHistogramName, i + 1, - AsyncHitTestReasons::kAsyncHitTestReasonCount + 1); - } - } -} - std::string HitTestQuery::PrintHitTestData() const { std::ostringstream oss; base::stack<uint32_t> parents; diff --git a/chromium/components/viz/host/hit_test/hit_test_query.h b/chromium/components/viz/host/hit_test/hit_test_query.h index c6c6f12ebaa..8451940c3e6 100644 --- a/chromium/components/viz/host/hit_test/hit_test_query.h +++ b/chromium/components/viz/host/hit_test/hit_test_query.h @@ -5,6 +5,7 @@ #ifndef COMPONENTS_VIZ_HOST_HIT_TEST_HIT_TEST_QUERY_H_ #define COMPONENTS_VIZ_HOST_HIT_TEST_HIT_TEST_QUERY_H_ +#include <string> #include <vector> #include "base/callback.h" @@ -153,8 +154,6 @@ class VIZ_HOST_EXPORT HitTestQuery { size_t region_index, gfx::Transform* transform) const; - void RecordSlowPathHitTestReasons(uint32_t) const; - std::vector<AggregatedHitTestRegion> hit_test_data_; }; diff --git a/chromium/components/viz/host/host_frame_sink_manager.cc b/chromium/components/viz/host/host_frame_sink_manager.cc index 1ad1e78b6a7..90429aee8e6 100644 --- a/chromium/components/viz/host/host_frame_sink_manager.cc +++ b/chromium/components/viz/host/host_frame_sink_manager.cc @@ -288,6 +288,15 @@ void HostFrameSinkManager::Throttle(const std::vector<FrameSinkId>& ids, frame_sink_manager_->Throttle(ids, interval); } +void HostFrameSinkManager::StartThrottlingAllFrameSinks( + base::TimeDelta interval) { + frame_sink_manager_->StartThrottlingAllFrameSinks(interval); +} + +void HostFrameSinkManager::StopThrottlingAllFrameSinks() { + frame_sink_manager_->StopThrottlingAllFrameSinks(); +} + void HostFrameSinkManager::AddHitTestRegionObserver( HitTestRegionObserver* observer) { observers_.AddObserver(observer); diff --git a/chromium/components/viz/host/host_frame_sink_manager.h b/chromium/components/viz/host/host_frame_sink_manager.h index 5a0d8946432..44db2fc33d9 100644 --- a/chromium/components/viz/host/host_frame_sink_manager.h +++ b/chromium/components/viz/host/host_frame_sink_manager.h @@ -194,6 +194,8 @@ class VIZ_HOST_EXPORT HostFrameSinkManager std::unique_ptr<CopyOutputRequest> request); void Throttle(const std::vector<FrameSinkId>& ids, base::TimeDelta interval); + void StartThrottlingAllFrameSinks(base::TimeDelta interval); + void StopThrottlingAllFrameSinks(); // Add/Remove an observer to receive notifications of when the host receives // new hit test data. 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 3da44d37dcd..79c205298b1 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 @@ -186,7 +186,9 @@ class TestGpuService : public mojom::GpuService { void GetPeakMemoryUsage(uint32_t sequence_num, GetPeakMemoryUsageCallback callback) override {} - void RequestHDRStatus(RequestHDRStatusCallback callback) override {} +#if BUILDFLAG(IS_WIN) + void RequestDXGIInfo(RequestDXGIInfoCallback callback) override {} +#endif void LoadedShader(int32_t client_id, const std::string& key, diff --git a/chromium/components/viz/host/renderer_settings_creation.cc b/chromium/components/viz/host/renderer_settings_creation.cc index 6a830ec9f29..9d34ced3660 100644 --- a/chromium/components/viz/host/renderer_settings_creation.cc +++ b/chromium/components/viz/host/renderer_settings_creation.cc @@ -66,7 +66,6 @@ RendererSettings CreateRendererSettings() { #endif renderer_settings.allow_antialiasing = !command_line->HasSwitch(switches::kDisableCompositedAntialiasing); - renderer_settings.use_skia_renderer = features::IsUsingSkiaRenderer(); if (command_line->HasSwitch(switches::kSlowDownCompositingScaleFactor)) { const int kMinSlowDownScaleFactor = 1; diff --git a/chromium/components/viz/service/BUILD.gn b/chromium/components/viz/service/BUILD.gn index 002b3ddfb0f..10d10f49e7a 100644 --- a/chromium/components/viz/service/BUILD.gn +++ b/chromium/components/viz/service/BUILD.gn @@ -39,12 +39,12 @@ viz_component("service") { "display/display.cc", "display/display.h", "display/display_client.h", + "display/display_compositor_memory_and_task_controller.cc", + "display/display_compositor_memory_and_task_controller.h", "display/display_damage_tracker.cc", "display/display_damage_tracker.h", "display/display_resource_provider.cc", "display/display_resource_provider.h", - "display/display_resource_provider_gl.cc", - "display/display_resource_provider_gl.h", "display/display_resource_provider_null.cc", "display/display_resource_provider_null.h", "display/display_resource_provider_skia.cc", @@ -57,22 +57,10 @@ viz_component("service") { "display/display_scheduler_base.h", "display/draw_polygon.cc", "display/draw_polygon.h", - "display/dynamic_geometry_binding.cc", - "display/dynamic_geometry_binding.h", "display/external_use_client.cc", "display/external_use_client.h", "display/frame_rate_decider.cc", "display/frame_rate_decider.h", - "display/geometry_binding.cc", - "display/geometry_binding.h", - "display/gl_renderer.cc", - "display/gl_renderer.h", - "display/gl_renderer_copier.cc", - "display/gl_renderer_copier.h", - "display/gl_renderer_draw_cache.cc", - "display/gl_renderer_draw_cache.h", - "display/layer_quad.cc", - "display/layer_quad.h", "display/null_renderer.cc", "display/null_renderer.h", "display/output_surface.cc", @@ -92,19 +80,11 @@ viz_component("service") { "display/overlay_processor_stub.h", "display/pending_swap_params.cc", "display/pending_swap_params.h", - "display/program_binding.cc", - "display/program_binding.h", "display/renderer_utils.cc", "display/renderer_utils.h", "display/resolved_frame_data.cc", "display/resolved_frame_data.h", "display/resource_fence.h", - "display/scoped_gpu_memory_buffer_texture.cc", - "display/scoped_gpu_memory_buffer_texture.h", - "display/scoped_render_pass_texture.cc", - "display/scoped_render_pass_texture.h", - "display/shader.cc", - "display/shader.h", "display/shared_bitmap_manager.h", "display/skia_output_surface.cc", "display/skia_output_surface.h", @@ -114,35 +94,46 @@ viz_component("service") { "display/software_output_device.h", "display/software_renderer.cc", "display/software_renderer.h", - "display/static_geometry_binding.cc", - "display/static_geometry_binding.h", "display/surface_aggregator.cc", "display/surface_aggregator.h", - "display/sync_query_collection.cc", - "display/sync_query_collection.h", - "display/texture_deleter.cc", - "display/texture_deleter.h", "display_embedder/buffer_queue.cc", "display_embedder/buffer_queue.h", "display_embedder/compositor_gpu_thread.cc", "display_embedder/compositor_gpu_thread.h", - "display_embedder/gl_output_surface.cc", - "display_embedder/gl_output_surface.h", - "display_embedder/gl_output_surface_buffer_queue.cc", - "display_embedder/gl_output_surface_buffer_queue.h", - "display_embedder/gl_output_surface_offscreen.cc", - "display_embedder/gl_output_surface_offscreen.h", + "display_embedder/image_context_impl.cc", + "display_embedder/image_context_impl.h", "display_embedder/in_process_gpu_memory_buffer_manager.cc", "display_embedder/in_process_gpu_memory_buffer_manager.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/output_surface_provider.h", "display_embedder/output_surface_provider_impl.cc", "display_embedder/output_surface_provider_impl.h", "display_embedder/server_shared_bitmap_manager.cc", "display_embedder/server_shared_bitmap_manager.h", + "display_embedder/skia_output_device.cc", + "display_embedder/skia_output_device.h", + "display_embedder/skia_output_device_buffer_queue.cc", + "display_embedder/skia_output_device_buffer_queue.h", + "display_embedder/skia_output_device_gl.cc", + "display_embedder/skia_output_device_gl.h", + "display_embedder/skia_output_device_offscreen.cc", + "display_embedder/skia_output_device_offscreen.h", + "display_embedder/skia_output_device_webview.cc", + "display_embedder/skia_output_device_webview.h", + "display_embedder/skia_output_surface_dependency.h", + "display_embedder/skia_output_surface_dependency_impl.cc", + "display_embedder/skia_output_surface_dependency_impl.h", + "display_embedder/skia_output_surface_impl.cc", + "display_embedder/skia_output_surface_impl.h", + "display_embedder/skia_output_surface_impl_on_gpu.cc", + "display_embedder/skia_output_surface_impl_on_gpu.h", + "display_embedder/skia_render_copy_results.cc", + "display_embedder/skia_render_copy_results.h", "display_embedder/software_output_surface.cc", "display_embedder/software_output_surface.h", - "display_embedder/viz_process_context_provider.cc", - "display_embedder/viz_process_context_provider.h", "display_embedder/vsync_parameter_listener.cc", "display_embedder/vsync_parameter_listener.h", "frame_sinks/begin_frame_tracker.cc", @@ -184,6 +175,8 @@ viz_component("service") { "frame_sinks/video_capture/video_frame_pool.h", "frame_sinks/video_detector.cc", "frame_sinks/video_detector.h", + "gl/gpu_service_impl.cc", + "gl/gpu_service_impl.h", "hit_test/hit_test_aggregator.cc", "hit_test/hit_test_aggregator.h", "hit_test/hit_test_aggregator_delegate.h", @@ -225,8 +218,6 @@ viz_component("service") { defines = [ "VIZ_SERVICE_IMPLEMENTATION" ] - allow_circular_includes_from = [ ":gpu_service_dependencies" ] - deps = [ "//build:chromecast_buildflags", "//build:chromeos_buildflags", @@ -234,13 +225,12 @@ viz_component("service") { "//cc/paint", "//components/crash/core/common:crash_key", "//components/power_scheduler", - "//gpu/command_buffer/client:gles2_cmd_helper", - "//gpu/command_buffer/client:gles2_implementation", - "//gpu/command_buffer/client:raster", # Note that dependency on //gpu/ipc/client is for GpuMemoryBufferImpl. This # dependency should not be in public_deps. "//components/viz/common", + "//gpu/command_buffer/client", + "//gpu/config", "//gpu/ipc/client", "//gpu/ipc/common:common", "//gpu/ipc/common:surface_handle_type", @@ -260,14 +250,20 @@ viz_component("service") { ] public_deps = [ - ":gpu_service_dependencies", "//base", "//cc", "//cc/debug", "//components/viz/common", - "//gpu/command_buffer/client:gles2_interface", + "//gpu/command_buffer/service:gles2", + "//gpu/ipc:gl_in_process_context", + "//gpu/ipc/service", + "//gpu/vulkan:buildflags", + "//media/gpu/ipc/service", + "//media/mojo/services", "//services/viz/privileged/mojom/compositing", + "//services/viz/privileged/mojom/gl", "//services/viz/public/mojom", + "//skia", "//ui/base/prediction", "//ui/gfx", "//ui/gfx/geometry", @@ -276,9 +272,17 @@ viz_component("service") { if (is_chromeos_ash) { sources += [ - "display_embedder/gl_output_surface_chromeos.cc", - "display_embedder/gl_output_surface_chromeos.h", + "display_embedder/output_surface_unified.cc", + "display_embedder/output_surface_unified.h", ] + + deps += [ + "//components/chromeos_camera:jpeg_encode_accelerator_service", + "//components/chromeos_camera:mjpeg_decode_accelerator_service", + "//gpu/command_buffer/service:gles2", + "//media/mojo/services", + ] + if (use_v4l2_codec || use_vaapi) { deps += [ "//ash/components/arc/video_accelerator" ] } @@ -329,8 +333,6 @@ viz_component("service") { "display/overlay_processor_android.h", "display/overlay_processor_surface_control.cc", "display/overlay_processor_surface_control.h", - "display_embedder/gl_output_surface_android.cc", - "display_embedder/gl_output_surface_android.h", "frame_sinks/external_begin_frame_source_android.cc", "frame_sinks/external_begin_frame_source_android.h", "gl/throw_uncaught_exception.cc", @@ -367,85 +369,6 @@ viz_component("service") { "display_embedder/output_device_backing.h", "display_embedder/software_output_device_win.cc", "display_embedder/software_output_device_win.h", - ] - } - - if (is_chromeos_ash) { - sources += [ - "display_embedder/output_surface_unified.cc", - "display_embedder/output_surface_unified.h", - ] - } - - if (enable_vulkan) { - deps += [ "//gpu/vulkan" ] - } -} - -# The gpu_service_dependencies source set contains source files that -# use the service side GL library (ui/gl), while the rest of -# viz/service use the client side GL library. This split is needed -# because the two GL libraries are incompatible and can't compile -# together in jumbo builds. -# -# Long term all service code is moving to be in the gpu process and -# then this build target will no longer be needed. -viz_source_set("gpu_service_dependencies") { - sources = [ - "display/display_compositor_memory_and_task_controller.cc", - "display/display_compositor_memory_and_task_controller.h", - "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", - "display_embedder/skia_output_device_buffer_queue.h", - "display_embedder/skia_output_device_gl.cc", - "display_embedder/skia_output_device_gl.h", - "display_embedder/skia_output_device_offscreen.cc", - "display_embedder/skia_output_device_offscreen.h", - "display_embedder/skia_output_device_webview.cc", - "display_embedder/skia_output_device_webview.h", - "display_embedder/skia_output_surface_dependency.h", - "display_embedder/skia_output_surface_dependency_impl.cc", - "display_embedder/skia_output_surface_dependency_impl.h", - "display_embedder/skia_output_surface_impl.cc", - "display_embedder/skia_output_surface_impl.h", - "display_embedder/skia_output_surface_impl_on_gpu.cc", - "display_embedder/skia_output_surface_impl_on_gpu.h", - "display_embedder/skia_render_copy_results.cc", - "display_embedder/skia_render_copy_results.h", - "gl/gpu_service_impl.cc", - "gl/gpu_service_impl.h", - ] - - public_deps = [ - "//gpu/command_buffer/service:gles2", - "//gpu/ipc:gl_in_process_context", - "//gpu/ipc/service", - "//gpu/vulkan:buildflags", - "//media/gpu/ipc/service", - "//media/mojo/services", - "//services/viz/privileged/mojom/gl", - "//skia", - "//ui/latency:latency", - ] - - defines = [ "VIZ_SERVICE_IMPLEMENTATION" ] - - deps = [ - "//base", - "//build:chromeos_buildflags", - "//gpu/config", - "//third_party/libyuv", - ] - - if (is_win) { - sources += [ "gl/info_collection_gpu_service_impl.cc", "gl/info_collection_gpu_service_impl.h", ] @@ -461,34 +384,25 @@ viz_source_set("gpu_service_dependencies") { ] } - if (is_chromeos_ash) { - deps += [ - "//components/chromeos_camera:jpeg_encode_accelerator_service", - "//components/chromeos_camera:mjpeg_decode_accelerator_service", - "//gpu/command_buffer/service:gles2", - "//media/mojo/services", + if (is_fuchsia) { + sources += [ + "display_embedder/output_presenter_fuchsia.cc", + "display_embedder/output_presenter_fuchsia.h", ] - if (use_v4l2_codec || use_vaapi) { - deps += [ "//ash/components/arc/video_accelerator" ] - } } if (use_vaapi) { deps += [ "//media/gpu/vaapi" ] } - if (use_ozone) { - deps += [ "//ui/ozone" ] - } - if (enable_vulkan) { + deps += [ "//gpu/vulkan" ] + sources += [ "display_embedder/skia_output_device_vulkan.cc", "display_embedder/skia_output_device_vulkan.h", ] - public_deps += [ "//gpu/vulkan" ] - if (is_android) { sources += [ "display_embedder/skia_output_device_vulkan_secondary_cb.cc", @@ -528,13 +442,6 @@ 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", - ] - } } viz_source_set("unit_tests") { @@ -546,26 +453,21 @@ viz_source_set("unit_tests") { "display/delegated_ink_point_pixel_test_helper.cc", "display/delegated_ink_point_pixel_test_helper.h", "display/display_damage_tracker_unittest.cc", - "display/display_resource_provider_gl_unittest.cc", "display/display_resource_provider_skia_unittest.cc", "display/display_resource_provider_software_unittest.cc", "display/display_scheduler_unittest.cc", "display/display_unittest.cc", "display/draw_polygon_unittest.cc", "display/frame_rate_decider_unittest.cc", - "display/layer_quad_unittest.cc", "display/renderer_pixeltest.cc", "display/resolved_frame_data_unittest.cc", - "display/shader_unittest.cc", "display/skia_readback_pixeltest.cc", "display/software_renderer_unittest.cc", "display/surface_aggregator_pixeltest.cc", "display/surface_aggregator_unittest.cc", - "display/texture_deleter_unittest.cc", "display/viz_pixel_test.cc", "display/viz_pixel_test.h", "display_embedder/buffer_queue_unittest.cc", - "display_embedder/gl_output_surface_buffer_queue_unittest.cc", "display_embedder/server_shared_bitmap_manager_unittest.cc", "display_embedder/skia_output_device_buffer_queue_unittest.cc", "display_embedder/skia_output_surface_impl_unittest.cc", @@ -591,14 +493,6 @@ viz_source_set("unit_tests") { "transitions/transferable_resource_tracker_unittest.cc", ] - if (enable_gl_renderer_tests) { - sources += [ - "display/gl_renderer_copier_pixeltest.cc", - "display/gl_renderer_copier_unittest.cc", - "display/gl_renderer_unittest.cc", - ] - } - if (!use_aura && !is_mac) { sources -= [ "display_embedder/buffer_queue_unittest.cc" ] } @@ -619,7 +513,6 @@ viz_source_set("unit_tests") { "//components/viz/test:test_suite", "//components/viz/test:test_support", "//gpu/command_buffer/client", - "//gpu/command_buffer/client:gles2_implementation", "//gpu/ipc:gl_in_process_context", "//gpu/ipc/service", "//media", @@ -690,7 +583,6 @@ viz_source_set("perf_tests") { sources = [ "display/bsp_tree_perftest.cc", "display/display_perftest.cc", - "display/gl_renderer_copier_perftest.cc", "display/renderer_perftest.cc", "display/surface_aggregator_perftest.cc", "display/viz_perftest.cc", @@ -728,6 +620,8 @@ if (is_android) { android_library("service_java") { deps = [ "//base:base_java", + "//base:jni_java", + "//build/android:build_java", "//ui/android:ui_no_recycler_view_java", ] annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] diff --git a/chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.cc b/chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.cc index 9b0b639fdae..941f49d8688 100644 --- a/chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.cc +++ b/chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.cc @@ -89,8 +89,7 @@ FuzzerSoftwareOutputSurfaceProvider::~FuzzerSoftwareOutputSurfaceProvider() = std::unique_ptr<DisplayCompositorMemoryAndTaskController> FuzzerSoftwareOutputSurfaceProvider::CreateGpuDependency( bool gpu_compositing, - gpu::SurfaceHandle surface_handle, - const RendererSettings& renderer_settings) { + gpu::SurfaceHandle surface_handle) { return nullptr; } diff --git a/chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.h b/chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.h index 2f115f653b8..24ccdee8476 100644 --- a/chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.h +++ b/chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.h @@ -30,8 +30,7 @@ class FuzzerSoftwareOutputSurfaceProvider : public OutputSurfaceProvider { // OutputSurfaceProvider implementation. std::unique_ptr<DisplayCompositorMemoryAndTaskController> CreateGpuDependency( bool gpu_compositing, - gpu::SurfaceHandle surface_handle, - const RendererSettings& renderer_settings) override; + gpu::SurfaceHandle surface_handle) override; std::unique_ptr<OutputSurface> CreateOutputSurface( gpu::SurfaceHandle surface_handle, bool gpu_compositing, diff --git a/chromium/components/viz/service/display/DEPS b/chromium/components/viz/service/display/DEPS index 057df66329e..ca3f9db0518 100644 --- a/chromium/components/viz/service/display/DEPS +++ b/chromium/components/viz/service/display/DEPS @@ -16,7 +16,7 @@ include_rules = [ "+components/viz/service/display_embedder/overlay_candidate_validator_win.h", "+components/viz/service/display_embedder/skia_output_surface_dependency.h", "+components/viz/common", - "+gpu/command_buffer/client", + "+gpu/command_buffer/client/shared_image_interface.h", "+gpu/command_buffer/common", "+gpu/command_buffer/service", "+gpu/GLES2", diff --git a/chromium/components/viz/service/display/ca_layer_overlay.cc b/chromium/components/viz/service/display/ca_layer_overlay.cc index 1c873a3e7a7..9da6b5e17d5 100644 --- a/chromium/components/viz/service/display/ca_layer_overlay.cc +++ b/chromium/components/viz/service/display/ca_layer_overlay.cc @@ -136,6 +136,10 @@ gfx::CALayerResult FromRenderPassQuad( ca_layer_overlay->rpdq = quad; ca_layer_overlay->contents_rect = gfx::RectF(0, 0, 1, 1); + // For RenderPassDrawQuad, the opacity is applied when its ddl is recorded, so + // the content already is with opacity applied. + ca_layer_overlay->opacity = 1.0; + return gfx::kCALayerSuccess; } @@ -156,7 +160,7 @@ gfx::CALayerResult FromSolidColorDrawQuad(const SolidColorDrawQuad* quad, CALayerOverlay* ca_layer_overlay, bool* skip) { // Do not generate quads that are completely transparent. - if (SkColorGetA(quad->color) == 0) { + if (quad->color.fA == 0.0f) { *skip = true; return gfx::kCALayerSuccess; } @@ -187,7 +191,7 @@ gfx::CALayerResult FromTextureQuad(DisplayResourceProvider* resource_provider, if (quad->vertex_opacity[i] != quad->vertex_opacity[0]) return gfx::kCALayerFailedDifferentVertexOpacities; } - ca_layer_overlay->shared_state->opacity *= quad->vertex_opacity[0]; + ca_layer_overlay->opacity *= quad->vertex_opacity[0]; ca_layer_overlay->filter = quad->nearest_neighbor ? GL_NEAREST : GL_LINEAR; if (quad->is_video_frame) ca_layer_overlay->protected_video_type = quad->protected_video_type; @@ -310,13 +314,13 @@ class CALayerOverlayProcessorInternal { // Enable edge anti-aliasing only on layer boundaries. ca_layer_overlay->edge_aa_mask = 0; if (quad->IsLeftEdge()) - ca_layer_overlay->edge_aa_mask |= GL_CA_LAYER_EDGE_LEFT_CHROMIUM; + ca_layer_overlay->edge_aa_mask |= ui::CALayerEdge::kLayerEdgeLeft; if (quad->IsRightEdge()) - ca_layer_overlay->edge_aa_mask |= GL_CA_LAYER_EDGE_RIGHT_CHROMIUM; + ca_layer_overlay->edge_aa_mask |= ui::CALayerEdge::kLayerEdgeRight; if (quad->IsBottomEdge()) - ca_layer_overlay->edge_aa_mask |= GL_CA_LAYER_EDGE_BOTTOM_CHROMIUM; + ca_layer_overlay->edge_aa_mask |= ui::CALayerEdge::kLayerEdgeBottom; if (quad->IsTopEdge()) - ca_layer_overlay->edge_aa_mask |= GL_CA_LAYER_EDGE_TOP_CHROMIUM; + ca_layer_overlay->edge_aa_mask |= ui::CALayerEdge::kLayerEdgeTop; if (most_recent_shared_quad_state_ != quad->shared_quad_state) { most_recent_shared_quad_state_ = quad->shared_quad_state; @@ -331,14 +335,13 @@ class CALayerOverlayProcessorInternal { most_recent_overlay_shared_state_->rounded_corner_bounds = quad->shared_quad_state->mask_filter_info.rounded_corner_bounds(); - most_recent_overlay_shared_state_->opacity = - quad->shared_quad_state->opacity; most_recent_overlay_shared_state_->transform = quad->shared_quad_state->quad_to_target_transform; } ca_layer_overlay->shared_state = most_recent_overlay_shared_state_; ca_layer_overlay->bounds_rect = gfx::RectF(quad->rect); + ca_layer_overlay->opacity = quad->shared_quad_state->opacity; *render_pass_draw_quad = quad->material == DrawQuad::Material::kAggregatedRenderPass; diff --git a/chromium/components/viz/service/display/ca_layer_overlay.h b/chromium/components/viz/service/display/ca_layer_overlay.h index 7d3dacb6ead..8174a19ac5a 100644 --- a/chromium/components/viz/service/display/ca_layer_overlay.h +++ b/chromium/components/viz/service/display/ca_layer_overlay.h @@ -14,7 +14,6 @@ #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/SkRefCnt.h" #include "ui/gfx/ca_layer_result.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/rrect_f.h" @@ -22,8 +21,6 @@ #include "ui/gfx/video_types.h" #include "ui/gl/ca_renderer_layer_params.h" -class SkDeferredDisplayList; - namespace viz { class AggregatedRenderPassDrawQuad; class DisplayResourceProvider; @@ -43,8 +40,6 @@ class VIZ_SERVICE_EXPORT CALayerOverlaySharedState bool is_clipped = false; gfx::RectF clip_rect; gfx::RRectF rounded_corner_bounds; - // The opacity property for the CAayer. - float opacity = 1; // The transform to apply to the CALayer. gfx::Transform transform; @@ -73,8 +68,10 @@ class VIZ_SERVICE_EXPORT CALayerOverlay { gfx::RectF contents_rect; // The bounds for the CALayer in pixels. gfx::RectF bounds_rect; + // The opacity property for the CAayer. + float opacity = 1; // The background color property for the CALayer. - SkColor background_color = SK_ColorTRANSPARENT; + SkColor4f background_color = SkColors::kTransparent; // The edge anti-aliasing mask property for the CALayer. unsigned edge_aa_mask = 0; // The minification and magnification filters for the CALayer. @@ -85,8 +82,6 @@ class VIZ_SERVICE_EXPORT CALayerOverlay { // If |rpdq| is present, then the renderer must draw the filter effects and // copy the result into an IOSurface. const AggregatedRenderPassDrawQuad* rpdq = nullptr; - // The DDL for generating render pass overlay buffer with SkiaRenderer. - sk_sp<SkDeferredDisplayList> ddl; }; typedef std::vector<CALayerOverlay> CALayerOverlayList; diff --git a/chromium/components/viz/service/display/dc_layer_overlay.cc b/chromium/components/viz/service/display/dc_layer_overlay.cc index 0d97d7c3277..bb845f7ea91 100644 --- a/chromium/components/viz/service/display/dc_layer_overlay.cc +++ b/chromium/components/viz/service/display/dc_layer_overlay.cc @@ -13,6 +13,7 @@ #include "build/build_config.h" #include "cc/base/math_util.h" #include "components/viz/common/display/renderer_settings.h" +#include "components/viz/common/overlay_state/win/overlay_state_service.h" #include "components/viz/common/quads/aggregated_render_pass_draw_quad.h" #include "components/viz/common/quads/debug_border_draw_quad.h" #include "components/viz/common/quads/solid_color_draw_quad.h" @@ -23,6 +24,7 @@ #include "components/viz/service/display/overlay_processor_interface.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/config/gpu_finch_features.h" +#include "media/base/win/mf_feature_checks.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_conversions.h" @@ -317,7 +319,7 @@ bool IsOccluded( overlap_rect = ClippedQuadRectangle(quad); if (quad->material == DrawQuad::Material::kSolidColor) { - SkColor color = SolidColorDrawQuad::MaterialCast(quad)->color; + SkColor color = SolidColorDrawQuad::MaterialCast(quad)->color.toSkColor(); float alpha = (SkColorGetA(color) * (1.0f / 255.0f)) * opacity; if (quad->ShouldDrawWithBlending() && alpha < std::numeric_limits<float>::epsilon()) @@ -436,6 +438,7 @@ DCLayerOverlayProcessor::DCLayerOverlayProcessor( UpdateHasHwOverlaySupport(); ui::GpuSwitchingManager::GetInstance()->AddObserver(this); } + allow_promotion_hinting_ = media::SupportMediaFoundationClearPlayback(); } DCLayerOverlayProcessor::~DCLayerOverlayProcessor() { @@ -675,6 +678,7 @@ void DCLayerOverlayProcessor::Process( continue; } + gpu::Mailbox promotion_hint_mailbox; DCLayerResult result; switch (it->material) { case DrawQuad::Material::kYuvVideoContent: @@ -686,19 +690,50 @@ void DCLayerOverlayProcessor::Process( if (result == DC_LAYER_SUCCESS) processed_yuv_overlay_count_++; break; - case DrawQuad::Material::kStreamVideoContent: + case DrawQuad::Material::kStreamVideoContent: { + if (allow_promotion_hinting_) { + // If this quad has marked itself as wanting promotion hints then get + // the associated mailbox. + const StreamVideoDrawQuad* sv_quad = + StreamVideoDrawQuad::MaterialCast(*it); + ResourceId id = sv_quad->resource_id(); + if (resource_provider->DoesResourceWantPromotionHint(id)) { + promotion_hint_mailbox = resource_provider->GetMailbox(id); + } + } // Stream video quads contain Media Foundation dcomp surface which is // always presented as overlay. result = DC_LAYER_SUCCESS; - break; - case DrawQuad::Material::kTextureContent: + } break; + case DrawQuad::Material::kTextureContent: { result = ValidateTextureQuad(TextureDrawQuad::MaterialCast(*it), backdrop_filter_rects, resource_provider); - break; + + if (allow_promotion_hinting_) { + // If this quad has marked itself as wanting promotion hints then get + // the associated mailbox. + const TextureDrawQuad* tex_quad = TextureDrawQuad::MaterialCast(*it); + ResourceId id = tex_quad->resource_id(); + if (resource_provider->DoesResourceWantPromotionHint(id)) { + promotion_hint_mailbox = resource_provider->GetMailbox(id); + } + } + } break; default: result = DC_LAYER_FAILED_UNSUPPORTED_QUAD; } + if (!promotion_hint_mailbox.IsZero()) { + DCHECK(allow_promotion_hinting_); + bool promoted = result == DC_LAYER_SUCCESS; + auto* overlay_state_service = OverlayStateService::GetInstance(); + // The OverlayStateService should always be initialized by GpuServiceImpl + // at creation - DCHECK here just to assert there aren't any corner cases + // where this isn't true. + DCHECK(overlay_state_service->IsInitialized()); + overlay_state_service->SetPromotionHint(promotion_hint_mailbox, promoted); + } + if (result != DC_LAYER_SUCCESS) { RecordDCLayerResult(result, it); continue; diff --git a/chromium/components/viz/service/display/dc_layer_overlay.h b/chromium/components/viz/service/display/dc_layer_overlay.h index 390be729590..fab70d446b3 100644 --- a/chromium/components/viz/service/display/dc_layer_overlay.h +++ b/chromium/components/viz/service/display/dc_layer_overlay.h @@ -162,16 +162,17 @@ class VIZ_SERVICE_EXPORT DCLayerOverlayProcessor const raw_ptr<const DebugRendererSettings> debug_settings_; bool previous_frame_underlay_is_opaque_ = true; + bool allow_promotion_hinting_ = false; gfx::RectF previous_display_rect_; std::vector<size_t> damages_to_be_removed_; struct OverlayRect { gfx::Rect rect; bool is_overlay; // If false, it's an underlay. - bool operator==(const OverlayRect& b) { + bool operator==(const OverlayRect& b) const { return rect == b.rect && is_overlay == b.is_overlay; } - bool operator!=(const OverlayRect& b) { return !(*this == b); } + bool operator!=(const OverlayRect& b) const { return !(*this == b); } }; std::vector<OverlayRect> previous_frame_overlay_rects_; std::vector<OverlayRect> current_frame_overlay_rects_; diff --git a/chromium/components/viz/service/display/direct_renderer.cc b/chromium/components/viz/service/display/direct_renderer.cc index 65407f9e679..ce0b247406d 100644 --- a/chromium/components/viz/service/display/direct_renderer.cc +++ b/chromium/components/viz/service/display/direct_renderer.cc @@ -92,21 +92,10 @@ DirectRenderer::DirectRenderer(const RendererSettings* settings, DirectRenderer::~DirectRenderer() = default; void DirectRenderer::Initialize() { - auto* context_provider = output_surface_->context_provider(); - use_partial_swap_ = settings_->partial_swap_enabled && CanPartialSwap(); - allow_empty_swap_ = use_partial_swap_; - if (context_provider) { - if (context_provider->ContextCapabilities().commit_overlay_planes) - allow_empty_swap_ = true; -#if DCHECK_IS_ON() - supports_occlusion_query_ = - context_provider->ContextCapabilities().occlusion_query; -#endif - } else { - allow_empty_swap_ |= - output_surface_->capabilities().supports_commit_overlay_planes; - } + allow_empty_swap_ = + use_partial_swap_ || + output_surface_->capabilities().supports_commit_overlay_planes; initialized_ = true; } @@ -226,28 +215,6 @@ void DirectRenderer::DrawFrame( auto* root_render_pass = render_passes_in_draw_order->back().get(); DCHECK(root_render_pass); -#if DCHECK_IS_ON() - bool overdraw_tracing_enabled; - TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("viz.overdraw"), - &overdraw_tracing_enabled); - DLOG_IF(WARNING, !overdraw_tracing_support_missing_logged_once_ && - overdraw_tracing_enabled && !supports_occlusion_query_) - << "Overdraw tracing enabled on platform without support."; - overdraw_tracing_support_missing_logged_once_ = true; -#endif - - bool overdraw_feedback = debug_settings_->show_overdraw_feedback; - if (overdraw_feedback && !output_surface_->capabilities().supports_stencil) { -#if DCHECK_IS_ON() - DLOG_IF(WARNING, !overdraw_feedback_support_missing_logged_once_) - << "Overdraw feedback enabled on platform without support."; - overdraw_feedback_support_missing_logged_once_ = true; -#endif - overdraw_feedback = false; - } - base::AutoReset<bool> auto_reset_overdraw_feedback(&overdraw_feedback_, - overdraw_feedback); - current_frame_valid_ = true; current_frame_ = DrawingFrame(); current_frame()->render_passes_in_draw_order = render_passes_in_draw_order; @@ -366,26 +333,21 @@ void DirectRenderer::DrawFrame( // Only reshape when we know we are going to draw. Otherwise, the reshape // can leave the window at the wrong size if we never draw and the proper // viewport size is never set. - bool use_stencil = overdraw_feedback_; bool needs_full_frame_redraw = false; auto display_transform = output_surface_->GetDisplayTransform(); + OutputSurface::ReshapeParams reshape_params; + reshape_params.size = surface_resource_size; + reshape_params.device_scale_factor = device_scale_factor; + reshape_params.color_space = frame_color_space; + reshape_params.sdr_white_level = CurrentFrameSDRWhiteLevel(); + reshape_params.format = frame_buffer_format; if (next_frame_needs_full_frame_redraw_ || - surface_resource_size != reshape_surface_size_ || - device_scale_factor != reshape_device_scale_factor_ || - frame_color_space != reshape_color_space_ || - frame_buffer_format != reshape_buffer_format_ || - use_stencil != reshape_use_stencil_ || + reshape_params != reshape_params_ || display_transform != reshape_display_transform_) { next_frame_needs_full_frame_redraw_ = false; - reshape_surface_size_ = surface_resource_size; - reshape_device_scale_factor_ = device_scale_factor; - reshape_color_space_ = frame_color_space; - reshape_buffer_format_ = frame_buffer_format; - reshape_use_stencil_ = overdraw_feedback_; + reshape_params_ = reshape_params; reshape_display_transform_ = display_transform; - output_surface_->Reshape(reshape_surface_size_, - reshape_device_scale_factor_, reshape_color_space_, - *reshape_buffer_format_, reshape_use_stencil_); + output_surface_->Reshape(reshape_params); #if BUILDFLAG(IS_APPLE) // For Mac, all render passes will be promoted to CALayer, the redraw full // frame is for the main surface only. @@ -422,16 +384,6 @@ void DirectRenderer::DrawFrame( if (!skip_drawing_root_render_pass) DrawRenderPassAndExecuteCopyRequests(root_render_pass); - // Use a fence to synchronize display of the main fb used by the output - // surface. Note that gpu_fence_id may have the special value 0 ("no fence") - // if fences are not supported. In that case synchronization will happen - // through other means on the service side. - // TODO(afrantzis): Consider using per-overlay fences instead of the one - // associated with the output surface when possible. - if (current_frame()->output_surface_plane) - current_frame()->output_surface_plane->gpu_fence_id = - output_surface_->UpdateGpuFence(); - if (overlay_processor_) overlay_processor_->TakeOverlayCandidates(¤t_frame()->overlay_list); @@ -657,16 +609,8 @@ void DirectRenderer::DrawRenderPass(const AggregatedRenderPass* render_pass) { const bool render_pass_requires_scissor = render_pass_is_clipped || (supports_dc_layers && is_root_render_pass); - const bool has_external_stencil_test = - is_root_render_pass && output_surface_->HasExternalStencilTest(); const bool should_clear_surface = - !has_external_stencil_test && - (!is_root_render_pass || settings_->should_clear_root_render_pass); - - // If |has_external_stencil_test| we can't discard or clear. Make sure we - // don't need to. - DCHECK(!has_external_stencil_test || - !current_frame()->current_render_pass->has_transparent_background); + !is_root_render_pass || settings_->should_clear_root_render_pass; SurfaceInitializationMode mode; if (should_clear_surface && render_pass_requires_scissor) { @@ -737,9 +681,6 @@ void DirectRenderer::DrawRenderPass(const AggregatedRenderPass* render_pass) { render_pass_requires_scissor); FinishDrawingQuadList(); - if (is_root_render_pass && overdraw_feedback_) - FlushOverdrawFeedback(render_pass_scissor_in_draw_space); - if (render_pass->generate_mipmap) GenerateMipmap(); } @@ -1007,6 +948,10 @@ bool DirectRenderer::ShouldApplyRoundedCorner(const DrawQuad* quad) const { return false; } +float DirectRenderer::CurrentFrameSDRWhiteLevel() const { + return current_frame()->display_color_spaces.GetSDRMaxLuminanceNits(); +} + gfx::ColorSpace DirectRenderer::RootRenderPassColorSpace() const { return current_frame()->display_color_spaces.GetOutputColorSpace( current_frame()->root_render_pass->content_color_usage, diff --git a/chromium/components/viz/service/display/direct_renderer.h b/chromium/components/viz/service/display/direct_renderer.h index 0c6a57744c2..881356c9c73 100644 --- a/chromium/components/viz/service/display/direct_renderer.h +++ b/chromium/components/viz/service/display/direct_renderer.h @@ -23,7 +23,6 @@ #include "components/viz/service/display/overlay_candidate.h" #include "components/viz/service/display/overlay_processor_interface.h" #include "components/viz/service/viz_service_export.h" -#include "gpu/command_buffer/common/texture_in_use_response.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/gfx/ca_layer_result.h" #include "ui/gfx/delegated_ink_metadata.h" @@ -111,8 +110,6 @@ class VIZ_SERVICE_EXPORT DirectRenderer { virtual void SwapBuffersSkipped() {} virtual void SwapBuffersComplete(gfx::GpuFenceHandle release_fence) {} virtual void BuffersPresented() {} - virtual void DidReceiveTextureInUseResponses( - const gpu::TextureInUseResponses& responses) {} virtual void DidReceiveReleasedOverlays( const std::vector<gpu::Mailbox>& released_overlays) {} @@ -264,7 +261,6 @@ class VIZ_SERVICE_EXPORT DirectRenderer { virtual void DoDrawQuad(const DrawQuad* quad, const gfx::QuadF* clip_region) = 0; virtual void BeginDrawingFrame() = 0; - virtual void FlushOverdrawFeedback(const gfx::Rect& output_rect) {} virtual void FinishDrawingFrame() = 0; // If a pass contains a single tile draw quad and can be drawn without // a render pass (e.g. applying a filter directly to the tile quad) @@ -282,7 +278,7 @@ class VIZ_SERVICE_EXPORT DirectRenderer { virtual void GenerateMipmap() = 0; gfx::Size surface_size_for_swap_buffers() const { - return reshape_surface_size_; + return reshape_params_ ? reshape_params_->size : gfx::Size(); } gfx::Size viewport_size_for_swap_buffers() const { return device_viewport_size_; @@ -290,8 +286,16 @@ class VIZ_SERVICE_EXPORT DirectRenderer { bool ShouldApplyRoundedCorner(const DrawQuad* quad) const; + float CurrentFrameSDRWhiteLevel() const; gfx::ColorSpace RootRenderPassColorSpace() const; gfx::ColorSpace CurrentRenderPassColorSpace() const; + // Return the SkColorSpace for rendering to the current render pass. Unlike + // CurrentRenderPassColorSpace, this color space has the value of + // CurrentFrameSDRWhiteLevel incorporated into it. + sk_sp<SkColorSpace> CurrentRenderPassSkColorSpace() const { + return CurrentRenderPassColorSpace().ToSkColorSpace( + CurrentFrameSDRWhiteLevel()); + } const raw_ptr<const RendererSettings> settings_; // Points to the viz-global singleton. @@ -308,8 +312,6 @@ class VIZ_SERVICE_EXPORT DirectRenderer { bool allow_empty_swap_ = false; // Whether partial swap can be used. bool use_partial_swap_ = false; - // Whether overdraw feedback is enabled and can be used. - bool overdraw_feedback_ = false; // A map from RenderPass id to the single quad present in and replacing the // RenderPass. The DrawQuads are owned by their RenderPasses, which outlive @@ -350,10 +352,13 @@ class VIZ_SERVICE_EXPORT DirectRenderer { return ¤t_frame_; } gfx::BufferFormat reshape_buffer_format() const { - DCHECK(reshape_buffer_format_); - return reshape_buffer_format_.value(); + DCHECK(reshape_params_); + return reshape_params_->format; + } + gfx::ColorSpace reshape_color_space() const { + DCHECK(reshape_params_); + return reshape_params_->color_space; } - gfx::ColorSpace reshape_color_space() const { return reshape_color_space_; } // Sets a DelegatedInkPointRendererSkiaForTest to be used for testing only, in // order to save delegated ink metadata values that would otherwise be reset. @@ -364,11 +369,7 @@ class VIZ_SERVICE_EXPORT DirectRenderer { virtual void DrawDelegatedInkTrail(); bool initialized_ = false; -#if DCHECK_IS_ON() - bool overdraw_feedback_support_missing_logged_once_ = false; - bool overdraw_tracing_support_missing_logged_once_ = false; - bool supports_occlusion_query_ = false; -#endif + gfx::Rect last_root_render_pass_scissor_rect_; gfx::Size enlarge_pass_texture_amount_; @@ -379,20 +380,16 @@ class VIZ_SERVICE_EXPORT DirectRenderer { bool current_frame_valid_ = false; // Time of most recent reshape that ended up with |device_viewport_size_| != - // |reshape_surface_size_|. + // |reshape_params->size|. base::TimeTicks last_viewport_resize_time_; bool next_frame_needs_full_frame_redraw_ = false; - // Cached values given to Reshape(). The |reshape_buffer_format_| is optional - // to prevent use of uninitialized values. This may be larger than the - // |device_viewport_size_| that users see. - gfx::Size reshape_surface_size_; + // Cached values given to Reshape(). The `reshape_params_` is optional + // to prevent use of uninitialized values. The size in these parameters + // may be larger than the `device_viewport_size_` that users see. + absl::optional<OutputSurface::ReshapeParams> reshape_params_; gfx::Size device_viewport_size_; - float reshape_device_scale_factor_ = 0.f; - gfx::ColorSpace reshape_color_space_; - absl::optional<gfx::BufferFormat> reshape_buffer_format_; - bool reshape_use_stencil_ = false; gfx::OverlayTransform reshape_display_transform_ = gfx::OVERLAY_TRANSFORM_INVALID; }; diff --git a/chromium/components/viz/service/display/display.cc b/chromium/components/viz/service/display/display.cc index 40db30fb146..07d38ad3d92 100644 --- a/chromium/components/viz/service/display/display.cc +++ b/chromium/components/viz/service/display/display.cc @@ -24,6 +24,7 @@ #include "components/viz/common/display/renderer_settings.h" #include "components/viz/common/features.h" #include "components/viz/common/frame_sinks/begin_frame_source.h" +#include "components/viz/common/quads/aggregated_render_pass_draw_quad.h" #include "components/viz/common/quads/compositor_frame.h" #include "components/viz/common/quads/draw_quad.h" #include "components/viz/common/quads/shared_quad_state.h" @@ -34,12 +35,10 @@ #include "components/viz/service/display/delegated_ink_point_renderer_base.h" #include "components/viz/service/display/direct_renderer.h" #include "components/viz/service/display/display_client.h" -#include "components/viz/service/display/display_resource_provider_gl.h" #include "components/viz/service/display/display_resource_provider_null.h" #include "components/viz/service/display/display_resource_provider_skia.h" #include "components/viz/service/display/display_resource_provider_software.h" #include "components/viz/service/display/display_scheduler.h" -#include "components/viz/service/display/gl_renderer.h" #include "components/viz/service/display/null_renderer.h" #include "components/viz/service/display/output_surface.h" #include "components/viz/service/display/renderer_utils.h" @@ -49,8 +48,6 @@ #include "components/viz/service/display/surface_aggregator.h" #include "components/viz/service/surfaces/surface.h" #include "components/viz/service/surfaces/surface_manager.h" -#include "gpu/command_buffer/client/context_support.h" -#include "gpu/command_buffer/client/gles2_interface.h" #include "gpu/ipc/scheduler_sequence.h" #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -108,9 +105,6 @@ int64_t GetStartingTraceId() { gfx::PresentationFeedback SanitizePresentationFeedback( const gfx::PresentationFeedback& feedback, base::TimeTicks draw_time) { - // Temporary to investigate large presentation times. - // https://crbug.com/894440 - DCHECK(!draw_time.is_null()); if (feedback.timestamp.is_null()) return feedback; @@ -130,26 +124,10 @@ gfx::PresentationFeedback SanitizePresentationFeedback( gfx::PresentationFeedback::kVSync)) != 0) ? kAllowedDeltaFromFuture : base::TimeDelta(); - if (feedback.timestamp > now + allowed_delta_from_future) { - const auto diff = feedback.timestamp - now; - UMA_HISTOGRAM_MEDIUM_TIMES( - "Graphics.PresentationTimestamp.InvalidFromFuture", diff); + if ((feedback.timestamp > now + allowed_delta_from_future) || + (feedback.timestamp < draw_time)) { return gfx::PresentationFeedback::Failure(); } - - if (feedback.timestamp < draw_time) { - const auto diff = draw_time - feedback.timestamp; - UMA_HISTOGRAM_MEDIUM_TIMES( - "Graphics.PresentationTimestamp.InvalidBeforeSwap", diff); - return gfx::PresentationFeedback::Failure(); - } - - const auto difference = feedback.timestamp - draw_time; - if (difference.InMinutes() > 3) { - UMA_HISTOGRAM_CUSTOM_TIMES( - "Graphics.PresentationTimestamp.LargePresentationDelta", difference, - base::Minutes(3), base::Hours(1), 50); - } return feedback; } @@ -360,15 +338,6 @@ Display::~Display() { if (resource_provider_) { resource_provider_->SetAllowAccessToGPUThread(true); } -#if BUILDFLAG(IS_ANDROID) - // In certain cases, drivers hang when tearing down the display. Finishing - // before teardown appears to address this. As we're during display teardown, - // an additional finish should have minimal impact. - // TODO(ericrk): Add a more robust workaround. crbug.com/899705 - if (auto* context = output_surface_->context_provider()) { - context->ContextGL()->Finish(); - } -#endif if (no_pending_swaps_callback_) std::move(no_pending_swaps_callback_).Run(); @@ -383,8 +352,6 @@ Display::~Display() { // Only do this if Initialize() happened. if (client_) { - if (auto* context = output_surface_->context_provider()) - context->RemoveObserver(this); if (skia_output_surface_) skia_output_surface_->RemoveContextLostObserver(this); } @@ -426,9 +393,6 @@ void Display::Initialize(DisplayClient* client, // This depends on assumptions that Display::Initialize will happen on the // same callstack as the ContextProvider being created/initialized or else // it could miss a callback before setting this. - if (auto* context = output_surface_->context_provider()) - context->AddObserver(this); - if (skia_output_surface_) skia_output_surface_->AddContextLostObserver(this); } @@ -508,8 +472,7 @@ void Display::DisableSwapUntilResize( scheduler_->ForceImmediateSwapIfPossible(); if (no_pending_swaps_callback && pending_swaps_ > 0 && - (output_surface_->context_provider() || - output_surface_->AsSkiaOutputSurface())) { + output_surface_->AsSkiaOutputSurface()) { no_pending_swaps_callback_ = std::move(no_pending_swaps_callback); } @@ -562,14 +525,6 @@ void Display::InitializeRenderer(bool enable_shared_images) { resource_provider.get(), overlay_processor_.get(), skia_output_surface_); resource_provider_ = std::move(resource_provider); - } else if (output_surface_->context_provider()) { - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface_->context_provider(), enable_shared_images); - renderer_ = std::make_unique<GLRenderer>( - &settings_, debug_settings_, output_surface_.get(), - resource_provider.get(), overlay_processor_.get(), - current_task_runner_); - resource_provider_ = std::move(resource_provider); } else if (output_surface_->capabilities().skips_draw) { auto resource_provider = std::make_unique<DisplayResourceProviderNull>(); renderer_ = std::make_unique<NullRenderer>( @@ -895,21 +850,6 @@ bool Display::DrawAndSwap(const DrawAndSwapParams& params) { renderer_->DrawFrame(&frame.render_pass_list, device_scale_factor_, current_surface_size, display_color_spaces_, std::move(frame.surface_damage_rect_list_)); - switch (output_surface_->type()) { - case OutputSurface::Type::kSoftware: - UMA_HISTOGRAM_COUNTS_1M( - "Compositing.DirectRenderer.Software.DrawFrameUs", - draw_timer->Elapsed().InMicroseconds()); - break; - case OutputSurface::Type::kOpenGL: - UMA_HISTOGRAM_COUNTS_1M("Compositing.DirectRenderer.GL.DrawFrameUs", - draw_timer->Elapsed().InMicroseconds()); - break; - case OutputSurface::Type::kVulkan: - UMA_HISTOGRAM_COUNTS_1M("Compositing.DirectRenderer.VK.DrawFrameUs", - draw_timer->Elapsed().InMicroseconds()); - break; - } } else { TRACE_EVENT_INSTANT0("viz", "Draw skipped.", TRACE_EVENT_SCOPE_THREAD); } @@ -1113,12 +1053,6 @@ void Display::DidReceiveSwapBuffersAck(const gfx::SwapTimings& timings, } } -void Display::DidReceiveTextureInUseResponses( - const gpu::TextureInUseResponses& responses) { - if (renderer_) - renderer_->DidReceiveTextureInUseResponses(responses); -} - void Display::DidReceiveCALayerParams( const gfx::CALayerParams& ca_layer_params) { if (client_) diff --git a/chromium/components/viz/service/display/display.h b/chromium/components/viz/service/display/display.h index 3cdb7e3012e..cb26d6cab8c 100644 --- a/chromium/components/viz/service/display/display.h +++ b/chromium/components/viz/service/display/display.h @@ -35,7 +35,6 @@ #include "components/viz/service/surfaces/surface.h" #include "components/viz/service/surfaces/surface_manager.h" #include "components/viz/service/viz_service_export.h" -#include "gpu/command_buffer/common/texture_in_use_response.h" #include "ui/gfx/display_color_spaces.h" #include "ui/gfx/overlay_transform.h" #include "ui/gfx/swap_result.h" @@ -161,8 +160,6 @@ class VIZ_SERVICE_EXPORT Display : public DisplaySchedulerClient, void SetNeedsRedrawRect(const gfx::Rect& damage_rect) override; void DidReceiveSwapBuffersAck(const gfx::SwapTimings& timings, gfx::GpuFenceHandle release_fence) override; - void DidReceiveTextureInUseResponses( - const gpu::TextureInUseResponses& responses) override; void DidReceiveCALayerParams( const gfx::CALayerParams& ca_layer_params) override; void DidSwapWithSize(const gfx::Size& pixel_size) override; diff --git a/chromium/components/viz/service/display/display_damage_tracker.cc b/chromium/components/viz/service/display/display_damage_tracker.cc index 2c477b65304..94bcdf68391 100644 --- a/chromium/components/viz/service/display/display_damage_tracker.cc +++ b/chromium/components/viz/service/display/display_damage_tracker.cc @@ -176,11 +176,6 @@ bool DisplayDamageTracker::OnSurfaceDamaged(const SurfaceId& surface_id, return display_damaged; } -void DisplayDamageTracker::OnSurfaceDestroyed(const SurfaceId& surface_id) { - TRACE_EVENT0("viz", "DisplayDamageTracker::SurfaceDestroyed"); - aggregator_->ReleaseResources(surface_id); -} - void DisplayDamageTracker::OnSurfaceDamageExpected(const SurfaceId& surface_id, const BeginFrameArgs& args) { TRACE_EVENT1("viz", "DisplayDamageTracker::SurfaceDamageExpected", diff --git a/chromium/components/viz/service/display/display_damage_tracker.h b/chromium/components/viz/service/display/display_damage_tracker.h index dfc451ee009..3abe233ff1c 100644 --- a/chromium/components/viz/service/display/display_damage_tracker.h +++ b/chromium/components/viz/service/display/display_damage_tracker.h @@ -81,7 +81,6 @@ class VIZ_SERVICE_EXPORT DisplayDamageTracker : public SurfaceObserver { void OnSurfaceMarkedForDestruction(const SurfaceId& surface_id) override; bool OnSurfaceDamaged(const SurfaceId& surface_id, const BeginFrameAck& ack) override; - void OnSurfaceDestroyed(const SurfaceId& surface_id) override; void OnSurfaceDamageExpected(const SurfaceId& surface_id, const BeginFrameArgs& args) override; diff --git a/chromium/components/viz/service/display/display_perftest.cc b/chromium/components/viz/service/display/display_perftest.cc index 5327ee598c3..0ee8fd42e42 100644 --- a/chromium/components/viz/service/display/display_perftest.cc +++ b/chromium/components/viz/service/display/display_perftest.cc @@ -22,7 +22,7 @@ #include "components/viz/service/display/overlay_processor_stub.h" #include "components/viz/service/display/shared_bitmap_manager.h" #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h" -#include "components/viz/test/fake_output_surface.h" +#include "components/viz/test/fake_skia_output_surface.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/perf/perf_result_reporter.h" @@ -68,8 +68,8 @@ class RemoveOverdrawQuadPerfTest : public testing::Test { auto scheduler = std::make_unique<DisplayScheduler>( &begin_frame_source_, task_runner_.get(), PendingSwapParams(1)); - std::unique_ptr<FakeOutputSurface> output_surface = - FakeOutputSurface::Create3d(); + std::unique_ptr<FakeSkiaOutputSurface> output_surface = + FakeSkiaOutputSurface::Create3d(); auto overlay_processor = std::make_unique<OverlayProcessorStub>(); // Normally display will need to take ownership of a diff --git a/chromium/components/viz/service/display/display_resource_provider.cc b/chromium/components/viz/service/display/display_resource_provider.cc index eb87fff486f..6f80ebffcfb 100644 --- a/chromium/components/viz/service/display/display_resource_provider.cc +++ b/chromium/components/viz/service/display/display_resource_provider.cc @@ -8,6 +8,7 @@ #include <string> #include "base/atomic_sequence_num.h" +#include "base/notreached.h" #include "base/numerics/safe_math.h" #include "base/strings/stringprintf.h" #include "base/threading/thread_task_runner_handle.h" @@ -306,9 +307,9 @@ void DisplayResourceProvider::TryReleaseResource(ResourceId id, } } -bool DisplayResourceProvider::ReadLockFenceHasPassed( +bool DisplayResourceProvider::ResourceFenceHasPassed( const ChildResource* resource) { - return !resource->read_lock_fence || resource->read_lock_fence->HasPassed(); + return !resource->resource_fence || resource->resource_fence->HasPassed(); } DisplayResourceProvider::CanDeleteNowResult @@ -322,7 +323,7 @@ DisplayResourceProvider::CanDeleteNow(const Child& child_info, // Defer this resource deletion. return CanDeleteNowResult::kNo; - } else if (!ReadLockFenceHasPassed(&resource)) { + } else if (!ResourceFenceHasPassed(&resource)) { // TODO(dcastagna): see if it's possible to use this logic for // the branch above too, where the resource is locked or still exported. // We can't postpone the deletion, so we'll have to lose it. @@ -473,7 +474,8 @@ void DisplayResourceProvider::ScopedReadLockSharedImage::SetReleaseFence( bool DisplayResourceProvider::ScopedReadLockSharedImage::HasReadLockFence() const { DCHECK(resource_); - return resource_->transferable.read_lock_fences_enabled; + return resource_->transferable.synchronization_type == + TransferableResource::SynchronizationType::kGpuCommandsCompleted; } void DisplayResourceProvider::ScopedReadLockSharedImage::Reset() { diff --git a/chromium/components/viz/service/display/display_resource_provider.h b/chromium/components/viz/service/display/display_resource_provider.h index 5d897625a42..be07f99feb7 100644 --- a/chromium/components/viz/service/display/display_resource_provider.h +++ b/chromium/components/viz/service/display/display_resource_provider.h @@ -162,10 +162,16 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider // Sets the current read fence. If a resource is locked for read // and has read fences enabled, the resource will not allow writes - // until this fence has passed. - void SetReadLockFence(ResourceFence* fence) { - current_read_lock_fence_ = fence; + // until this fence has passed. This is used if a client uses + // TransferableResource::SynchronizationType::kGpuCommandsCompleted. + void SetGpuCommandsCompletedFence(ResourceFence* fence) { + current_gpu_commands_completed_fence_ = fence; } + // Sets the current release fence. If a client uses + // TransferableResource::SynchronizationType::kReleaseFence, resources must be + // returned only after a release fence is stored in this resource fence. + // Returned only when gpu commands and the gpu fence are submitted. + void SetReleaseFence(ResourceFence* fence) { current_release_fence_ = fence; } // Creates accounting for a child. Returns a child ID. surface_id is used to // associate resources to the surface they belong to. This is used for @@ -333,12 +339,14 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider // mapped for use in the display compositor. base::UnguessableToken shared_bitmap_tracing_guid; - // A fence used for accessing a gpu resource for reading, that ensures any - // writing done to the resource has been completed. This is implemented and - // used to implement transferring ownership of the resource from the client - // to the service, and in the GL drawing code before reading from the - // texture. - scoped_refptr<ResourceFence> read_lock_fence; + // A fence used for returning resources after the display compositor has + // completed accessing the resources it received from a client. This can + // either be a read lock or a release fence. If the |transferable| has + // synchronization type set as kGpuCommandsCompleted, the resource can be + // returned after ResourceFence::HasPassed is true. If the |transferable| + // has the synchronization type set as kReleaseFence, the resource can be + // returned after the fence has a release fence set. + scoped_refptr<ResourceFence> resource_fence; // SkiaRenderer specific details about this resource. Added to ChildResource // to avoid map lookups further down the pipeline. @@ -371,10 +379,7 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider ChildResource* TryGetResource(ResourceId id); void TryReleaseResource(ResourceId id, ChildResource* resource); - // Binds the given GL resource to a texture target for sampling using the - // specified filter for both minification and magnification. Returns the - // texture target used. The resource must be locked for reading. - bool ReadLockFenceHasPassed(const ChildResource* resource); + bool ResourceFenceHasPassed(const ChildResource* resource); void DeleteAndReturnUnusedResourcesToChild( ChildMap::iterator child_it, @@ -404,7 +409,8 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider ChildMap children_; base::flat_map<int, std::vector<ResourceId>> batched_returning_resources_; - scoped_refptr<ResourceFence> current_read_lock_fence_; + scoped_refptr<ResourceFence> current_gpu_commands_completed_fence_; + scoped_refptr<ResourceFence> current_release_fence_; // Keep track of whether deleted resources should be batched up or returned // immediately. int batch_return_resources_lock_count_ = 0; diff --git a/chromium/components/viz/service/display/display_resource_provider_gl.cc b/chromium/components/viz/service/display/display_resource_provider_gl.cc deleted file mode 100644 index 501cd716a1f..00000000000 --- a/chromium/components/viz/service/display/display_resource_provider_gl.cc +++ /dev/null @@ -1,425 +0,0 @@ -// Copyright 2021 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/display_resource_provider_gl.h" - -#include <utility> -#include <vector> - -#include "base/dcheck_is_on.h" -#include "base/memory/raw_ptr.h" -#include "base/trace_event/trace_event.h" -#include "build/build_config.h" -#include "components/viz/common/gpu/context_provider.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "gpu/command_buffer/client/gles2_interface.h" -#include "ui/gfx/gpu_fence.h" -#include "ui/gl/gl_fence.h" - -using gpu::gles2::GLES2Interface; - -namespace viz { -namespace { - -class ScopedSetActiveTexture { - public: - ScopedSetActiveTexture(GLES2Interface* gl, GLenum unit) - : gl_(gl), unit_(unit) { -#if DCHECK_IS_ON() - GLint active_unit = 0; - gl->GetIntegerv(GL_ACTIVE_TEXTURE, &active_unit); - DCHECK_EQ(GL_TEXTURE0, active_unit); -#endif - - if (unit_ != GL_TEXTURE0) - gl_->ActiveTexture(unit_); - } - - ~ScopedSetActiveTexture() { - // Active unit being GL_TEXTURE0 is effectively the ground state. - if (unit_ != GL_TEXTURE0) - gl_->ActiveTexture(GL_TEXTURE0); - } - - private: - raw_ptr<GLES2Interface> gl_; - GLenum unit_; -}; - -} // namespace - -DisplayResourceProviderGL::DisplayResourceProviderGL( - ContextProvider* compositor_context_provider, - bool enable_shared_images) - : DisplayResourceProvider(DisplayResourceProvider::kGpu), - compositor_context_provider_(compositor_context_provider), - enable_shared_images_(enable_shared_images) { - DCHECK(compositor_context_provider_); -} - -DisplayResourceProviderGL::~DisplayResourceProviderGL() { - Destroy(); - GLES2Interface* gl = ContextGL(); - if (gl) - gl->Finish(); - - while (!resources_.empty()) - DeleteResourceInternal(resources_.begin()); -} - -void DisplayResourceProviderGL::DeleteResourceInternal( - ResourceMap::iterator it) { - TRACE_EVENT0("viz", "DisplayResourceProvider::DeleteResourceInternal"); - ChildResource* resource = &it->second; - - if (resource->gl_id) { - GLES2Interface* gl = ContextGL(); - DCHECK(gl); - gl->DeleteTextures(1, &resource->gl_id); - } - - resources_.erase(it); -} - -GLES2Interface* DisplayResourceProviderGL::ContextGL() const { - DCHECK(compositor_context_provider_); - return compositor_context_provider_->ContextGL(); -} - -const DisplayResourceProvider::ChildResource* -DisplayResourceProviderGL::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 - ChildResource* resource = TryGetResource(id); - if (!resource) - return nullptr; - - // Mailbox sync_tokens must be processed by a call to WaitSyncToken() prior to - // calling LockForRead(). - DCHECK_NE(NEEDS_WAIT, resource->synchronization_state()); - DCHECK(resource->is_gpu_resource_type()); - - const gpu::Mailbox& mailbox = resource->transferable.mailbox_holder.mailbox; - GLES2Interface* gl = ContextGL(); - DCHECK(gl); - if (!resource->gl_id) { - if (mailbox.IsSharedImage() && enable_shared_images_) { - resource->gl_id = - gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name); - } else { - resource->gl_id = gl->CreateAndConsumeTextureCHROMIUM( - resource->transferable.mailbox_holder.mailbox.name); - } - resource->SetLocallyUsed(); - } - 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); - } - } - } - - 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(); - resource->read_lock_fence = current_read_lock_fence_; - } - - return resource; -} - -void DisplayResourceProviderGL::UnlockForRead(ResourceId id, - bool overlay_only) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - ChildResource* resource = TryGetResource(id); - // TODO(ericrk): We should never fail to find id, 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 - if (!resource) - return; - - DCHECK(resource->is_gpu_resource_type()); - if (resource->transferable.mailbox_holder.mailbox.IsSharedImage() && - 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); - if (!resource->release_fence.is_null()) { - auto fence = gfx::GpuFence(resource->release_fence.Clone()); - if (gl::GLFence::IsGpuFenceSupported()) { - auto fence_id = - gl->CreateClientGpuFenceCHROMIUM(fence.AsClientGpuFence()); - gl->WaitGpuFenceCHROMIUM(fence_id); - gl->DestroyGpuFenceCHROMIUM(fence_id); - } else { - fence.Wait(); - } - } - 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--; - } - TryReleaseResource(id, resource); -} - -std::vector<ReturnedResource> -DisplayResourceProviderGL::DeleteAndReturnUnusedResourcesToChildImpl( - Child& child_info, - DeleteStyle style, - const std::vector<ResourceId>& unused) { - std::vector<ReturnedResource> to_return; - // Reserve enough space to avoid re-allocating, so we can keep item pointers - // for later using. - to_return.reserve(unused.size()); - std::vector<ReturnedResource*> need_synchronization_resources; - std::vector<GLbyte*> unverified_sync_tokens; - - GLES2Interface* gl = ContextGL(); - DCHECK(gl); - DCHECK(can_access_gpu_thread_); - for (ResourceId local_id : unused) { - auto it = resources_.find(local_id); - CHECK(it != resources_.end()); - ChildResource& resource = it->second; - DCHECK(resource.is_gpu_resource_type()); - - ResourceId child_id = resource.transferable.id; - DCHECK(child_info.child_to_parent_map.count(child_id)); - - auto can_delete = CanDeleteNow(child_info, resource, style); - if (can_delete == CanDeleteNowResult::kNo) { - // Defer this resource deletion. - resource.marked_for_deletion = true; - continue; - } - - const bool is_lost = can_delete == CanDeleteNowResult::kYesButLoseResource; - - if (resource.gl_id && resource.filter != resource.transferable.filter) { - DCHECK(resource.transferable.mailbox_holder.texture_target); - DCHECK(!resource.ShouldWaitSyncToken()); - gl->BindTexture(resource.transferable.mailbox_holder.texture_target, - resource.gl_id); - gl->TexParameteri(resource.transferable.mailbox_holder.texture_target, - GL_TEXTURE_MIN_FILTER, resource.transferable.filter); - gl->TexParameteri(resource.transferable.mailbox_holder.texture_target, - GL_TEXTURE_MAG_FILTER, resource.transferable.filter); - resource.SetLocallyUsed(); - } - - to_return.emplace_back(child_id, resource.sync_token(), - std::move(resource.release_fence), - resource.imported_count, is_lost); - auto& returned = to_return.back(); - - if (resource.needs_sync_token()) { - need_synchronization_resources.push_back(&returned); - } else if (returned.sync_token.HasData() && - !returned.sync_token.verified_flush()) { - unverified_sync_tokens.push_back(returned.sync_token.GetData()); - } - - child_info.child_to_parent_map.erase(child_id); - resource.imported_count = 0; - DeleteResourceInternal(it); - } - - gpu::SyncToken new_sync_token; - if (!need_synchronization_resources.empty()) { - gl->GenUnverifiedSyncTokenCHROMIUM(new_sync_token.GetData()); - unverified_sync_tokens.push_back(new_sync_token.GetData()); - } - - if (!unverified_sync_tokens.empty()) { - gl->VerifySyncTokensCHROMIUM(unverified_sync_tokens.data(), - unverified_sync_tokens.size()); - } - - // Set sync token after verification. - for (ReturnedResource* returned : need_synchronization_resources) - returned->sync_token = new_sync_token; - - return to_return; -} - -GLenum DisplayResourceProviderGL::GetResourceTextureTarget(ResourceId id) { - return GetResource(id)->transferable.mailbox_holder.texture_target; -} - -void DisplayResourceProviderGL::WaitSyncToken(ResourceId id) { - ChildResource* resource = TryGetResource(id); - // 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 - if (!resource) - return; - WaitSyncTokenInternal(resource); -} - -GLenum DisplayResourceProviderGL::BindForSampling(ResourceId resource_id, - GLenum unit, - GLenum filter) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - GLES2Interface* gl = ContextGL(); - auto it = resources_.find(resource_id); - // TODO(ericrk): We should never fail to find resource_id, 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 - if (it == resources_.end()) - return GL_TEXTURE_2D; - - ChildResource* resource = &it->second; - DCHECK(resource->lock_for_read_count); - - ScopedSetActiveTexture scoped_active_tex(gl, unit); - GLenum target = resource->transferable.mailbox_holder.texture_target; - gl->BindTexture(target, resource->gl_id); - - // Texture parameters can be modified by concurrent reads so reset them - // before binding the texture. See https://crbug.com/1092080. - gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); - gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); - resource->filter = filter; - - return target; -} - -void DisplayResourceProviderGL::WaitSyncTokenInternal(ChildResource* resource) { - DCHECK(resource); - if (!resource->ShouldWaitSyncToken()) - return; - GLES2Interface* gl = ContextGL(); - DCHECK(gl); - // In the case of context lost, this sync token may be empty (see comment in - // the UpdateSyncToken() function). The WaitSyncTokenCHROMIUM() function - // handles empty sync tokens properly so just wait anyways and update the - // state the synchronized. - gl->WaitSyncTokenCHROMIUM(resource->sync_token().GetConstData()); - resource->SetSynchronized(); -} - -DisplayResourceProviderGL::ScopedReadLockGL::ScopedReadLockGL( - DisplayResourceProviderGL* resource_provider, - ResourceId resource_id) - : resource_provider_(resource_provider), resource_id_(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 - if (!resource) - return; - - texture_id_ = resource->gl_id; - target_ = resource->transferable.mailbox_holder.texture_target; - size_ = resource->transferable.size; - color_space_ = resource->transferable.color_space; - hdr_metadata_ = resource->transferable.hdr_metadata; -} - -DisplayResourceProviderGL::ScopedReadLockGL::~ScopedReadLockGL() { - resource_provider_->UnlockForRead(resource_id_, false /* overlay_only */); -} - -DisplayResourceProviderGL::ScopedSamplerGL::ScopedSamplerGL( - DisplayResourceProviderGL* resource_provider, - ResourceId resource_id, - GLenum filter) - : resource_lock_(resource_provider, resource_id), - unit_(GL_TEXTURE0), - target_(resource_provider->BindForSampling(resource_id, unit_, filter)) {} - -DisplayResourceProviderGL::ScopedSamplerGL::ScopedSamplerGL( - DisplayResourceProviderGL* resource_provider, - ResourceId resource_id, - GLenum unit, - GLenum filter) - : resource_lock_(resource_provider, resource_id), - unit_(unit), - target_(resource_provider->BindForSampling(resource_id, unit_, filter)) {} - -DisplayResourceProviderGL::ScopedSamplerGL::~ScopedSamplerGL() = default; - -DisplayResourceProviderGL::ScopedOverlayLockGL::ScopedOverlayLockGL( - DisplayResourceProviderGL* 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; -} - -DisplayResourceProviderGL::ScopedOverlayLockGL::~ScopedOverlayLockGL() { - resource_provider_->UnlockForRead(resource_id_, true /* overlay_only */); -} - -void DisplayResourceProviderGL::ScopedOverlayLockGL::SetReleaseFence( - gfx::GpuFenceHandle release_fence) { - auto* resource = resource_provider_->GetResource(resource_id_); - DCHECK(resource); - resource->release_fence = std::move(release_fence); -} - -bool DisplayResourceProviderGL::ScopedOverlayLockGL::HasReadLockFence() const { - auto* resource = resource_provider_->GetResource(resource_id_); - DCHECK(resource); - return resource->transferable.read_lock_fences_enabled; -} - -DisplayResourceProviderGL::SynchronousFence::SynchronousFence( - gpu::gles2::GLES2Interface* gl) - : gl_(gl), has_synchronized_(true) {} - -DisplayResourceProviderGL::SynchronousFence::~SynchronousFence() = default; - -void DisplayResourceProviderGL::SynchronousFence::Set() { - has_synchronized_ = false; -} - -bool DisplayResourceProviderGL::SynchronousFence::HasPassed() { - if (!has_synchronized_) { - has_synchronized_ = true; - Synchronize(); - } - return true; -} - -void DisplayResourceProviderGL::SynchronousFence::Synchronize() { - TRACE_EVENT0("viz", "DisplayResourceProvider::SynchronousFence::Synchronize"); - gl_->Finish(); -} - -} // namespace viz diff --git a/chromium/components/viz/service/display/display_resource_provider_gl.h b/chromium/components/viz/service/display/display_resource_provider_gl.h deleted file mode 100644 index 8a75cf568a8..00000000000 --- a/chromium/components/viz/service/display/display_resource_provider_gl.h +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2021 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_DISPLAY_RESOURCE_PROVIDER_GL_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_GL_H_ - -#include <vector> - -#include "base/memory/raw_ptr.h" -#include "components/viz/service/display/display_resource_provider.h" -#include "components/viz/service/viz_service_export.h" - -namespace gpu { -namespace gles2 { -class GLES2Interface; -} // namespace gles2 -} // namespace gpu - -namespace viz { - -class ContextProvider; - -// DisplayResourceProvider implementation used with GLRenderer. -class VIZ_SERVICE_EXPORT DisplayResourceProviderGL - : public DisplayResourceProvider { - public: - // Android with GLRenderer doesn't support overlays with shared images - // enabled. For everything else |enable_shared_images| is true. - DisplayResourceProviderGL(ContextProvider* compositor_context_provider, - bool enable_shared_images = true); - ~DisplayResourceProviderGL() override; - - GLenum GetResourceTextureTarget(ResourceId id); - void WaitSyncToken(ResourceId id); - - // The following lock classes are part of the DisplayResourceProvider API and - // are needed to read the resource contents. The user must ensure that they - // only use GL locks on GL resources, etc, and this is enforced by assertions. - class VIZ_SERVICE_EXPORT ScopedReadLockGL { - public: - ScopedReadLockGL(DisplayResourceProviderGL* resource_provider, - ResourceId resource_id); - ~ScopedReadLockGL(); - - ScopedReadLockGL(const ScopedReadLockGL&) = delete; - ScopedReadLockGL& operator=(const ScopedReadLockGL&) = delete; - - GLuint texture_id() const { return texture_id_; } - GLenum target() const { return target_; } - const gfx::Size& size() const { return size_; } - const gfx::ColorSpace& color_space() const { return color_space_; } - const absl::optional<gfx::HDRMetadata>& hdr_metadata() const { - return hdr_metadata_; - } - - private: - const raw_ptr<DisplayResourceProviderGL> resource_provider_; - const ResourceId resource_id_; - - GLuint texture_id_ = 0; - GLenum target_ = GL_TEXTURE_2D; - gfx::Size size_; - gfx::ColorSpace color_space_; - absl::optional<gfx::HDRMetadata> hdr_metadata_; - }; - - class VIZ_SERVICE_EXPORT ScopedSamplerGL { - public: - ScopedSamplerGL(DisplayResourceProviderGL* resource_provider, - ResourceId resource_id, - GLenum filter); - ScopedSamplerGL(DisplayResourceProviderGL* resource_provider, - ResourceId resource_id, - GLenum unit, - GLenum filter); - ~ScopedSamplerGL(); - - ScopedSamplerGL(const ScopedSamplerGL&) = delete; - ScopedSamplerGL& operator=(const ScopedSamplerGL&) = delete; - - GLuint texture_id() const { return resource_lock_.texture_id(); } - GLenum target() const { return target_; } - const gfx::ColorSpace& color_space() const { - return resource_lock_.color_space(); - } - const absl::optional<gfx::HDRMetadata>& hdr_metadata() const { - return resource_lock_.hdr_metadata(); - } - - private: - const ScopedReadLockGL resource_lock_; - const GLenum unit_; - const GLenum target_; - }; - - class VIZ_SERVICE_EXPORT ScopedOverlayLockGL { - public: - ScopedOverlayLockGL(DisplayResourceProviderGL* resource_provider, - ResourceId resource_id); - ~ScopedOverlayLockGL(); - - ScopedOverlayLockGL(const ScopedOverlayLockGL&) = delete; - ScopedOverlayLockGL& operator=(const ScopedOverlayLockGL&) = delete; - - GLuint texture_id() const { return texture_id_; } - - // Sets the given |release_fence| onto this resource. - // This is propagated to ReturnedResource when the resource is freed. - void SetReleaseFence(gfx::GpuFenceHandle release_fence); - - // Returns true iff this resource has a read lock fence set. - bool HasReadLockFence() const; - - private: - const raw_ptr<DisplayResourceProviderGL> resource_provider_; - const ResourceId resource_id_; - GLuint texture_id_ = 0; - }; - - class VIZ_SERVICE_EXPORT SynchronousFence : public ResourceFence { - public: - explicit SynchronousFence(gpu::gles2::GLES2Interface* gl); - - SynchronousFence(const SynchronousFence&) = delete; - SynchronousFence& operator=(const SynchronousFence&) = delete; - - // ResourceFence implementation. - void Set() override; - bool HasPassed() override; - - // Returns true if fence has been set but not yet synchornized. - bool has_synchronized() const { return has_synchronized_; } - - private: - ~SynchronousFence() override; - - void Synchronize(); - - raw_ptr<gpu::gles2::GLES2Interface> gl_; - bool has_synchronized_; - }; - - private: - const ChildResource* LockForRead(ResourceId id, bool overlay_only); - void UnlockForRead(ResourceId id, bool overlay_only); - - // DisplayResourceProvider overrides: - std::vector<ReturnedResource> DeleteAndReturnUnusedResourcesToChildImpl( - Child& child_info, - DeleteStyle style, - const std::vector<ResourceId>& unused) override; - - gpu::gles2::GLES2Interface* ContextGL() const; - void DeleteResourceInternal(ResourceMap::iterator it); - GLenum BindForSampling(ResourceId resource_id, GLenum unit, GLenum filter); - void WaitSyncTokenInternal(ChildResource* resource); - - const raw_ptr<ContextProvider> compositor_context_provider_; - const bool enable_shared_images_; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_GL_H_ diff --git a/chromium/components/viz/service/display/display_resource_provider_gl_unittest.cc b/chromium/components/viz/service/display/display_resource_provider_gl_unittest.cc deleted file mode 100644 index 1c2d058d1a3..00000000000 --- a/chromium/components/viz/service/display/display_resource_provider_gl_unittest.cc +++ /dev/null @@ -1,718 +0,0 @@ -// Copyright 2021 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/display_resource_provider_gl.h" - -#include <stddef.h> -#include <stdint.h> - -#include <memory> -#include <set> -#include <unordered_map> -#include <utility> -#include <vector> - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/check.h" -#include "base/memory/raw_ptr.h" -#include "base/memory/ref_counted.h" -#include "build/build_config.h" -#include "components/viz/client/client_resource_provider.h" -#include "components/viz/common/resources/release_callback.h" -#include "components/viz/common/resources/resource_format_utils.h" -#include "components/viz/common/resources/returned_resource.h" -#include "components/viz/test/test_context_provider.h" -#include "components/viz/test/test_gles2_interface.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/khronos/GLES2/gl2.h" -#include "third_party/khronos/GLES2/gl2ext.h" -#include "ui/gfx/geometry/rect.h" - -using testing::_; -using testing::ByMove; -using testing::DoAll; -using testing::Return; -using testing::SaveArg; - -namespace viz { -namespace { - -class MockReleaseCallback { - public: - MOCK_METHOD2(Released, void(const gpu::SyncToken& token, bool lost)); -}; - -MATCHER_P(MatchesSyncToken, sync_token, "") { - gpu::SyncToken other; - memcpy(&other, arg, sizeof(other)); - return other == sync_token; -} - -static void CollectResources(std::vector<ReturnedResource>* array, - std::vector<ReturnedResource> returned) { - array->insert(array->end(), std::make_move_iterator(returned.begin()), - std::make_move_iterator(returned.end())); -} - -class ResourceProviderGLES2Interface : public TestGLES2Interface { - public: - ResourceProviderGLES2Interface() = default; - - void WaitSyncTokenCHROMIUM(const GLbyte* sync_token) override { - gpu::SyncToken sync_token_data; - if (sync_token) - memcpy(&sync_token_data, sync_token, sizeof(sync_token_data)); - - if (sync_token_data.release_count() > - last_waited_sync_token_.release_count()) { - last_waited_sync_token_ = sync_token_data; - } - } - - const gpu::SyncToken& last_waited_sync_token() const { - return last_waited_sync_token_; - } - - private: - gpu::SyncToken last_waited_sync_token_; -}; - -class DisplayResourceProviderGLTest : public testing::Test { - public: - DisplayResourceProviderGLTest() { - auto gl_owned = std::make_unique<ResourceProviderGLES2Interface>(); - gl_ = gl_owned.get(); - context_provider_ = TestContextProvider::Create(std::move(gl_owned)); - context_provider_->UnboundTestContextGL() - ->set_support_texture_format_bgra8888(true); - context_provider_->BindToCurrentThread(); - - child_context_provider_ = TestContextProvider::Create(); - child_context_provider_->UnboundTestContextGL() - ->set_support_texture_format_bgra8888(true); - child_context_provider_->BindToCurrentThread(); - - resource_provider_ = - std::make_unique<DisplayResourceProviderGL>(context_provider_.get()); - - child_resource_provider_ = std::make_unique<ClientResourceProvider>(); - } - - ~DisplayResourceProviderGLTest() override { - child_resource_provider_->ShutdownAndReleaseAllResources(); - } - - static ReturnCallback GetReturnCallback( - std::vector<ReturnedResource>* array) { - return base::BindRepeating(&CollectResources, array); - } - - static void SetResourceFilter(DisplayResourceProviderGL* resource_provider, - ResourceId id, - GLenum filter) { - DisplayResourceProviderGL::ScopedSamplerGL sampler(resource_provider, id, - GL_TEXTURE_2D, filter); - } - - TransferableResource CreateResource(ResourceFormat format) { - constexpr gfx::Size size(64, 64); - gpu::Mailbox gpu_mailbox = gpu::Mailbox::Generate(); - gpu::SyncToken sync_token = GenSyncToken(); - EXPECT_TRUE(sync_token.HasData()); - - TransferableResource gl_resource = TransferableResource::MakeGL( - gpu_mailbox, GL_LINEAR, GL_TEXTURE_2D, sync_token, size, - false /* is_overlay_candidate */); - gl_resource.format = format; - return gl_resource; - } - - ResourceId MakeGpuResourceAndSendToDisplay( - GLuint filter, - GLuint target, - const gpu::SyncToken& sync_token, - DisplayResourceProvider* resource_provider) { - ReturnCallback return_callback = base::DoNothing(); - - int child = resource_provider->CreateChild(return_callback, SurfaceId()); - - gpu::Mailbox gpu_mailbox = gpu::Mailbox::Generate(); - constexpr gfx::Size size(64, 64); - auto resource = - TransferableResource::MakeGL(gpu_mailbox, GL_LINEAR, target, sync_token, - size, false /* is_overlay_candidate */); - resource.id = ResourceId(11); - resource_provider->ReceiveFromChild(child, {resource}); - auto& map = resource_provider->GetChildToParentMap(child); - return map.find(resource.id)->second; - } - - gpu::SyncToken GenSyncToken() { - gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO, - gpu::CommandBufferId::FromUnsafeValue(0x123), - next_fence_sync_++); - sync_token.SetVerifyFlush(); - return sync_token; - } - - protected: - raw_ptr<ResourceProviderGLES2Interface> gl_ = nullptr; - uint64_t next_fence_sync_ = 1; - scoped_refptr<TestContextProvider> context_provider_; - scoped_refptr<TestContextProvider> child_context_provider_; - std::unique_ptr<DisplayResourceProviderGL> resource_provider_; - std::unique_ptr<ClientResourceProvider> child_resource_provider_; -}; - -TEST_F(DisplayResourceProviderGLTest, ReadLockCountStopsReturnToChildOrDelete) { - MockReleaseCallback release; - TransferableResource tran = CreateResource(RGBA_8888); - ResourceId id1 = child_resource_provider_->ImportResource( - tran, base::BindOnce(&MockReleaseCallback::Released, - base::Unretained(&release))); - - std::vector<ReturnedResource> returned_to_child; - int child_id = resource_provider_->CreateChild( - GetReturnCallback(&returned_to_child), SurfaceId()); - { - // Transfer some resources to the parent. - std::vector<TransferableResource> list; - child_resource_provider_->PrepareSendToParent( - {id1}, &list, - static_cast<RasterContextProvider*>(child_context_provider_.get())); - ASSERT_EQ(1u, list.size()); - EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1)); - - resource_provider_->ReceiveFromChild(child_id, list); - - // In DisplayResourceProvider's namespace, use the mapped resource id. - std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = - resource_provider_->GetChildToParentMap(child_id); - ResourceId mapped_resource_id = resource_map[list[0].id]; - resource_provider_->WaitSyncToken(mapped_resource_id); - DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider_.get(), - mapped_resource_id); - - resource_provider_->DeclareUsedResourcesFromChild(child_id, - ResourceIdSet()); - EXPECT_EQ(0u, returned_to_child.size()); - } - - EXPECT_EQ(1u, returned_to_child.size()); - child_resource_provider_->ReceiveReturnsFromParent( - std::move(returned_to_child)); - - // No need to wait for the sync token here -- it will be returned to the - // client on delete. - { - EXPECT_CALL(release, Released(_, _)); - child_resource_provider_->RemoveImportedResource(id1); - } - - resource_provider_->DestroyChild(child_id); -} - -class TestFence : public ResourceFence { - public: - TestFence() = default; - - // ResourceFence implementation. - void Set() override {} - bool HasPassed() override { return passed; } - - bool passed = false; - - private: - ~TestFence() override = default; -}; - -TEST_F(DisplayResourceProviderGLTest, ReadLockFenceStopsReturnToChildOrDelete) { - MockReleaseCallback release; - TransferableResource tran1 = CreateResource(RGBA_8888); - tran1.read_lock_fences_enabled = true; - ResourceId id1 = child_resource_provider_->ImportResource( - tran1, base::BindOnce(&MockReleaseCallback::Released, - base::Unretained(&release))); - - std::vector<ReturnedResource> returned_to_child; - int child_id = resource_provider_->CreateChild( - GetReturnCallback(&returned_to_child), SurfaceId()); - - // Transfer some resources to the parent. - std::vector<TransferableResource> list; - child_resource_provider_->PrepareSendToParent( - {id1}, &list, - static_cast<RasterContextProvider*>(child_context_provider_.get())); - ASSERT_EQ(1u, list.size()); - EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1)); - EXPECT_TRUE(list[0].read_lock_fences_enabled); - - resource_provider_->ReceiveFromChild(child_id, list); - - // In DisplayResourceProvider's namespace, use the mapped resource id. - std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = - resource_provider_->GetChildToParentMap(child_id); - - scoped_refptr<TestFence> fence(new TestFence); - resource_provider_->SetReadLockFence(fence.get()); - { - ResourceId parent_id = resource_map[list.front().id]; - resource_provider_->WaitSyncToken(parent_id); - DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider_.get(), - parent_id); - } - resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet()); - EXPECT_EQ(0u, returned_to_child.size()); - - resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet()); - EXPECT_EQ(0u, returned_to_child.size()); - fence->passed = true; - - resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet()); - EXPECT_EQ(1u, returned_to_child.size()); - - child_resource_provider_->ReceiveReturnsFromParent( - std::move(returned_to_child)); - EXPECT_CALL(release, Released(_, _)); - child_resource_provider_->RemoveImportedResource(id1); -} - -TEST_F(DisplayResourceProviderGLTest, ReadLockFenceDestroyChild) { - MockReleaseCallback release; - - TransferableResource tran1 = CreateResource(RGBA_8888); - tran1.read_lock_fences_enabled = true; - ResourceId id1 = child_resource_provider_->ImportResource( - tran1, base::BindOnce(&MockReleaseCallback::Released, - base::Unretained(&release))); - - TransferableResource tran2 = CreateResource(RGBA_8888); - tran2.read_lock_fences_enabled = false; - ResourceId id2 = child_resource_provider_->ImportResource( - tran2, base::BindOnce(&MockReleaseCallback::Released, - base::Unretained(&release))); - - std::vector<ReturnedResource> returned_to_child; - int child_id = resource_provider_->CreateChild( - GetReturnCallback(&returned_to_child), SurfaceId()); - - // Transfer resources to the parent. - std::vector<TransferableResource> list; - child_resource_provider_->PrepareSendToParent( - {id1, id2}, &list, - static_cast<RasterContextProvider*>(child_context_provider_.get())); - ASSERT_EQ(2u, list.size()); - EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1)); - EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2)); - - resource_provider_->ReceiveFromChild(child_id, list); - - // In DisplayResourceProvider's namespace, use the mapped resource id. - std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = - resource_provider_->GetChildToParentMap(child_id); - - scoped_refptr<TestFence> fence(new TestFence); - resource_provider_->SetReadLockFence(fence.get()); - { - for (auto& resource : list) { - ResourceId parent_id = resource_map[resource.id]; - resource_provider_->WaitSyncToken(parent_id); - DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider_.get(), - parent_id); - } - } - EXPECT_EQ(0u, returned_to_child.size()); - - EXPECT_EQ(2u, resource_provider_->num_resources()); - - resource_provider_->DestroyChild(child_id); - - EXPECT_EQ(0u, resource_provider_->num_resources()); - EXPECT_EQ(2u, returned_to_child.size()); - - // id1 should be lost and id2 should not. - EXPECT_EQ(returned_to_child[0].lost, returned_to_child[0].id == id1); - EXPECT_EQ(returned_to_child[1].lost, returned_to_child[1].id == id1); - - child_resource_provider_->ReceiveReturnsFromParent( - std::move(returned_to_child)); - EXPECT_CALL(release, Released(_, _)).Times(2); - child_resource_provider_->RemoveImportedResource(id1); - child_resource_provider_->RemoveImportedResource(id2); -} - -// Test that ScopedBatchReturnResources batching works. -TEST_F(DisplayResourceProviderGLTest, - ScopedBatchReturnResourcesPreventsReturn) { - MockReleaseCallback release; - - std::vector<ReturnedResource> returned_to_child; - int child_id = resource_provider_->CreateChild( - GetReturnCallback(&returned_to_child), SurfaceId()); - - // Transfer some resources to the parent. - constexpr size_t kTotalResources = 5; - constexpr size_t kLockedResources = 3; - constexpr size_t kUsedResources = 4; - ResourceId ids[kTotalResources]; - for (auto& id : ids) { - TransferableResource tran = CreateResource(RGBA_8888); - id = child_resource_provider_->ImportResource( - tran, base::BindOnce(&MockReleaseCallback::Released, - base::Unretained(&release))); - } - std::vector<ResourceId> resource_ids_to_transfer(ids, ids + kTotalResources); - - std::vector<TransferableResource> list; - child_resource_provider_->PrepareSendToParent( - resource_ids_to_transfer, &list, - static_cast<RasterContextProvider*>(child_context_provider_.get())); - ASSERT_EQ(kTotalResources, list.size()); - for (const auto& id : ids) - EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id)); - - resource_provider_->ReceiveFromChild(child_id, list); - - // In DisplayResourceProvider's namespace, use the mapped resource id. - std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = - resource_provider_->GetChildToParentMap(child_id); - std::vector<std::unique_ptr<DisplayResourceProviderGL::ScopedReadLockGL>> - read_locks; - for (size_t i = 0; i < kLockedResources; i++) { - ResourceId mapped_resource_id = resource_map[ids[i]]; - resource_provider_->WaitSyncToken(mapped_resource_id); - read_locks.push_back( - std::make_unique<DisplayResourceProviderGL::ScopedReadLockGL>( - resource_provider_.get(), mapped_resource_id)); - } - - // Mark all locked resources, and one unlocked resource as used for first - // batch. - { - DisplayResourceProviderGL::ScopedBatchReturnResources returner( - resource_provider_.get()); - resource_provider_->DeclareUsedResourcesFromChild( - child_id, ResourceIdSet(ids, ids + kUsedResources)); - EXPECT_EQ(0u, returned_to_child.size()); - } - EXPECT_EQ(1u, returned_to_child.size()); - child_resource_provider_->ReceiveReturnsFromParent( - std::move(returned_to_child)); - returned_to_child.clear(); - - // Return all locked resources. - { - DisplayResourceProviderGL::ScopedBatchReturnResources returner( - resource_provider_.get()); - resource_provider_->DeclareUsedResourcesFromChild( - child_id, ResourceIdSet(ids + kLockedResources, ids + kUsedResources)); - // Can be called multiple times while batching is enabled. This happens in - // practice when the same surface is visited using different paths during - // surface aggregation. - resource_provider_->DeclareUsedResourcesFromChild( - child_id, ResourceIdSet(ids + kLockedResources, ids + kUsedResources)); - read_locks.clear(); - EXPECT_EQ(0u, returned_to_child.size()); - } - EXPECT_EQ(kLockedResources, returned_to_child.size()); - // Returned resources that were locked share the same sync token. - for (const auto& resource : returned_to_child) - EXPECT_EQ(resource.sync_token, returned_to_child[0].sync_token); - - child_resource_provider_->ReceiveReturnsFromParent( - std::move(returned_to_child)); - returned_to_child.clear(); - - // Returns from destroying the child is also batched. - { - DisplayResourceProviderGL::ScopedBatchReturnResources returner( - resource_provider_.get()); - resource_provider_->DestroyChild(child_id); - EXPECT_EQ(0u, returned_to_child.size()); - } - EXPECT_EQ(1u, returned_to_child.size()); - child_resource_provider_->ReceiveReturnsFromParent( - std::move(returned_to_child)); - returned_to_child.clear(); - - EXPECT_CALL(release, Released(_, _)).Times(kTotalResources); - for (const auto& id : ids) - child_resource_provider_->RemoveImportedResource(id); -} - -class TextureStateTrackingGLES2Interface : public TestGLES2Interface { - public: - MOCK_METHOD2(BindTexture, void(GLenum target, GLuint texture)); - MOCK_METHOD3(TexParameteri, void(GLenum target, GLenum pname, GLint param)); - MOCK_METHOD1(WaitSyncTokenCHROMIUM, void(const GLbyte* sync_token)); - MOCK_METHOD1(CreateAndConsumeTextureCHROMIUM, - unsigned(const GLbyte* mailbox)); - - // Force all textures to be consecutive numbers starting at "1", - // so we easily can test for them. - GLuint NextTextureId() override { return next_texture_id_++; } - - void RetireTextureId(GLuint) override {} -}; - -class ResourceProviderTestImportedResourceGLFilters { - public: - static void RunTest(bool mailbox_nearest_neighbor, GLenum sampler_filter) { - auto gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>(); - TextureStateTrackingGLES2Interface* gl = gl_owned.get(); - auto context_provider = TestContextProvider::Create(std::move(gl_owned)); - context_provider->BindToCurrentThread(); - - auto resource_provider = - std::make_unique<DisplayResourceProviderGL>(context_provider.get()); - - auto child_gl_owned = - std::make_unique<TextureStateTrackingGLES2Interface>(); - TextureStateTrackingGLES2Interface* child_gl = child_gl_owned.get(); - auto child_context_provider = - TestContextProvider::Create(std::move(child_gl_owned)); - child_context_provider->BindToCurrentThread(); - - auto child_resource_provider = std::make_unique<ClientResourceProvider>(); - - unsigned texture_id = 1; - gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO, - gpu::CommandBufferId::FromUnsafeValue(0x12), - 0x34); - - EXPECT_CALL(*child_gl, BindTexture(_, _)).Times(0); - EXPECT_CALL(*child_gl, WaitSyncTokenCHROMIUM(_)).Times(0); - EXPECT_CALL(*child_gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0); - - gpu::Mailbox gpu_mailbox = gpu::Mailbox::Generate(); - GLuint filter = mailbox_nearest_neighbor ? GL_NEAREST : GL_LINEAR; - constexpr gfx::Size size(64, 64); - auto resource = TransferableResource::MakeGL( - gpu_mailbox, filter, GL_TEXTURE_2D, sync_token, size, - false /* is_overlay_candidate */); - - MockReleaseCallback release; - ResourceId resource_id = child_resource_provider->ImportResource( - resource, base::BindOnce(&MockReleaseCallback::Released, - base::Unretained(&release))); - EXPECT_NE(kInvalidResourceId, resource_id); - - testing::Mock::VerifyAndClearExpectations(child_gl); - - // Transfer resources to the parent. - std::vector<TransferableResource> send_to_parent; - std::vector<ReturnedResource> returned_to_child; - int child_id = resource_provider->CreateChild( - base::BindRepeating(&CollectResources, &returned_to_child), - SurfaceId()); - child_resource_provider->PrepareSendToParent( - {resource_id}, &send_to_parent, - static_cast<RasterContextProvider*>(child_context_provider.get())); - resource_provider->ReceiveFromChild(child_id, send_to_parent); - - // In DisplayResourceProvider's namespace, use the mapped resource id. - std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = - resource_provider->GetChildToParentMap(child_id); - ResourceId mapped_resource_id = resource_map[resource_id]; - { - // The verified flush flag will be set by - // ClientResourceProvider::PrepareSendToParent. Before checking if - // the gpu::SyncToken matches, set this flag first. - sync_token.SetVerifyFlush(); - - // Mailbox sync point WaitSyncToken before using the texture. - EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(MatchesSyncToken(sync_token))); - resource_provider->WaitSyncToken(mapped_resource_id); - testing::Mock::VerifyAndClearExpectations(gl); - - EXPECT_CALL(*gl, CreateAndConsumeTextureCHROMIUM(_)) - .WillOnce(Return(texture_id)); - EXPECT_CALL(*gl, BindTexture(GL_TEXTURE_2D, texture_id)); - - // The sampler will reset these if |mailbox_nearest_neighbor| does not - // match |sampler_filter|. - if (mailbox_nearest_neighbor != (sampler_filter == GL_NEAREST)) { - EXPECT_CALL(*gl, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, - sampler_filter)); - EXPECT_CALL(*gl, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, - sampler_filter)); - } - - DisplayResourceProviderGL::ScopedSamplerGL lock( - resource_provider.get(), mapped_resource_id, sampler_filter); - testing::Mock::VerifyAndClearExpectations(gl); - - // When done with it, a sync point should be inserted, but no produce is - // necessary. - EXPECT_CALL(*child_gl, WaitSyncTokenCHROMIUM(_)).Times(0); - EXPECT_CALL(*child_gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0); - } - - EXPECT_EQ(0u, returned_to_child.size()); - // Transfer resources back from the parent to the child. Set no resources as - // being in use. - resource_provider->DeclareUsedResourcesFromChild(child_id, ResourceIdSet()); - EXPECT_EQ(1u, returned_to_child.size()); - child_resource_provider->ReceiveReturnsFromParent( - std::move(returned_to_child)); - - gpu::SyncToken released_sync_token; - { - EXPECT_CALL(release, Released(_, false)) - .WillOnce(SaveArg<0>(&released_sync_token)); - child_resource_provider->RemoveImportedResource(resource_id); - } - EXPECT_TRUE(released_sync_token.HasData()); - } -}; - -TEST_F(DisplayResourceProviderGLTest, ReceiveGLTexture2D_LinearToLinear) { - ResourceProviderTestImportedResourceGLFilters::RunTest(false, GL_LINEAR); -} - -TEST_F(DisplayResourceProviderGLTest, ReceiveGLTexture2D_NearestToNearest) { - ResourceProviderTestImportedResourceGLFilters::RunTest(true, GL_NEAREST); -} - -TEST_F(DisplayResourceProviderGLTest, ReceiveGLTexture2D_NearestToLinear) { - ResourceProviderTestImportedResourceGLFilters::RunTest(true, GL_LINEAR); -} - -TEST_F(DisplayResourceProviderGLTest, ReceiveGLTexture2D_LinearToNearest) { - ResourceProviderTestImportedResourceGLFilters::RunTest(false, GL_NEAREST); -} - -TEST_F(DisplayResourceProviderGLTest, ReceiveGLTextureExternalOES) { - auto gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>(); - TextureStateTrackingGLES2Interface* gl = gl_owned.get(); - auto context_provider = TestContextProvider::Create(std::move(gl_owned)); - context_provider->BindToCurrentThread(); - - auto resource_provider = - std::make_unique<DisplayResourceProviderGL>(context_provider.get()); - - auto child_gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>(); - TextureStateTrackingGLES2Interface* child_gl = child_gl_owned.get(); - auto child_context_provider = - TestContextProvider::Create(std::move(child_gl_owned)); - child_context_provider->BindToCurrentThread(); - - auto child_resource_provider = std::make_unique<ClientResourceProvider>(); - - gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO, - gpu::CommandBufferId::FromUnsafeValue(0x12), 0x34); - - EXPECT_CALL(*child_gl, BindTexture(_, _)).Times(0); - EXPECT_CALL(*child_gl, WaitSyncTokenCHROMIUM(_)).Times(0); - EXPECT_CALL(*child_gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0); - - gpu::Mailbox gpu_mailbox = gpu::Mailbox::Generate(); - ReleaseCallback callback = base::DoNothing(); - - constexpr gfx::Size size(64, 64); - auto resource = TransferableResource::MakeGL( - gpu_mailbox, GL_LINEAR, GL_TEXTURE_EXTERNAL_OES, sync_token, size, - false /* is_overlay_candidate */); - - ResourceId resource_id = - child_resource_provider->ImportResource(resource, std::move(callback)); - EXPECT_NE(kInvalidResourceId, resource_id); - - testing::Mock::VerifyAndClearExpectations(child_gl); - - // Transfer resources to the parent. - std::vector<TransferableResource> send_to_parent; - std::vector<ReturnedResource> returned_to_child; - int child_id = resource_provider->CreateChild( - base::BindRepeating(&CollectResources, &returned_to_child), SurfaceId()); - child_resource_provider->PrepareSendToParent( - {resource_id}, &send_to_parent, - static_cast<RasterContextProvider*>(child_context_provider_.get())); - resource_provider->ReceiveFromChild(child_id, send_to_parent); - - // Before create DrawQuad in DisplayResourceProvider's namespace, get the - // mapped resource id first. - std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = - resource_provider->GetChildToParentMap(child_id); - ResourceId mapped_resource_id = resource_map[resource_id]; - { - // The verified flush flag will be set by - // ClientResourceProvider::PrepareSendToParent. Before checking if - // the gpu::SyncToken matches, set this flag first. - sync_token.SetVerifyFlush(); - - // Mailbox sync point WaitSyncToken before using the texture. - EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(MatchesSyncToken(sync_token))); - resource_provider->WaitSyncToken(mapped_resource_id); - testing::Mock::VerifyAndClearExpectations(gl); - - unsigned texture_id = 1; - - EXPECT_CALL(*gl, CreateAndConsumeTextureCHROMIUM(_)) - .WillOnce(Return(texture_id)); - - DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider.get(), - mapped_resource_id); - testing::Mock::VerifyAndClearExpectations(gl); - - // When done with it, a sync point should be inserted, but no produce is - // necessary. - EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(0); - EXPECT_CALL(*gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0); - testing::Mock::VerifyAndClearExpectations(gl); - } - EXPECT_EQ(0u, returned_to_child.size()); - // Transfer resources back from the parent to the child. Set no resources as - // being in use. - resource_provider->DeclareUsedResourcesFromChild(child_id, ResourceIdSet()); - EXPECT_EQ(1u, returned_to_child.size()); - child_resource_provider->ReceiveReturnsFromParent( - std::move(returned_to_child)); - - child_resource_provider->RemoveImportedResource(resource_id); -} - -TEST_F(DisplayResourceProviderGLTest, WaitSyncTokenIfNeeded) { - auto gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>(); - TextureStateTrackingGLES2Interface* gl = gl_owned.get(); - auto context_provider = TestContextProvider::Create(std::move(gl_owned)); - context_provider->BindToCurrentThread(); - - auto resource_provider = - std::make_unique<DisplayResourceProviderGL>(context_provider.get()); - - EXPECT_CALL(*gl, BindTexture(_, _)).Times(0); - EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(0); - EXPECT_CALL(*gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0); - - gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO, - gpu::CommandBufferId::FromUnsafeValue(0x12), 0x34); - ResourceId id_with_sync = MakeGpuResourceAndSendToDisplay( - GL_LINEAR, GL_TEXTURE_2D, sync_token, resource_provider.get()); - ResourceId id_without_sync = MakeGpuResourceAndSendToDisplay( - GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(), resource_provider.get()); - - // First call to WaitSyncToken should call WaitSyncToken, but only if a - // SyncToken was present. - { - EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(MatchesSyncToken(sync_token))) - .Times(1); - resource_provider->WaitSyncToken(id_with_sync); - EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(0); - resource_provider->WaitSyncToken(id_without_sync); - } - - { - // Subsequent calls to WaitSyncToken shouldn't call WaitSyncToken. - EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(0); - resource_provider->WaitSyncToken(id_with_sync); - resource_provider->WaitSyncToken(id_without_sync); - } -} - -} // namespace -} // namespace viz diff --git a/chromium/components/viz/service/display/display_resource_provider_skia.cc b/chromium/components/viz/service/display/display_resource_provider_skia.cc index 3d4b0e51011..6e4ea305a8c 100644 --- a/chromium/components/viz/service/display/display_resource_provider_skia.cc +++ b/chromium/components/viz/service/display/display_resource_provider_skia.cc @@ -84,6 +84,13 @@ DisplayResourceProviderSkia::DeleteAndReturnUnusedResourcesToChildImpl( continue; } + if (resource.transferable.synchronization_type == + TransferableResource::SynchronizationType::kReleaseFence) { + // The resource might have never been used. + if (resource.resource_fence) + resource.release_fence = resource.resource_fence->GetGpuFenceHandle(); + } + const bool is_lost = can_delete == CanDeleteNowResult::kYesButLoseResource; to_return.emplace_back(child_id, resource.sync_token(), @@ -130,7 +137,7 @@ DisplayResourceProviderSkia::LockSetForExternalUse::LockResource( ResourceId id, bool maybe_concurrent_reads, bool is_video_plane, - const absl::optional<gfx::ColorSpace>& override_color_space, + sk_sp<SkColorSpace> override_color_space, bool raw_draw_is_possible) { auto it = resource_provider_->resources_.find(id); DCHECK(it != resource_provider_->resources_.end()); @@ -154,8 +161,9 @@ DisplayResourceProviderSkia::LockSetForExternalUse::LockResource( // is very subtle. image_color_space = override_color_space - .value_or(resource.transferable.color_space.GetAsFullRangeRGB()) - .ToSkColorSpace(); + ? override_color_space + : resource.transferable.color_space.GetAsFullRangeRGB() + .ToSkColorSpace(); } resource.image_context = resource_provider_->external_use_client_->CreateImageContext( @@ -166,11 +174,20 @@ DisplayResourceProviderSkia::LockSetForExternalUse::LockResource( } resource.locked_for_external_use = true; - if (resource.transferable.read_lock_fences_enabled) { - if (resource_provider_->current_read_lock_fence_.get()) - resource_provider_->current_read_lock_fence_->Set(); - resource.read_lock_fence = resource_provider_->current_read_lock_fence_; + switch (resource.transferable.synchronization_type) { + case TransferableResource::SynchronizationType::kGpuCommandsCompleted: + resource.resource_fence = + resource_provider_->current_gpu_commands_completed_fence_; + break; + case TransferableResource::SynchronizationType::kReleaseFence: + resource.resource_fence = resource_provider_->current_release_fence_; + break; + default: + break; } + + if (resource.resource_fence) + resource.resource_fence->Set(); } DCHECK(base::Contains(resources_, std::make_pair(id, &resource))); diff --git a/chromium/components/viz/service/display/display_resource_provider_skia.h b/chromium/components/viz/service/display/display_resource_provider_skia.h index 32982471449..6fc3b4385cf 100644 --- a/chromium/components/viz/service/display/display_resource_provider_skia.h +++ b/chromium/components/viz/service/display/display_resource_provider_skia.h @@ -51,15 +51,14 @@ class VIZ_SERVICE_EXPORT DisplayResourceProviderSkia // Lock a resource for external use. The return value was created by // |client| at some point in the past. The SkImage color space will be set - // to |color_space| if valid, otherwise it will be set to the resource's - // color space. If |is_video_plane| is true, the image color space will be - // set to nullptr (to avoid LOG spam). + // to |override_color_space| if non-nullptr, otherwise it will be set to the + // resource's color space. If |is_video_plane| is true, the image color + // space will be set to nullptr (to avoid LOG spam). ExternalUseClient::ImageContext* LockResource( ResourceId resource_id, bool maybe_concurrent_reads, bool is_video_plane, - const absl::optional<gfx::ColorSpace>& override_color_space = - absl::nullopt, + sk_sp<SkColorSpace> override_color_space = nullptr, bool raw_draw_if_possible = false); // Unlock all locked resources with a |sync_token|. The |sync_token| should diff --git a/chromium/components/viz/service/display/display_resource_provider_skia_unittest.cc b/chromium/components/viz/service/display/display_resource_provider_skia_unittest.cc index 35ac8f1b95b..878db989dd3 100644 --- a/chromium/components/viz/service/display/display_resource_provider_skia_unittest.cc +++ b/chromium/components/viz/service/display/display_resource_provider_skia_unittest.cc @@ -17,10 +17,12 @@ #include "base/callback_helpers.h" #include "base/check.h" #include "base/memory/ref_counted.h" +#include "base/memory/scoped_refptr.h" #include "build/build_config.h" #include "components/viz/client/client_resource_provider.h" #include "components/viz/common/resources/release_callback.h" #include "components/viz/common/resources/returned_resource.h" +#include "components/viz/common/resources/transferable_resource.h" #include "components/viz/test/test_context_provider.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -29,6 +31,7 @@ #include "third_party/khronos/GLES2/gl2ext.h" #include "third_party/skia/include/core/SkColorSpace.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/gpu_fence_handle.h" using testing::_; using testing::ByMove; @@ -296,134 +299,218 @@ TEST_F(DisplayResourceProviderSkiaTest, LockForExternalUseWebView) { child_resource_provider_->RemoveImportedResource(id1); } -class TestFence : public ResourceFence { +class TestGpuCommandsCompletedFence : public ResourceFence { public: - TestFence() = default; + TestGpuCommandsCompletedFence() = default; // ResourceFence implementation. void Set() override {} bool HasPassed() override { return passed; } + gfx::GpuFenceHandle GetGpuFenceHandle() override { + NOTREACHED(); + return gfx::GpuFenceHandle(); + } bool passed = false; private: - ~TestFence() override = default; + ~TestGpuCommandsCompletedFence() override = default; }; -TEST_F(DisplayResourceProviderSkiaTest, - ReadLockFenceStopsReturnToChildOrDelete) { - MockReleaseCallback release; - TransferableResource tran1 = CreateResource(RGBA_8888); - tran1.read_lock_fences_enabled = true; - ResourceId id1 = child_resource_provider_->ImportResource( - tran1, base::BindOnce(&MockReleaseCallback::Released, - base::Unretained(&release))); - - std::vector<ReturnedResource> returned_to_child; - int child_id = resource_provider_->CreateChild( - GetReturnCallback(&returned_to_child), SurfaceId()); - - // Transfer some resources to the parent. - std::vector<TransferableResource> list; - child_resource_provider_->PrepareSendToParent( - {id1}, &list, - static_cast<RasterContextProvider*>(child_context_provider_.get())); - ASSERT_EQ(1u, list.size()); - EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1)); - EXPECT_TRUE(list[0].read_lock_fences_enabled); - - resource_provider_->ReceiveFromChild(child_id, list); - - // In DisplayResourceProvider's namespace, use the mapped resource id. - std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = - resource_provider_->GetChildToParentMap(child_id); +class TestReleaseFence : public ResourceFence { + public: + TestReleaseFence() = default; - scoped_refptr<TestFence> fence(new TestFence); - resource_provider_->SetReadLockFence(fence.get()); - { - ResourceId parent_id = resource_map[list.front().id]; - lock_set_->LockResource(parent_id, /*maybe_concurrent_reads=*/true, - /*is_video_plane=*/false); - lock_set_->UnlockResources(GenSyncToken()); + // ResourceFence implementation. + void Set() override {} + bool HasPassed() override { return release_fence_.has_value(); } + gfx::GpuFenceHandle GetGpuFenceHandle() override { + return HasPassed() ? release_fence_->Clone() : gfx::GpuFenceHandle(); } - resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet()); - EXPECT_EQ(0u, returned_to_child.size()); - resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet()); - EXPECT_EQ(0u, returned_to_child.size()); - fence->passed = true; + void SetReleaseFence(gfx::GpuFenceHandle release_fence) { + release_fence_ = std::move(release_fence); + } - resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet()); - EXPECT_EQ(1u, returned_to_child.size()); + private: + ~TestReleaseFence() override = default; - child_resource_provider_->ReceiveReturnsFromParent( - std::move(returned_to_child)); - EXPECT_CALL(release, Released(_, _)); - child_resource_provider_->RemoveImportedResource(id1); -} + absl::optional<gfx::GpuFenceHandle> release_fence_; +}; -TEST_F(DisplayResourceProviderSkiaTest, ReadLockFenceDestroyChild) { - MockReleaseCallback release; +TEST_F(DisplayResourceProviderSkiaTest, + ResourceFenceStopsReturnToChildOrDelete) { + const std::vector<TransferableResource::SynchronizationType> + kSynchronizationTypes = { + TransferableResource::SynchronizationType::kGpuCommandsCompleted, + TransferableResource::SynchronizationType::kReleaseFence}; + for (auto sync_type : kSynchronizationTypes) { + MockReleaseCallback release; + TransferableResource tran1 = CreateResource(RGBA_8888); + tran1.synchronization_type = sync_type; + ResourceId id1 = child_resource_provider_->ImportResource( + tran1, base::BindOnce(&MockReleaseCallback::Released, + base::Unretained(&release))); + + std::vector<ReturnedResource> returned_to_child; + int child_id = resource_provider_->CreateChild( + GetReturnCallback(&returned_to_child), SurfaceId()); + + // Transfer some resources to the parent. + std::vector<TransferableResource> list; + child_resource_provider_->PrepareSendToParent( + {id1}, &list, + static_cast<RasterContextProvider*>(child_context_provider_.get())); + ASSERT_EQ(1u, list.size()); + EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1)); + EXPECT_EQ(list[0].synchronization_type, sync_type); + + resource_provider_->ReceiveFromChild(child_id, list); + + // In DisplayResourceProvider's namespace, use the mapped resource id. + std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = + resource_provider_->GetChildToParentMap(child_id); + + scoped_refptr<ResourceFence> fence; + TestGpuCommandsCompletedFence* gpu_commands_completed_fence = nullptr; + TestReleaseFence* release_fence = nullptr; + if (sync_type == + TransferableResource::SynchronizationType::kGpuCommandsCompleted) { + fence = base::MakeRefCounted<TestGpuCommandsCompletedFence>(); + gpu_commands_completed_fence = + static_cast<TestGpuCommandsCompletedFence*>(fence.get()); + resource_provider_->SetGpuCommandsCompletedFence(fence.get()); + } else { + ASSERT_EQ(TransferableResource::SynchronizationType::kReleaseFence, + sync_type); + fence = base::MakeRefCounted<TestReleaseFence>(); + release_fence = static_cast<TestReleaseFence*>(fence.get()); + resource_provider_->SetReleaseFence(fence.get()); + } - TransferableResource tran1 = CreateResource(RGBA_8888); - tran1.read_lock_fences_enabled = true; - ResourceId id1 = child_resource_provider_->ImportResource( - tran1, base::BindOnce(&MockReleaseCallback::Released, - base::Unretained(&release))); + { + ResourceId parent_id = resource_map[list.front().id]; + lock_set_->LockResource(parent_id, /*maybe_concurrent_reads=*/true, + /*is_video_plane=*/false); + lock_set_->UnlockResources(GenSyncToken()); + } - TransferableResource tran2 = CreateResource(RGBA_8888); - tran2.read_lock_fences_enabled = false; - ResourceId id2 = child_resource_provider_->ImportResource( - tran2, base::BindOnce(&MockReleaseCallback::Released, - base::Unretained(&release))); + resource_provider_->DeclareUsedResourcesFromChild(child_id, + ResourceIdSet()); + EXPECT_EQ(0u, returned_to_child.size()); - std::vector<ReturnedResource> returned_to_child; - int child_id = resource_provider_->CreateChild( - GetReturnCallback(&returned_to_child), SurfaceId()); + resource_provider_->DeclareUsedResourcesFromChild(child_id, + ResourceIdSet()); + EXPECT_EQ(0u, returned_to_child.size()); - // Transfer resources to the parent. - std::vector<TransferableResource> list; - child_resource_provider_->PrepareSendToParent( - {id1, id2}, &list, - static_cast<RasterContextProvider*>(child_context_provider_.get())); - ASSERT_EQ(2u, list.size()); - EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1)); - EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2)); + if (gpu_commands_completed_fence) { + gpu_commands_completed_fence->passed = true; + } else { + gfx::GpuFenceHandle fake_handle; +#if BUILDFLAG(IS_POSIX) + const int32_t kFenceFd = dup(1); + fake_handle.owned_fd.reset(kFenceFd); +#endif + release_fence->SetReleaseFence(std::move(fake_handle)); + } - resource_provider_->ReceiveFromChild(child_id, list); + resource_provider_->DeclareUsedResourcesFromChild(child_id, + ResourceIdSet()); + EXPECT_EQ(1u, returned_to_child.size()); + +#if BUILDFLAG(IS_POSIX) + if (release_fence) + EXPECT_FALSE(returned_to_child.begin()->release_fence.is_null()); + else + EXPECT_TRUE(returned_to_child.begin()->release_fence.is_null()); +#endif + + child_resource_provider_->ReceiveReturnsFromParent( + std::move(returned_to_child)); + EXPECT_CALL(release, Released(_, _)); + child_resource_provider_->RemoveImportedResource(id1); + } +} - // In DisplayResourceProvider's namespace, use the mapped resource id. - std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = - resource_provider_->GetChildToParentMap(child_id); +TEST_F(DisplayResourceProviderSkiaTest, ResourceFenceDestroyChild) { + const std::vector<TransferableResource::SynchronizationType> + kSynchronizationTypes = { + TransferableResource::SynchronizationType::kGpuCommandsCompleted, + TransferableResource::SynchronizationType::kReleaseFence}; + for (auto sync_type : kSynchronizationTypes) { + MockReleaseCallback release; + + TransferableResource tran1 = CreateResource(RGBA_8888); + tran1.synchronization_type = sync_type; + ResourceId id1 = child_resource_provider_->ImportResource( + tran1, base::BindOnce(&MockReleaseCallback::Released, + base::Unretained(&release))); + + TransferableResource tran2 = CreateResource(RGBA_8888); + ASSERT_EQ(tran2.synchronization_type, + TransferableResource::SynchronizationType::kSyncToken); + ResourceId id2 = child_resource_provider_->ImportResource( + tran2, base::BindOnce(&MockReleaseCallback::Released, + base::Unretained(&release))); + + std::vector<ReturnedResource> returned_to_child; + int child_id = resource_provider_->CreateChild( + GetReturnCallback(&returned_to_child), SurfaceId()); + + // Transfer resources to the parent. + std::vector<TransferableResource> list; + child_resource_provider_->PrepareSendToParent( + {id1, id2}, &list, + static_cast<RasterContextProvider*>(child_context_provider_.get())); + ASSERT_EQ(2u, list.size()); + EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1)); + EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2)); + + resource_provider_->ReceiveFromChild(child_id, list); + + // In DisplayResourceProvider's namespace, use the mapped resource id. + std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = + resource_provider_->GetChildToParentMap(child_id); + + scoped_refptr<ResourceFence> fence; + if (sync_type == + TransferableResource::SynchronizationType::kGpuCommandsCompleted) { + fence = base::MakeRefCounted<TestGpuCommandsCompletedFence>(); + resource_provider_->SetGpuCommandsCompletedFence(fence.get()); + } else { + ASSERT_EQ(TransferableResource::SynchronizationType::kReleaseFence, + sync_type); + fence = base::MakeRefCounted<TestReleaseFence>(); + resource_provider_->SetReleaseFence(fence.get()); + } - scoped_refptr<TestFence> fence(new TestFence); - resource_provider_->SetReadLockFence(fence.get()); - { - for (auto& resource : list) { - ResourceId parent_id = resource_map[resource.id]; - lock_set_->LockResource(parent_id, /*maybe_concurrent_reads=*/true, - /*is_video_plane=*/false); + { + for (auto& resource : list) { + ResourceId parent_id = resource_map[resource.id]; + lock_set_->LockResource(parent_id, /*maybe_concurrent_reads=*/true, + /*is_video_plane=*/false); + } + lock_set_->UnlockResources(GenSyncToken()); } - lock_set_->UnlockResources(GenSyncToken()); - } - EXPECT_EQ(0u, returned_to_child.size()); + EXPECT_EQ(0u, returned_to_child.size()); - EXPECT_EQ(2u, resource_provider_->num_resources()); + EXPECT_EQ(2u, resource_provider_->num_resources()); - resource_provider_->DestroyChild(child_id); + resource_provider_->DestroyChild(child_id); - EXPECT_EQ(0u, resource_provider_->num_resources()); - EXPECT_EQ(2u, returned_to_child.size()); + EXPECT_EQ(0u, resource_provider_->num_resources()); + EXPECT_EQ(2u, returned_to_child.size()); - // id1 should be lost and id2 should not. - EXPECT_EQ(returned_to_child[0].lost, returned_to_child[0].id == id1); - EXPECT_EQ(returned_to_child[1].lost, returned_to_child[1].id == id1); + // id1 should be lost and id2 should not. + EXPECT_EQ(returned_to_child[0].lost, returned_to_child[0].id == id1); + EXPECT_EQ(returned_to_child[1].lost, returned_to_child[1].id == id1); - child_resource_provider_->ReceiveReturnsFromParent( - std::move(returned_to_child)); - EXPECT_CALL(release, Released(_, _)).Times(2); - child_resource_provider_->RemoveImportedResource(id1); - child_resource_provider_->RemoveImportedResource(id2); + child_resource_provider_->ReceiveReturnsFromParent( + std::move(returned_to_child)); + EXPECT_CALL(release, Released(_, _)).Times(2); + child_resource_provider_->RemoveImportedResource(id1); + child_resource_provider_->RemoveImportedResource(id2); + } } // Test that ScopedBatchReturnResources batching works. diff --git a/chromium/components/viz/service/display/display_scheduler.cc b/chromium/components/viz/service/display/display_scheduler.cc index 1139afa955e..b1757695422 100644 --- a/chromium/components/viz/service/display/display_scheduler.cc +++ b/chromium/components/viz/service/display/display_scheduler.cc @@ -17,6 +17,23 @@ namespace viz { +namespace { + +base::TimeDelta ComputeAdpfTarget(const BeginFrameArgs& args) { + int target_ms = features::kAdpfTargetDurationMs.Get(); + if (target_ms > 0 && target_ms <= 1000) { + return base::Milliseconds(target_ms); + } + if (args.possible_deadlines) { + const auto& deadline = args.possible_deadlines->GetPreferredDeadline(); + // Arbitrarily use 75% of the deadline for CPU work. + return deadline.latch_delta * 3 / 4; + } + return base::Milliseconds(12); +} + +} // namespace + class DisplayScheduler::BeginFrameObserver : public BeginFrameObserverBase { public: explicit BeginFrameObserver(DisplayScheduler* scheduler) @@ -160,12 +177,9 @@ void DisplayScheduler::MaybeCreateHintSession( if ((!create_session_for_current_thread_ids_failed_ && !hint_session_) || current_thread_ids_ != thread_ids) { hint_session_.reset(); - int target_ms = features::kAdpfTargetDurationMs.Get(); - if (target_ms <= 0 || target_ms > 1000) - target_ms = 12; current_thread_ids_ = std::move(thread_ids); hint_session_ = hint_session_factory_->CreateSession( - current_thread_ids_, base::Milliseconds(target_ms)); + current_thread_ids_, ComputeAdpfTarget(current_begin_frame_args_)); create_session_for_current_thread_ids_failed_ = !hint_session_; } } @@ -241,6 +255,9 @@ bool DisplayScheduler::OnBeginFrame(const BeginFrameArgs& args) { // Schedule the deadline. current_begin_frame_args_ = save_args; + if (hint_session_) { + hint_session_->UpdateTargetDuration(ComputeAdpfTarget(save_args)); + } base::TimeDelta delta; if (client_ && dynamic_scheduler_deadlines_percentile_.has_value() && diff --git a/chromium/components/viz/service/display/display_scheduler_unittest.cc b/chromium/components/viz/service/display/display_scheduler_unittest.cc index 041b801f58e..c26548bfae7 100644 --- a/chromium/components/viz/service/display/display_scheduler_unittest.cc +++ b/chromium/components/viz/service/display/display_scheduler_unittest.cc @@ -181,7 +181,9 @@ class DisplaySchedulerTest : public testing::Test { : wait_for_all_surfaces_before_draw_(wait_for_all_surfaces_before_draw), fake_begin_frame_source_(0.f, false), task_runner_(new base::NullTaskRunner), - surface_manager_(nullptr, 4u), + surface_manager_(nullptr, + /*activation_deadline_in_frames=*/4u, + /*max_uncommitted_frames=*/0), resource_provider_(&shared_bitmap_manager_), aggregator_(&surface_manager_, &resource_provider_, false, false), damage_tracker_( diff --git a/chromium/components/viz/service/display/display_unittest.cc b/chromium/components/viz/service/display/display_unittest.cc index df18d96d450..7175b9ef94d 100644 --- a/chromium/components/viz/service/display/display_unittest.cc +++ b/chromium/components/viz/service/display/display_unittest.cc @@ -68,7 +68,6 @@ #include "ui/gfx/delegated_ink_point.h" #include "ui/gfx/mojom/delegated_ink_point_renderer.mojom.h" #include "ui/gfx/overlay_transform.h" -#include "ui/gfx/presentation_feedback.h" using testing::_; using testing::AnyNumber; @@ -169,10 +168,11 @@ class DisplayTest : public testing::Test { ~DisplayTest() override {} void SetUpSoftwareDisplay(const RendererSettings& settings) { - std::unique_ptr<FakeOutputSurface> output_surface; + std::unique_ptr<FakeSoftwareOutputSurface> output_surface; auto device = std::make_unique<TestSoftwareOutputDevice>(); software_output_device_ = device.get(); - output_surface = FakeOutputSurface::CreateSoftware(std::move(device)); + output_surface = + std::make_unique<FakeSoftwareOutputSurface>(std::move(device)); output_surface_ = output_surface.get(); CreateDisplaySchedulerAndDisplay(settings, kArbitraryFrameSinkId, @@ -272,7 +272,7 @@ class DisplayTest : public testing::Test { std::unique_ptr<BeginFrameSource> begin_frame_source_; std::unique_ptr<Display> display_; raw_ptr<TestSoftwareOutputDevice> software_output_device_ = nullptr; - raw_ptr<FakeOutputSurface> output_surface_ = nullptr; + raw_ptr<FakeSoftwareOutputSurface> output_surface_ = nullptr; raw_ptr<FakeSkiaOutputSurface> skia_output_surface_ = nullptr; raw_ptr<TestDisplayScheduler> scheduler_ = nullptr; }; @@ -283,7 +283,7 @@ TEST_F(DisplayTest, DisplayDamaged) { settings.partial_swap_enabled = true; SetUpSoftwareDisplay(settings); gfx::ColorSpace color_space_1 = gfx::ColorSpace::CreateXYZD50(); - gfx::ColorSpace color_space_2 = gfx::ColorSpace::CreateSCRGBLinear(); + gfx::ColorSpace color_space_2 = gfx::ColorSpace::CreateSRGBLinear(); gfx::DisplayColorSpaces color_spaces_1(color_space_1); gfx::DisplayColorSpaces color_spaces_2(color_space_2); @@ -862,7 +862,7 @@ TEST_F(DisplayTest, CompositorFrameDamagesCorrectDisplay) { TestDisplayScheduler* scheduler2 = scheduler_for_display2.get(); auto display2 = CreateDisplay( settings, kAnotherFrameSinkId, std::move(scheduler_for_display2), - FakeOutputSurface::CreateSoftware( + std::make_unique<FakeSoftwareOutputSurface>( std::make_unique<TestSoftwareOutputDevice>())); manager_.RegisterBeginFrameSource(begin_frame_source2.get(), kAnotherFrameSinkId); @@ -1055,9 +1055,9 @@ TEST_F(DisplayTest, DrawOcclusionWithNonCoveringDrawQuad) { // DrawQuad, so the size of quad_list remains unchanged after calling // RemoveOverdrawQuads. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } SharedQuadState* shared_quad_state2 = frame.render_pass_list.front()->CreateAndAppendSharedQuadState(); @@ -1089,12 +1089,12 @@ TEST_F(DisplayTest, DrawOcclusionWithNonCoveringDrawQuad) { // which cannot be represented by a smaller rect (its visible_rect stays // the same). EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect2, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } // +------+ +------+ @@ -1123,13 +1123,12 @@ TEST_F(DisplayTest, DrawOcclusionWithNonCoveringDrawQuad) { // screen is rect3 - rect1 U rect3 = (25, 100, 50x25), which updates its // visible_rect accordingly. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(gfx::Rect(25, 100, 50, 25).ToString(), - frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + gfx::Rect(25, 100, 50, 25), + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } // +--+ +--+ @@ -1159,12 +1158,12 @@ TEST_F(DisplayTest, DrawOcclusionWithNonCoveringDrawQuad) { // cannot be represented by a smaller rect (its visible_rect stays the // same). EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect7.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect6.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + rect7, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect6, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } // +----+ +--+ @@ -1190,12 +1189,12 @@ TEST_F(DisplayTest, DrawOcclusionWithNonCoveringDrawQuad) { // after calling RemoveOverdrawQuads. The visible region of |quad2| on // screen is rect4 (150, 0, 50x50), its visible_rect stays the same. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect4.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect4, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } // +-----++ // | || @@ -1223,12 +1222,12 @@ TEST_F(DisplayTest, DrawOcclusionWithNonCoveringDrawQuad) { // which cannot be represented by a smaller rect (its visible_rect stays the // same). EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect5.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect5, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } } @@ -1278,12 +1277,12 @@ TEST_F(DisplayTest, DrawOcclusionWithSingleOverlapBehindDisjointedDrawQuads) { // third quad is removed from the |quad_list|, leaving the first and second // (defined by rects[1](150, 0, 150x150); the largest) quads intact. ASSERT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rects[0].ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rects[1].ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + rects[0], + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rects[1], + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } } @@ -1334,12 +1333,12 @@ TEST_F(DisplayTest, DrawOcclusionWithMultipleOverlapBehindDisjointedDrawQuads) { // 0, 150x150)) quads, respectively, so both are removed from the // |quad_list|, leaving the first and and second quads intact. ASSERT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rects[0].ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rects[1].ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + rects[0], + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rects[1], + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } } @@ -1385,9 +1384,9 @@ TEST_F(DisplayTest, CompositorFrameWithOverlapDrawQuad) { display_->RemoveOverdrawQuads(&frame); // |quad2| overlaps |quad1|, so |quad2| is removed from the |quad_list|. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } // +-----+ // | +-+ | @@ -1410,9 +1409,9 @@ TEST_F(DisplayTest, CompositorFrameWithOverlapDrawQuad) { // |quad2| is hiding behind |quad1|, so |quad2| is removed from the // |quad_list|. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } // +-----+ @@ -1436,9 +1435,9 @@ TEST_F(DisplayTest, CompositorFrameWithOverlapDrawQuad) { // |quad2| is behind |quad1| and aligns with the edge of |quad1|, so |quad2| // is removed from the |quad_list|. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } // +-----++ @@ -1464,9 +1463,9 @@ TEST_F(DisplayTest, CompositorFrameWithOverlapDrawQuad) { // |quad2| is covered by |quad 1|, so |quad2| is removed from the // |quad_list|. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } } @@ -1527,9 +1526,9 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) { // |quad2| is now covered by |quad|. So the size of |quad_list| is reduced // by 1. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } { @@ -1550,9 +1549,9 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) { // |quad2| is now covered by |quad|. So the size of |quad_list| is reduced // by 1. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } { @@ -1575,9 +1574,9 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) { // |quad2| is now covered by |quad1|. So the size of |quad_list| is reduced // by 1. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } { @@ -1591,9 +1590,9 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) { // The compositor frame contains only one quad, so |quad_list| remains 1 // after calling RemoveOverdrawQuads. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } { @@ -1618,12 +1617,12 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) { // Since visible region of |rect5| is not a rect, quad2::visible_rect stays // the same. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect5.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(4) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect5, + frame.render_pass_list.front()->quad_list.ElementAt(4)->visible_rect); } { @@ -1646,13 +1645,12 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) { // Since visible region of |rect5| is (12, 50, 25x12), quad2::visible_rect // updates accordingly. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(gfx::Rect(12, 50, 25, 12).ToString(), - frame.render_pass_list.front() - ->quad_list.ElementAt(4) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + gfx::Rect(12, 50, 25, 12), + frame.render_pass_list.front()->quad_list.ElementAt(4)->visible_rect); } { @@ -1675,12 +1673,12 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) { // Since visible region of |rect7| is not a rect, quad2::visible_rect stays // the same. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect7.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(4) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect7, + frame.render_pass_list.front()->quad_list.ElementAt(4)->visible_rect); } { @@ -1703,12 +1701,12 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) { // Since visible region of |rect8| is not a rect, quad2::visible_rect stays // the same. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect8.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(4) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect8, + frame.render_pass_list.front()->quad_list.ElementAt(4)->visible_rect); } { @@ -1731,12 +1729,12 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) { // Since visible region of |rect9| is not a rect, quad2::visible_rect stays // the same EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect10.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect9.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(4) - ->visible_rect.ToString()); + EXPECT_EQ( + rect10, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect9, + frame.render_pass_list.front()->quad_list.ElementAt(4)->visible_rect); } } @@ -1786,12 +1784,12 @@ TEST_F(DisplayTest, CompositorFrameWithEpsilonScaleTransform) { // draw occlusion algorithm. EXPECT_FALSE(zero_scale.GetInverse(&inverted)); EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + rect, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } { @@ -1816,12 +1814,12 @@ TEST_F(DisplayTest, CompositorFrameWithEpsilonScaleTransform) { epsilon_scale, rect) .IsEmpty()); EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + rect, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } { @@ -1843,9 +1841,9 @@ TEST_F(DisplayTest, CompositorFrameWithEpsilonScaleTransform) { // occlusion rect, so |quad2| is removed. EXPECT_TRUE(larger_epsilon_scale.GetInverse(&inverted)); EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } } @@ -1894,12 +1892,12 @@ TEST_F(DisplayTest, CompositorFrameWithNegativeScaleTransform) { // | // | EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + rect, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } { @@ -1926,12 +1924,12 @@ TEST_F(DisplayTest, CompositorFrameWithNegativeScaleTransform) { // | | // |----+ EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + rect, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } { @@ -1958,9 +1956,9 @@ TEST_F(DisplayTest, CompositorFrameWithNegativeScaleTransform) { // | // | EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } } @@ -2012,12 +2010,12 @@ TEST_F(DisplayTest, CompositorFrameWithRotation) { // transform) and |quad2| becomes (75, 75 10x10). So |quad2| does not // intersect with |quad|. No changes in quads. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect2, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } { @@ -2036,9 +2034,9 @@ TEST_F(DisplayTest, CompositorFrameWithRotation) { // (53, 75 8x10) (after applying rotation transform). So |quad2| is behind // |quad|. |quad2| is removed from |quad_list|. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } { @@ -2058,12 +2056,12 @@ TEST_F(DisplayTest, CompositorFrameWithRotation) { // transform) and |quad2| becomes (50, 50, 25x100). So |quad2| does not // intersect with |quad|. No changes in quads. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect3.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(2) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect3, + frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect); } { @@ -2084,12 +2082,12 @@ TEST_F(DisplayTest, CompositorFrameWithRotation) { // does not cover |rect3| initially, |quad| does not cover |quad2| in target // space. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect3.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(2) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect3, + frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect); } } @@ -2137,12 +2135,12 @@ TEST_F(DisplayTest, CompositorFrameWithPerspective) { // long to define a enclosed rect to describe the occlusion region, // occlusion region is not defined and no changes in quads. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } { @@ -2161,9 +2159,9 @@ TEST_F(DisplayTest, CompositorFrameWithPerspective) { // an enclosing rect to describe |quad2|. |quad2| is hiding behind |quad|, // so it's removed from |quad_list|. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } } @@ -2204,12 +2202,12 @@ TEST_F(DisplayTest, CompositorFrameWithOpacityChange) { // Since the opacity of |rect2| is less than 1, |rect1| cannot occlude // |rect2| even though |rect2| is inside |rect1|. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect2, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } { @@ -2225,9 +2223,9 @@ TEST_F(DisplayTest, CompositorFrameWithOpacityChange) { display_->RemoveOverdrawQuads(&frame); // Repeat the above test and set the opacity of |rect1| to 1. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } } @@ -2267,12 +2265,12 @@ TEST_F(DisplayTest, CompositorFrameWithOpaquenessChange) { // Since the opaqueness of |rect2| is false, |rect1| cannot occlude // |rect2| even though |rect2| is inside |rect1|. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect2, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } { @@ -2288,9 +2286,9 @@ TEST_F(DisplayTest, CompositorFrameWithOpaquenessChange) { display_->RemoveOverdrawQuads(&frame); // Repeat the above test and set the opaqueness of |rect2| to true. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } } @@ -2338,12 +2336,10 @@ TEST_F(DisplayTest, CompositorFrameZTranslate) { // Since both |quad| and |quad2| are inside of a 3d object, DrawOcclusion // will not be applied to them. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->rect.ToString()); - EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->rect.ToString()); + EXPECT_EQ(rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->rect); + EXPECT_EQ(rect2, + frame.render_pass_list.front()->quad_list.ElementAt(1)->rect); } } @@ -2394,12 +2390,12 @@ TEST_F(DisplayTest, CompositorFrameWithTranslateTransformer) { // |rect2| and |rect1| are disjoined as show in the first image. The size of // |quad_list| remains 2. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect2, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } { @@ -2424,9 +2420,9 @@ TEST_F(DisplayTest, CompositorFrameWithTranslateTransformer) { // translation transform. |quad2| will be covered by |quad|, so |quad_list| // size is reduced by 1. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } { @@ -2455,13 +2451,12 @@ TEST_F(DisplayTest, CompositorFrameWithTranslateTransformer) { // |quad2| is (100, 100, 100x20). So the visible region of |quad2| is // (150, 100, 50x20). EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(gfx::Rect(150, 100, 50, 20).ToString(), - frame.render_pass_list.front() - ->quad_list.ElementAt(2) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + gfx::Rect(150, 100, 50, 20), + frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect); } } @@ -2521,12 +2516,12 @@ TEST_F(DisplayTest, CompositorFrameWithCombinedSharedQuadState) { // |rect2|. |rect3| is covered by both |rect1| and |rect2|, so |rect3| is // removed from |quad_list|. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect2, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } { @@ -2548,16 +2543,15 @@ TEST_F(DisplayTest, CompositorFrameWithCombinedSharedQuadState) { // and |rect2|, is (0, 0, 160x60). Since visible region of rect 4 is // (160, 10, 30x30), |visible_rect| of |quad3| is updated. EXPECT_EQ(3u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); - EXPECT_EQ(gfx::Rect(160, 10, 30, 30).ToString(), - frame.render_pass_list.front() - ->quad_list.ElementAt(3) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect2, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); + EXPECT_EQ( + gfx::Rect(160, 10, 30, 30), + frame.render_pass_list.front()->quad_list.ElementAt(3)->visible_rect); } { @@ -2577,16 +2571,15 @@ TEST_F(DisplayTest, CompositorFrameWithCombinedSharedQuadState) { // and |rect2|, is (0, 0, 160x60). Since visible region of rect 5 is // (10, 60, 120x50), |visible_rect| of |quad3| is updated. EXPECT_EQ(3u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); - EXPECT_EQ(gfx::Rect(10, 60, 120, 50).ToString(), - frame.render_pass_list.front() - ->quad_list.ElementAt(3) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect2, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); + EXPECT_EQ( + gfx::Rect(10, 60, 120, 50), + frame.render_pass_list.front()->quad_list.ElementAt(3)->visible_rect); } } @@ -2699,15 +2692,15 @@ TEST_F(DisplayTest, CompositorFrameWithMultipleRenderPass) { // But |rect3| so |rect3| is to be removed from |quad_list|. EXPECT_EQ(2u, frame.render_pass_list.at(1)->quad_list.size()); EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.at(1) - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect2.ToString(), frame.render_pass_list.at(1) - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); - EXPECT_EQ(rect3.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.at(1)->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect2, + frame.render_pass_list.at(1)->quad_list.ElementAt(1)->visible_rect); + EXPECT_EQ( + rect3, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } } @@ -2770,12 +2763,12 @@ TEST_F(DisplayTest, CompositorFrameWithCoveredRenderPass) { // |quad_list|. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); EXPECT_EQ(1u, frame.render_pass_list.at(1)->quad_list.size()); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.at(1) - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect1, + frame.render_pass_list.at(1)->quad_list.ElementAt(0)->visible_rect); } } @@ -2823,9 +2816,9 @@ TEST_F(DisplayTest, CompositorFrameWithClip) { // |rect1| covers |rect2| as shown in the figure above, So the size of // |quad_list| is reduced by 1. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } { @@ -2851,12 +2844,12 @@ TEST_F(DisplayTest, CompositorFrameWithClip) { // (0, 0, 60x60) |quad| and |quad2| (50, 50, 25x25) don't intersect in the // target space. So no change is applied to quads. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(2) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect2, + frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect); } { @@ -2881,13 +2874,12 @@ TEST_F(DisplayTest, CompositorFrameWithClip) { // visible region of |quad2| is (60, 50, 10x10). So |quad2| is updated // accordingly. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(gfx::Rect(60, 50, 10, 10).ToString(), - frame.render_pass_list.front() - ->quad_list.ElementAt(2) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + gfx::Rect(60, 50, 10, 10), + frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect); } } @@ -2931,9 +2923,9 @@ TEST_F(DisplayTest, CompositorFrameWithCopyRequest) { // occlusion with copy_request on root RenderPass, |quad_list| reduces its // size by 1 after calling remove overdraw. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } } @@ -3009,18 +3001,18 @@ TEST_F(DisplayTest, CompositorFrameWithRenderPass) { // occlude each other. Since AggregatedRenderPassDrawQuad |r1| and |r2| // cannot be removed to reduce overdraw, |quad_list| remains unchanged. EXPECT_EQ(4u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); - EXPECT_EQ(rect3.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(2) - ->visible_rect.ToString()); - EXPECT_EQ(rect4.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(3) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect2, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); + EXPECT_EQ( + rect3, + frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect); + EXPECT_EQ( + rect4, + frame.render_pass_list.front()->quad_list.ElementAt(3)->visible_rect); } { @@ -3057,18 +3049,18 @@ TEST_F(DisplayTest, CompositorFrameWithRenderPass) { // occlude each other. Since AggregatedRenderPassDrawQuad |r1| and |r2| // cannot be removed to reduce overdraw, |quad_list| remains unchanged. EXPECT_EQ(4u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect5.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); - EXPECT_EQ(rect3.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(2) - ->visible_rect.ToString()); - EXPECT_EQ(rect6.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(3) - ->visible_rect.ToString()); + EXPECT_EQ( + rect5, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); + EXPECT_EQ( + rect3, + frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect); + EXPECT_EQ( + rect6, + frame.render_pass_list.front()->quad_list.ElementAt(3)->visible_rect); } { @@ -3104,15 +3096,15 @@ TEST_F(DisplayTest, CompositorFrameWithRenderPass) { // Since AggregatedRenderPassDrawQuad |r1| and |r2| cannot be removed to // reduce overdraw, |quad_list| is reduced by 1. EXPECT_EQ(3u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect5.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); - EXPECT_EQ(rect3.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(2) - ->visible_rect.ToString()); + EXPECT_EQ( + rect5, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); + EXPECT_EQ( + rect3, + frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect); } } @@ -3183,18 +3175,18 @@ TEST_F(DisplayTest, CompositorFrameWithMultipleDrawQuadInSharedQuadState) { // |visible_rect| of |shared_quad_state| is formed by 4 DrawQuads and it // covers the visible region of |shared_quad_state2|. EXPECT_EQ(4u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1_1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect1_2.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); - EXPECT_EQ(rect1_3.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(2) - ->visible_rect.ToString()); - EXPECT_EQ(rect1_4.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(3) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1_1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect1_2, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); + EXPECT_EQ( + rect1_3, + frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect); + EXPECT_EQ( + rect1_4, + frame.render_pass_list.front()->quad_list.ElementAt(3)->visible_rect); } { @@ -3219,22 +3211,21 @@ TEST_F(DisplayTest, CompositorFrameWithMultipleDrawQuadInSharedQuadState) { // partially covers the visible region of |shared_quad_state2|. The // |visible_rect| of |quad5| is updated. EXPECT_EQ(5u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1_1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect1_2.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); - EXPECT_EQ(rect1_3.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(2) - ->visible_rect.ToString()); - EXPECT_EQ(rect1_4.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(3) - ->visible_rect.ToString()); - EXPECT_EQ(gfx::Rect(100, 0, 30, 30).ToString(), - frame.render_pass_list.front() - ->quad_list.ElementAt(5) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1_1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect1_2, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); + EXPECT_EQ( + rect1_3, + frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect); + EXPECT_EQ( + rect1_4, + frame.render_pass_list.front()->quad_list.ElementAt(3)->visible_rect); + EXPECT_EQ( + gfx::Rect(100, 0, 30, 30), + frame.render_pass_list.front()->quad_list.ElementAt(5)->visible_rect); } { @@ -3267,26 +3258,24 @@ TEST_F(DisplayTest, CompositorFrameWithMultipleDrawQuadInSharedQuadState) { // |visible_rect| of DrawQuads in |share_quad_state2| are updated to the // region shown on screen. EXPECT_EQ(6u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect2_1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect2_2.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); - EXPECT_EQ(rect2_3.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(2) - ->visible_rect.ToString()); - EXPECT_EQ(rect2_4.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(3) - ->visible_rect.ToString()); - EXPECT_EQ(gfx::Rect(0, 0, 20, 30).ToString(), - frame.render_pass_list.front() - ->quad_list.ElementAt(5) - ->visible_rect.ToString()); - EXPECT_EQ(gfx::Rect(120, 0, 20, 30).ToString(), - frame.render_pass_list.front() - ->quad_list.ElementAt(6) - ->visible_rect.ToString()); + EXPECT_EQ( + rect2_1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect2_2, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); + EXPECT_EQ( + rect2_3, + frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect); + EXPECT_EQ( + rect2_4, + frame.render_pass_list.front()->quad_list.ElementAt(3)->visible_rect); + EXPECT_EQ( + gfx::Rect(0, 0, 20, 30), + frame.render_pass_list.front()->quad_list.ElementAt(5)->visible_rect); + EXPECT_EQ( + gfx::Rect(120, 0, 20, 30), + frame.render_pass_list.front()->quad_list.ElementAt(6)->visible_rect); } } @@ -3354,12 +3343,12 @@ TEST_F(DisplayTest, CompositorFrameWithNonInvertibleTransform) { display_->RemoveOverdrawQuads(&frame); // |quad2| is removed because it is not shown on screen in the target space. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(rect3.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(2) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + rect3, + frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect); } { @@ -3385,9 +3374,9 @@ TEST_F(DisplayTest, CompositorFrameWithNonInvertibleTransform) { // |quad3| follows an non-invertible transform and it's covered by the // occlusion rect. So |quad3| is removed from the |frame|. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } } @@ -3425,9 +3414,9 @@ TEST_F(DisplayTest, DrawOcclusionWithLargeDrawQuad) { // DrawQuad, so the size of quad_list remains unchanged after calling // RemoveOverdrawQuads. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + rect1, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } } @@ -3762,137 +3751,6 @@ TEST_F(DisplayTest, DontThrottleWhenParentBlocked) { UpdateBeginFrameTime(sub_support.get(), frame_time); } -TEST_F(DisplayTest, InvalidPresentationTimestamps) { - RendererSettings settings; - id_allocator_.GenerateId(); - const LocalSurfaceId local_surface_id( - id_allocator_.GetCurrentLocalSurfaceId()); - - // Set up first display. - SetUpSoftwareDisplay(settings); - StubDisplayClient client; - display_->Initialize(&client, manager_.surface_manager()); - display_->SetLocalSurfaceId(local_surface_id, 1.f); - display_->Resize(gfx::Size(25, 25)); - - { - // A regular presentation timestamp. - base::HistogramTester histograms; - CompositorFrame frame = - CompositorFrameBuilder() - .AddRenderPass(gfx::Rect(25, 25), gfx::Rect(25, 25)) - .Build(); - support_->SubmitCompositorFrame(local_surface_id, std::move(frame)); - display_->DrawAndSwap({base::TimeTicks::Now(), base::TimeTicks::Now()}); - display_->DidReceiveSwapBuffersAck(GetTestSwapTimings(), - /*release_fence=*/gfx::GpuFenceHandle()); - display_->DidReceivePresentationFeedback({base::TimeTicks::Now(), {}, 0}); - EXPECT_THAT(histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidBeforeSwap"), - testing::IsEmpty()); - EXPECT_THAT(histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidFromFuture"), - testing::IsEmpty()); - } - - { - // A presentation-timestamp that is earlier than the swap time. - base::HistogramTester histograms; - CompositorFrame frame = - CompositorFrameBuilder() - .AddRenderPass(gfx::Rect(25, 25), gfx::Rect(25, 25)) - .Build(); - support_->SubmitCompositorFrame(local_surface_id, std::move(frame)); - display_->DrawAndSwap({base::TimeTicks::Now(), base::TimeTicks::Now()}); - display_->DidReceiveSwapBuffersAck(GetTestSwapTimings(), - /*release_fence=*/gfx::GpuFenceHandle()); - display_->DidReceivePresentationFeedback( - {base::TimeTicks::Now() - base::Seconds(1), {}, 0}); - EXPECT_THAT(histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidFromFuture"), - testing::IsEmpty()); - auto buckets = histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidBeforeSwap"); - ASSERT_EQ(buckets.size(), 1u); - EXPECT_GT(buckets[0].min, 0); - EXPECT_LE(buckets[0].min, 1000); - EXPECT_EQ(buckets[0].count, 1); - } - - { - // A presentation-timestamp that is in the near-future with hwclock: this - // should be valid. - base::HistogramTester histograms; - CompositorFrame frame = - CompositorFrameBuilder() - .AddRenderPass(gfx::Rect(25, 25), gfx::Rect(25, 25)) - .Build(); - support_->SubmitCompositorFrame(local_surface_id, std::move(frame)); - display_->DrawAndSwap({base::TimeTicks::Now(), base::TimeTicks::Now()}); - display_->DidReceiveSwapBuffersAck(GetTestSwapTimings(), - /*release_fence=*/gfx::GpuFenceHandle()); - display_->DidReceivePresentationFeedback( - {base::TimeTicks::Now() + base::Milliseconds(1), - {}, - gfx::PresentationFeedback::kHWClock}); - EXPECT_THAT(histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidBeforeSwap"), - testing::IsEmpty()); - EXPECT_THAT(histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidFromFuture"), - testing::IsEmpty()); - } - - { - // A presentation-timestamp that is in the near-future. - base::HistogramTester histograms; - CompositorFrame frame = - CompositorFrameBuilder() - .AddRenderPass(gfx::Rect(25, 25), gfx::Rect(25, 25)) - .Build(); - support_->SubmitCompositorFrame(local_surface_id, std::move(frame)); - display_->DrawAndSwap({base::TimeTicks::Now(), base::TimeTicks::Now()}); - display_->DidReceiveSwapBuffersAck(GetTestSwapTimings(), - /*release_fence=*/gfx::GpuFenceHandle()); - display_->DidReceivePresentationFeedback( - {base::TimeTicks::Now() + base::Milliseconds(1), {}, 0}); - EXPECT_THAT(histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidBeforeSwap"), - testing::IsEmpty()); - auto buckets = histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidFromFuture"); - ASSERT_EQ(buckets.size(), 1u); - EXPECT_GE(buckets[0].min, 0); - EXPECT_LE(buckets[0].min, 1); - EXPECT_EQ(buckets[0].count, 1); - } - - { - // A presentation-timestamp that is in the future. - base::HistogramTester histograms; - CompositorFrame frame = - CompositorFrameBuilder() - .AddRenderPass(gfx::Rect(25, 25), gfx::Rect(25, 25)) - .Build(); - support_->SubmitCompositorFrame(local_surface_id, std::move(frame)); - display_->DrawAndSwap({base::TimeTicks::Now(), base::TimeTicks::Now()}); - display_->DidReceiveSwapBuffersAck(GetTestSwapTimings(), - /*release_fence=*/gfx::GpuFenceHandle()); - display_->DidReceivePresentationFeedback( - {base::TimeTicks::Now() + base::Seconds(1), {}, 0}); - EXPECT_THAT(histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidBeforeSwap"), - testing::IsEmpty()); - - auto buckets = histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidFromFuture"); - ASSERT_EQ(buckets.size(), 1u); - EXPECT_GT(buckets[0].min, 0); - EXPECT_LE(buckets[0].min, 1000); - EXPECT_EQ(buckets[0].count, 1); - } -} - TEST_F(DisplayTest, DrawOcclusionWithRoundedCornerDoesNotOcclude) { SetUpGpuDisplay(RendererSettings()); @@ -3939,12 +3797,12 @@ TEST_F(DisplayTest, DrawOcclusionWithRoundedCornerDoesNotOcclude) { // Since none of the quads are culled, there should be 2 quads. EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(quad_rect.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); - EXPECT_EQ(quad_rect.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(1) - ->visible_rect.ToString()); + EXPECT_EQ( + quad_rect, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); + EXPECT_EQ( + quad_rect, + frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect); } } @@ -3995,9 +3853,9 @@ TEST_F(DisplayTest, DrawOcclusionWithRoundedCornerDoesOcclude) { // no rounded corner, the later quad is culled. We should only have 1 quad // in the final list now. EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(quad_rect.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + quad_rect, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); } } @@ -4061,9 +3919,9 @@ TEST_F(DisplayTest, DrawOcclusionSplit) { EXPECT_EQ(4u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); display_->RemoveOverdrawQuads(&frame); ASSERT_EQ(6u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(occluding_rect.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + occluding_rect, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); // Computed the expected quads // +--------------------------------+ @@ -4333,9 +4191,9 @@ TEST_F(DisplayTest, DrawOcclusionWithRoundedCornerPartialOcclude) { // no rounded corner, the later quad is culled. We should only have 1 quad // in the final list now. EXPECT_EQ(5u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); - EXPECT_EQ(quad_rect.ToString(), frame.render_pass_list.front() - ->quad_list.ElementAt(0) - ->visible_rect.ToString()); + EXPECT_EQ( + quad_rect, + frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect); // For rounded rect of bounds (10, 10, 1000, 1000) and corner radius of 10, // the occluding rect for it would be (13, 13, 994, 994). @@ -4475,10 +4333,7 @@ class SkiaDelegatedInkRendererTest : public DisplayTest { void SetUp() override { EnablePrediction(); } void SetUpRenderers() { - // First set up the display to use the Skia renderer. - RendererSettings settings; - settings.use_skia_renderer = true; - SetUpGpuDisplay(settings); + SetUpGpuDisplay(RendererSettings()); // Initialize the renderer and create an ink renderer. display_->Initialize(&client_, manager_.surface_manager()); @@ -5001,9 +4856,7 @@ class DelegatedInkDisplayTest features::kUsePlatformDelegatedInk); // Set up the display to use the Skia renderer. - RendererSettings settings; - settings.use_skia_renderer = true; - SetUpGpuDisplaySkiaWithPlatformInk(settings); + SetUpGpuDisplaySkiaWithPlatformInk(RendererSettings()); display_->Initialize(&client_, manager_.surface_manager()); } diff --git a/chromium/components/viz/service/display/dynamic_geometry_binding.cc b/chromium/components/viz/service/display/dynamic_geometry_binding.cc deleted file mode 100644 index bb47dba7320..00000000000 --- a/chromium/components/viz/service/display/dynamic_geometry_binding.cc +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2015 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/dynamic_geometry_binding.h" - -#include <stdint.h> - -#include "gpu/command_buffer/client/gles2_interface.h" -#include "ui/gfx/geometry/quad_f.h" -#include "ui/gfx/geometry/rect_f.h" - -namespace viz { - -DynamicGeometryBinding::DynamicGeometryBinding(gpu::gles2::GLES2Interface* gl) - : gl_(gl), quad_vertices_vbo_(0), quad_elements_vbo_(0) { - GLuint buffers[2]; - gl_->GenBuffers(2, buffers); - quad_vertices_vbo_ = buffers[0]; - quad_elements_vbo_ = buffers[1]; - - gl_->BindBuffer(GL_ARRAY_BUFFER, quad_vertices_vbo_); - gl_->BufferData(GL_ARRAY_BUFFER, sizeof(GeometryBindingQuad), nullptr, - GL_DYNAMIC_DRAW); - - gl_->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_elements_vbo_); - gl_->BufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GeometryBindingQuadIndex), - nullptr, GL_DYNAMIC_DRAW); -} - -DynamicGeometryBinding::~DynamicGeometryBinding() { - GLuint buffers[2] = {quad_vertices_vbo_, quad_elements_vbo_}; - gl_->DeleteBuffers(2, buffers); -} - -void DynamicGeometryBinding::InitializeCustomQuad(const gfx::QuadF& quad) { - float uv[] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f}; - InitializeCustomQuadWithUVs(quad, uv); -} - -void DynamicGeometryBinding::InitializeCustomQuadWithUVs(const gfx::QuadF& quad, - const float uv[8]) { - GeometryBindingVertex v0 = { - {quad.p1().x(), quad.p1().y(), 0.0f}, {uv[0], uv[1]}, 0.0f}; - GeometryBindingVertex v1 = { - {quad.p2().x(), quad.p2().y(), 0.0f}, {uv[2], uv[3]}, 1.0f}; - GeometryBindingVertex v2 = { - {quad.p3().x(), quad.p3().y(), 0.0f}, {uv[4], uv[5]}, 2.0f}; - GeometryBindingVertex v3 = { - {quad.p4().x(), quad.p4().y(), 0.0f}, {uv[6], uv[7]}, 3.0f}; - - GeometryBindingQuad local_quad = {v0, v1, v2, v3}; - GeometryBindingQuadIndex quad_index( - static_cast<uint16_t>(0), static_cast<uint16_t>(1), - static_cast<uint16_t>(2), static_cast<uint16_t>(3), - static_cast<uint16_t>(0), static_cast<uint16_t>(2)); - - gl_->BufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GeometryBindingQuad), - &local_quad); - gl_->BufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, - sizeof(GeometryBindingQuadIndex), &quad_index); -} - -void DynamicGeometryBinding::PrepareForDraw() { - SetupGLContext(gl_, quad_elements_vbo_, quad_vertices_vbo_); -} - -} // namespace viz diff --git a/chromium/components/viz/service/display/dynamic_geometry_binding.h b/chromium/components/viz/service/display/dynamic_geometry_binding.h deleted file mode 100644 index 2d340962fad..00000000000 --- a/chromium/components/viz/service/display/dynamic_geometry_binding.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2015 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_DYNAMIC_GEOMETRY_BINDING_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_DYNAMIC_GEOMETRY_BINDING_H_ - -#include "base/memory/raw_ptr.h" -#include "components/viz/service/display/geometry_binding.h" -#include "components/viz/service/viz_service_export.h" - -namespace gfx { -class QuadF; -} - -namespace viz { - -class VIZ_SERVICE_EXPORT DynamicGeometryBinding { - public: - explicit DynamicGeometryBinding(gpu::gles2::GLES2Interface* gl); - - DynamicGeometryBinding(const DynamicGeometryBinding&) = delete; - DynamicGeometryBinding& operator=(const DynamicGeometryBinding&) = delete; - - ~DynamicGeometryBinding(); - - void PrepareForDraw(); - void InitializeCustomQuad(const gfx::QuadF& quad); - void InitializeCustomQuadWithUVs(const gfx::QuadF& quad, const float uv[8]); - - private: - raw_ptr<gpu::gles2::GLES2Interface> gl_; - - GLuint quad_vertices_vbo_; - GLuint quad_elements_vbo_; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_DYNAMIC_GEOMETRY_BINDING_H_ diff --git a/chromium/components/viz/service/display/external_use_client.h b/chromium/components/viz/service/display/external_use_client.h index dbac7444dfb..e37901e41c5 100644 --- a/chromium/components/viz/service/display/external_use_client.h +++ b/chromium/components/viz/service/display/external_use_client.h @@ -87,8 +87,10 @@ class VIZ_SERVICE_EXPORT ExternalUseClient { void set_paint_op_buffer(const cc::PaintOpBuffer* buffer) { paint_op_buffer_ = buffer; } - const absl::optional<SkColor>& clear_color() const { return clear_color_; } - void set_clear_color(const absl::optional<SkColor>& color) { + const absl::optional<SkColor4f>& clear_color() const { + return clear_color_; + } + void set_clear_color(const absl::optional<SkColor4f>& color) { clear_color_ = color; } @@ -110,7 +112,7 @@ class VIZ_SERVICE_EXPORT ExternalUseClient { sk_sp<SkImage> image_; GrBackendFormat backend_format_; raw_ptr<const cc::PaintOpBuffer> paint_op_buffer_ = nullptr; - absl::optional<SkColor> clear_color_; + absl::optional<SkColor4f> clear_color_; }; // If |maybe_concurrent_reads| is true then there can be concurrent reads to diff --git a/chromium/components/viz/service/display/frame_rate_decider_unittest.cc b/chromium/components/viz/service/display/frame_rate_decider_unittest.cc index e9f55fa7c1e..2be3702bc4b 100644 --- a/chromium/components/viz/service/display/frame_rate_decider_unittest.cc +++ b/chromium/components/viz/service/display/frame_rate_decider_unittest.cc @@ -29,7 +29,9 @@ class FrameRateDeciderTest : public testing::Test, ~FrameRateDeciderTest() override = default; void SetUp() override { - surface_manager_ = std::make_unique<SurfaceManager>(this, absl::nullopt); + surface_manager_ = std::make_unique<SurfaceManager>( + this, /*activation_deadline_in_frames=*/absl::nullopt, + /*max_uncommitted_frames=*/0); bool hw_support_for_multiple_refresh_rates = true; frame_rate_decider_ = std::make_unique<FrameRateDecider>( surface_manager_.get(), this, hw_support_for_multiple_refresh_rates, diff --git a/chromium/components/viz/service/display/geometry_binding.cc b/chromium/components/viz/service/display/geometry_binding.cc deleted file mode 100644 index e2013e1c029..00000000000 --- a/chromium/components/viz/service/display/geometry_binding.cc +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2011 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/geometry_binding.h" - -#include <stdint.h> -#include <string.h> - -#include "gpu/command_buffer/client/gles2_interface.h" -#include "ui/gfx/geometry/rect_f.h" - -namespace viz { - -void SetupGLContext(gpu::gles2::GLES2Interface* gl, - GLuint quad_elements_vbo, - GLuint quad_vertices_vbo) { - gl->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_elements_vbo); - - gl->BindBuffer(GL_ARRAY_BUFFER, quad_vertices_vbo); - // OpenGL defines the last parameter to VertexAttribPointer as type - // "const GLvoid*" even though it is actually an offset into the buffer - // object's data store and not a pointer to the client's address space. - const void* offsets[3] = { - nullptr, reinterpret_cast<const void*>(3 * sizeof(float)), - reinterpret_cast<const void*>(5 * sizeof(float)), - }; - - gl->VertexAttribPointer(GeometryBinding::PositionAttribLocation(), 3, - GL_FLOAT, false, 6 * sizeof(float), offsets[0]); - gl->VertexAttribPointer(GeometryBinding::TexCoordAttribLocation(), 2, - GL_FLOAT, false, 6 * sizeof(float), offsets[1]); - gl->VertexAttribPointer(GeometryBinding::TriangleIndexAttribLocation(), 1, - GL_FLOAT, false, 6 * sizeof(float), offsets[2]); - gl->EnableVertexAttribArray(GeometryBinding::PositionAttribLocation()); - gl->EnableVertexAttribArray(GeometryBinding::TexCoordAttribLocation()); - gl->EnableVertexAttribArray(GeometryBinding::TriangleIndexAttribLocation()); -} - -GeometryBindingQuad::GeometryBindingQuad() { - v0 = {{0, 0, 0}, {0, 0}, 0}; - v1 = {{0, 0, 0}, {0, 0}, 0}; - v2 = {{0, 0, 0}, {0, 0}, 0}; - v3 = {{0, 0, 0}, {0, 0}, 0}; -} - -GeometryBindingQuad::GeometryBindingQuad(const GeometryBindingVertex& vert0, - const GeometryBindingVertex& vert1, - const GeometryBindingVertex& vert2, - const GeometryBindingVertex& vert3) { - v0 = vert0; - v1 = vert1; - v2 = vert2; - v3 = vert3; -} - -GeometryBindingQuadIndex::GeometryBindingQuadIndex() { - memset(data, 0x0, sizeof(data)); -} - -GeometryBindingQuadIndex::GeometryBindingQuadIndex(uint16_t index0, - uint16_t index1, - uint16_t index2, - uint16_t index3, - uint16_t index4, - uint16_t index5) { - data[0] = index0; - data[1] = index1; - data[2] = index2; - data[3] = index3; - data[4] = index4; - data[5] = index5; -} - -} // namespace viz diff --git a/chromium/components/viz/service/display/geometry_binding.h b/chromium/components/viz/service/display/geometry_binding.h deleted file mode 100644 index 64bdf0b9ae4..00000000000 --- a/chromium/components/viz/service/display/geometry_binding.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2011 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_GEOMETRY_BINDING_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_GEOMETRY_BINDING_H_ - -#include <stdint.h> - -#include "gpu/command_buffer/client/gles2_interface.h" -#include "third_party/khronos/GLES2/gl2.h" -#include "third_party/khronos/GLES2/gl2ext.h" -#include "ui/gfx/geometry/rect_f.h" - -namespace viz { - -struct GeometryBindingVertex { - float a_position[3]; - float a_texCoord[2]; - // Index of the vertex, divide by 4 to have the matrix for this quad. - float a_index; -}; - -struct GeometryBindingQuad { - GeometryBindingQuad(); - GeometryBindingQuad(const GeometryBindingVertex& vert0, - const GeometryBindingVertex& vert1, - const GeometryBindingVertex& vert2, - const GeometryBindingVertex& vert3); - GeometryBindingVertex v0, v1, v2, v3; -}; -static_assert(sizeof(GeometryBindingQuad) == 24 * sizeof(float), - "struct Quad should be densely packed"); - -struct GeometryBindingQuadIndex { - GeometryBindingQuadIndex(); - GeometryBindingQuadIndex(uint16_t index0, - uint16_t index1, - uint16_t index2, - uint16_t index3, - uint16_t index4, - uint16_t index5); - - uint16_t data[6]; -}; -static_assert(sizeof(GeometryBindingQuadIndex) == 6 * sizeof(uint16_t), - "struct QuadIndex should be densely packed"); - -struct GeometryBinding { - // All layer shaders share the same attribute locations for the vertex - // positions and texture coordinates. This allows switching shaders without - // rebinding attribute arrays. - static int PositionAttribLocation() { return 0; } - static int TexCoordAttribLocation() { return 1; } - static int TriangleIndexAttribLocation() { return 2; } -}; - -void SetupGLContext(gpu::gles2::GLES2Interface* gl, - GLuint quad_elements_vbo, - GLuint quad_vertices_vbo); - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_GEOMETRY_BINDING_H_ diff --git a/chromium/components/viz/service/display/gl_renderer.cc b/chromium/components/viz/service/display/gl_renderer.cc deleted file mode 100644 index 7fb664525ba..00000000000 --- a/chromium/components/viz/service/display/gl_renderer.cc +++ /dev/null @@ -1,4514 +0,0 @@ -// Copyright 2010 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/gl_renderer.h" - -#include <stddef.h> -#include <stdint.h> - -#include <algorithm> -#include <limits> -#include <memory> -#include <numeric> -#include <set> -#include <vector> - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/check_op.h" -#include "base/containers/cxx20_erase.h" -#include "base/feature_list.h" -#include "base/memory/ptr_util.h" -#include "base/memory/raw_ptr.h" -#include "base/notreached.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/time/time.h" -#include "base/trace_event/trace_event.h" -#include "build/build_config.h" -#include "cc/base/math_util.h" -#include "cc/debug/debug_colors.h" -#include "cc/paint/render_surface_filters.h" -#include "cc/raster/scoped_gpu_raster.h" -#include "components/viz/common/display/renderer_settings.h" -#include "components/viz/common/features.h" -#include "components/viz/common/frame_sinks/copy_output_request.h" -#include "components/viz/common/gpu/context_provider.h" -#include "components/viz/common/quads/compositor_frame.h" -#include "components/viz/common/quads/compositor_frame_metadata.h" -#include "components/viz/common/quads/compositor_render_pass.h" -#include "components/viz/common/quads/picture_draw_quad.h" -#include "components/viz/common/quads/stream_video_draw_quad.h" -#include "components/viz/common/quads/texture_draw_quad.h" -#include "components/viz/common/resources/platform_color.h" -#include "components/viz/common/resources/resource_format_utils.h" -#include "components/viz/common/resources/resource_id.h" -#include "components/viz/common/skia_helper.h" -#include "components/viz/common/viz_utils.h" -#include "components/viz/service/display/draw_polygon.h" -#include "components/viz/service/display/dynamic_geometry_binding.h" -#include "components/viz/service/display/layer_quad.h" -#include "components/viz/service/display/output_surface.h" -#include "components/viz/service/display/output_surface_frame.h" -#include "components/viz/service/display/resource_fence.h" -#include "components/viz/service/display/scoped_render_pass_texture.h" -#include "components/viz/service/display/static_geometry_binding.h" -#include "components/viz/service/display/texture_deleter.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "gpu/command_buffer/client/context_support.h" -#include "gpu/command_buffer/client/gles2_interface.h" -#include "gpu/command_buffer/common/gpu_memory_allocation.h" -#include "gpu/config/gpu_driver_bug_workaround_type.h" -#include "gpu/config/gpu_feature_info.h" -#include "third_party/abseil-cpp/absl/types/optional.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkColorFilter.h" -#include "third_party/skia/include/core/SkImage.h" -#include "third_party/skia/include/core/SkSurface.h" -#include "third_party/skia/include/core/SkTypes.h" -#include "third_party/skia/include/effects/SkShaderMaskFilter.h" -#include "third_party/skia/include/gpu/GrBackendSurface.h" -#include "third_party/skia/include/gpu/GrDirectContext.h" -#include "third_party/skia/include/gpu/gl/GrGLInterface.h" -#include "third_party/skia/include/gpu/gl/GrGLTypes.h" -#include "ui/gfx/color_space.h" -#include "ui/gfx/color_transform.h" -#include "ui/gfx/geometry/quad_f.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/geometry/rect_conversions.h" -#include "ui/gfx/geometry/rrect_f.h" -#include "ui/gfx/geometry/size_conversions.h" -#include "ui/gfx/geometry/skia_conversions.h" -#include "ui/gfx/geometry/vector2d.h" -#include "ui/gfx/gpu_fence_handle.h" - -using gpu::gles2::GLES2Interface; - -namespace viz { -namespace { - -Float4 UVTransform(const TextureDrawQuad* quad) { - gfx::RectF uv_rect = - gfx::BoundingRect(quad->uv_top_left, quad->uv_bottom_right); - gfx::RectF visible_uv_rect = cc::MathUtil::ScaleRectProportional( - uv_rect, gfx::RectF(quad->rect), gfx::RectF(quad->visible_rect)); - - gfx::PointF uv0 = visible_uv_rect.origin(); - gfx::PointF uv1 = visible_uv_rect.bottom_right(); - Float4 xform = {{uv0.x(), uv0.y(), uv1.x() - uv0.x(), uv1.y() - uv0.y()}}; - if (quad->y_flipped) { - xform.data[1] = 1.0f - xform.data[1]; - xform.data[3] = -xform.data[3]; - } - return xform; -} - -// To prevent sampling outside the visible rect. -Float4 UVClampRect(gfx::RectF uv_visible_rect, - const gfx::Size& texture_size, - SamplerType sampler) { - gfx::SizeF half_texel(0.5f, 0.5f); - if (sampler != SAMPLER_TYPE_2D_RECT) { - half_texel.Scale(1.f / texture_size.width(), 1.f / texture_size.height()); - } else { - uv_visible_rect.Scale(texture_size.width(), texture_size.height()); - } - uv_visible_rect.Inset( - gfx::InsetsF::VH(half_texel.height(), half_texel.width())); - return {{uv_visible_rect.x(), uv_visible_rect.y(), uv_visible_rect.right(), - uv_visible_rect.bottom()}}; -} - -Float4 PremultipliedColor(SkColor color, float opacity) { - const U8CPU alpha255 = SkColorGetA(color); - const unsigned int alpha256 = alpha255 + 1; - const unsigned int premultiplied_red = (SkColorGetR(color) * alpha256) >> 8; - const unsigned int premultiplied_green = (SkColorGetG(color) * alpha256) >> 8; - const unsigned int premultiplied_blue = (SkColorGetB(color) * alpha256) >> 8; - const float factor = opacity / 255.0f; - return {{premultiplied_red * factor, premultiplied_green * factor, - premultiplied_blue * factor, alpha255 * factor}}; -} - -SamplerType SamplerTypeFromTextureTarget(GLenum target) { - switch (target) { - case GL_TEXTURE_2D: - return SAMPLER_TYPE_2D; - case GL_TEXTURE_RECTANGLE_ARB: - return SAMPLER_TYPE_2D_RECT; - case GL_TEXTURE_EXTERNAL_OES: - return SAMPLER_TYPE_EXTERNAL_OES; - default: - NOTREACHED(); - return SAMPLER_TYPE_2D; - } -} - -BlendMode BlendModeFromSkXfermode(SkBlendMode mode) { - switch (mode) { - case SkBlendMode::kSrcOver: - return BLEND_MODE_NORMAL; - case SkBlendMode::kDstIn: - return BLEND_MODE_DESTINATION_IN; - case SkBlendMode::kScreen: - return BLEND_MODE_SCREEN; - case SkBlendMode::kOverlay: - return BLEND_MODE_OVERLAY; - case SkBlendMode::kDarken: - return BLEND_MODE_DARKEN; - case SkBlendMode::kLighten: - return BLEND_MODE_LIGHTEN; - case SkBlendMode::kColorDodge: - return BLEND_MODE_COLOR_DODGE; - case SkBlendMode::kColorBurn: - return BLEND_MODE_COLOR_BURN; - case SkBlendMode::kHardLight: - return BLEND_MODE_HARD_LIGHT; - case SkBlendMode::kSoftLight: - return BLEND_MODE_SOFT_LIGHT; - case SkBlendMode::kDifference: - return BLEND_MODE_DIFFERENCE; - case SkBlendMode::kExclusion: - return BLEND_MODE_EXCLUSION; - case SkBlendMode::kMultiply: - return BLEND_MODE_MULTIPLY; - case SkBlendMode::kHue: - return BLEND_MODE_HUE; - case SkBlendMode::kSaturation: - return BLEND_MODE_SATURATION; - case SkBlendMode::kColor: - return BLEND_MODE_COLOR; - case SkBlendMode::kLuminosity: - return BLEND_MODE_LUMINOSITY; - case SkBlendMode::kSrc: - return BLEND_MODE_NONE; - default: - NOTREACHED(); - return BLEND_MODE_NONE; - } -} - -// Adds a timer query that spans all GL calls in its scope. |viz.composite_time| -// trace category must be enabled for this to work. -// Note:: Multiple timer queries cannot be nested. -class ScopedTimerQuery { - public: - ScopedTimerQuery(bool tracing_enabled, - gpu::gles2::GLES2Interface* gl, - base::queue<std::pair<unsigned, std::string>>* timer_queries, - const std::string& quad_type_str) - : gl_(gl) { - if (!tracing_enabled) { - gl_ = nullptr; - return; - } - unsigned timer_query; - gl_->GenQueriesEXT(1, &timer_query); - gl_->BeginQueryEXT(GL_TIME_ELAPSED_EXT, timer_query); - timer_queries->emplace(timer_query, quad_type_str); - } - - ~ScopedTimerQuery() { - if (gl_) - gl_->EndQueryEXT(GL_TIME_ELAPSED_EXT); - } - - private: - raw_ptr<gpu::gles2::GLES2Interface> gl_; -}; - -void AccumulateDrawRects(const gfx::Rect& quad_rect, - const gfx::Transform& target_transform, - std::vector<gfx::Rect>* drawn_rects) { - gfx::RectF quad_rect_f(quad_rect); - - // If the transform is not axis aligned then assume the largest possible - // bounds the quad can take in the render target. In this case, we take the - // sum of 2 sides. - if (!target_transform.Preserves2dAxisAlignment()) { - // Increase the length of each side to |width + height|. - const int total_length = quad_rect.width() + quad_rect.height(); - quad_rect_f.set_height(total_length); - quad_rect_f.set_width(total_length); - - // Ensure that the increase is equally distributed on either sides of the - // quad such that the position of the center of the quad does not change. - const float delta_x = -(quad_rect.height() / 2.f); - const float delta_y = -(quad_rect.width() / 2.f); - quad_rect_f.Offset(gfx::Vector2d(delta_x, delta_y)); - - // Apply only the scale and translation component. - const gfx::Vector2dF& translate = target_transform.To2dTranslation(); - const gfx::Vector2dF& scale = target_transform.To2dScale(); - quad_rect_f.Scale(scale.x(), scale.y()); - quad_rect_f.Offset(translate.x(), translate.y()); - } else { - target_transform.TransformRect(&quad_rect_f); - } - drawn_rects->push_back(gfx::ToRoundedRect(quad_rect_f)); -} - -// Smallest unit that impact anti-aliasing output. We use this to -// determine when anti-aliasing is unnecessary. -const float kAntiAliasingEpsilon = 1.0f / 1024.0f; - -// A dummy timer query ID used to identify the beginning of a frame in the queue -// of timer queries. -const unsigned kTimerQueryDummy = 0; - -} // anonymous namespace - -static GLint GetActiveTextureUnit(GLES2Interface* gl) { - GLint active_unit = 0; - gl->GetIntegerv(GL_ACTIVE_TEXTURE, &active_unit); - return active_unit; -} - -// Parameters needed to draw a CompositorRenderPassDrawQuad. -struct GLRenderer::DrawRenderPassDrawQuadParams { - DrawRenderPassDrawQuadParams() {} - ~DrawRenderPassDrawQuadParams() { - // Don't leak the texture. - DCHECK(!background_texture); - } - - // Required inputs below. - raw_ptr<const AggregatedRenderPassDrawQuad> quad = nullptr; - - // Either |contents_texture| or |bypass_quad_texture| is populated. The - // |contents_texture| will be valid if non-null, and when null the - // bypass_quad_texture will be valid instead. - raw_ptr<ScopedRenderPassTexture> contents_texture = nullptr; - struct { - ResourceId resource_id = kInvalidResourceId; - gfx::Size size; - } bypass_quad_texture; - - raw_ptr<const gfx::QuadF> clip_region = nullptr; - bool flip_texture = false; - - // |window_matrix| maps from [-1,-1]-[1,1] unit square coordinates to window - // pixel coordinates. - gfx::Transform window_matrix; - // |projection_matrix| maps texture coordinates (in pixels) to the 2D plane in - // [-1,-1]-[1,1] unit square coordinates. If FlippedFrameBuffer() is true, - // |projection_matrix| includes this flip. - gfx::Transform projection_matrix; - // |quad_to_target_transform| transforms from local quad pixel coordinates to - // target content space pixel coordinates, including scale, offset, - // perspective, and rotation. - gfx::Transform quad_to_target_transform; - raw_ptr<const cc::FilterOperations> filters = nullptr; - raw_ptr<const cc::FilterOperations> backdrop_filters = nullptr; - absl::optional<gfx::RRectF> backdrop_filter_bounds; - - // Whether the texture to be sampled from needs to be flipped. - bool source_needs_flip = false; - - float edge[24]; - SkScalar color_matrix[20]; - - // Blending in the fragment shaders is used for modifications to the backdrop - // and for supporting advanced blending equation when not available by the - // underlying graphics API. - bool use_shaders_for_blending = false; - SkBlendMode blend_mode = SkBlendMode::kSrcOver; - - bool use_aa = false; - - // Some filters affect pixels outside the original contents bounds, in which - // case ApplyImageFilter will modify this rect. - gfx::RectF dst_rect; - - // A Skia image that should be sampled from instead of the original - // contents. - sk_sp<SkImage> filter_image; - - // The original contents, bound for sampling. - std::unique_ptr<DisplayResourceProviderGL::ScopedSamplerGL> - bypass_quad_resource_lock; - - // A mask to be applied when drawing the RPDQ. - std::unique_ptr<DisplayResourceProviderGL::ScopedSamplerGL> - mask_resource_lock; - - // Whether a color matrix needs to be applied by the shaders when drawing - // the RPDQ. - bool use_color_matrix = false; - - gfx::QuadF surface_quad; - - // |contents_device_transform| transforms from vertex geometry, which is often - // the unit quad [-0.5, 0.5], all the way to 2D window pixel coordinates, - // including 3D effects, frame buffer orientation, and window offset. The - // definition of the incoming vertex geometry comes from either - // shared_geometry_ or clipped_geometry_, which are initialized from - // DirectRenderer::QuadVertexRect or DynamicGeometryBinding, respectively. - // |contents_device_transform| is typically calculated as - // |window_matrix| * |projection_matrix| * |quad_rect_matrix| - // and then flattened with FlattenTo2d(). Here, |quad_rect_matrix| is a - // combination of the geometry->quad transform as well as the quad->target - // space transform. The geometry->quad is the mapping from the bound geometry, - // often [-0.5, 0.5], to the quad, which is quad->rect. - gfx::Transform contents_device_transform; - - gfx::RectF tex_coord_rect; - - // The color space of the texture bound for sampling (from filter_image or - // bypass_quad_resource_lock, depending on the path taken). - gfx::ColorSpace contents_and_bypass_color_space; - - // Background filters block. - // Original background texture. - uint32_t background_texture = 0; - GLenum background_texture_format = 0; - // Backdrop bounding box. - gfx::Rect background_rect; - // Filtered background texture. - sk_sp<SkImage> background_image; - GLuint background_image_id = 0; - // A multiplier for the temporary surface we create to apply the backdrop - // filter. - float backdrop_filter_quality = 1.0; - // Whether the original background texture is needed for the mask. - bool mask_for_background = false; - - bool apply_shader_based_rounded_corner = true; -}; - -class GLRenderer::ScopedUseGrContext { - public: - static std::unique_ptr<ScopedUseGrContext> Create(GLRenderer* renderer) { - // GrContext for filters is created lazily, and may fail if the context - // is lost. - // TODO(vmiura,bsalomon): crbug.com/487850 Ensure that - // ContextProvider::GrContext() does not return NULL. - GrDirectContext* direct = GrAsDirectContext( - renderer->output_surface_->context_provider()->GrContext()); - if (direct) - return base::WrapUnique(new ScopedUseGrContext(renderer)); - return nullptr; - } - - ScopedUseGrContext(const ScopedUseGrContext&) = delete; - ScopedUseGrContext& operator=(const ScopedUseGrContext&) = delete; - - ~ScopedUseGrContext() { - // Pass context control back to GLrenderer. - scoped_gpu_raster_ = nullptr; - renderer_->RestoreGLStateAfterSkia(); - } - - GrDirectContext* context() const { - return renderer_->output_surface_->context_provider()->GrContext(); - } - - private: - explicit ScopedUseGrContext(GLRenderer* renderer) - : scoped_gpu_raster_(new cc::ScopedGpuRaster( - renderer->output_surface_->context_provider())), - renderer_(renderer) { - // scoped_gpu_raster_ passes context control to Skia. - } - - std::unique_ptr<cc::ScopedGpuRaster> scoped_gpu_raster_; - raw_ptr<GLRenderer> renderer_; -}; - -GLRenderer::GLRenderer( - const RendererSettings* settings, - const DebugRendererSettings* debug_settings, - OutputSurface* output_surface, - DisplayResourceProviderGL* resource_provider, - OverlayProcessorInterface* overlay_processor, - scoped_refptr<base::SingleThreadTaskRunner> current_task_runner) - : DirectRenderer(settings, - debug_settings, - output_surface, - resource_provider, - overlay_processor), - shared_geometry_quad_(QuadVertexRect()), - gl_(output_surface->context_provider()->ContextGL()), - context_support_(output_surface->context_provider()->ContextSupport()), - texture_deleter_(current_task_runner), - copier_(output_surface->context_provider(), &texture_deleter_), - sync_queries_(gl_), - bound_geometry_(NO_BINDING), - current_task_runner_(std::move(current_task_runner)) { - DCHECK(gl_); - DCHECK(context_support_); - - const auto& context_caps = - output_surface_->context_provider()->ContextCapabilities(); - - use_discard_framebuffer_ = context_caps.discard_framebuffer; - use_sync_query_ = context_caps.sync_query; - use_blend_equation_advanced_ = context_caps.blend_equation_advanced; - use_blend_equation_advanced_coherent_ = - context_caps.blend_equation_advanced_coherent; - use_occlusion_query_ = context_caps.occlusion_query; - use_timer_query_ = context_caps.timer_queries; - use_swap_with_bounds_ = context_caps.swap_buffers_with_bounds; - supports_multi_sampling_ = context_caps.max_samples > 0; - prefer_draw_to_copy_ = output_surface_->context_provider() - ->GetGpuFeatureInfo() - .IsWorkaroundEnabled(gpu::PREFER_DRAW_TO_COPY); - use_fast_path_solid_color_quad_ = - features::IsUsingFastPathForSolidColorQuad(); - InitializeSharedObjects(); -} - -GLRenderer::~GLRenderer() { - CleanupSharedObjects(); - - auto* context_provider = output_surface_->context_provider(); - auto* cache_controller = context_provider->CacheController(); - - if (context_busy_) { - cache_controller->ClientBecameNotBusy(std::move(context_busy_)); - } - if (context_visibility_) { - cache_controller->ClientBecameNotVisibleDuringShutdown( - std::move(context_visibility_)); - } -} - -bool GLRenderer::CanPartialSwap() { - if (use_swap_with_bounds_) - return false; - auto* context_provider = output_surface_->context_provider(); - return context_provider->ContextCapabilities().post_sub_buffer; -} - -void GLRenderer::DidChangeVisibility() { - if (visible_) { - output_surface_->EnsureBackbuffer(); - } else { - TRACE_EVENT0("viz", "GLRenderer::DidChangeVisibility dropping resources"); - ReleaseRenderPassTextures(); - output_surface_->DiscardBackbuffer(); - gl_->ReleaseShaderCompiler(); - } - - PrepareGeometry(NO_BINDING); - - auto* context_provider = output_surface_->context_provider(); - auto* cache_controller = context_provider->CacheController(); - if (visible_) { - DCHECK(!context_visibility_); - context_visibility_ = cache_controller->ClientBecameVisible(); - } else { - DCHECK(context_visibility_); - cache_controller->ClientBecameNotVisible(std::move(context_visibility_)); - } -} - -void GLRenderer::ReleaseRenderPassTextures() { - render_pass_textures_.clear(); - render_pass_backdrop_textures_.clear(); -} - -void GLRenderer::DiscardPixels() { - if (!use_discard_framebuffer_) - return; - bool using_default_framebuffer = - !current_framebuffer_texture_ && - output_surface_->capabilities().uses_default_gl_framebuffer; - GLenum attachments[] = {static_cast<GLenum>( - using_default_framebuffer ? GL_COLOR_EXT : GL_COLOR_ATTACHMENT0_EXT)}; - gl_->DiscardFramebufferEXT(GL_FRAMEBUFFER, std::size(attachments), - attachments); -} - -void GLRenderer::PrepareSurfaceForPass( - SurfaceInitializationMode initialization_mode, - const gfx::Rect& render_pass_scissor) { - SetViewport(); - - switch (initialization_mode) { - case SURFACE_INITIALIZATION_MODE_PRESERVE: - EnsureScissorTestDisabled(); - return; - case SURFACE_INITIALIZATION_MODE_FULL_SURFACE_CLEAR: - EnsureScissorTestDisabled(); - DiscardPixels(); - ClearFramebuffer(); - break; - case SURFACE_INITIALIZATION_MODE_SCISSORED_CLEAR: - SetScissorTestRect(render_pass_scissor); - ClearFramebuffer(); - break; - } - - if (OverdrawTracingEnabled()) { - gl_->GenQueriesEXT(1, &occlusion_query_); - gl_->BeginQueryEXT(GL_SAMPLES_PASSED_ARB, occlusion_query_); - } - - // For each render pass, reset the drawn region. - drawn_rects_.clear(); -} - -void GLRenderer::ClearFramebuffer() { - // On DEBUG builds, opaque render passes are cleared to blue to easily see - // regions that were not drawn on the screen. - if (current_frame()->current_render_pass->has_transparent_background) - gl_->ClearColor(0, 0, 0, 0); - else - gl_->ClearColor(0, 0, 1, 1); - - gl_->ClearStencil(0); - - bool always_clear = overdraw_feedback_; -#ifndef NDEBUG - always_clear = true; -#endif - if (always_clear || - current_frame()->current_render_pass->has_transparent_background) { - GLbitfield clear_bits = GL_COLOR_BUFFER_BIT; - if (always_clear) - clear_bits |= GL_STENCIL_BUFFER_BIT; - gl_->Clear(clear_bits); - } -} - -void GLRenderer::BeginDrawingFrame() { - TRACE_EVENT0("viz", "GLRenderer::BeginDrawingFrame"); - - if (!context_busy_) { - context_busy_ = output_surface_->context_provider() - ->CacheController() - ->ClientBecameBusy(); - } - - // Begin batching read of shared images. - gl_->BeginBatchReadAccessSharedImageCHROMIUM(); - - scoped_refptr<ResourceFence> read_lock_fence; - if (use_sync_query_) { - read_lock_fence = sync_queries_.StartNewFrame(); - } else { - read_lock_fence = - base::MakeRefCounted<DisplayResourceProviderGL::SynchronousFence>(gl_); - } - resource_provider()->SetReadLockFence(read_lock_fence.get()); - - // Insert WaitSyncTokenCHROMIUM on quad resources prior to drawing the frame, - // so that drawing can proceed without GL context switching interruptions. - for (const auto& pass : *current_frame()->render_passes_in_draw_order) { - for (auto* quad : pass->quad_list) { - for (ResourceId resource_id : quad->resources) - resource_provider()->WaitSyncToken(resource_id); - } - } - - // TODO(enne): Do we need to reinitialize all of this state per frame? - ReinitializeGLState(); - - // Add a dummy timer query as a fence to identify the beginning of a frame in - // the circular queue. - if (CompositeTimeTracingEnabled()) - timer_queries_.emplace(kTimerQueryDummy, ""); - - num_triangles_drawn_ = 0; -} - -void GLRenderer::DoDrawQuad(const DrawQuad* quad, - const gfx::QuadF* clip_region) { - DCHECK(quad->rect.Contains(quad->visible_rect)); - if (quad->material != DrawQuad::Material::kTextureContent) { - FlushTextureQuadCache(SHARED_BINDING); - } - - switch (quad->material) { - case DrawQuad::Material::kInvalid: - NOTREACHED(); - break; - case DrawQuad::Material::kAggregatedRenderPass: - DrawRenderPassQuad(AggregatedRenderPassDrawQuad::MaterialCast(quad), - clip_region); - break; - case DrawQuad::Material::kDebugBorder: - DrawDebugBorderQuad(DebugBorderDrawQuad::MaterialCast(quad)); - break; - case DrawQuad::Material::kPictureContent: - // PictureDrawQuad should only be used for resourceless software draws. - NOTREACHED(); - break; - case DrawQuad::Material::kCompositorRenderPass: - // At this point, RenderPassDrawQuads should be replaced by - // AggregatedRenderPassDrawQuad. - NOTREACHED(); - break; - case DrawQuad::Material::kSolidColor: - DrawSolidColorQuad(SolidColorDrawQuad::MaterialCast(quad), clip_region); - break; - case DrawQuad::Material::kStreamVideoContent: - DrawStreamVideoQuad(StreamVideoDrawQuad::MaterialCast(quad), clip_region); - break; - case DrawQuad::Material::kSurfaceContent: - // Surface content should be fully resolved to other quad types before - // reaching a direct renderer. - NOTREACHED(); - break; - case DrawQuad::Material::kSharedElement: - // Shared element should be fully resolved to other quad types before - // reaching a direct renderer. - NOTREACHED(); - break; - case DrawQuad::Material::kTextureContent: - EnqueueTextureQuad(TextureDrawQuad::MaterialCast(quad), clip_region); - break; - case DrawQuad::Material::kTiledContent: - DrawTileQuad(TileDrawQuad::MaterialCast(quad), clip_region); - break; - case DrawQuad::Material::kYuvVideoContent: - DrawYUVVideoQuad(YUVVideoDrawQuad::MaterialCast(quad), clip_region); - break; - case DrawQuad::Material::kVideoHole: - // VideoHoleDrawQuad should only be used by Cast, and should - // have been replaced by cast-specific OverlayProcessor before - // reach here. In non-cast build, an untrusted render could send such - // Quad and the quad would then reach here unexpectedly. Therefore - // we should skip NOTREACHED() so an untrusted render is not capable - // of causing a crash. - break; - } -} - -// This function does not handle 3D sorting right now, since the debug border -// quads are just drawn as their original quads and not in split pieces. This -// results in some debug border quads drawing over foreground quads. -void GLRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad) { - SetBlendEnabled(quad->ShouldDrawWithBlending()); - - SetUseProgram(ProgramKey::DebugBorder(), gfx::ColorSpace::CreateSRGB(), - CurrentRenderPassColorSpace()); - - // Use the full quad_rect for debug quads to not move the edges based on - // partial swaps. - gfx::Rect layer_rect = quad->rect; - gfx::Transform render_matrix; - QuadRectTransform(&render_matrix, - quad->shared_quad_state->quad_to_target_transform, - gfx::RectF(layer_rect)); - SetShaderMatrix(current_frame()->projection_matrix * render_matrix); - SetShaderColor(quad->color, 1.f); - - gl_->LineWidth(quad->width); - - // The indices for the line are stored in the same array as the triangle - // indices. - gl_->DrawElements(GL_LINE_LOOP, 4, GL_UNSIGNED_SHORT, nullptr); -} - -// This is a utility to convert from GrGLenum color format into the equivalent -// skColorType format. Note: this only supports the limited set of values that -// can get returned by GLRenderer::GetBackdropTexture(). -static SkColorType GlFormatToSkFormat(GrGLenum format) { - switch (format) { - case GL_RGB: - return kRGB_888x_SkColorType; - case GL_RGBA: - return kRGBA_8888_SkColorType; - case GL_BGRA_EXT: - return kBGRA_8888_SkColorType; - case GL_RGB10_A2_EXT: - return kRGBA_1010102_SkColorType; - default: - NOTREACHED() << std::hex << std::showbase << format; - return kN32_SkColorType; - } -} - -static GrGLenum SkFormatToGlFormat(SkColorType format) { - switch (format) { - case kRGB_888x_SkColorType: - return GL_RGB8_OES; - case kRGBA_8888_SkColorType: - return GL_RGBA8_OES; - case kBGRA_8888_SkColorType: - return GL_BGRA8_EXT; - case kRGBA_1010102_SkColorType: - return GL_RGB10_A2_EXT; - default: - NOTREACHED(); - return GL_RGBA8_OES; - } -} - -// Wrap a given texture in a Ganesh backend texture. -static sk_sp<SkImage> WrapTexture(uint32_t texture_id, - uint32_t target, - const gfx::Size& size, - GrDirectContext* context, - bool flip_texture, - SkColorType format, - bool adopt_texture) { - GrGLenum texture_format = SkFormatToGlFormat(format); - GrGLTextureInfo texture_info; - texture_info.fTarget = target; - texture_info.fID = texture_id; - texture_info.fFormat = texture_format; - GrBackendTexture backend_texture(size.width(), size.height(), - GrMipMapped::kNo, texture_info); - GrSurfaceOrigin origin = - flip_texture ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin; - if (adopt_texture) { - return SkImage::MakeFromAdoptedTexture( - context, backend_texture, origin, format, kPremul_SkAlphaType, nullptr); - } else { - return SkImage::MakeFromTexture(context, backend_texture, origin, format, - kPremul_SkAlphaType, nullptr); - } -} - -static gfx::RectF CenteredRect(const gfx::Rect& tile_rect) { - return gfx::RectF( - gfx::PointF(-0.5f * tile_rect.width(), -0.5f * tile_rect.height()), - gfx::SizeF(tile_rect.size())); -} - -bool GLRenderer::CanApplyBlendModeUsingBlendFunc(SkBlendMode blend_mode) { - return use_blend_equation_advanced_ || blend_mode == SkBlendMode::kSrcOver || - blend_mode == SkBlendMode::kDstIn || - blend_mode == SkBlendMode::kScreen; -} - -void GLRenderer::ApplyBlendModeUsingBlendFunc(SkBlendMode blend_mode) { - // Any modes set here must be reset in RestoreBlendFuncToDefault - if (blend_mode == SkBlendMode::kSrcOver) { - // Left no-op intentionally. - } else if (blend_mode == SkBlendMode::kDstIn) { - gl_->BlendFunc(GL_ZERO, GL_SRC_ALPHA); - } else if (blend_mode == SkBlendMode::kDstOut) { - gl_->BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA); - } else if (blend_mode == SkBlendMode::kScreen) { - gl_->BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE); - } else { - DCHECK(use_blend_equation_advanced_); - GLenum equation = GL_FUNC_ADD; - switch (blend_mode) { - case SkBlendMode::kScreen: - equation = GL_SCREEN_KHR; - break; - case SkBlendMode::kOverlay: - equation = GL_OVERLAY_KHR; - break; - case SkBlendMode::kDarken: - equation = GL_DARKEN_KHR; - break; - case SkBlendMode::kLighten: - equation = GL_LIGHTEN_KHR; - break; - case SkBlendMode::kColorDodge: - equation = GL_COLORDODGE_KHR; - break; - case SkBlendMode::kColorBurn: - equation = GL_COLORBURN_KHR; - break; - case SkBlendMode::kHardLight: - equation = GL_HARDLIGHT_KHR; - break; - case SkBlendMode::kSoftLight: - equation = GL_SOFTLIGHT_KHR; - break; - case SkBlendMode::kDifference: - equation = GL_DIFFERENCE_KHR; - break; - case SkBlendMode::kExclusion: - equation = GL_EXCLUSION_KHR; - break; - case SkBlendMode::kMultiply: - equation = GL_MULTIPLY_KHR; - break; - case SkBlendMode::kHue: - equation = GL_HSL_HUE_KHR; - break; - case SkBlendMode::kSaturation: - equation = GL_HSL_SATURATION_KHR; - break; - case SkBlendMode::kColor: - equation = GL_HSL_COLOR_KHR; - break; - case SkBlendMode::kLuminosity: - equation = GL_HSL_LUMINOSITY_KHR; - break; - default: - NOTREACHED() << "Unexpected blend mode: SkBlendMode::k" - << SkBlendMode_Name(blend_mode); - return; - } - gl_->BlendEquation(equation); - } -} - -void GLRenderer::RestoreBlendFuncToDefault(SkBlendMode blend_mode) { - switch (blend_mode) { - case SkBlendMode::kSrcOver: - break; - case SkBlendMode::kDstIn: - case SkBlendMode::kDstOut: - case SkBlendMode::kScreen: - gl_->BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - break; - default: - DCHECK(use_blend_equation_advanced_); - gl_->BlendEquation(GL_FUNC_ADD); - } -} - -bool GLRenderer::ShouldApplyBackdropFilters( - const DrawRenderPassDrawQuadParams* params) { - if (!params->backdrop_filters) - return false; - if (params->quad->shared_quad_state->opacity == 0.f) - return false; - DCHECK(!params->backdrop_filters->IsEmpty()); - return true; -} - -gfx::Rect GLRenderer::GetBackdropBoundingBoxForRenderPassQuad( - DrawRenderPassDrawQuadParams* params, - gfx::Transform* backdrop_filter_bounds_transform, - absl::optional<gfx::RRectF>* backdrop_filter_bounds, - gfx::Rect* unclipped_rect) const { - DCHECK(backdrop_filter_bounds_transform); - DCHECK(backdrop_filter_bounds); - DCHECK(unclipped_rect); - - const auto* quad = params->quad.get(); - gfx::QuadF scaled_region; - // |scaled_region| is a quad in [-0.5,0.5] space that represents |clip_region| - // as a fraction of the space defined by |quad->rect|. If |clip_region| is - // nullptr, then scaled_region is [-0.5,0.5]. - if (!GetScaledRegion(quad->rect, params->clip_region, &scaled_region)) { - scaled_region = SharedGeometryQuad().BoundingBox(); - } - // |backdrop_filter_bounds| is a rounded rect in [-0.5,0.5] space that - // represents |params->backdrop_filter_bounds| as a fraction of the space - // defined by |quad->rect|, not including its offset. - *backdrop_filter_bounds = gfx::RRectF(); - if (!params->backdrop_filter_bounds || - !GetScaledRRectF(quad->rect, params->backdrop_filter_bounds.value(), - &backdrop_filter_bounds->value())) { - backdrop_filter_bounds->reset(); - } - - // |backdrop_rect| is now the bounding box of clip_region, in window pixel - // coordinates, and with flip applied. - gfx::Rect backdrop_rect = gfx::ToEnclosingRect(cc::MathUtil::MapClippedRect( - params->contents_device_transform, scaled_region.BoundingBox())); - - if (!backdrop_rect.IsEmpty() && (params->filters || params->use_aa)) { - // If we have regular filters or antialiasing, grab an extra one-pixel - // border around the background, so texture edge clamping gives us a - // transparent border. - backdrop_rect.Inset(-1); - } - - *unclipped_rect = backdrop_rect; - backdrop_rect.Intersect(MoveFromDrawToWindowSpace( - current_frame()->current_render_pass->output_rect)); - if (ShouldApplyBackdropFilters(params)) { - float max_pixel_movement = params->backdrop_filters->MaximumPixelMovement(); - gfx::Rect scissor_rect(current_window_space_viewport_); - scissor_rect.Inset(-max_pixel_movement); - backdrop_rect.Intersect(scissor_rect); - } - - // The frame buffer flip is already included in the captured backdrop image, - // and it is included in |contents_device_transform| (through - // |projection_matrix|). Don't double-flip. - *backdrop_filter_bounds_transform = params->contents_device_transform; - float new_y = 2 * backdrop_filter_bounds_transform->To2dTranslation().y() + - backdrop_rect.bottom() - unclipped_rect->bottom() + - backdrop_rect.y() - unclipped_rect->y(); - backdrop_filter_bounds_transform->PostScale(1, -1); - backdrop_filter_bounds_transform->PostTranslate(0, new_y); - - // Shift to the space of the captured backdrop image. - backdrop_filter_bounds_transform->PostTranslate(-backdrop_rect.x(), - -backdrop_rect.y()); - - return backdrop_rect; -} - -GLenum GLRenderer::GetFramebufferCopyTextureFormat() { - // If copying a non-root renderpass then use the format of the bound - // texture. Otherwise, we use the format of the default framebuffer. But - // whatever the format is, convert it to a valid format for CopyTexSubImage2D. - GLenum format; - if (!current_framebuffer_texture_) { - format = output_surface_->GetFramebufferCopyTextureFormat(); - } else { - ResourceFormat resource_format = CurrentRenderPassResourceFormat(); - DCHECK(GLSupportsFormat(resource_format)); - format = GLCopyTextureInternalFormat(resource_format); - } - // Verify the format is valid for GLES2's glCopyTexSubImage2D. - DCHECK(format == GL_ALPHA || format == GL_LUMINANCE || - format == GL_LUMINANCE_ALPHA || format == GL_RGB || - format == GL_RGBA || - (output_surface_->context_provider() - ->ContextCapabilities() - .texture_format_bgra8888 && - format == GL_BGRA_EXT) || - format == GL_RGB10_A2_EXT) - << std::hex << std::showbase << format; - return format; -} - -uint32_t GLRenderer::GetBackdropTexture(const gfx::Rect& window_rect, - float scale, - GLenum* internal_format) { - DCHECK(internal_format); - DCHECK_GE(window_rect.x(), 0); - DCHECK_GE(window_rect.y(), 0); - DCHECK_LE(window_rect.right(), current_surface_size_.width()); - DCHECK_LE(window_rect.bottom(), current_surface_size_.height()); - - uint32_t texture_id; - gl_->GenTextures(1, &texture_id); - DCHECK(texture_id); - gl_->BindTexture(GL_TEXTURE_2D, texture_id); - - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - ResourceFormat resource_format = CurrentRenderPassResourceFormat(); - // Get gl_format, gl_type and internal_format. - DCHECK(GLSupportsFormat(resource_format)); - *internal_format = GLInternalFormat(resource_format); - GLenum gl_format = GLDataFormat(resource_format); - GLenum gl_type = GLDataType(resource_format); - - if (supports_multi_sampling_ && scale != 1.0f) { - DCHECK(!prefer_draw_to_copy_ || !current_framebuffer_texture_); - - gfx::Size target_size = window_rect.size(); - target_size = gfx::ScaleToCeiledSize(target_size, scale); - - gl_->TexImage2D(GL_TEXTURE_2D, 0, *internal_format, target_size.width(), - target_size.height(), 0, gl_format, gl_type, nullptr); - - unsigned fbo = 0; - gl_->GenFramebuffers(1, &fbo); - gl_->BindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); - gl_->FramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, texture_id, 0); - DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), - gl_->CheckFramebufferStatus(GL_DRAW_FRAMEBUFFER_EXT)); - - gl_->Scissor(0, 0, target_size.width(), target_size.height()); - - gl_->BlitFramebufferCHROMIUM(window_rect.x(), window_rect.y(), - window_rect.right(), window_rect.bottom(), 0, - 0, target_size.width(), target_size.height(), - GL_COLOR_BUFFER_BIT, GL_LINEAR); - - gl_->DeleteFramebuffers(1, &fbo); - } else if (prefer_draw_to_copy_ && current_framebuffer_texture_) { - // If there is a source texture |current_framebuffer_texture_| and the - // workaround |prefer_draw_to_copy_| is enabled, then do texture to texture - // copy via draw instead of glCopyTexImage2D. - - // Size the destination texture with empty data. This is required since - // CopySubTextureCHROMIUM() does not sizes the texture but CopyTexImage2D - // does. - gl_->TexImage2D(GL_TEXTURE_2D, 0, *internal_format, window_rect.width(), - window_rect.height(), 0, gl_format, gl_type, nullptr); - gl_->CopySubTextureCHROMIUM( - current_framebuffer_texture_->id(), 0, GL_TEXTURE_2D, texture_id, 0, 0, - 0, window_rect.x(), window_rect.y(), window_rect.width(), - window_rect.height(), GL_FALSE, GL_FALSE, GL_FALSE); - } else { - *internal_format = GetFramebufferCopyTextureFormat(); - - // CopyTexImage2D requires inernalformat channels to be a subset of - // the channels of the source texture internalformat. - DCHECK(*internal_format == GL_RGB || *internal_format == GL_RGBA || - *internal_format == GL_BGRA_EXT || - *internal_format == GL_RGB10_A2_EXT); - if (*internal_format == GL_BGRA_EXT) - *internal_format = GL_RGBA; - gl_->CopyTexImage2D(GL_TEXTURE_2D, 0, *internal_format, window_rect.x(), - window_rect.y(), window_rect.width(), - window_rect.height(), 0); - } - gl_->BindTexture(GL_TEXTURE_2D, 0); - return texture_id; -} - -static sk_sp<SkImage> FinalizeImage(sk_sp<SkSurface> surface) { - // Flush the drawing before source texture read lock goes out of scope. - // Skia API does not guarantee that when the SkImage goes out of scope, - // its externally referenced resources would force the rendering to be - // flushed. - surface->getCanvas()->flush(); - sk_sp<SkImage> image = surface->makeImageSnapshot(); - if (!image || !image->isTextureBacked()) { - return nullptr; - } - return image; -} - -sk_sp<SkImage> GLRenderer::ApplyBackdropFilters( - DrawRenderPassDrawQuadParams* params, - const gfx::Rect& unclipped_rect, - const absl::optional<gfx::RRectF>& backdrop_filter_bounds, - const gfx::Transform& backdrop_filter_bounds_transform) { - DCHECK(ShouldApplyBackdropFilters(params)); - DCHECK(params->backdrop_filter_quality > 0.0f && - params->backdrop_filter_quality <= 1.0f); - DCHECK(!params->filters) - << "Filters should always be in a separate Effect node"; - const auto* quad = params->quad.get(); - auto use_gr_context = ScopedUseGrContext::Create(this); - - // Check if cached result can be used - auto bg_texture_it = - render_pass_backdrop_textures_.find(quad->render_pass_id); - if (bg_texture_it != render_pass_backdrop_textures_.end()) { - if (!quad->intersects_damage_under) - return bg_texture_it->second; - else - render_pass_backdrop_textures_.erase(bg_texture_it); - } - - gfx::Vector2d clipping_offset = - (params->background_rect.top_right() - unclipped_rect.top_right()) + - (params->background_rect.bottom_left() - unclipped_rect.bottom_left()); - - gfx::Rect quality_adjusted_rect = ScaleToEnclosingRect( - params->background_rect, params->backdrop_filter_quality); - - // When backdrop_filter_quality is less than 1.0f, scale the blur amount - // accordingly. - cc::FilterOperations filter_operations; - if (params->backdrop_filter_quality < 1.0f) { - for (const cc::FilterOperation& op : - params->backdrop_filters->operations()) { - if (op.type() == cc::FilterOperation::BLUR) { - cc::FilterOperation blur_op(op); - blur_op.set_amount(op.amount() * params->backdrop_filter_quality); - filter_operations.Append(blur_op); - } else { - filter_operations.Append(op); - } - } - } - const cc::FilterOperations& filters = params->backdrop_filter_quality < 1.0f - ? filter_operations - : *params->backdrop_filters; - - auto paint_filter = cc::RenderSurfaceFilters::BuildImageFilter( - filters, gfx::SizeF(quality_adjusted_rect.size()), - gfx::Vector2dF(clipping_offset)); - - // TODO(senorblanco): background filters should be moved to the - // makeWithFilter fast-path, and go back to calling ApplyImageFilter(). - // See http://crbug.com/613233. - if (!paint_filter || !use_gr_context) - return nullptr; - - auto filter = paint_filter->cached_sk_filter_; - sk_sp<SkImage> src_image = WrapTexture( - params->background_texture, GL_TEXTURE_2D, quality_adjusted_rect.size(), - use_gr_context->context(), /*flip_texture=*/true, - GlFormatToSkFormat(params->background_texture_format), - /*adopt_texture=*/false); - if (!src_image) { - TRACE_EVENT_INSTANT0("cc", - "ApplyBackdropFilters wrap background texture failed", - TRACE_EVENT_SCOPE_THREAD); - return nullptr; - } - - // Create surface to draw into. - SkImageInfo dst_info = SkImageInfo::MakeN32Premul( - quality_adjusted_rect.width(), quality_adjusted_rect.height()); - sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget( - use_gr_context->context(), SkBudgeted::kYes, dst_info); - if (!surface) { - TRACE_EVENT_INSTANT0("viz", - "ApplyBackdropFilters surface allocation failed", - TRACE_EVENT_SCOPE_THREAD); - return nullptr; - } - - // Big filters can sometimes fallback to CPU. Therefore, we need - // to disable subnormal floats for performance and security reasons. - cc::ScopedSubnormalFloatDisabler disabler; - - gfx::RectF src_image_rect = - gfx::RectF(quality_adjusted_rect.width(), quality_adjusted_rect.height()); - SkRect dest_rect = RectToSkRect(gfx::Rect(quality_adjusted_rect.size())); - - // If the content underneath the backdrop filter can be exposed because of - // blending or bounds, paint the backdrop at full opacity first. The - // backdrop-filtered content will not be blended with the backdrop later, it - // will be rastered over the top. So we need to paint it here, unfiltered. - if (backdrop_filter_bounds.has_value() || quad->ShouldDrawWithBlending()) { - surface->getCanvas()->drawImageRect( - src_image, RectFToSkRect(src_image_rect), dest_rect, - SkSamplingOptions(), nullptr, SkCanvas::kStrict_SrcRectConstraint); - } - - if (backdrop_filter_bounds.has_value()) { - // Crop the source image to the backdrop_filter_bounds. - gfx::Rect filter_clip = gfx::ToEnclosingRect(cc::MathUtil::MapClippedRect( - backdrop_filter_bounds_transform, backdrop_filter_bounds->rect())); - gfx::Rect src_rect(params->background_rect.width(), - params->background_rect.height()); - filter_clip.Intersect(src_rect); - if (filter_clip.IsEmpty()) - return FinalizeImage(surface); - if (filter_clip != src_rect) { - filter_clip = gfx::ScaleToEnclosingRect(filter_clip, - params->backdrop_filter_quality); - src_image = src_image->makeSubset(RectToSkIRect(filter_clip), - use_gr_context->context()); - src_image_rect = gfx::RectF(filter_clip.width(), filter_clip.height()); - dest_rect = RectToSkRect(filter_clip); - } - } - - SkIPoint offset; - SkIRect subset; - sk_sp<SkImage> filtered_image = SkiaHelper::ApplyImageFilter( - use_gr_context->context(), src_image, src_image_rect, src_image_rect, - quad->filters_scale, std::move(filter), &offset, &subset, - quad->filters_origin, true); - - // Clip the filtered image to the (rounded) bounding box of the element. - if (backdrop_filter_bounds.has_value()) { - surface->getCanvas()->save(); - gfx::RRectF clip_rect(backdrop_filter_bounds.value()); - surface->getCanvas()->setMatrix( - backdrop_filter_bounds_transform.matrix().asM33()); - surface->getCanvas()->clipRRect(SkRRect(clip_rect), SkClipOp::kIntersect, - true /* antialias */); - surface->getCanvas()->resetMatrix(); - } - - SkPaint paint; - // Paint the filtered backdrop image with opacity. - if (quad->shared_quad_state->opacity < 1.0) { - paint.setImageFilter( - SkiaHelper::BuildOpacityFilter(quad->shared_quad_state->opacity)); - } - // Now paint the pre-filtered image onto the canvas (possibly with mask - // applied). - surface->getCanvas()->drawImageRect(filtered_image, SkRect::Make(subset), - dest_rect, SkSamplingOptions(), &paint, - SkCanvas::kStrict_SrcRectConstraint); - - if (backdrop_filter_bounds.has_value()) { - surface->getCanvas()->restore(); - } - - sk_sp<SkImage> filtered_image_texture = FinalizeImage(surface); - if (!quad->intersects_damage_under) { - render_pass_backdrop_textures_[params->quad->render_pass_id] = - filtered_image_texture; - } - return filtered_image_texture; -} - -const DrawQuad* GLRenderer::CanPassBeDrawnDirectly( - const AggregatedRenderPass* pass) { -#if BUILDFLAG(IS_APPLE) - // On Macs, this path can sometimes lead to all black output. - // TODO(enne): investigate this and remove this hack. - return nullptr; -#else - // Can only collapse a single tile quad. - if (pass->quad_list.size() != 1) - return nullptr; - - const DrawQuad* quad = *pass->quad_list.BackToFrontBegin(); - // Hack: this could be supported by concatenating transforms, but - // in practice if there is one quad, it is at the origin of the render pass - // and has the same size as the pass. - if (!quad->shared_quad_state->quad_to_target_transform.IsIdentity() || - quad->rect != pass->output_rect) - return nullptr; - // The quad is expected to be the entire layer so that AA edges are correct. - if (quad->shared_quad_state->quad_layer_rect != quad->rect) - return nullptr; - if (quad->material != DrawQuad::Material::kTiledContent) - return nullptr; - - // TODO(chrishtr): support could be added for opacity, but care needs - // to be taken to make sure it is correct w.r.t. non-commutative filters etc. - if (quad->shared_quad_state->opacity != 1.0f) - return nullptr; - - if (quad->shared_quad_state->blend_mode != SkBlendMode::kSrcOver) - return nullptr; - - const TileDrawQuad* tile_quad = TileDrawQuad::MaterialCast(quad); - // Hack: this could be supported by passing in a subrectangle to draw - // render pass, although in practice if there is only one quad there - // will be no border texels on the input. - if (tile_quad->tex_coord_rect != gfx::RectF(tile_quad->rect)) - return nullptr; - // Tile quad features not supported in render pass shaders. - if (tile_quad->nearest_neighbor) - return nullptr; - // BUG=skia:3868, Skia currently doesn't support texture rectangle inputs. - // See also the DCHECKs about GL_TEXTURE_2D in DrawRenderPassQuad. - GLenum target = - resource_provider()->GetResourceTextureTarget(tile_quad->resource_id()); - if (target != GL_TEXTURE_2D) - return nullptr; - - return tile_quad; -#endif -} - -void GLRenderer::DrawRenderPassQuad(const AggregatedRenderPassDrawQuad* quad, - const gfx::QuadF* clip_region) { - auto bypass = render_pass_bypass_quads_.find(quad->render_pass_id); - DrawRenderPassDrawQuadParams params; - params.quad = quad; - params.clip_region = clip_region; - params.window_matrix = current_frame()->window_matrix; - params.projection_matrix = current_frame()->projection_matrix; - params.tex_coord_rect = quad->tex_coord_rect; - ScopedTimerQuery scoped_timer_query(CompositeTimeTracingEnabled(), gl_, - &timer_queries_, "kRenderPassDrawQuad"); - if (bypass != render_pass_bypass_quads_.end()) { - DCHECK(bypass->second->material == DrawQuad::Material::kTiledContent); - const TileDrawQuad* tile_quad = TileDrawQuad::MaterialCast(bypass->second); - // The projection matrix used by GLRenderer has a flip. As tile texture - // inputs are oriented opposite to framebuffer outputs, don't flip via - // texture coords and let the projection matrix naturallyd o it. - params.flip_texture = false; - params.bypass_quad_texture.resource_id = tile_quad->resource_id(); - params.bypass_quad_texture.size = tile_quad->texture_size; - DrawRenderPassQuadInternal(¶ms); - } else { - auto contents_texture_it = render_pass_textures_.find(quad->render_pass_id); - DCHECK(contents_texture_it->second.id()); - // See above comments about texture flipping. When the input is a - // render pass, it needs to an extra flip to be oriented correctly. - params.flip_texture = true; - params.contents_texture = &contents_texture_it->second; - DrawRenderPassQuadInternal(¶ms); - } - - if (params.background_texture) { - gl_->DeleteTextures(1, ¶ms.background_texture); - params.background_texture = 0; - } -} - -void GLRenderer::DrawRenderPassQuadInternal( - DrawRenderPassDrawQuadParams* params) { - params->quad_to_target_transform = - params->quad->shared_quad_state->quad_to_target_transform; - if (!InitializeRPDQParameters(params)) - return; - - UpdateRPDQShadersForBlending(params); - bool can_draw = UpdateRPDQWithSkiaFilters(params); - // The above calls use ScopedUseGrContext which can change the bound - // framebuffer, so we need to restore it for the current RenderPass. - UseRenderPass(current_frame()->current_render_pass); - // As part of restoring the framebuffer, we call SetViewport directly, rather - // than through PrepareSurfaceForPass. PrepareSurfaceForPass also clears the - // surface, which is not desired when restoring. - SetViewport(); - - if (!can_draw) - return; - - UpdateRPDQTexturesForSampling(params); - UpdateRPDQBlendMode(params); - ChooseRPDQProgram(params, CurrentRenderPassColorSpace()); - UpdateRPDQUniforms(params); - DrawRPDQ(*params); - - AccumulateDrawRects(params->quad->visible_rect, - params->quad->shared_quad_state->quad_to_target_transform, - &drawn_rects_); -} - -bool GLRenderer::InitializeRPDQParameters( - DrawRenderPassDrawQuadParams* params) { - DCHECK(params); - const auto* quad = params->quad.get(); - SkMatrix local_matrix; - local_matrix.setTranslate(quad->filters_origin.x(), quad->filters_origin.y()); - local_matrix.postScale(quad->filters_scale.x(), quad->filters_scale.y()); - params->filters = FiltersForPass(quad->render_pass_id); - params->backdrop_filters = BackdropFiltersForPass(quad->render_pass_id); - if (ShouldApplyBackdropFilters(params)) { - params->backdrop_filter_bounds = - BackdropFilterBoundsForPass(quad->render_pass_id); - if (params->backdrop_filter_bounds.has_value()) { - params->backdrop_filter_bounds->Scale(quad->filters_scale.x(), - quad->filters_scale.y()); - } - } else { - params->backdrop_filter_bounds.reset(); - } - params->backdrop_filter_quality = quad->backdrop_filter_quality; - gfx::Rect dst_rect = params->filters - ? params->filters->MapRect(quad->rect, local_matrix) - : quad->rect; - params->dst_rect.SetRect(static_cast<float>(dst_rect.x()), - static_cast<float>(dst_rect.y()), - static_cast<float>(dst_rect.width()), - static_cast<float>(dst_rect.height())); - gfx::Transform quad_rect_matrix; - gfx::Rect quad_layer_rect(quad->shared_quad_state->quad_layer_rect); - if (params->filters) - quad_layer_rect = params->filters->MapRect(quad_layer_rect, local_matrix); - QuadRectTransform(&quad_rect_matrix, params->quad_to_target_transform, - gfx::RectF(quad_layer_rect)); - params->contents_device_transform = - params->window_matrix * params->projection_matrix * quad_rect_matrix; - params->contents_device_transform.FlattenTo2d(); - - // Can only draw surface if device matrix is invertible. - if (!params->contents_device_transform.IsInvertible()) - return false; - - // TODO(sunxd): unify the anti-aliasing logic of RPDQ and TileDrawQuad. - params->surface_quad = SharedGeometryQuad(); - gfx::QuadF device_layer_quad; - if (settings_->allow_antialiasing && !quad->force_anti_aliasing_off && - quad->IsEdge()) { - bool clipped = false; - device_layer_quad = cc::MathUtil::MapQuad(params->contents_device_transform, - params->surface_quad, &clipped); - params->use_aa = ShouldAntialiasQuad(device_layer_quad, clipped, - settings_->force_antialiasing); - } - - const gfx::QuadF* aa_quad = params->use_aa ? &device_layer_quad : nullptr; - SetupRenderPassQuadForClippingAndAntialiasing( - params->contents_device_transform, quad, aa_quad, params->clip_region, - ¶ms->surface_quad, params->edge); - - return true; -} - -// Get a GL texture id from an SkImage. An optional origin pointer can be -// passed in which will be filled out with the origin for the texture -// backing the SkImage. -static GLuint GetGLTextureIDFromSkImage(const SkImage* image, - GrSurfaceOrigin* origin = nullptr) { - GrBackendTexture backend_texture = image->getBackendTexture(true, origin); - if (!backend_texture.isValid()) { - return 0; - } - GrGLTextureInfo info; - bool result = backend_texture.getGLTextureInfo(&info); - DCHECK(result); - return info.fID; -} - -void GLRenderer::UpdateRPDQShadersForBlending( - DrawRenderPassDrawQuadParams* params) { - const auto* quad = params->quad.get(); - params->blend_mode = quad->shared_quad_state->blend_mode; - params->use_shaders_for_blending = - !CanApplyBlendModeUsingBlendFunc(params->blend_mode) || - ShouldApplyBackdropFilters(params) || - settings_->force_blending_with_shaders; - - if (params->use_shaders_for_blending) { - // Compute a bounding box around the pixels that will be visible through - // the quad. - absl::optional<gfx::RRectF> backdrop_filter_bounds; - gfx::Transform backdrop_filter_bounds_transform; - gfx::Rect unclipped_rect; - params->background_rect = GetBackdropBoundingBoxForRenderPassQuad( - params, &backdrop_filter_bounds_transform, &backdrop_filter_bounds, - &unclipped_rect); - - if (!params->background_rect.IsEmpty()) { - // The pixels from the filtered background should completely replace the - // current pixel values. - if (blend_enabled()) - SetBlendEnabled(false); - - // Read the pixels in the bounding box into a buffer R. - // This function allocates a texture, which should contribute to the - // amount of memory used by render surfaces: - // LayerTreeHost::CalculateMemoryForRenderSurfaces. - const auto& operations = params->backdrop_filters->operations(); - DCHECK(params->backdrop_filter_quality == 1.0f || - (operations.size() == 1 && - operations.front().type() == cc::FilterOperation::BLUR)); - params->background_texture = GetBackdropTexture( - params->background_rect, params->backdrop_filter_quality, - ¶ms->background_texture_format); - - if (ShouldApplyBackdropFilters(params)) { - // Apply the background filters to R, so that it is applied in the - // pixels' coordinate space. - params->background_image = - ApplyBackdropFilters(params, unclipped_rect, backdrop_filter_bounds, - backdrop_filter_bounds_transform); - if (params->background_image) { - params->background_image_id = - GetGLTextureIDFromSkImage(params->background_image.get()); - DCHECK(params->background_image_id || IsContextLost()); - } - } - if (params->background_image_id) { - // Reset original background texture if there is not any mask. - if (!quad->mask_resource_id()) { - gl_->DeleteTextures(1, ¶ms->background_texture); - params->background_texture = 0; - } - } else if (CanApplyBlendModeUsingBlendFunc(params->blend_mode) && - ShouldApplyBackdropFilters(params)) { - // Something went wrong with applying backdrop filters to the - // backdrop. - params->use_shaders_for_blending = false; - gl_->DeleteTextures(1, ¶ms->background_texture); - params->background_texture = 0; - } - } else { // params->background_rect.IsEmpty() - DCHECK(!params->background_image_id); - params->use_shaders_for_blending = false; - params->blend_mode = SkBlendMode::kSrcOver; - } - } - - // Need original background texture for mask? - params->mask_for_background = - params->background_texture && // Have original background texture - params->background_image_id; // Have mask texture - // If we have background texture + background image, then we also have mask - // resource. - if (params->background_texture && params->background_image_id) { - DCHECK(params->mask_for_background); - DCHECK(quad->mask_resource_id()); - } - - DCHECK_EQ(params->background_texture || params->background_image_id, - params->use_shaders_for_blending); -} - -bool GLRenderer::UpdateRPDQWithSkiaFilters( - DrawRenderPassDrawQuadParams* params) { - const auto* quad = params->quad.get(); - // Apply filters to the contents texture. - if (params->filters) { - DCHECK(!params->filters->IsEmpty()); - gfx::Size size = params->contents_texture - ? params->contents_texture->size() - : params->bypass_quad_texture.size; - auto paint_filter = cc::RenderSurfaceFilters::BuildImageFilter( - *params->filters, gfx::SizeF(size)); - auto filter = paint_filter ? paint_filter->cached_sk_filter_ : nullptr; - if (filter) { - SkColorFilter* colorfilter_rawptr = nullptr; - filter->asColorFilter(&colorfilter_rawptr); - sk_sp<SkColorFilter> cf(colorfilter_rawptr); - - if (cf && cf->asAColorMatrix(params->color_matrix)) { - // We have a color matrix at the root of the filter DAG; apply it - // locally in the compositor and process the rest of the DAG (if any) - // in Skia. - params->use_color_matrix = true; - filter = sk_ref_sp(filter->getInput(0)); - } - if (filter) { - gfx::Rect clip_rect = - quad->shared_quad_state->clip_rect.value_or(current_draw_rect_); - gfx::Transform transform = params->quad_to_target_transform; - transform.FlattenTo2d(); - if (!transform.IsInvertible()) { - return false; - } - // If the transform has perspective, there might be visible content - // outside of the bounds of the quad. - if (!transform.HasPerspective()) { - gfx::QuadF clip_quad = gfx::QuadF(gfx::RectF(clip_rect)); - gfx::QuadF local_clip = - cc::MathUtil::InverseMapQuadToLocalSpace(transform, clip_quad); - params->dst_rect.Intersect(local_clip.BoundingBox()); - } - // If we've been fully clipped out (by crop rect or clipping), there's - // nothing to draw. - if (params->dst_rect.IsEmpty()) { - return false; - } - SkIPoint offset; - SkIRect subset; - gfx::RectF src_rect(quad->rect); - auto use_gr_context = ScopedUseGrContext::Create(this); - if (!use_gr_context) - return false; - - if (params->contents_texture) { - params->contents_and_bypass_color_space = - params->contents_texture->color_space(); - sk_sp<SkImage> src_image = WrapTexture( - params->contents_texture->id(), GL_TEXTURE_2D, - params->contents_texture->size(), use_gr_context->context(), - params->flip_texture, kN32_SkColorType, /*adopt_texture=*/false); - params->filter_image = SkiaHelper::ApplyImageFilter( - use_gr_context->context(), src_image, src_rect, params->dst_rect, - quad->filters_scale, std::move(filter), &offset, &subset, - quad->filters_origin, true); - } else { - DisplayResourceProviderGL::ScopedReadLockGL - prefilter_bypass_quad_texture_lock( - resource_provider(), params->bypass_quad_texture.resource_id); - params->contents_and_bypass_color_space = - prefilter_bypass_quad_texture_lock.color_space(); - sk_sp<SkImage> src_image = - WrapTexture(prefilter_bypass_quad_texture_lock.texture_id(), - prefilter_bypass_quad_texture_lock.target(), - prefilter_bypass_quad_texture_lock.size(), - use_gr_context->context(), params->flip_texture, - kN32_SkColorType, /*adopt_texture=*/false); - params->filter_image = SkiaHelper::ApplyImageFilter( - use_gr_context->context(), src_image, src_rect, params->dst_rect, - quad->filters_scale, std::move(filter), &offset, &subset, - quad->filters_origin, true); - } - - if (!params->filter_image) - return false; - params->dst_rect = - gfx::RectF(src_rect.x() + offset.fX, src_rect.y() + offset.fY, - subset.width(), subset.height()); - gfx::RectF tex_rect = gfx::RectF(gfx::PointF(subset.x(), subset.y()), - params->dst_rect.size()); - params->tex_coord_rect = tex_rect; - } - } - } - return true; -} - -void GLRenderer::UpdateRPDQTexturesForSampling( - DrawRenderPassDrawQuadParams* params) { - if (params->quad->mask_resource_id()) { - params->mask_resource_lock = - std::make_unique<DisplayResourceProviderGL::ScopedSamplerGL>( - - resource_provider(), params->quad->mask_resource_id(), GL_TEXTURE1, - GL_LINEAR); - } - - if (params->filter_image) { - GrSurfaceOrigin origin; - GLuint filter_image_id = - GetGLTextureIDFromSkImage(params->filter_image.get(), &origin); - DCHECK(filter_image_id || IsContextLost()); - DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(gl_)); - gl_->BindTexture(GL_TEXTURE_2D, filter_image_id); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - // |params->contents_and_bypass_color_space| was populated when - // |params->filter_image| was populated. - params->source_needs_flip = kBottomLeft_GrSurfaceOrigin == origin; - } else if (params->contents_texture) { - params->contents_texture->BindForSampling(); - params->contents_and_bypass_color_space = - params->contents_texture->color_space(); - params->source_needs_flip = params->flip_texture; - } else { - params->bypass_quad_resource_lock = - std::make_unique<DisplayResourceProviderGL::ScopedSamplerGL>( - resource_provider(), params->bypass_quad_texture.resource_id, - GL_LINEAR); - DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), - params->bypass_quad_resource_lock->target()); - params->contents_and_bypass_color_space = - params->bypass_quad_resource_lock->color_space(); - params->source_needs_flip = params->flip_texture; - } -} - -void GLRenderer::UpdateRPDQBlendMode(DrawRenderPassDrawQuadParams* params) { - SkBlendMode blend_mode = params->blend_mode; - SetBlendEnabled((!params->use_shaders_for_blending && - (params->quad->ShouldDrawWithBlending() || - !IsDefaultBlendMode(blend_mode))) || - ShouldApplyRoundedCorner(params->quad)); - if (!params->use_shaders_for_blending) { - if (!use_blend_equation_advanced_coherent_ && use_blend_equation_advanced_) - gl_->BlendBarrierKHR(); - - ApplyBlendModeUsingBlendFunc(blend_mode); - } -} - -void GLRenderer::ChooseRPDQProgram(DrawRenderPassDrawQuadParams* params, - const gfx::ColorSpace& target_color_space) { - TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired( - gl_, &highp_threshold_cache_, settings_->highp_threshold_min, - params->quad->shared_quad_state->visible_quad_layer_rect.size()); - - BlendMode shader_blend_mode = - params->use_shaders_for_blending - ? BlendModeFromSkXfermode(params->blend_mode) - : BLEND_MODE_NONE; - - SamplerType sampler_type = SAMPLER_TYPE_2D; - MaskMode mask_mode = NO_MASK; - bool mask_for_background = params->mask_for_background; - if (params->mask_resource_lock) { - mask_mode = HAS_MASK; - sampler_type = - SamplerTypeFromTextureTarget(params->mask_resource_lock->target()); - } - SetUseProgram( - ProgramKey::RenderPass( - tex_coord_precision, sampler_type, shader_blend_mode, - params->use_aa ? USE_AA : NO_AA, mask_mode, mask_for_background, - params->use_color_matrix, tint_gl_composited_content_, - params->apply_shader_based_rounded_corner && - ShouldApplyRoundedCorner(params->quad)), - params->contents_and_bypass_color_space, target_color_space); -} - -void GLRenderer::UpdateRPDQUniforms(DrawRenderPassDrawQuadParams* params) { - gfx::RectF tex_rect = params->tex_coord_rect; - - gfx::Size texture_size; - if (params->filter_image) { - texture_size.set_width(params->filter_image->width()); - texture_size.set_height(params->filter_image->height()); - } else if (params->contents_texture) { - texture_size = params->contents_texture->size(); - } else { - texture_size = params->bypass_quad_texture.size; - } - - tex_rect.Scale(1.0f / texture_size.width(), 1.0f / texture_size.height()); - - DCHECK(current_program_->vertex_tex_transform_location() != -1 || - IsContextLost()); - if (params->source_needs_flip) { - // Flip the content vertically in the shader, as the RenderPass input - // texture is already oriented the same way as the framebuffer, but the - // projection transform does a flip. - gl_->Uniform4f(current_program_->vertex_tex_transform_location(), - tex_rect.x(), 1.0f - tex_rect.y(), tex_rect.width(), - -tex_rect.height()); - } else { - // Tile textures are oriented opposite the framebuffer, so can use - // the projection transform to do the flip. - gl_->Uniform4f(current_program_->vertex_tex_transform_location(), - tex_rect.x(), tex_rect.y(), tex_rect.width(), - tex_rect.height()); - } - - GLint last_texture_unit = 0; - if (current_program_->mask_sampler_location() != -1) { - DCHECK(params->mask_resource_lock); - DCHECK_NE(current_program_->mask_tex_coord_scale_location(), 1); - DCHECK_NE(current_program_->mask_tex_coord_offset_location(), 1); - gl_->Uniform1i(current_program_->mask_sampler_location(), 1); - - gfx::RectF mask_uv_rect = params->quad->mask_uv_rect; - if (SamplerTypeFromTextureTarget(params->mask_resource_lock->target()) != - SAMPLER_TYPE_2D) { - mask_uv_rect.Scale(params->quad->mask_texture_size.width(), - params->quad->mask_texture_size.height()); - } - - SkMatrix tex_to_mask = SkMatrix::RectToRect(RectFToSkRect(tex_rect), - RectFToSkRect(mask_uv_rect)); - - if (params->source_needs_flip) { - // Mask textures are oriented vertically flipped relative to the - // framebuffer and the RenderPass contents texture, so we flip the tex - // coords from the RenderPass texture to find the mask texture coords. - tex_to_mask.preTranslate(0, 1); - tex_to_mask.preScale(1, -1); - } - - gl_->Uniform2f(current_program_->mask_tex_coord_offset_location(), - tex_to_mask.getTranslateX(), tex_to_mask.getTranslateY()); - gl_->Uniform2f(current_program_->mask_tex_coord_scale_location(), - tex_to_mask.getScaleX(), tex_to_mask.getScaleY()); - last_texture_unit = 1; - } - - if (current_program_->edge_location() != -1) - gl_->Uniform3fv(current_program_->edge_location(), 8, params->edge); - - if (current_program_->color_matrix_location() != -1) { - float matrix[16]; - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 4; ++j) - matrix[i * 4 + j] = SkScalarToFloat(params->color_matrix[j * 5 + i]); - } - gl_->UniformMatrix4fv(current_program_->color_matrix_location(), 1, false, - matrix); - } - - if (current_program_->color_offset_location() != -1) { - float offset[4]; - for (int i = 0; i < 4; ++i) - offset[i] = params->color_matrix[i * 5 + 4]; - - gl_->Uniform4fv(current_program_->color_offset_location(), 1, offset); - } - - if (current_program_->tint_color_matrix_location() != -1) { - auto matrix = cc::DebugColors::TintCompositedContentColorTransformMatrix(); - gl_->UniformMatrix4fv(current_program_->tint_color_matrix_location(), 1, - false, matrix.data()); - } - - if (current_program_->backdrop_location() != -1) { - DCHECK(params->background_texture || params->background_image_id); - DCHECK_NE(current_program_->backdrop_location(), 0); - DCHECK_NE(current_program_->backdrop_rect_location(), 0); - - ++last_texture_unit; - gl_->Uniform1i(current_program_->backdrop_location(), last_texture_unit); - - gl_->Uniform4f(current_program_->backdrop_rect_location(), - params->background_rect.x(), params->background_rect.y(), - 1.0f / params->background_rect.width(), - 1.0f / params->background_rect.height()); - - // Either |background_image_id| or |background_texture| will be the - // |backdrop_location| in the shader. - if (params->background_image_id) { - gl_->ActiveTexture(GL_TEXTURE0 + last_texture_unit); - gl_->BindTexture(GL_TEXTURE_2D, params->background_image_id); - if (params->backdrop_filter_quality != 1.0f) - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl_->ActiveTexture(GL_TEXTURE0); - } - // If |mask_for_background| then we have both |background_image_id| and - // |background_texture|, and the latter will be the - // |original_backdrop_location| in the shader. - if (params->mask_for_background) { - DCHECK(params->background_image_id); - DCHECK(params->background_texture); - ++last_texture_unit; - gl_->Uniform1i(current_program_->original_backdrop_location(), - last_texture_unit); - } - if (params->background_texture) { - gl_->ActiveTexture(GL_TEXTURE0 + last_texture_unit); - gl_->BindTexture(GL_TEXTURE_2D, params->background_texture); - gl_->ActiveTexture(GL_TEXTURE0); - } - } - - SetShaderOpacity(params->quad->shared_quad_state->opacity); - if (current_program_->rounded_corner_rect_location() != -1) { - SetShaderRoundedCorner(params->quad->shared_quad_state->mask_filter_info - .rounded_corner_bounds(), - params->window_matrix * params->projection_matrix); - } - SetShaderQuadF(params->surface_quad); -} - -void GLRenderer::DrawRPDQ(const DrawRenderPassDrawQuadParams& params) { - DrawQuadGeometry(params.projection_matrix, params.quad_to_target_transform, - params.dst_rect); - - // Flush the compositor context before the filter bitmap goes out of - // scope, so the draw gets processed before the filter texture gets deleted. - if (params.filter_image) - gl_->Flush(); - - if (!params.use_shaders_for_blending) - RestoreBlendFuncToDefault(params.blend_mode); -} - -namespace { -// These functions determine if a quad, clipped by a clip_region contains -// the entire {top|bottom|left|right} edge. -bool is_top(const gfx::QuadF* clip_region, const DrawQuad* quad) { - if (!quad->IsTopEdge()) - return false; - if (!clip_region) - return true; - - return std::abs(clip_region->p1().y()) < kAntiAliasingEpsilon && - std::abs(clip_region->p2().y()) < kAntiAliasingEpsilon; -} - -bool is_bottom(const gfx::QuadF* clip_region, const DrawQuad* quad) { - if (!quad->IsBottomEdge()) - return false; - if (!clip_region) - return true; - - return std::abs(clip_region->p3().y() - - quad->shared_quad_state->quad_layer_rect.height()) < - kAntiAliasingEpsilon && - std::abs(clip_region->p4().y() - - quad->shared_quad_state->quad_layer_rect.height()) < - kAntiAliasingEpsilon; -} - -bool is_left(const gfx::QuadF* clip_region, const DrawQuad* quad) { - if (!quad->IsLeftEdge()) - return false; - if (!clip_region) - return true; - - return std::abs(clip_region->p1().x()) < kAntiAliasingEpsilon && - std::abs(clip_region->p4().x()) < kAntiAliasingEpsilon; -} - -bool is_right(const gfx::QuadF* clip_region, const DrawQuad* quad) { - if (!quad->IsRightEdge()) - return false; - if (!clip_region) - return true; - - return std::abs(clip_region->p2().x() - - quad->shared_quad_state->quad_layer_rect.width()) < - kAntiAliasingEpsilon && - std::abs(clip_region->p3().x() - - quad->shared_quad_state->quad_layer_rect.width()) < - kAntiAliasingEpsilon; -} -} // anonymous namespace - -static gfx::QuadF GetDeviceQuadWithAntialiasingOnExteriorEdges( - const LayerQuad& device_layer_edges, - const gfx::Transform& device_transform, - const gfx::QuadF& tile_quad, - const gfx::QuadF* clip_region, - const DrawQuad* quad) { - auto tile_rect = gfx::RectF(quad->visible_rect); - - gfx::PointF bottom_right = tile_quad.p3(); - gfx::PointF bottom_left = tile_quad.p4(); - gfx::PointF top_left = tile_quad.p1(); - gfx::PointF top_right = tile_quad.p2(); - bool clipped = false; - - // Map points to device space. We ignore |clipped|, since the result of - // |MapPoint()| still produces a valid point to draw the quad with. When - // clipped, the point will be outside of the viewport. See crbug.com/416367. - bottom_right = - cc::MathUtil::MapPoint(device_transform, bottom_right, &clipped); - bottom_left = cc::MathUtil::MapPoint(device_transform, bottom_left, &clipped); - top_left = cc::MathUtil::MapPoint(device_transform, top_left, &clipped); - top_right = cc::MathUtil::MapPoint(device_transform, top_right, &clipped); - - LayerQuad::Edge bottom_edge(bottom_right, bottom_left); - LayerQuad::Edge left_edge(bottom_left, top_left); - LayerQuad::Edge top_edge(top_left, top_right); - LayerQuad::Edge right_edge(top_right, bottom_right); - - // Only apply anti-aliasing to edges not clipped by culling or scissoring. - // If an edge is degenerate we do not want to replace it with a "proper" edge - // as that will cause the quad to possibly expand in strange ways. - if (!top_edge.degenerate() && is_top(clip_region, quad) && - tile_rect.y() == quad->rect.y()) { - top_edge = device_layer_edges.top(); - } - if (!left_edge.degenerate() && is_left(clip_region, quad) && - tile_rect.x() == quad->rect.x()) { - left_edge = device_layer_edges.left(); - } - if (!right_edge.degenerate() && is_right(clip_region, quad) && - tile_rect.right() == quad->rect.right()) { - right_edge = device_layer_edges.right(); - } - if (!bottom_edge.degenerate() && is_bottom(clip_region, quad) && - tile_rect.bottom() == quad->rect.bottom()) { - bottom_edge = device_layer_edges.bottom(); - } - - float sign = tile_quad.IsCounterClockwise() ? -1 : 1; - bottom_edge.scale(sign); - left_edge.scale(sign); - top_edge.scale(sign); - right_edge.scale(sign); - - // Create device space quad. - return LayerQuad(left_edge, top_edge, right_edge, bottom_edge).ToQuadF(); -} - -float GetTotalQuadError(const gfx::QuadF* clipped_quad, - const gfx::QuadF* ideal_rect) { - return (clipped_quad->p1() - ideal_rect->p1()).LengthSquared() + - (clipped_quad->p2() - ideal_rect->p2()).LengthSquared() + - (clipped_quad->p3() - ideal_rect->p3()).LengthSquared() + - (clipped_quad->p4() - ideal_rect->p4()).LengthSquared(); -} - -// Attempt to rotate the clipped quad until it lines up the most -// correctly. This is necessary because we check the edges of this -// quad against the expected left/right/top/bottom for anti-aliasing. -void AlignQuadToBoundingBox(gfx::QuadF* clipped_quad) { - auto bounding_quad = gfx::QuadF(clipped_quad->BoundingBox()); - gfx::QuadF best_rotation = *clipped_quad; - float least_error_amount = GetTotalQuadError(clipped_quad, &bounding_quad); - for (size_t i = 1; i < 4; ++i) { - clipped_quad->Realign(1); - float new_error = GetTotalQuadError(clipped_quad, &bounding_quad); - if (new_error < least_error_amount) { - least_error_amount = new_error; - best_rotation = *clipped_quad; - } - } - *clipped_quad = best_rotation; -} - -void InflateAntiAliasingDistances(const gfx::QuadF& quad, - LayerQuad* device_layer_edges, - float edge[24]) { - DCHECK(!quad.BoundingBox().IsEmpty()); - LayerQuad device_layer_bounds(gfx::QuadF(quad.BoundingBox())); - - device_layer_edges->InflateAntiAliasingDistance(); - device_layer_edges->ToFloatArray(edge); - - device_layer_bounds.InflateAntiAliasingDistance(); - device_layer_bounds.ToFloatArray(&edge[12]); -} - -// static -bool GLRenderer::ShouldAntialiasQuad(const gfx::QuadF& device_layer_quad, - bool clipped, - bool force_aa) { - // AAing clipped quads is not supported by the code yet. - if (clipped) - return false; - if (device_layer_quad.BoundingBox().IsEmpty()) - return false; - if (force_aa) - return true; - - bool is_axis_aligned_in_target = device_layer_quad.IsRectilinear(); - bool is_nearest_rect_within_epsilon = - is_axis_aligned_in_target && - gfx::IsNearestRectWithinDistance(device_layer_quad.BoundingBox(), - kAntiAliasingEpsilon); - return !is_nearest_rect_within_epsilon; -} - -// static -void GLRenderer::SetupQuadForClippingAndAntialiasing( - const gfx::Transform& device_transform, - const DrawQuad* quad, - const gfx::QuadF* aa_quad, - const gfx::QuadF* clip_region, - gfx::QuadF* local_quad, - float edge[24]) { - gfx::QuadF rotated_clip; - const gfx::QuadF* local_clip_region = clip_region; - if (local_clip_region) { - rotated_clip = *clip_region; - AlignQuadToBoundingBox(&rotated_clip); - local_clip_region = &rotated_clip; - } - - if (!aa_quad) { - if (local_clip_region) - *local_quad = *local_clip_region; - return; - } - - LayerQuad device_layer_edges(*aa_quad); - InflateAntiAliasingDistances(*aa_quad, &device_layer_edges, edge); - - // If we have a clip region then we are split, and therefore - // by necessity, at least one of our edges is not an external - // one. - bool is_full_rect = quad->visible_rect == quad->rect; - - bool region_contains_all_outside_edges = - is_full_rect && - (is_top(local_clip_region, quad) && is_left(local_clip_region, quad) && - is_bottom(local_clip_region, quad) && is_right(local_clip_region, quad)); - - bool use_aa_on_all_four_edges = - !local_clip_region && region_contains_all_outside_edges; - - gfx::QuadF device_quad; - if (use_aa_on_all_four_edges) { - device_quad = device_layer_edges.ToQuadF(); - } else { - gfx::QuadF tile_quad(local_clip_region - ? *local_clip_region - : gfx::QuadF(gfx::RectF(quad->visible_rect))); - device_quad = GetDeviceQuadWithAntialiasingOnExteriorEdges( - device_layer_edges, device_transform, tile_quad, local_clip_region, - quad); - } - - *local_quad = - cc::MathUtil::InverseMapQuadToLocalSpace(device_transform, device_quad); -} - -// static -void GLRenderer::SetupRenderPassQuadForClippingAndAntialiasing( - const gfx::Transform& device_transform, - const AggregatedRenderPassDrawQuad* quad, - const gfx::QuadF* aa_quad, - const gfx::QuadF* clip_region, - gfx::QuadF* local_quad, - float edge[24]) { - gfx::QuadF rotated_clip; - const gfx::QuadF* local_clip_region = clip_region; - if (local_clip_region) { - rotated_clip = *clip_region; - AlignQuadToBoundingBox(&rotated_clip); - local_clip_region = &rotated_clip; - } - - if (!aa_quad) { - GetScaledRegion(quad->rect, local_clip_region, local_quad); - return; - } - - LayerQuad device_layer_edges(*aa_quad); - InflateAntiAliasingDistances(*aa_quad, &device_layer_edges, edge); - - gfx::QuadF device_quad; - - // Apply anti-aliasing only to the edges that are not being clipped - if (local_clip_region) { - gfx::QuadF tile_quad(gfx::RectF(quad->visible_rect)); - GetScaledRegion(quad->rect, local_clip_region, &tile_quad); - device_quad = GetDeviceQuadWithAntialiasingOnExteriorEdges( - device_layer_edges, device_transform, tile_quad, local_clip_region, - quad); - } else { - device_quad = device_layer_edges.ToQuadF(); - } - - *local_quad = - cc::MathUtil::InverseMapQuadToLocalSpace(device_transform, device_quad); -} - -void GLRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad, - const gfx::QuadF* clip_region) { - gfx::Rect tile_rect = quad->visible_rect; - - SkColor color = quad->color; - float opacity = quad->shared_quad_state->opacity; - - // Early out if alpha is small enough that quad doesn't contribute to output, - // for kSrcOver blend mode. - if (quad->shared_quad_state->blend_mode == SkBlendMode::kSrcOver) { - float alpha = (SkColorGetA(color) * (1.0f / 255.0f)) * opacity; - if (alpha < std::numeric_limits<float>::epsilon() && - quad->ShouldDrawWithBlending() && - quad->shared_quad_state->blend_mode == SkBlendMode::kSrcOver) - return; - } - - gfx::Transform device_transform = - current_frame()->window_matrix * current_frame()->projection_matrix * - quad->shared_quad_state->quad_to_target_transform; - device_transform.FlattenTo2d(); - if (!device_transform.IsInvertible()) - return; - - auto local_quad = gfx::QuadF(gfx::RectF(tile_rect)); - - gfx::QuadF device_layer_quad; - bool use_aa = false; - bool allow_aa = settings_->allow_antialiasing && - !quad->force_anti_aliasing_off && quad->IsEdge(); - - if (allow_aa) { - bool clipped = false; - bool force_aa = false; - device_layer_quad = cc::MathUtil::MapQuad( - device_transform, - gfx::QuadF( - gfx::RectF(quad->shared_quad_state->visible_quad_layer_rect)), - &clipped); - use_aa = ShouldAntialiasQuad(device_layer_quad, clipped, force_aa); - } - - ScopedTimerQuery scoped_timer_query(CompositeTimeTracingEnabled(), gl_, - &timer_queries_, - use_aa ? "kSolidColorAA" : "kSolidColor"); - - float edge[24]; - const gfx::QuadF* aa_quad = use_aa ? &device_layer_quad : nullptr; - SetupQuadForClippingAndAntialiasing(device_transform, quad, aa_quad, - clip_region, &local_quad, edge); - - SetUseProgram(ProgramKey::SolidColor(use_aa ? USE_AA : NO_AA, - tint_gl_composited_content_, - ShouldApplyRoundedCorner(quad)), - CurrentRenderPassColorSpace(), CurrentRenderPassColorSpace()); - - gfx::ColorSpace quad_color_space = gfx::ColorSpace::CreateSRGB(); - SkColor4f color_f = SkColor4f::FromColor(color); - - // Apply color transform if the color space or source and target do not match. - if (quad_color_space != CurrentRenderPassColorSpace()) { - const gfx::ColorTransform* color_transform = - GetColorTransform(quad_color_space, CurrentRenderPassColorSpace()); - gfx::ColorTransform::TriStim col(color_f.fR, color_f.fG, color_f.fB); - color_transform->Transform(&col, 1); - color_f.fR = col.x(); - color_f.fG = col.y(); - color_f.fB = col.z(); - color = color_f.toSkColor(); - } - - // Apply any color matrix that may be present. - if (HasOutputColorMatrix()) { - const SkM44& output_color_matrix = output_surface_->color_matrix(); - const SkV4 color_v{color_f.fR, color_f.fG, color_f.fB, color_f.fA}; - const SkV4 result = output_color_matrix * color_v; - std::copy(result.ptr(), result.ptr() + 4, color_f.vec()); - color = color_f.toSkColor(); - } - - // Try using glClear to draw the solid color quad if possible. This is much - // more performant than executing the shader pipeline. - if (CanUseFastSolidColorDraw(quad) && !use_aa) { - // Pre-multiply the alpha and opacity to get the correct blending in case of - // transparent buffers. glClear does not have any alpha blending stage. - Float4 result = PremultipliedColor(color, opacity); - SkRGBA4f<kPremul_SkAlphaType> color_f_premul; - std::copy(result.data, result.data + 4, color_f_premul.vec()); - - gfx::RectF quad_rect_in_target_f(quad->visible_rect); - - device_transform.TransformRect(&quad_rect_in_target_f); - gfx::Rect quad_rect_in_target = gfx::ToRoundedRect(quad_rect_in_target_f); - - // If we are using partial swap, make sure the new scissor rect is within - // the partial swap bounds. - if (!scissor_rect_.IsEmpty() && is_scissor_enabled_) - quad_rect_in_target.Intersect(scissor_rect_); - - gl_->Enable(GL_SCISSOR_TEST); - gl_->Scissor(quad_rect_in_target.x(), quad_rect_in_target.y(), - quad_rect_in_target.width(), quad_rect_in_target.height()); - - gl_->ClearColor(color_f_premul.fR, color_f_premul.fG, color_f_premul.fB, - color_f_premul.fA); - gl_->Clear(GL_COLOR_BUFFER_BIT); - - // Restore GL scissor state. - if (is_scissor_enabled_) - gl_->Enable(GL_SCISSOR_TEST); - else - gl_->Disable(GL_SCISSOR_TEST); - - gl_->Scissor(scissor_rect_.x(), scissor_rect_.y(), scissor_rect_.width(), - scissor_rect_.height()); - } else { - SetShaderColor(color, opacity); - if (current_program_->rounded_corner_rect_location() != -1) { - SetShaderRoundedCorner( - quad->shared_quad_state->mask_filter_info.rounded_corner_bounds(), - current_frame()->window_matrix * current_frame()->projection_matrix); - } - - if (current_program_->tint_color_matrix_location() != -1) { - auto matrix = - cc::DebugColors::TintCompositedContentColorTransformMatrix(); - gl_->UniformMatrix4fv(current_program_->tint_color_matrix_location(), 1, - false, matrix.data()); - } - - if (use_aa) { - gl_->Uniform3fv(current_program_->edge_location(), 8, edge); - } - - // Enable blending when the quad properties require it or if we decided - // to use antialiasing. - SetBlendEnabled(quad->ShouldDrawWithBlending() || use_aa); - ApplyBlendModeUsingBlendFunc(quad->shared_quad_state->blend_mode); - - // Antialiasing requires a normalized quad, but this could lead to floating - // point precision errors, so only normalize when antialiasing is on. - if (use_aa) { - DrawQuadGeometryWithAA(quad, &local_quad, tile_rect); - } else { - PrepareGeometry(SHARED_BINDING); - SetShaderQuadF(local_quad); - SetShaderMatrix(current_frame()->projection_matrix * - quad->shared_quad_state->quad_to_target_transform); - gl_->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr); - num_triangles_drawn_ += 2; - } - RestoreBlendFuncToDefault(quad->shared_quad_state->blend_mode); - } - - // Add the quad to the region that has been drawn. - AccumulateDrawRects(quad->visible_rect, - quad->shared_quad_state->quad_to_target_transform, - &drawn_rects_); -} - -void GLRenderer::DrawTileQuad(const TileDrawQuad* quad, - const gfx::QuadF* clip_region) { - DrawContentQuad(quad, quad->resource_id(), clip_region); -} - -void GLRenderer::DrawContentQuad(const ContentDrawQuadBase* quad, - ResourceId resource_id, - const gfx::QuadF* clip_region) { - gfx::Transform device_transform = - current_frame()->window_matrix * current_frame()->projection_matrix * - quad->shared_quad_state->quad_to_target_transform; - device_transform.FlattenTo2d(); - - gfx::QuadF device_layer_quad; - bool use_aa = false; - bool allow_aa = settings_->allow_antialiasing && - !quad->force_anti_aliasing_off && quad->IsEdge(); - if (allow_aa) { - bool clipped = false; - bool force_aa = false; - device_layer_quad = cc::MathUtil::MapQuad( - device_transform, - gfx::QuadF( - gfx::RectF(quad->shared_quad_state->visible_quad_layer_rect)), - &clipped); - use_aa = ShouldAntialiasQuad(device_layer_quad, clipped, force_aa); - } - - // TODO(timav): simplify coordinate transformations in DrawContentQuadAA - // similar to the way DrawContentQuadNoAA works and then consider - // combining DrawContentQuadAA and DrawContentQuadNoAA into one method. - if (use_aa) - DrawContentQuadAA(quad, resource_id, device_transform, device_layer_quad, - clip_region); - else - DrawContentQuadNoAA(quad, resource_id, clip_region); - - AccumulateDrawRects(quad->visible_rect, - quad->shared_quad_state->quad_to_target_transform, - &drawn_rects_); -} - -void GLRenderer::DrawContentQuadAA(const ContentDrawQuadBase* quad, - ResourceId resource_id, - const gfx::Transform& device_transform, - const gfx::QuadF& aa_quad, - const gfx::QuadF* clip_region) { - if (!device_transform.IsInvertible()) - return; - - ScopedTimerQuery scoped_timer_query(CompositeTimeTracingEnabled(), gl_, - &timer_queries_, "kTiledContentAA"); - - gfx::Rect tile_rect = quad->visible_rect; - - gfx::RectF tex_coord_rect = cc::MathUtil::ScaleRectProportional( - quad->tex_coord_rect, gfx::RectF(quad->rect), gfx::RectF(tile_rect)); - float tex_to_geom_scale_x = quad->rect.width() / quad->tex_coord_rect.width(); - float tex_to_geom_scale_y = - quad->rect.height() / quad->tex_coord_rect.height(); - - gfx::RectF clamp_geom_rect(tile_rect); - gfx::RectF clamp_tex_rect(tex_coord_rect); - // Clamp texture coordinates to avoid sampling outside the layer - // by deflating the tile region half a texel or half a texel - // minus epsilon for one pixel layers. The resulting clamp region - // is mapped to the unit square by the vertex shader and mapped - // back to normalized texture coordinates by the fragment shader - // after being clamped to 0-1 range. - float tex_clamp_x = - std::min(0.5f, 0.5f * clamp_tex_rect.width() - kAntiAliasingEpsilon); - float tex_clamp_y = - std::min(0.5f, 0.5f * clamp_tex_rect.height() - kAntiAliasingEpsilon); - float geom_clamp_x = - std::min(tex_clamp_x * tex_to_geom_scale_x, - 0.5f * clamp_geom_rect.width() - kAntiAliasingEpsilon); - float geom_clamp_y = - std::min(tex_clamp_y * tex_to_geom_scale_y, - 0.5f * clamp_geom_rect.height() - kAntiAliasingEpsilon); - clamp_geom_rect.Inset(gfx::InsetsF::VH(geom_clamp_y, geom_clamp_x)); - clamp_tex_rect.Inset(gfx::InsetsF::VH(tex_clamp_y, tex_clamp_x)); - - // Map clamping rectangle to unit square. - float vertex_tex_translate_x = -clamp_geom_rect.x() / clamp_geom_rect.width(); - float vertex_tex_translate_y = - -clamp_geom_rect.y() / clamp_geom_rect.height(); - float vertex_tex_scale_x = tile_rect.width() / clamp_geom_rect.width(); - float vertex_tex_scale_y = tile_rect.height() / clamp_geom_rect.height(); - - TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired( - gl_, &highp_threshold_cache_, settings_->highp_threshold_min, - quad->texture_size); - - auto local_quad = gfx::QuadF(gfx::RectF(tile_rect)); - float edge[24]; - SetupQuadForClippingAndAntialiasing(device_transform, quad, &aa_quad, - clip_region, &local_quad, edge); - DisplayResourceProviderGL::ScopedSamplerGL quad_resource_lock( - resource_provider(), resource_id, - quad->nearest_neighbor ? GL_NEAREST : GL_LINEAR); - SamplerType sampler = - SamplerTypeFromTextureTarget(quad_resource_lock.target()); - - float fragment_tex_translate_x = clamp_tex_rect.x(); - float fragment_tex_translate_y = clamp_tex_rect.y(); - float fragment_tex_scale_x = clamp_tex_rect.width(); - float fragment_tex_scale_y = clamp_tex_rect.height(); - - // Map to normalized texture coordinates. - if (sampler != SAMPLER_TYPE_2D_RECT) { - gfx::Size texture_size = quad->texture_size; - DCHECK(!texture_size.IsEmpty()); - fragment_tex_translate_x /= texture_size.width(); - fragment_tex_translate_y /= texture_size.height(); - fragment_tex_scale_x /= texture_size.width(); - fragment_tex_scale_y /= texture_size.height(); - } - - SetUseProgram( - ProgramKey::Tile(tex_coord_precision, sampler, USE_AA, - quad->is_premultiplied ? PREMULTIPLIED_ALPHA - : NON_PREMULTIPLIED_ALPHA, - false, false, tint_gl_composited_content_, - ShouldApplyRoundedCorner(quad)), - quad_resource_lock.color_space(), CurrentRenderPassColorSpace()); - - if (current_program_->tint_color_matrix_location() != -1) { - auto matrix = cc::DebugColors::TintCompositedContentColorTransformMatrix(); - gl_->UniformMatrix4fv(current_program_->tint_color_matrix_location(), 1, - false, matrix.data()); - } - - gl_->Uniform3fv(current_program_->edge_location(), 8, edge); - - gl_->Uniform4f(current_program_->vertex_tex_transform_location(), - vertex_tex_translate_x, vertex_tex_translate_y, - vertex_tex_scale_x, vertex_tex_scale_y); - gl_->Uniform4f(current_program_->fragment_tex_transform_location(), - fragment_tex_translate_x, fragment_tex_translate_y, - fragment_tex_scale_x, fragment_tex_scale_y); - - // Blending is required for antialiasing. - SetBlendEnabled(true); - SetShaderOpacity(quad->shared_quad_state->opacity); - if (current_program_->rounded_corner_rect_location() != -1) { - SetShaderRoundedCorner( - quad->shared_quad_state->mask_filter_info.rounded_corner_bounds(), - current_frame()->window_matrix * current_frame()->projection_matrix); - } - DCHECK(CanApplyBlendModeUsingBlendFunc(quad->shared_quad_state->blend_mode)); - ApplyBlendModeUsingBlendFunc(quad->shared_quad_state->blend_mode); - - // Draw the quad with antialiasing. - DrawQuadGeometryWithAA(quad, &local_quad, tile_rect); - RestoreBlendFuncToDefault(quad->shared_quad_state->blend_mode); -} - -void GLRenderer::DrawContentQuadNoAA(const ContentDrawQuadBase* quad, - ResourceId resource_id, - const gfx::QuadF* clip_region) { - gfx::RectF tex_coord_rect = cc::MathUtil::ScaleRectProportional( - quad->tex_coord_rect, gfx::RectF(quad->rect), - gfx::RectF(quad->visible_rect)); - float tex_to_geom_scale_x = quad->rect.width() / quad->tex_coord_rect.width(); - float tex_to_geom_scale_y = - quad->rect.height() / quad->tex_coord_rect.height(); - - ScopedTimerQuery scoped_timer_query(CompositeTimeTracingEnabled(), gl_, - &timer_queries_, "kTiledContent"); - - bool scaled = (tex_to_geom_scale_x != 1.f || tex_to_geom_scale_y != 1.f); - GLenum filter = (scaled || !quad->shared_quad_state->quad_to_target_transform - .IsIdentityOrIntegerTranslation()) && - !quad->nearest_neighbor - ? GL_LINEAR - : GL_NEAREST; - - DisplayResourceProviderGL::ScopedSamplerGL quad_resource_lock( - resource_provider(), resource_id, filter); - SamplerType sampler = - SamplerTypeFromTextureTarget(quad_resource_lock.target()); - - // Tiles are guaranteed to have been entirely filled except for the - // bottom/right external edge tiles. Because of border texels, any - // internal edge will have uvs that are offset from 0 and 1, so - // clamping to tex_coord_rect in all cases would cause these border - // texels to not be sampled. Therefore, only clamp texture coordinates - // for external edge bottom/right tiles that don't have content all - // the way to the edge and are using bilinear filtering. - gfx::Size texture_size = quad->texture_size; - bool fills_right_edge = - !quad->IsRightEdge() || texture_size.width() == tex_coord_rect.right(); - bool fills_bottom_edge = - !quad->IsBottomEdge() || texture_size.height() == tex_coord_rect.bottom(); - bool has_tex_clamp_rect = - filter == GL_LINEAR && (!fills_right_edge || !fills_bottom_edge); - gfx::SizeF tex_clamp_size(texture_size); - // Clamp from the original tex coord rect, instead of the one that has - // been adjusted by the visible rect. - if (!fills_right_edge) - tex_clamp_size.set_width(quad->tex_coord_rect.right() - 0.5f); - if (!fills_bottom_edge) - tex_clamp_size.set_height(quad->tex_coord_rect.bottom() - 0.5f); - - // Map to normalized texture coordinates. - if (sampler != SAMPLER_TYPE_2D_RECT) { - DCHECK(!texture_size.IsEmpty()); - tex_coord_rect.Scale(1.f / texture_size.width(), - 1.f / texture_size.height()); - tex_clamp_size.Scale(1.f / texture_size.width(), - 1.f / texture_size.height()); - } - - TexCoordPrecision tex_coord_precision = - TexCoordPrecisionRequired(gl_, &highp_threshold_cache_, - settings_->highp_threshold_min, texture_size); - SetUseProgram( - ProgramKey::Tile(tex_coord_precision, sampler, NO_AA, - quad->is_premultiplied ? PREMULTIPLIED_ALPHA - : NON_PREMULTIPLIED_ALPHA, - !quad->ShouldDrawWithBlending(), has_tex_clamp_rect, - tint_gl_composited_content_, - ShouldApplyRoundedCorner(quad)), - quad_resource_lock.color_space(), CurrentRenderPassColorSpace()); - - if (current_program_->tint_color_matrix_location() != -1) { - auto matrix = cc::DebugColors::TintCompositedContentColorTransformMatrix(); - gl_->UniformMatrix4fv(current_program_->tint_color_matrix_location(), 1, - false, matrix.data()); - } - - if (has_tex_clamp_rect) { - gl_->Uniform4f(current_program_->tex_clamp_rect_location(), 0, 0, - tex_clamp_size.width(), tex_clamp_size.height()); - } - gl_->Uniform4f(current_program_->vertex_tex_transform_location(), - tex_coord_rect.x(), tex_coord_rect.y(), tex_coord_rect.width(), - tex_coord_rect.height()); - - DCHECK(CanApplyBlendModeUsingBlendFunc(quad->shared_quad_state->blend_mode)); - SetBlendEnabled(quad->ShouldDrawWithBlending()); - ApplyBlendModeUsingBlendFunc(quad->shared_quad_state->blend_mode); - - SetShaderOpacity(quad->shared_quad_state->opacity); - if (current_program_->rounded_corner_rect_location() != -1) { - SetShaderRoundedCorner( - quad->shared_quad_state->mask_filter_info.rounded_corner_bounds(), - current_frame()->window_matrix * current_frame()->projection_matrix); - } - - // Pass quad coordinates to the uniform in the same order as GeometryBinding - // does, then vertices will match the texture mapping in the vertex buffer. - // The method SetShaderQuadF() changes the order of vertices and so it's - // not used here. - auto tile_quad = gfx::QuadF(gfx::RectF(quad->visible_rect)); - float width = quad->visible_rect.width(); - float height = quad->visible_rect.height(); - auto top_left = gfx::PointF(quad->visible_rect.origin()); - if (clip_region) { - tile_quad = *clip_region; - float gl_uv[8] = { - (tile_quad.p4().x() - top_left.x()) / width, - (tile_quad.p4().y() - top_left.y()) / height, - (tile_quad.p1().x() - top_left.x()) / width, - (tile_quad.p1().y() - top_left.y()) / height, - (tile_quad.p2().x() - top_left.x()) / width, - (tile_quad.p2().y() - top_left.y()) / height, - (tile_quad.p3().x() - top_left.x()) / width, - (tile_quad.p3().y() - top_left.y()) / height, - }; - PrepareGeometry(CLIPPED_BINDING); - clipped_geometry_->InitializeCustomQuadWithUVs( - gfx::QuadF(gfx::RectF(quad->visible_rect)), gl_uv); - } else { - PrepareGeometry(SHARED_BINDING); - } - float gl_quad[8] = { - tile_quad.p4().x(), tile_quad.p4().y(), tile_quad.p1().x(), - tile_quad.p1().y(), tile_quad.p2().x(), tile_quad.p2().y(), - tile_quad.p3().x(), tile_quad.p3().y(), - }; - gl_->Uniform2fv(current_program_->quad_location(), 4, gl_quad); - - SetShaderMatrix(current_frame()->projection_matrix * - quad->shared_quad_state->quad_to_target_transform); - - gl_->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr); - num_triangles_drawn_ += 2; - RestoreBlendFuncToDefault(quad->shared_quad_state->blend_mode); -} - -void GLRenderer::DrawYUVVideoQuad(const YUVVideoDrawQuad* quad, - const gfx::QuadF* clip_region) { - std::string gpu_composite_time_string; - if (!clip_region && quad->rect == quad->visible_rect) - gpu_composite_time_string = "kYuvVideoContent"; - else - gpu_composite_time_string = "kYuvVideoContentClipped"; - ScopedTimerQuery scoped_timer_query(CompositeTimeTracingEnabled(), gl_, - &timer_queries_, - gpu_composite_time_string); - - SetBlendEnabled(quad->ShouldDrawWithBlending()); - - TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired( - gl_, &highp_threshold_cache_, settings_->highp_threshold_min, - quad->shared_quad_state->visible_quad_layer_rect.size()); - YUVAlphaTextureMode alpha_texture_mode = quad->a_plane_resource_id() - ? YUV_HAS_ALPHA_TEXTURE - : YUV_NO_ALPHA_TEXTURE; - UVTextureMode uv_texture_mode = - quad->v_plane_resource_id() == quad->u_plane_resource_id() - ? UV_TEXTURE_MODE_UV - : UV_TEXTURE_MODE_U_V; - - DisplayResourceProviderGL::ScopedSamplerGL y_plane_lock( - resource_provider(), quad->y_plane_resource_id(), GL_TEXTURE1, GL_LINEAR); - DisplayResourceProviderGL::ScopedSamplerGL u_plane_lock( - resource_provider(), quad->u_plane_resource_id(), GL_TEXTURE2, GL_LINEAR); - DCHECK_EQ(y_plane_lock.target(), u_plane_lock.target()); - DCHECK_EQ(y_plane_lock.color_space(), u_plane_lock.color_space()); - - // TODO(ccameron): There are currently two sources of the color space: the - // resource color space and quad->video_color_space. Remove one of them. - gfx::ColorSpace src_color_space = quad->video_color_space; - // Invalid or unspecified color spaces should be treated as REC709. - if (!src_color_space.IsValid()) - src_color_space = gfx::ColorSpace::CreateREC709(); - else - DCHECK_EQ(src_color_space, y_plane_lock.color_space()); - // The source color space should never be RGB. - DCHECK_NE(src_color_space, src_color_space.GetAsFullRangeRGB()); - - gfx::ColorSpace dst_color_space = CurrentRenderPassColorSpace(); - -#if BUILDFLAG(IS_WIN) - // Force sRGB output on Windows for overlay candidate video quads to match - // DirectComposition behavior in case these switch between overlays and - // compositing. See https://crbug.com/811118 for details. - // Currently if HDR is supported, OverlayProcessor doesn't promote HDR video - // frame as overlay candidate. So it's unnecessary to worry about the - // compositing-overlay switch here. In addition drawing a HDR video using sRGB - // can cancel the advantages of HDR. - const bool supports_dc_layers = - output_surface_->capabilities().supports_dc_layers; - if (supports_dc_layers && !src_color_space.IsHDR() && - resource_provider()->IsOverlayCandidate(quad->y_plane_resource_id())) { - DCHECK( - resource_provider()->IsOverlayCandidate(quad->u_plane_resource_id())); - dst_color_space = gfx::ColorSpace::CreateSRGB(); - } -#endif - - // TODO(jbauman): Use absl::optional when available. - std::unique_ptr<DisplayResourceProviderGL::ScopedSamplerGL> v_plane_lock; - if (uv_texture_mode == UV_TEXTURE_MODE_U_V) { - v_plane_lock = std::make_unique<DisplayResourceProviderGL::ScopedSamplerGL>( - resource_provider(), quad->v_plane_resource_id(), GL_TEXTURE3, - GL_LINEAR); - DCHECK_EQ(y_plane_lock.target(), v_plane_lock->target()); - DCHECK_EQ(y_plane_lock.color_space(), v_plane_lock->color_space()); - } - std::unique_ptr<DisplayResourceProviderGL::ScopedSamplerGL> a_plane_lock; - if (alpha_texture_mode == YUV_HAS_ALPHA_TEXTURE) { - a_plane_lock = std::make_unique<DisplayResourceProviderGL::ScopedSamplerGL>( - resource_provider(), quad->a_plane_resource_id(), GL_TEXTURE4, - GL_LINEAR); - DCHECK_EQ(y_plane_lock.target(), a_plane_lock->target()); - } - - // All planes must have the same sampler type. - SamplerType sampler = SamplerTypeFromTextureTarget(y_plane_lock.target()); - - SetUseProgram( - ProgramKey::YUVVideo(tex_coord_precision, sampler, alpha_texture_mode, - uv_texture_mode, tint_gl_composited_content_, - ShouldApplyRoundedCorner(quad)), - src_color_space, dst_color_space, /*adjust_src_white_level=*/true); - - if (current_program_->tint_color_matrix_location() != -1) { - auto matrix = cc::DebugColors::TintCompositedContentColorTransformMatrix(); - gl_->UniformMatrix4fv(current_program_->tint_color_matrix_location(), 1, - false, matrix.data()); - } - - if (current_program_->rounded_corner_rect_location() != -1) { - SetShaderRoundedCorner( - quad->shared_quad_state->mask_filter_info.rounded_corner_bounds(), - current_frame()->window_matrix * current_frame()->projection_matrix); - } - - gfx::SizeF ya_tex_scale(1.0f, 1.0f); - gfx::SizeF uv_tex_scale(1.0f, 1.0f); - if (sampler != SAMPLER_TYPE_2D_RECT) { - DCHECK(!quad->ya_tex_size.IsEmpty()); - DCHECK(!quad->uv_tex_size.IsEmpty()); - ya_tex_scale = gfx::SizeF(1.0f / quad->ya_tex_size.width(), - 1.0f / quad->ya_tex_size.height()); - uv_tex_scale = gfx::SizeF(1.0f / quad->uv_tex_size.width(), - 1.0f / quad->uv_tex_size.height()); - } - - float ya_vertex_tex_translate_x = - quad->ya_tex_coord_rect.x() * ya_tex_scale.width(); - float ya_vertex_tex_translate_y = - quad->ya_tex_coord_rect.y() * ya_tex_scale.height(); - float ya_vertex_tex_scale_x = - quad->ya_tex_coord_rect.width() * ya_tex_scale.width(); - float ya_vertex_tex_scale_y = - quad->ya_tex_coord_rect.height() * ya_tex_scale.height(); - - float uv_vertex_tex_translate_x = - quad->uv_tex_coord_rect.x() * uv_tex_scale.width(); - float uv_vertex_tex_translate_y = - quad->uv_tex_coord_rect.y() * uv_tex_scale.height(); - float uv_vertex_tex_scale_x = - quad->uv_tex_coord_rect.width() * uv_tex_scale.width(); - float uv_vertex_tex_scale_y = - quad->uv_tex_coord_rect.height() * uv_tex_scale.height(); - - gl_->Uniform2f(current_program_->ya_tex_scale_location(), - ya_vertex_tex_scale_x, ya_vertex_tex_scale_y); - gl_->Uniform2f(current_program_->ya_tex_offset_location(), - ya_vertex_tex_translate_x, ya_vertex_tex_translate_y); - gl_->Uniform2f(current_program_->uv_tex_scale_location(), - uv_vertex_tex_scale_x, uv_vertex_tex_scale_y); - gl_->Uniform2f(current_program_->uv_tex_offset_location(), - uv_vertex_tex_translate_x, uv_vertex_tex_translate_y); - - gfx::RectF ya_clamp_rect(ya_vertex_tex_translate_x, ya_vertex_tex_translate_y, - ya_vertex_tex_scale_x, ya_vertex_tex_scale_y); - ya_clamp_rect.Inset(gfx::InsetsF::VH(0.5f * ya_tex_scale.height(), - 0.5f * ya_tex_scale.width())); - gfx::RectF uv_clamp_rect(uv_vertex_tex_translate_x, uv_vertex_tex_translate_y, - uv_vertex_tex_scale_x, uv_vertex_tex_scale_y); - uv_clamp_rect.Inset(gfx::InsetsF::VH(0.5f * uv_tex_scale.height(), - 0.5f * uv_tex_scale.width())); - gl_->Uniform4f(current_program_->ya_clamp_rect_location(), ya_clamp_rect.x(), - ya_clamp_rect.y(), ya_clamp_rect.right(), - ya_clamp_rect.bottom()); - gl_->Uniform4f(current_program_->uv_clamp_rect_location(), uv_clamp_rect.x(), - uv_clamp_rect.y(), uv_clamp_rect.right(), - uv_clamp_rect.bottom()); - - gl_->Uniform1i(current_program_->y_texture_location(), 1); - if (uv_texture_mode == UV_TEXTURE_MODE_UV) { - gl_->Uniform1i(current_program_->uv_texture_location(), 2); - } else { - gl_->Uniform1i(current_program_->u_texture_location(), 2); - gl_->Uniform1i(current_program_->v_texture_location(), 3); - } - if (alpha_texture_mode == YUV_HAS_ALPHA_TEXTURE) - gl_->Uniform1i(current_program_->a_texture_location(), 4); - - gl_->Uniform1f(current_program_->resource_multiplier_location(), - quad->resource_multiplier); - gl_->Uniform1f(current_program_->resource_offset_location(), - quad->resource_offset); - - // The transform and vertex data are used to figure out the extents that the - // un-antialiased quad should have and which vertex this is and the float - // quad passed in via uniform is the actual geometry that gets used to draw - // it. This is why this centered rect is used and not the original quad_rect. - auto tile_rect = gfx::RectF(quad->rect); - - SetShaderOpacity(quad->shared_quad_state->opacity); - if (!clip_region && quad->rect == quad->visible_rect) { - DrawQuadGeometry(current_frame()->projection_matrix, - quad->shared_quad_state->quad_to_target_transform, - tile_rect); - } else { - gfx::QuadF region_quad = - clip_region ? *clip_region : gfx::QuadF(gfx::RectF(quad->visible_rect)); - float uvs[8] = {0}; - GetScaledUVs(quad->rect, ®ion_quad, uvs); - region_quad.Scale(1.0f / tile_rect.width(), 1.0f / tile_rect.height()); - region_quad -= gfx::Vector2dF(0.5f, 0.5f); - DrawQuadGeometryClippedByQuadF( - quad->shared_quad_state->quad_to_target_transform, tile_rect, - region_quad, uvs); - } - - // Track the region in the current target surface that has been drawn to. - AccumulateDrawRects(quad->visible_rect, - quad->shared_quad_state->quad_to_target_transform, - &drawn_rects_); -} - -void GLRenderer::DrawStreamVideoQuad(const StreamVideoDrawQuad* quad, - const gfx::QuadF* clip_region) { - std::string gpu_composite_time_string; - if (!clip_region && quad->rect == quad->visible_rect) { - gpu_composite_time_string = "kStreamVideoContent"; - } else { - gpu_composite_time_string = "kStreamVideoContentClipped"; - } - ScopedTimerQuery scoped_timer_query(CompositeTimeTracingEnabled(), gl_, - &timer_queries_, - gpu_composite_time_string); - SetBlendEnabled(quad->ShouldDrawWithBlending()); - - DCHECK(output_surface_->context_provider() - ->ContextCapabilities() - .egl_image_external); - - TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired( - gl_, &highp_threshold_cache_, settings_->highp_threshold_min, - quad->shared_quad_state->visible_quad_layer_rect.size()); - - DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider(), - quad->resource_id()); - - SetUseProgram(ProgramKey::VideoStream(tex_coord_precision, - ShouldApplyRoundedCorner(quad)), - lock.color_space(), CurrentRenderPassColorSpace()); - - DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(gl_)); - gl_->BindTexture(GL_TEXTURE_EXTERNAL_OES, lock.texture_id()); - - static float gl_matrix[16]; - gfx::Transform matrix; - matrix.Scale(quad->uv_bottom_right.x() - quad->uv_top_left.x(), - quad->uv_bottom_right.y() - quad->uv_top_left.y()); - matrix.Translate(quad->uv_top_left.x(), quad->uv_top_left.y()); - ToGLMatrix(&gl_matrix[0], matrix); - gl_->UniformMatrix4fv(current_program_->tex_matrix_location(), 1, false, - gl_matrix); - - SetShaderOpacity(quad->shared_quad_state->opacity); - if (current_program_->rounded_corner_rect_location() != -1) { - SetShaderRoundedCorner( - quad->shared_quad_state->mask_filter_info.rounded_corner_bounds(), - current_frame()->window_matrix * current_frame()->projection_matrix); - } - gfx::Size texture_size = lock.size(); - gfx::RectF uv_visible_rect(quad->uv_top_left.x(), quad->uv_top_left.y(), - quad->uv_bottom_right.x() - quad->uv_top_left.x(), - quad->uv_bottom_right.y() - quad->uv_top_left.y()); - const SamplerType sampler = SamplerTypeFromTextureTarget(lock.target()); - Float4 tex_clamp_rect = UVClampRect(uv_visible_rect, texture_size, sampler); - gl_->Uniform4f(current_program_->tex_clamp_rect_location(), - tex_clamp_rect.data[0], tex_clamp_rect.data[1], - tex_clamp_rect.data[2], tex_clamp_rect.data[3]); - - auto tile_rect = gfx::RectF(quad->rect); - - if (!clip_region && quad->rect == quad->visible_rect) { - DrawQuadGeometry(current_frame()->projection_matrix, - quad->shared_quad_state->quad_to_target_transform, - tile_rect); - } else { - gfx::QuadF region_quad = - clip_region ? *clip_region : gfx::QuadF(gfx::RectF(quad->visible_rect)); - float uvs[8] = {0}; - GetScaledUVs(quad->rect, ®ion_quad, uvs); - region_quad.Scale(1.0f / tile_rect.width(), 1.0f / tile_rect.height()); - region_quad -= gfx::Vector2dF(0.5f, 0.5f); - DrawQuadGeometryClippedByQuadF( - quad->shared_quad_state->quad_to_target_transform, tile_rect, - region_quad, uvs); - } - - AccumulateDrawRects(quad->visible_rect, - quad->shared_quad_state->quad_to_target_transform, - &drawn_rects_); -} - -void GLRenderer::FlushTextureQuadCache(BoundGeometry flush_binding) { - // Check to see if we have anything to draw. - if (draw_cache_.is_empty) - return; - ScopedTimerQuery scoped_timer_query(CompositeTimeTracingEnabled(), gl_, - &timer_queries_, "kTextureContentFlush"); - - PrepareGeometry(flush_binding); - - // Set the correct blending mode. - SetBlendEnabled(draw_cache_.needs_blending); - - // Assume the current active textures is 0. - DisplayResourceProviderGL::ScopedSamplerGL locked_quad( - resource_provider(), draw_cache_.resource_id, - draw_cache_.nearest_neighbor ? GL_NEAREST : GL_LINEAR); - - // Bind the program to the GL state. - SetUseProgram(draw_cache_.program_key, locked_quad.color_space(), - CurrentRenderPassColorSpace(), - /*adjust_src_white_level=*/draw_cache_.is_video_frame, - locked_quad.hdr_metadata()); - - if (current_program_->rounded_corner_rect_location() != -1) { - SetShaderRoundedCorner( - draw_cache_.mask_filter_info.rounded_corner_bounds(), - current_frame()->window_matrix * current_frame()->projection_matrix); - } - - DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(gl_)); - gl_->BindTexture(locked_quad.target(), locked_quad.texture_id()); - - static_assert(sizeof(Float4) == 4 * sizeof(float), - "Float4 struct should be densely packed"); - static_assert(sizeof(Float16) == 16 * sizeof(float), - "Float16 struct should be densely packed"); - - // Upload the tranforms for both points and uvs. - gl_->UniformMatrix4fv( - current_program_->matrix_location(), - static_cast<int>(draw_cache_.matrix_data.size()), false, - reinterpret_cast<float*>(&draw_cache_.matrix_data.front())); - gl_->Uniform4fv(current_program_->vertex_tex_transform_location(), - static_cast<int>(draw_cache_.uv_xform_data.size()), - reinterpret_cast<float*>(&draw_cache_.uv_xform_data.front())); - - if (current_program_->tint_color_matrix_location() != -1) { - auto matrix = cc::DebugColors::TintCompositedContentColorTransformMatrix(); - gl_->UniformMatrix4fv(current_program_->tint_color_matrix_location(), 1, - false, matrix.data()); - } - - if (current_program_->tex_clamp_rect_location() != -1) { - // Draw batching is not allowed with texture clamping. - DCHECK_EQ(1u, draw_cache_.matrix_data.size()); - gl_->Uniform4f(current_program_->tex_clamp_rect_location(), - draw_cache_.tex_clamp_rect_data.data[0], - draw_cache_.tex_clamp_rect_data.data[1], - draw_cache_.tex_clamp_rect_data.data[2], - draw_cache_.tex_clamp_rect_data.data[3]); - } - - if (draw_cache_.background_color != SK_ColorTRANSPARENT) { - Float4 background_color = - PremultipliedColor(draw_cache_.background_color, 1.f); - gl_->Uniform4fv(current_program_->background_color_location(), 1, - background_color.data); - } - - gl_->Uniform1fv( - current_program_->vertex_opacity_location(), - static_cast<int>(draw_cache_.vertex_opacity_data.size()), - static_cast<float*>(&draw_cache_.vertex_opacity_data.front())); - - DCHECK_LE(draw_cache_.matrix_data.size(), - static_cast<size_t>(std::numeric_limits<int>::max()) / 6u); - - // Draw the quads! - gl_->DrawElements(GL_TRIANGLES, - 6 * static_cast<int>(draw_cache_.matrix_data.size()), - GL_UNSIGNED_SHORT, nullptr); - num_triangles_drawn_ += 2 * static_cast<int>(draw_cache_.matrix_data.size()); - - // Clear the cache. - draw_cache_.is_empty = true; - draw_cache_.resource_id = kInvalidResourceId; - draw_cache_.uv_xform_data.resize(0); - draw_cache_.vertex_opacity_data.resize(0); - draw_cache_.matrix_data.resize(0); - draw_cache_.tex_clamp_rect_data = Float4(); - draw_cache_.is_video_frame = false; - - // If we had a clipped binding, prepare the shared binding for the - // next inserts. - if (flush_binding == CLIPPED_BINDING) { - PrepareGeometry(SHARED_BINDING); - } -} - -void GLRenderer::EnqueueTextureQuad(const TextureDrawQuad* quad, - const gfx::QuadF* clip_region) { - // If we have a clip_region then we have to render the next quad - // with dynamic geometry, therefore we must flush all pending - // texture quads. - if (clip_region) { - // We send in false here because we want to flush what's currently in the - // queue using the shared_geometry and not clipped_geometry - FlushTextureQuadCache(SHARED_BINDING); - } - - DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider(), - quad->resource_id()); - // ScopedReadLockGL contains the correct texture size, even when - // quad->resource_size_in_pixels() is empty. - const gfx::Size texture_size = lock.size(); - TexCoordPrecision tex_coord_precision = - TexCoordPrecisionRequired(gl_, &highp_threshold_cache_, - settings_->highp_threshold_min, texture_size); - - const SamplerType sampler = SamplerTypeFromTextureTarget(lock.target()); - - bool need_tex_clamp_rect = !quad->resource_size_in_pixels().IsEmpty() && - (quad->uv_top_left != gfx::PointF(0, 0) || - quad->uv_bottom_right != gfx::PointF(1, 1)); - - ProgramKey program_key = ProgramKey::Texture( - tex_coord_precision, sampler, - quad->premultiplied_alpha ? PREMULTIPLIED_ALPHA : NON_PREMULTIPLIED_ALPHA, - quad->background_color != SK_ColorTRANSPARENT, need_tex_clamp_rect, - tint_gl_composited_content_, ShouldApplyRoundedCorner(quad)); - ResourceId resource_id = quad->resource_id(); - - size_t max_quads = StaticGeometryBinding::NUM_QUADS; - if (draw_cache_.is_empty || draw_cache_.program_key != program_key || - draw_cache_.resource_id != resource_id || - draw_cache_.needs_blending != quad->ShouldDrawWithBlending() || - draw_cache_.nearest_neighbor != quad->nearest_neighbor || - draw_cache_.background_color != quad->background_color || - draw_cache_.mask_filter_info != - quad->shared_quad_state->mask_filter_info || - draw_cache_.matrix_data.size() >= max_quads || - draw_cache_.is_video_frame != quad->is_video_frame) { - FlushTextureQuadCache(SHARED_BINDING); - draw_cache_.is_empty = false; - draw_cache_.program_key = program_key; - draw_cache_.resource_id = resource_id; - draw_cache_.needs_blending = quad->ShouldDrawWithBlending(); - draw_cache_.nearest_neighbor = quad->nearest_neighbor; - draw_cache_.background_color = quad->background_color; - draw_cache_.mask_filter_info = quad->shared_quad_state->mask_filter_info; - draw_cache_.is_video_frame = quad->is_video_frame; - } - - // Generate the uv-transform - auto uv_transform = UVTransform(quad); - if (sampler == SAMPLER_TYPE_2D_RECT) { - // Un-normalize the texture coordiantes for rectangle targets. - uv_transform.data[0] *= texture_size.width(); - uv_transform.data[2] *= texture_size.width(); - uv_transform.data[1] *= texture_size.height(); - uv_transform.data[3] *= texture_size.height(); - } - draw_cache_.uv_xform_data.push_back(uv_transform); - - if (need_tex_clamp_rect) { - DCHECK_EQ(1u, draw_cache_.uv_xform_data.size()); - DCHECK_EQ(texture_size.ToString(), - quad->resource_size_in_pixels().ToString()); - DCHECK(!texture_size.IsEmpty()); - gfx::RectF uv_visible_rect( - quad->uv_top_left.x(), quad->uv_top_left.y(), - quad->uv_bottom_right.x() - quad->uv_top_left.x(), - quad->uv_bottom_right.y() - quad->uv_top_left.y()); - Float4 tex_clamp_rect = UVClampRect(uv_visible_rect, texture_size, sampler); - draw_cache_.tex_clamp_rect_data = tex_clamp_rect; - } - - // Generate the vertex opacity - const float opacity = quad->shared_quad_state->opacity; - draw_cache_.vertex_opacity_data.push_back(quad->vertex_opacity[0] * opacity); - draw_cache_.vertex_opacity_data.push_back(quad->vertex_opacity[1] * opacity); - draw_cache_.vertex_opacity_data.push_back(quad->vertex_opacity[2] * opacity); - draw_cache_.vertex_opacity_data.push_back(quad->vertex_opacity[3] * opacity); - - // Generate the transform matrix - gfx::Transform quad_rect_matrix; - QuadRectTransform(&quad_rect_matrix, - quad->shared_quad_state->quad_to_target_transform, - gfx::RectF(quad->visible_rect)); - quad_rect_matrix = current_frame()->projection_matrix * quad_rect_matrix; - - Float16 m; - quad_rect_matrix.matrix().getColMajor(m.data); - draw_cache_.matrix_data.push_back(m); - - // Track the region in the current target surface that has been drawn to. - AccumulateDrawRects(quad->visible_rect, - quad->shared_quad_state->quad_to_target_transform, - &drawn_rects_); - - if (clip_region) { - DCHECK_EQ(quad->rect, quad->visible_rect); - gfx::QuadF scaled_region; - if (!GetScaledRegion(quad->rect, clip_region, &scaled_region)) { - scaled_region = SharedGeometryQuad().BoundingBox(); - } - // Both the scaled region and the SharedGeomtryQuad are in the space - // -0.5->0.5. We need to move that to the space 0->1. - float uv[8]; - uv[0] = scaled_region.p1().x() + 0.5f; - uv[1] = scaled_region.p1().y() + 0.5f; - uv[2] = scaled_region.p2().x() + 0.5f; - uv[3] = scaled_region.p2().y() + 0.5f; - uv[4] = scaled_region.p3().x() + 0.5f; - uv[5] = scaled_region.p3().y() + 0.5f; - uv[6] = scaled_region.p4().x() + 0.5f; - uv[7] = scaled_region.p4().y() + 0.5f; - PrepareGeometry(CLIPPED_BINDING); - clipped_geometry_->InitializeCustomQuadWithUVs(scaled_region, uv); - FlushTextureQuadCache(CLIPPED_BINDING); - } else if (need_tex_clamp_rect) { - FlushTextureQuadCache(SHARED_BINDING); - } -} - -void GLRenderer::FinishDrawingFrame() { - if (use_sync_query_) { - sync_queries_.EndCurrentFrame(); - } - - swap_buffer_rect_.Union(current_frame()->root_damage_rect); - - if (use_swap_with_bounds_) - swap_content_bounds_ = current_frame()->root_content_bounds; - - copier_.FreeUnusedCachedResources(); - - current_framebuffer_texture_ = nullptr; - - gl_->Disable(GL_BLEND); - blend_shadow_ = false; - - // Schedule output surface as overlay first to preserve existing ordering - // semantics during overlay refactoring. - ScheduleOutputSurfaceAsOverlay(); - -#if BUILDFLAG(IS_ANDROID) || defined(USE_OZONE) - ScheduleOverlays(); -#elif BUILDFLAG(IS_APPLE) - ScheduleCALayers(); -#elif BUILDFLAG(IS_WIN) - ScheduleDCLayers(); -#endif - - TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("viz.triangles"), "Triangles Drawn", - num_triangles_drawn_); - - // Mark the end of batched read of shared images. - gl_->EndBatchReadAccessSharedImageCHROMIUM(); -} - -bool GLRenderer::OverdrawTracingEnabled() { - // Only collect trace data if we select viz.overdraw. - bool tracing_enabled; - TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("viz.overdraw"), - &tracing_enabled); - // ARB_occlusion_query is required for tracing. - // Trace only the root render pass. - return tracing_enabled && use_occlusion_query_ && - current_frame()->current_render_pass == - current_frame()->root_render_pass; -} - -bool GLRenderer::CompositeTimeTracingEnabled() { - bool tracing_enabled; - TRACE_EVENT_CATEGORY_GROUP_ENABLED( - TRACE_DISABLED_BY_DEFAULT("viz.gpu_composite_time"), &tracing_enabled); - - return tracing_enabled && use_timer_query_; -} - -void GLRenderer::AddCompositeTimeTraces(base::TimeTicks ready_timestamp) { - DCHECK(CompositeTimeTracingEnabled()); - DCHECK_EQ(timer_queries_.front().first, kTimerQueryDummy); - - std::size_t count = 0; - uint64_t duration = 0; - - // List of queries to delete after their results are retrieved. - std::vector<unsigned> queries_to_delete; - - // Queue of durations per draw call. The |second| in the pair represents the - // draw call type as string. - base::queue<std::pair<uint64_t, std::string>> durations; - - // Pop the fence query as it does not represent a timer query. - timer_queries_.pop(); - - // Initialize |start_time_ticks| as the end timestamp and walk backwards to - // find the actual timestamp. - base::TimeTicks start_time_ticks = ready_timestamp; - - while (timer_queries_.size() && - timer_queries_.front().first != kTimerQueryDummy) { - count++; - gl_->GetQueryObjectui64vEXT(timer_queries_.front().first, - GL_QUERY_RESULT_EXT, &duration); - durations.emplace(duration, timer_queries_.front().second); - queries_to_delete.push_back(timer_queries_.front().first); - timer_queries_.pop(); - start_time_ticks -= base::Nanoseconds(duration); - } - - // Delete all timer queries for which results have been retrieved. - gl_->DeleteQueriesEXT(count, queries_to_delete.data()); - - base::TimeDelta unique_id_delta = ready_timestamp - start_time_ticks; - const int trace_unique_id = unique_id_delta.InMilliseconds() * count; - - TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0( - TRACE_DISABLED_BY_DEFAULT("viz.gpu_composite_time"), "Composite Time", - TRACE_ID_LOCAL(trace_unique_id), start_time_ticks); - - while (!durations.empty()) { - duration = durations.front().first; - - // |duration| may be set to 0 if the timer query result was unavailable in - // |GetQueryObjectui64vEXT| function call. - if (!duration) { - durations.pop(); - continue; - } - TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0( - TRACE_DISABLED_BY_DEFAULT("viz.gpu_composite_time"), "Composite Time", - TRACE_ID_LOCAL(trace_unique_id), durations.front().second.c_str(), - start_time_ticks); - start_time_ticks += base::Nanoseconds(duration); - durations.pop(); - } - - TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP0( - TRACE_DISABLED_BY_DEFAULT("viz.gpu_composite_time"), "Composite Time", - TRACE_ID_LOCAL(trace_unique_id), ready_timestamp); -} - -void GLRenderer::FinishDrawingQuadList() { - FlushTextureQuadCache(SHARED_BINDING); - if (occlusion_query_) { - // 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. - 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( - occlusion_query_, base::BindOnce(&GLRenderer::ProcessOverdrawFeedback, - weak_ptr_factory_.GetWeakPtr(), - surface_area, occlusion_query_)); - occlusion_query_ = 0; - } -} - -void GLRenderer::GenerateMipmap() { - DCHECK(current_framebuffer_texture_); - current_framebuffer_texture_->set_generate_mipmap(); -} - -bool GLRenderer::FlippedFramebuffer() const { -#if BUILDFLAG(IS_APPLE) - if (force_drawing_frame_framebuffer_unflipped_) - return false; -#endif - if (current_frame()->current_render_pass != current_frame()->root_render_pass) - return true; - return FlippedRootFramebuffer(); -} - -bool GLRenderer::FlippedRootFramebuffer() const { - // GL is normally flipped, so a flipped output results in an unflipping. - return output_surface_->capabilities().output_surface_origin == - gfx::SurfaceOrigin::kBottomLeft; -} - -void GLRenderer::EnsureScissorTestEnabled() { - if (is_scissor_enabled_) - return; - - FlushTextureQuadCache(SHARED_BINDING); - gl_->Enable(GL_SCISSOR_TEST); - is_scissor_enabled_ = true; -} - -void GLRenderer::EnsureScissorTestDisabled() { - if (!is_scissor_enabled_) - return; - - FlushTextureQuadCache(SHARED_BINDING); - gl_->Disable(GL_SCISSOR_TEST); - is_scissor_enabled_ = false; -} - -void GLRenderer::CopyDrawnRenderPass( - const copy_output::RenderPassGeometry& geometry, - std::unique_ptr<CopyOutputRequest> request) { - TRACE_EVENT0("viz", "GLRenderer::CopyDrawnRenderPass"); - - GLuint framebuffer_texture = 0; - gfx::Size framebuffer_texture_size; - if (current_framebuffer_texture_) { - framebuffer_texture = current_framebuffer_texture_->id(); - framebuffer_texture_size = current_framebuffer_texture_->size(); - } - copier_.CopyFromTextureOrFramebuffer( - std::move(request), geometry, GetFramebufferCopyTextureFormat(), - framebuffer_texture, framebuffer_texture_size, FlippedFramebuffer(), - CurrentRenderPassColorSpace()); - - // The copier modified texture/framebuffer bindings, shader programs, and - // other GL state; and so this must be restored before continuing. - RestoreGLState(); - - // CopyDrawnRenderPass() can change the binding of the framebuffer target as - // a part of its usual scaling and readback operations. It will break next - // CopyDrawnRenderPass() call for the root render pass. Therefore, make sure - // to restore the correct framebuffer between readbacks. (Even if it did - // not, a Mac-specific bug requires this workaround: http://crbug.com/99393) - const auto* render_pass = current_frame()->current_render_pass; - if (render_pass == current_frame()->root_render_pass) - BindFramebufferToOutputSurface(); -} - -void GLRenderer::ToGLMatrix(float* gl_matrix, const gfx::Transform& transform) { - transform.matrix().getColMajor(gl_matrix); -} - -void GLRenderer::SetShaderQuadF(const gfx::QuadF& quad) { - if (!current_program_ || current_program_->quad_location() == -1) - return; - float gl_quad[8]; - gl_quad[0] = quad.p1().x(); - gl_quad[1] = quad.p1().y(); - gl_quad[2] = quad.p2().x(); - gl_quad[3] = quad.p2().y(); - gl_quad[4] = quad.p3().x(); - gl_quad[5] = quad.p3().y(); - gl_quad[6] = quad.p4().x(); - gl_quad[7] = quad.p4().y(); - gl_->Uniform2fv(current_program_->quad_location(), 4, gl_quad); -} - -void GLRenderer::SetShaderOpacity(float opacity) { - if (!current_program_ || current_program_->alpha_location() == -1) - return; - gl_->Uniform1f(current_program_->alpha_location(), opacity); -} - -void GLRenderer::SetShaderMatrix(const gfx::Transform& transform) { - if (!current_program_ || current_program_->matrix_location() == -1) - return; - float gl_matrix[16]; - ToGLMatrix(gl_matrix, transform); - gl_->UniformMatrix4fv(current_program_->matrix_location(), 1, false, - gl_matrix); -} - -void GLRenderer::SetShaderColor(SkColor color, float opacity) { - if (!current_program_ || current_program_->color_location() == -1) - return; - Float4 float_color = PremultipliedColor(color, opacity); - gl_->Uniform4fv(current_program_->color_location(), 1, float_color.data); -} - -void GLRenderer::SetStencilEnabled(bool enabled) { - if (enabled == stencil_shadow_) - return; - - if (enabled) - gl_->Enable(GL_STENCIL_TEST); - else - gl_->Disable(GL_STENCIL_TEST); - stencil_shadow_ = enabled; -} - -void GLRenderer::SetBlendEnabled(bool enabled) { - if (enabled == blend_shadow_) - return; - - if (enabled) - gl_->Enable(GL_BLEND); - else - gl_->Disable(GL_BLEND); - blend_shadow_ = enabled; -} - -void GLRenderer::SetShaderRoundedCorner( - const gfx::RRectF& rounded_corner_bounds, - const gfx::Transform& screen_transform) { - DCHECK(current_program_); - DCHECK(!rounded_corner_bounds.IsEmpty()); - DCHECK_NE(current_program_->rounded_corner_rect_location(), -1); - DCHECK_NE(current_program_->rounded_corner_radius_location(), -1); - DCHECK(screen_transform.IsScaleOrTranslation()); - - const gfx::Vector2dF& translate = screen_transform.To2dTranslation(); - const gfx::Vector2dF& scale = screen_transform.To2dScale(); - gfx::RRectF bounds_in_screen = rounded_corner_bounds; - bounds_in_screen.Scale(scale.x(), scale.y()); - bounds_in_screen.Offset(translate.x(), translate.y()); - - gfx::RectF rect = bounds_in_screen.rect(); - - gl_->Uniform4f(current_program_->rounded_corner_rect_location(), rect.x(), - rect.y(), rect.width(), rect.height()); - gl_->Uniform4f( - current_program_->rounded_corner_radius_location(), - bounds_in_screen.GetCornerRadii(gfx::RRectF::Corner::kUpperLeft).x(), - bounds_in_screen.GetCornerRadii(gfx::RRectF::Corner::kUpperRight).x(), - bounds_in_screen.GetCornerRadii(gfx::RRectF::Corner::kLowerRight).x(), - bounds_in_screen.GetCornerRadii(gfx::RRectF::Corner::kLowerLeft).x()); -} - -void GLRenderer::DrawQuadGeometryClippedByQuadF( - const gfx::Transform& draw_transform, - const gfx::RectF& quad_rect, - const gfx::QuadF& clipping_region_quad, - const float* uvs) { - PrepareGeometry(CLIPPED_BINDING); - if (uvs) { - clipped_geometry_->InitializeCustomQuadWithUVs(clipping_region_quad, uvs); - } else { - clipped_geometry_->InitializeCustomQuad(clipping_region_quad); - } - gfx::Transform quad_rect_matrix; - QuadRectTransform(&quad_rect_matrix, draw_transform, quad_rect); - SetShaderMatrix(current_frame()->projection_matrix * quad_rect_matrix); - - gl_->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, - reinterpret_cast<const void*>(0)); - num_triangles_drawn_ += 2; -} - -void GLRenderer::DrawQuadGeometry(const gfx::Transform& projection_matrix, - const gfx::Transform& draw_transform, - const gfx::RectF& quad_rect) { - PrepareGeometry(SHARED_BINDING); - gfx::Transform quad_rect_matrix; - QuadRectTransform(&quad_rect_matrix, draw_transform, quad_rect); - SetShaderMatrix(projection_matrix * quad_rect_matrix); - - gl_->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr); - num_triangles_drawn_ += 2; -} - -void GLRenderer::DrawQuadGeometryWithAA(const DrawQuad* quad, - gfx::QuadF* local_quad, - const gfx::Rect& tile_rect) { - DCHECK(local_quad); - // Normalize to tile_rect. - local_quad->Scale(1.0f / tile_rect.width(), 1.0f / tile_rect.height()); - - SetShaderQuadF(*local_quad); - - // The transform and vertex data are used to figure out the extents that the - // un-antialiased quad should have and which vertex this is and the float - // quad passed in via uniform is the actual geometry that gets used to draw - // it. This is why this centered rect is used and not the original quad_rect. - DrawQuadGeometry(current_frame()->projection_matrix, - quad->shared_quad_state->quad_to_target_transform, - CenteredRect(tile_rect)); -} - -void GLRenderer::SwapBuffers(SwapFrameData swap_frame_data) { - DCHECK(visible_); - - TRACE_EVENT0("viz", "GLRenderer::SwapBuffers"); - // We're done! Time to swapbuffers! - - gfx::Size surface_size = surface_size_for_swap_buffers(); - - OutputSurfaceFrame output_frame; - output_frame.latency_info = std::move(swap_frame_data.latency_info); - output_frame.top_controls_visible_height_changed = - swap_frame_data.top_controls_visible_height_changed; - output_frame.size = surface_size; -#if BUILDFLAG(IS_MAC) - output_frame.ca_layer_error_code = swap_frame_data.ca_layer_error_code; -#endif - - if (use_swap_with_bounds_) { - output_frame.content_bounds = std::move(swap_content_bounds_); - } else if (use_partial_swap_) { - // If supported, we can save significant bandwidth by only swapping the - // damaged/scissored region (clamped to the viewport). - swap_buffer_rect_.Intersect(gfx::Rect(surface_size)); - int flipped_y_pos_of_rect_bottom = surface_size.height() - - swap_buffer_rect_.y() - - swap_buffer_rect_.height(); - output_frame.sub_buffer_rect = - gfx::Rect(swap_buffer_rect_.x(), - FlippedRootFramebuffer() ? flipped_y_pos_of_rect_bottom - : swap_buffer_rect_.y(), - swap_buffer_rect_.width(), swap_buffer_rect_.height()); - } else if (swap_buffer_rect_.IsEmpty() && allow_empty_swap_) { - output_frame.sub_buffer_rect = swap_buffer_rect_; - } - - // Record resources from viz clients that have been shipped as overlays to the - // gpu together. - swapping_overlay_resources_.push_back(std::move(pending_overlay_resources_)); - pending_overlay_resources_.clear(); - if (settings_->release_overlay_resources_after_gpu_query) { - // Record RenderPass textures that have been shipped as overlays to the gpu - // together. - displayed_overlay_textures_.push_back( - std::move(awaiting_swap_overlay_textures_)); - awaiting_swap_overlay_textures_.clear(); - } else { - // If |displayed_overlay_textures_| is appended to in this case then - // SwapBuffersComplete needs to be extended to handle it. - DCHECK(awaiting_swap_overlay_textures_.empty()); - } - - output_surface_->SwapBuffers(std::move(output_frame)); - - swap_buffer_rect_ = gfx::Rect(); - - if (context_busy_) { - output_surface_->context_provider()->CacheController()->ClientBecameNotBusy( - std::move(context_busy_)); - } -} - -void GLRenderer::SwapBuffersSkipped() { - if (context_busy_) { - output_surface_->context_provider()->CacheController()->ClientBecameNotBusy( - std::move(context_busy_)); - } -} - -void GLRenderer::SwapBuffersComplete(gfx::GpuFenceHandle release_fence) { - // Returned release fence is signalled when the latest swap is presented, - // and tells us we can re-use the buffers from the /previous/ swap. - if (swapping_overlay_resources_.size() > 1) { - for (auto& lock : swapping_overlay_resources_[0]) { - lock->SetReleaseFence(release_fence.Clone()); - } - } - - if (settings_->release_overlay_resources_after_gpu_query) { - // Once a resource has been swap-ACKed, send a query to the GPU process to - // ask if the resource is no longer being consumed by the system compositor. - // The response will come with the next swap-ACK. - if (!swapping_overlay_resources_.empty()) { - for (OverlayResourceLock& lock : swapping_overlay_resources_.front()) { - unsigned texture = lock->texture_id(); - if (swapped_and_acked_overlay_resources_.find(texture) == - swapped_and_acked_overlay_resources_.end()) { - swapped_and_acked_overlay_resources_[texture] = std::move(lock); - } - } - swapping_overlay_resources_.pop_front(); - } - if (!displayed_overlay_textures_.empty()) { - for (auto& overlay : displayed_overlay_textures_.front()) - awaiting_release_overlay_textures_.push_back(std::move(overlay)); - displayed_overlay_textures_.erase(displayed_overlay_textures_.begin()); - } - - size_t query_texture_count = swapped_and_acked_overlay_resources_.size() + - awaiting_release_overlay_textures_.size(); - if (query_texture_count) { - std::vector<uint32_t> query_texture_ids; - query_texture_ids.reserve(query_texture_count); - - for (auto& pair : swapped_and_acked_overlay_resources_) - query_texture_ids.push_back(pair.first); - for (auto& overlay : awaiting_release_overlay_textures_) - query_texture_ids.push_back(overlay->texture.id()); - - // We query for *all* outstanding texture ids, even if we previously - // queried, as we will not hear back about things becoming available - // until after we query again. - gl_->ScheduleCALayerInUseQueryCHROMIUM(query_texture_count, - query_texture_ids.data()); - } - } else { - // If a query is not needed to release the overlay buffers, we can assume - // that once a swap buffer has completed we can remove the oldest buffers - // from the queue, but only once we've swapped another frame afterward. - if (swapping_overlay_resources_.size() > 1) { - auto& read_lock_release_fence_overlay_locks = - read_lock_release_fence_overlay_locks_.emplace_back(); - if (!release_fence.is_null()) { - auto read_lock_iter = std::partition( - swapping_overlay_resources_.front().begin(), - swapping_overlay_resources_.front().end(), - [](auto& lock) { return !lock->HasReadLockFence(); }); - read_lock_release_fence_overlay_locks.insert( - read_lock_release_fence_overlay_locks.end(), - std::make_move_iterator(read_lock_iter), - std::make_move_iterator(swapping_overlay_resources_.front().end())); - } - - DisplayResourceProviderGL::ScopedBatchReturnResources returner( - resource_provider()); - swapping_overlay_resources_.pop_front(); - } - // If |displayed_overlay_textures_| has a non-empty member that means we're - // sending RenderPassDrawQuads as an overlay. This is only supported for - // CALayers now, where |release_overlay_resources_after_gpu_query| will be - // true. In order to support them here, the OverlayTextures would need to - // move to |awaiting_release_overlay_textures_| and stay there until the - // ResourceFence that was in use for the frame they were submitted is - // passed. - DCHECK(displayed_overlay_textures_.empty()); - } -} - -void GLRenderer::BuffersPresented() { - if (!read_lock_release_fence_overlay_locks_.empty()) - read_lock_release_fence_overlay_locks_.pop_front(); -} - -void GLRenderer::DidReceiveTextureInUseResponses( - const gpu::TextureInUseResponses& responses) { - DCHECK(settings_->release_overlay_resources_after_gpu_query); - DisplayResourceProviderGL::ScopedBatchReturnResources returner( - resource_provider()); - for (const gpu::TextureInUseResponse& response : responses) { - if (response.in_use) - continue; - - // Returned texture ids may be for resources from clients of the - // display compositor, in |swapped_and_acked_overlay_resources_|. In that - // case we remove the lock from the map, allowing them to be returned to the - // client if the resource has been deleted from the - // DisplayResourceProviderGL. - if (swapped_and_acked_overlay_resources_.erase(response.texture)) - continue; - // If not, then they would be a RenderPass copy texture, which is held in - // |awaiting_release_overlay_textures_|. We move it back to the available - // texture list to use it for the next frame. - auto it = std::find_if( - awaiting_release_overlay_textures_.begin(), - awaiting_release_overlay_textures_.end(), - [&response](const std::unique_ptr<OverlayTexture>& overlay) { - return overlay->texture.id() == response.texture; - }); - if (it != awaiting_release_overlay_textures_.end()) { - // Mark the OverlayTexture as newly returned to the available set. - (*it)->frames_waiting_for_reuse = 0; - available_overlay_textures_.push_back(std::move(*it)); - awaiting_release_overlay_textures_.erase(it); - } - } -} - -void GLRenderer::BindFramebufferToOutputSurface() { - current_framebuffer_texture_ = nullptr; - output_surface_->BindFramebuffer(); - tint_gl_composited_content_ = debug_settings_->tint_composited_content; - if (overdraw_feedback_) { - // Output surfaces that require an external stencil test should not allow - // overdraw feedback by setting |supports_stencil| to false. - DCHECK(!output_surface_->HasExternalStencilTest()); - SetupOverdrawFeedback(); - SetStencilEnabled(true); - } else if (output_surface_->HasExternalStencilTest()) { - output_surface_->ApplyExternalStencil(); - SetStencilEnabled(true); - } else { - SetStencilEnabled(false); - } -} - -void GLRenderer::BindFramebufferToTexture( - const AggregatedRenderPassId render_pass_id) { - tint_gl_composited_content_ = false; - gl_->BindFramebuffer(GL_FRAMEBUFFER, offscreen_framebuffer_id_); - - auto contents_texture_it = render_pass_textures_.find(render_pass_id); - current_framebuffer_texture_ = &contents_texture_it->second; - GLuint texture_id = current_framebuffer_texture_->id(); - DCHECK(texture_id); - gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - texture_id, 0); - if (overdraw_feedback_) { - if (!offscreen_stencil_renderbuffer_id_) - gl_->GenRenderbuffers(1, &offscreen_stencil_renderbuffer_id_); - if (current_framebuffer_texture_->size() != - offscreen_stencil_renderbuffer_size_) { - gl_->BindRenderbuffer(GL_RENDERBUFFER, - offscreen_stencil_renderbuffer_id_); - gl_->RenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, - current_framebuffer_texture_->size().width(), - current_framebuffer_texture_->size().height()); - gl_->BindRenderbuffer(GL_RENDERBUFFER, 0); - offscreen_stencil_renderbuffer_size_ = - current_framebuffer_texture_->size(); - } - gl_->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, - offscreen_stencil_renderbuffer_id_); - } - -#if EXPENSIVE_DCHECKS_ARE_ON() - DCHECK(gl_->CheckFramebufferStatus(GL_FRAMEBUFFER) == - GL_FRAMEBUFFER_COMPLETE || - IsContextLost()); -#endif // EXPENSIVE_DCHECKS_ARE_ON() - - if (overdraw_feedback_) { - SetupOverdrawFeedback(); - SetStencilEnabled(true); - } else { - SetStencilEnabled(false); - } -} - -void GLRenderer::SetScissorTestRect(const gfx::Rect& scissor_rect) { - EnsureScissorTestEnabled(); - - // Don't unnecessarily ask the context to change the scissor, because it - // may cause undesired GPU pipeline flushes. - if (scissor_rect == scissor_rect_) - return; - - scissor_rect_ = scissor_rect; - FlushTextureQuadCache(SHARED_BINDING); - gl_->Scissor(scissor_rect.x(), scissor_rect.y(), scissor_rect.width(), - scissor_rect.height()); -} - -void GLRenderer::SetViewport() { - gl_->Viewport(current_window_space_viewport_.x(), - current_window_space_viewport_.y(), - current_window_space_viewport_.width(), - current_window_space_viewport_.height()); -} - -void GLRenderer::InitializeSharedObjects() { - TRACE_EVENT0("viz", "GLRenderer::InitializeSharedObjects"); - - // Create an FBO for doing offscreen rendering. - gl_->GenFramebuffers(1, &offscreen_framebuffer_id_); - - shared_geometry_ = - std::make_unique<StaticGeometryBinding>(gl_, QuadVertexRect()); - clipped_geometry_ = std::make_unique<DynamicGeometryBinding>(gl_); -} - -void GLRenderer::PrepareGeometry(BoundGeometry binding) { - if (binding == bound_geometry_) { - return; - } - - switch (binding) { - case SHARED_BINDING: - shared_geometry_->PrepareForDraw(); - break; - case CLIPPED_BINDING: - clipped_geometry_->PrepareForDraw(); - break; - case NO_BINDING: - break; - } - bound_geometry_ = binding; -} - -void GLRenderer::SetUseProgram(const ProgramKey& program_key_no_color, - const gfx::ColorSpace& src_color_space, - const gfx::ColorSpace& dst_color_space, - bool adjust_src_white_level, - absl::optional<gfx::HDRMetadata> hdr_metadata) { - DCHECK(dst_color_space.IsValid()); - gfx::ColorSpace adjusted_src_color_space = src_color_space; - if (adjust_src_white_level && src_color_space.IsHDR()) { - // TODO(b/183236148): consider using the destination's HDR static metadata - // in current_frame()->display_color_spaces.hdr_static_metadata() and the - // color volume metadata in |src_hdr_metadata| for the tone mapping; e.g. - // the content might be mastered in 0-1000 nits but the display only be able - // to represent 0 to 500. - adjusted_src_color_space = src_color_space.GetWithSDRWhiteLevel( - current_frame()->display_color_spaces.GetSDRMaxLuminanceNits()); - } - - ProgramKey program_key = program_key_no_color; - const gfx::ColorTransform* color_transform = - GetColorTransform(adjusted_src_color_space, dst_color_space); - program_key.SetColorTransform(color_transform); - - bool has_output_color_matrix = false; - if (program_key.type() != ProgramType::PROGRAM_TYPE_SOLID_COLOR) - has_output_color_matrix = HasOutputColorMatrix(); - program_key.set_has_output_color_matrix(has_output_color_matrix); - - // Create and set the program if needed. - std::unique_ptr<Program>& program = program_cache_[program_key]; - if (!program) { - program = std::make_unique<Program>(); - program->Initialize(output_surface_->context_provider(), program_key); - } - DCHECK(program); - if (current_program_ != program.get()) { - current_program_ = program.get(); - gl_->UseProgram(current_program_->program()); - } - if (!current_program_->initialized()) { - DCHECK(IsContextLost()); - return; - } - - // Set uniforms that are common to all programs. - if (current_program_->sampler_location() != -1) - gl_->Uniform1i(current_program_->sampler_location(), 0); - if (current_program_->viewport_location() != -1) { - float viewport[4] = { - static_cast<float>(current_window_space_viewport_.x()), - static_cast<float>(current_window_space_viewport_.y()), - static_cast<float>(current_window_space_viewport_.width()), - static_cast<float>(current_window_space_viewport_.height()), - }; - gl_->Uniform4fv(current_program_->viewport_location(), 1, viewport); - } - - if (has_output_color_matrix) { - DCHECK_NE(current_program_->output_color_matrix_location(), -1); - float matrix[16]; - output_surface_->color_matrix().getColMajor(matrix); - gl_->UniformMatrix4fv(current_program_->output_color_matrix_location(), 1, - false, matrix); - } -} - -const Program* GLRenderer::GetProgramIfInitialized( - const ProgramKey& desc) const { - const auto found = program_cache_.find(desc); - if (found == program_cache_.end()) - return nullptr; - return found->second.get(); -} - -const gfx::ColorTransform* GLRenderer::GetColorTransform( - const gfx::ColorSpace& src, - const gfx::ColorSpace& dst) { - ColorTransformKey key; - key.src = src; - key.dst = dst; - key.sdr_max_luminance_nits = - current_frame()->display_color_spaces.GetSDRMaxLuminanceNits(); - std::unique_ptr<gfx::ColorTransform>& transform = color_transform_cache_[key]; - if (!transform) { - gfx::ColorTransform::Options options; - options.tone_map_pq_and_hlg_to_sdr = !dst.IsHDR(); - options.sdr_max_luminance_nits = key.sdr_max_luminance_nits; - transform = gfx::ColorTransform::NewColorTransform(src, dst, options); - } - return transform.get(); -} - -void GLRenderer::CleanupSharedObjects() { - shared_geometry_ = nullptr; - - gl_->ReleaseShaderCompiler(); - for (auto& iter : program_cache_) - iter.second->Cleanup(gl_); - program_cache_.clear(); - color_transform_cache_.clear(); - - if (offscreen_framebuffer_id_) - gl_->DeleteFramebuffers(1, &offscreen_framebuffer_id_); - - if (offscreen_stencil_renderbuffer_id_) - gl_->DeleteRenderbuffers(1, &offscreen_stencil_renderbuffer_id_); -} - -void GLRenderer::ReinitializeGLState() { - is_scissor_enabled_ = false; - scissor_rect_ = gfx::Rect(); - stencil_shadow_ = false; - blend_shadow_ = true; - current_program_ = nullptr; - - RestoreGLState(); -} - -void GLRenderer::RestoreGLStateAfterSkia() { - // After using Skia we need to disable vertex attributes we don't use - int attribs_count = output_surface_->context_provider() - ->ContextCapabilities() - .max_vertex_attribs; - for (int i = 0; i < attribs_count; i++) - gl_->DisableVertexAttribArray(i); - - RestoreGLState(); -} - -void GLRenderer::RestoreGLState() { - // This restores the current GLRenderer state to the GL context. - bound_geometry_ = NO_BINDING; - PrepareGeometry(SHARED_BINDING); - - gl_->Disable(GL_DEPTH_TEST); - gl_->Disable(GL_CULL_FACE); - gl_->ColorMask(true, true, true, true); - gl_->BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - gl_->ActiveTexture(GL_TEXTURE0); - - if (current_program_) - gl_->UseProgram(current_program_->program()); - - if (stencil_shadow_) - gl_->Enable(GL_STENCIL_TEST); - else - gl_->Disable(GL_STENCIL_TEST); - - if (blend_shadow_) - gl_->Enable(GL_BLEND); - else - gl_->Disable(GL_BLEND); - - if (is_scissor_enabled_) - gl_->Enable(GL_SCISSOR_TEST); - else - gl_->Disable(GL_SCISSOR_TEST); - - gl_->Scissor(scissor_rect_.x(), scissor_rect_.y(), scissor_rect_.width(), - scissor_rect_.height()); -} - -bool GLRenderer::IsContextLost() { - return gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR; -} - -#if BUILDFLAG(IS_APPLE) -void GLRenderer::ScheduleCALayers() { - // The use of OverlayTextures for RenderPasses is only supported on the code - // paths for |release_overlay_resources_after_gpu_query| at the moment. See - // SwapBuffersComplete for notes on the missing support for other paths. This - // method uses ScheduleRenderPassDrawQuad to send RenderPass outputs as - // overlays, so it can only be used because this setting is true. - if (!settings_->release_overlay_resources_after_gpu_query) - return; - - scoped_refptr<CALayerOverlaySharedState> shared_state; - - for (const CALayerOverlay& ca_layer_overlay : current_frame()->overlay_list) { - if (ca_layer_overlay.rpdq) { - std::unique_ptr<OverlayTexture> overlay_texture = - ScheduleRenderPassDrawQuad(&ca_layer_overlay); - if (overlay_texture) - awaiting_swap_overlay_textures_.push_back(std::move(overlay_texture)); - shared_state = nullptr; - continue; - } - - ResourceId contents_resource_id = ca_layer_overlay.contents_resource_id; - unsigned texture_id = 0; - if (contents_resource_id) { - pending_overlay_resources_.push_back( - std::make_unique<DisplayResourceProviderGL::ScopedOverlayLockGL>( - resource_provider(), contents_resource_id)); - texture_id = pending_overlay_resources_.back()->texture_id(); - } - GLfloat contents_rect[4] = { - ca_layer_overlay.contents_rect.x(), ca_layer_overlay.contents_rect.y(), - ca_layer_overlay.contents_rect.width(), - ca_layer_overlay.contents_rect.height(), - }; - GLfloat bounds_rect[4] = { - ca_layer_overlay.bounds_rect.x(), ca_layer_overlay.bounds_rect.y(), - ca_layer_overlay.bounds_rect.width(), - ca_layer_overlay.bounds_rect.height(), - }; - GLboolean is_clipped = ca_layer_overlay.shared_state->is_clipped; - GLfloat clip_rect[4] = {ca_layer_overlay.shared_state->clip_rect.x(), - ca_layer_overlay.shared_state->clip_rect.y(), - ca_layer_overlay.shared_state->clip_rect.width(), - ca_layer_overlay.shared_state->clip_rect.height()}; - - const gfx::RectF& rect = - ca_layer_overlay.shared_state->rounded_corner_bounds.rect(); - GLfloat rounded_corner_bounds[5] = { - rect.x(), rect.y(), rect.width(), rect.height(), - ca_layer_overlay.shared_state->rounded_corner_bounds.GetSimpleRadius()}; - - GLint sorting_context_id = - ca_layer_overlay.shared_state->sorting_context_id; - GLfloat transform[16]; - ca_layer_overlay.shared_state->transform.matrix().getColMajor(transform); - unsigned filter = ca_layer_overlay.filter; - - if (ca_layer_overlay.shared_state != shared_state) { - shared_state = ca_layer_overlay.shared_state; - gl_->ScheduleCALayerSharedStateCHROMIUM( - ca_layer_overlay.shared_state->opacity, is_clipped, clip_rect, - rounded_corner_bounds, sorting_context_id, transform); - } - gl_->ScheduleCALayerCHROMIUM( - texture_id, contents_rect, ca_layer_overlay.background_color, - ca_layer_overlay.edge_aa_mask, bounds_rect, filter); - } - - ReduceAvailableOverlayTextures(); -} -#endif // BUILDFLAG(IS_APPLE) - -#if BUILDFLAG(IS_WIN) -void GLRenderer::ScheduleDCLayers() { - for (DCLayerOverlay& dc_layer_overlay : current_frame()->overlay_list) { - DCHECK_EQ(DCLayerOverlay::kNumResources, 2u); - GLuint texture_ids[DCLayerOverlay::kNumResources] = {}; - for (size_t i = 0; i < DCLayerOverlay::kNumResources; i++) { - ResourceId resource_id = dc_layer_overlay.resources[i]; - if (resource_id == kInvalidResourceId) - break; - pending_overlay_resources_.push_back( - std::make_unique<DisplayResourceProviderGL::ScopedOverlayLockGL>( - resource_provider(), resource_id)); - texture_ids[i] = pending_overlay_resources_.back()->texture_id(); - } - DCHECK(texture_ids[0]); - // TODO(sunnyps): Set color space in renderer like we do for tiles. - gl_->SetColorSpaceMetadataCHROMIUM( - texture_ids[0], dc_layer_overlay.color_space.AsGLColorSpace()); - - int z_order = dc_layer_overlay.z_order; - const gfx::Rect& content_rect = dc_layer_overlay.content_rect; - const gfx::Rect& quad_rect = dc_layer_overlay.quad_rect; - DCHECK(dc_layer_overlay.transform.IsFlat()); - const auto& matrix = dc_layer_overlay.transform.matrix(); - bool is_clipped = dc_layer_overlay.clip_rect.has_value(); - const gfx::Rect& clip_rect = - dc_layer_overlay.clip_rect.value_or(gfx::Rect()); - unsigned protected_video_type = - static_cast<unsigned>(dc_layer_overlay.protected_video_type); - - gl_->ScheduleDCLayerCHROMIUM( - texture_ids[0], texture_ids[1], z_order, content_rect.x(), - content_rect.y(), content_rect.width(), content_rect.height(), - quad_rect.x(), quad_rect.y(), quad_rect.width(), quad_rect.height(), - matrix.rc(0, 0), matrix.rc(0, 1), matrix.rc(1, 0), matrix.rc(1, 1), - matrix.rc(0, 3), matrix.rc(1, 3), is_clipped, clip_rect.x(), - clip_rect.y(), clip_rect.width(), clip_rect.height(), - protected_video_type); - } -} -#endif // BUILDFLAG(IS_WIN) - -#if BUILDFLAG(IS_ANDROID) || defined(USE_OZONE) -void GLRenderer::ScheduleOverlays() { - if (current_frame()->overlay_list.empty()) - return; - - OverlayCandidateList& overlays = current_frame()->overlay_list; - for (const auto& overlay_candidate : overlays) { - pending_overlay_resources_.push_back( - std::make_unique<DisplayResourceProviderGL::ScopedOverlayLockGL>( - resource_provider(), overlay_candidate.resource_id)); - unsigned texture_id = pending_overlay_resources_.back()->texture_id(); - - context_support_->ScheduleOverlayPlane( - overlay_candidate.plane_z_order, overlay_candidate.transform, - texture_id, ToNearestRect(overlay_candidate.display_rect), - overlay_candidate.uv_rect, !overlay_candidate.is_opaque, - overlay_candidate.gpu_fence_id); - } -} -#endif // BUILDFLAG(IS_ANDROID) || defined(USE_OZONE) - -void GLRenderer::ScheduleOutputSurfaceAsOverlay() { - if (!current_frame()->output_surface_plane) - return; - - // Initialize correct values to use an output surface as overlay candidate. - auto& overlay_candidate = *(current_frame()->output_surface_plane); - unsigned texture_id = output_surface_->GetOverlayTextureId(); - DCHECK(texture_id || IsContextLost()); - // Output surface is also z-order 0. - int plane_z_order = 0; - - context_support_->ScheduleOverlayPlane( - plane_z_order, overlay_candidate.transform, texture_id, - ToNearestRect(overlay_candidate.display_rect), overlay_candidate.uv_rect, - overlay_candidate.enable_blending, overlay_candidate.gpu_fence_id); -} - -#if BUILDFLAG(IS_APPLE) -// This function draws the CompositorRenderPassDrawQuad into a temporary -// texture/framebuffer, and then copies the result into an IOSurface. The -// inefficient (but simple) way to do this would be to: -// 1. Allocate a framebuffer the size of the screen. -// 2. Draw using all the normal RPDQ draw logic. -// -// Instead, this method does the following: -// 1. Configure parameters as if drawing to a framebuffer the size of the -// screen. This reuses most of the RPDQ draw logic. -// 2. Update parameters to draw into a framebuffer only as large as needed. -// 3. Fix shader uniforms that were broken by (2). -// -// Then: -// 4. Allocate an IOSurface as the drawing destination. -// 5. Draw the RPDQ. -void GLRenderer::CopyRenderPassDrawQuadToOverlayResource( - const CALayerOverlay* ca_layer_overlay, - std::unique_ptr<OverlayTexture>* overlay_texture, - gfx::RectF* new_bounds) { - // Don't carry over any GL state from previous RenderPass draw operations. - ReinitializeGLState(); - auto contents_texture_it = - render_pass_textures_.find(ca_layer_overlay->rpdq->render_pass_id); - DCHECK(contents_texture_it != render_pass_textures_.end()); - - // Configure parameters as if drawing to a framebuffer the size of the - // screen. - DrawRenderPassDrawQuadParams params; - params.quad = ca_layer_overlay->rpdq; - params.flip_texture = true; - params.contents_texture = &contents_texture_it->second; - params.quad_to_target_transform = - params.quad->shared_quad_state->quad_to_target_transform; - params.tex_coord_rect = params.quad->tex_coord_rect; - - // Calculate projection and window matrices using InitializeViewport(). This - // requires creating a dummy DrawingFrame. - { - DrawingFrame dummy_frame; - gfx::Rect frame_rect(current_frame()->device_viewport_size); - force_drawing_frame_framebuffer_unflipped_ = true; - InitializeViewport(&dummy_frame, frame_rect, frame_rect, frame_rect.size()); - force_drawing_frame_framebuffer_unflipped_ = false; - params.projection_matrix = dummy_frame.projection_matrix; - params.window_matrix = dummy_frame.window_matrix; - } - - // Perform basic initialization with the screen-sized viewport. - if (!InitializeRPDQParameters(¶ms)) - return; - - if (!UpdateRPDQWithSkiaFilters(¶ms)) - return; - - // |params.dst_rect| now contain values that reflect a potentially increased - // size quad. - gfx::RectF updated_dst_rect = params.dst_rect; - gfx::Size dst_pixel_size = gfx::ToCeiledSize(updated_dst_rect.size()); - - int iosurface_width = dst_pixel_size.width(); - int iosurface_height = dst_pixel_size.height(); - if (!settings_->dont_round_texture_sizes_for_pixel_tests) { - // Round the size of the IOSurface to a multiple of 64 pixels. This reduces - // memory fragmentation. https://crbug.com/146070. This also allows - // IOSurfaces to be more easily reused during a resize operation. - int iosurface_multiple = 64; - iosurface_width = - cc::MathUtil::CheckedRoundUp(iosurface_width, iosurface_multiple); - iosurface_height = - cc::MathUtil::CheckedRoundUp(iosurface_height, iosurface_multiple); - } - - *overlay_texture = - FindOrCreateOverlayTexture(params.quad->render_pass_id, iosurface_width, - iosurface_height, RootRenderPassColorSpace()); - *new_bounds = gfx::RectF(updated_dst_rect.origin(), - gfx::SizeF((*overlay_texture)->texture.size())); - - // Calculate new projection and window matrices for a minimally sized viewport - // using InitializeViewport(). This requires creating a dummy DrawingFrame. - { - DrawingFrame dummy_frame; - force_drawing_frame_framebuffer_unflipped_ = true; - gfx::Rect frame_rect = - gfx::Rect(0, 0, updated_dst_rect.width(), updated_dst_rect.height()); - InitializeViewport(&dummy_frame, frame_rect, frame_rect, frame_rect.size()); - force_drawing_frame_framebuffer_unflipped_ = false; - params.projection_matrix = dummy_frame.projection_matrix; - params.window_matrix = dummy_frame.window_matrix; - } - - // Calculate a new quad_to_target_transform. - params.quad_to_target_transform = gfx::Transform(); - params.quad_to_target_transform.Translate(-updated_dst_rect.x(), - -updated_dst_rect.y()); - - // Antialiasing works by fading out content that is close to the edge of the - // viewport. All of these values need to be recalculated. - if (params.use_aa) { - current_window_space_viewport_ = - gfx::Rect(0, 0, updated_dst_rect.width(), updated_dst_rect.height()); - gfx::Transform quad_rect_matrix; - QuadRectTransform(&quad_rect_matrix, params.quad_to_target_transform, - updated_dst_rect); - params.contents_device_transform = - params.window_matrix * params.projection_matrix * quad_rect_matrix; - bool clipped = false; - params.contents_device_transform.FlattenTo2d(); - gfx::QuadF device_layer_quad = cc::MathUtil::MapQuad( - params.contents_device_transform, SharedGeometryQuad(), &clipped); - LayerQuad device_layer_edges(device_layer_quad); - InflateAntiAliasingDistances(device_layer_quad, &device_layer_edges, - params.edge); - } - - // Establish destination texture. - GLuint temp_fbo; - gl_->GenFramebuffers(1, &temp_fbo); - gl_->BindFramebuffer(GL_FRAMEBUFFER, temp_fbo); - gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - (*overlay_texture)->texture.target(), - (*overlay_texture)->texture.id(), 0); - DCHECK(gl_->CheckFramebufferStatus(GL_FRAMEBUFFER) == - GL_FRAMEBUFFER_COMPLETE || - IsContextLost()); - - // Clear to 0 to ensure the background is transparent. - gl_->ClearColor(0, 0, 0, 0); - gl_->Clear(GL_COLOR_BUFFER_BIT); - - UpdateRPDQTexturesForSampling(¶ms); - UpdateRPDQBlendMode(¶ms); - // The code in this method (CopyRenderPassDrawQuadToOverlayResource) is - // only called when we are drawing for the purpose of copying to - // a CALayerOverlay. In such cases, the CALayerOverlay applies rounded - // corners via CALayer parameters, so the shader-based rounded corners - // should be disabled here. - params.apply_shader_based_rounded_corner = false; - ChooseRPDQProgram(¶ms, (*overlay_texture)->texture.color_space()); - UpdateRPDQUniforms(¶ms); - - // Prior to drawing, set up the destination framebuffer and viewport. - gl_->BindFramebuffer(GL_FRAMEBUFFER, temp_fbo); - gl_->Viewport(0, 0, updated_dst_rect.width(), updated_dst_rect.height()); - - DrawRPDQ(params); - if (params.background_texture) { - gl_->DeleteTextures(1, ¶ms.background_texture); - params.background_texture = 0; - } - gl_->DeleteFramebuffers(1, &temp_fbo); -} - -std::unique_ptr<GLRenderer::OverlayTexture> -GLRenderer::FindOrCreateOverlayTexture( - const AggregatedRenderPassId& render_pass_id, - int width, - int height, - const gfx::ColorSpace& color_space) { - // First try to use a texture for the same CompositorRenderPassId, to keep - // things more stable and less likely to clobber each others textures. - auto match_with_id = [&](const std::unique_ptr<OverlayTexture>& overlay) { - return overlay->render_pass_id == render_pass_id && - overlay->texture.size().width() >= width && - overlay->texture.size().height() >= height && - overlay->texture.size().width() <= width * 2 && - overlay->texture.size().height() <= height * 2; - }; - auto it = std::find_if(available_overlay_textures_.begin(), - available_overlay_textures_.end(), match_with_id); - if (it != available_overlay_textures_.end()) { - std::unique_ptr<OverlayTexture> result = std::move(*it); - available_overlay_textures_.erase(it); - - result->render_pass_id = render_pass_id; - return result; - } - - // Then fallback to trying other textures that still match. - auto match = [&](const std::unique_ptr<OverlayTexture>& overlay) { - return overlay->texture.size().width() >= width && - overlay->texture.size().height() >= height && - overlay->texture.size().width() <= width * 2 && - overlay->texture.size().height() <= height * 2; - }; - it = std::find_if(available_overlay_textures_.begin(), - available_overlay_textures_.end(), match); - if (it != available_overlay_textures_.end()) { - std::unique_ptr<OverlayTexture> result = std::move(*it); - available_overlay_textures_.erase(it); - - result->render_pass_id = render_pass_id; - return result; - } - - // Make a new texture if we could not find a match. Sadtimes. - auto result = std::make_unique<OverlayTexture>(); - result->texture = ScopedGpuMemoryBufferTexture( - output_surface_->context_provider(), - gfx::Size(width, height), color_space); - result->render_pass_id = render_pass_id; - return result; -} - -void GLRenderer::ReduceAvailableOverlayTextures() { - // Overlay resources may get returned back to the compositor at varying rates, - // so we may get a number of resources returned at once, then none for a - // while. As such, we want to hold onto enough resources to not have to create - // any when none are released for a while. Emperical study by erikchen@ on - // crbug.com/636884 found that saving 5 spare textures per RenderPass was - // sufficient for important benchmarks. This seems to imply that the OS may - // hold up to 5 frames of textures before releasing them. - static const int kKeepCountPerRenderPass = 5; - - // In order to accomodate the above requirements, we hold any released texture - // in the |available_overlay_textures_| set for up to 5 frames before - // discarding it. - for (const auto& overlay : available_overlay_textures_) - overlay->frames_waiting_for_reuse++; - base::EraseIf(available_overlay_textures_, - [](const std::unique_ptr<OverlayTexture>& overlay) { - return overlay->frames_waiting_for_reuse >= - kKeepCountPerRenderPass; - }); -} - -std::unique_ptr<GLRenderer::OverlayTexture> -GLRenderer::ScheduleRenderPassDrawQuad(const CALayerOverlay* ca_layer_overlay) { - DCHECK(ca_layer_overlay->rpdq); - - std::unique_ptr<OverlayTexture> overlay_texture; - gfx::RectF new_bounds; - CopyRenderPassDrawQuadToOverlayResource(ca_layer_overlay, &overlay_texture, - &new_bounds); - if (!overlay_texture) - return {}; - - GLfloat contents_rect[4] = { - ca_layer_overlay->contents_rect.x(), ca_layer_overlay->contents_rect.y(), - ca_layer_overlay->contents_rect.width(), - ca_layer_overlay->contents_rect.height(), - }; - GLfloat bounds_rect[4] = { - new_bounds.x(), new_bounds.y(), new_bounds.width(), new_bounds.height(), - }; - GLboolean is_clipped = ca_layer_overlay->shared_state->is_clipped; - GLfloat clip_rect[4] = {ca_layer_overlay->shared_state->clip_rect.x(), - ca_layer_overlay->shared_state->clip_rect.y(), - ca_layer_overlay->shared_state->clip_rect.width(), - ca_layer_overlay->shared_state->clip_rect.height()}; - - const gfx::RectF& rect = - ca_layer_overlay->shared_state->rounded_corner_bounds.rect(); - GLfloat rounded_corner_rect[5] = { - rect.x(), rect.y(), rect.width(), rect.height(), - ca_layer_overlay->shared_state->rounded_corner_bounds.GetSimpleRadius()}; - - GLint sorting_context_id = ca_layer_overlay->shared_state->sorting_context_id; - GLfloat gl_transform[16]; - ca_layer_overlay->shared_state->transform.matrix().getColMajor(gl_transform); - unsigned filter = ca_layer_overlay->filter; - - // The alpha has already been applied when copying the RPDQ to an IOSurface. - GLfloat alpha = 1; - gl_->ScheduleCALayerSharedStateCHROMIUM(alpha, is_clipped, clip_rect, - rounded_corner_rect, - sorting_context_id, gl_transform); - gl_->ScheduleCALayerCHROMIUM(overlay_texture->texture.id(), contents_rect, - ca_layer_overlay->background_color, - ca_layer_overlay->edge_aa_mask, bounds_rect, - filter); - return overlay_texture; -} -#endif // BUILDFLAG(IS_APPLE) - -void GLRenderer::SetupOverdrawFeedback() { - gl_->StencilFunc(GL_ALWAYS, 1, 0xffffffff); - // First two values are ignored as test always passes. - gl_->StencilOp(GL_KEEP, GL_KEEP, GL_INCR); - gl_->StencilMask(0xffffffff); -} - -void GLRenderer::FlushOverdrawFeedback(const gfx::Rect& output_rect) { - DCHECK(stencil_shadow_); - - // Test only, keep everything. - gl_->StencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - - EnsureScissorTestDisabled(); - SetBlendEnabled(true); - - PrepareGeometry(SHARED_BINDING); - - SetUseProgram(ProgramKey::DebugBorder(), gfx::ColorSpace::CreateSRGB(), - CurrentRenderPassColorSpace()); - - gfx::Transform render_matrix; - render_matrix.Translate(0.5 * output_rect.width() + output_rect.x(), - 0.5 * output_rect.height() + output_rect.y()); - render_matrix.Scale(output_rect.width(), output_rect.height()); - SetShaderMatrix(current_frame()->projection_matrix * render_matrix); - - // Produce hinting for the amount of overdraw on screen for each pixel by - // drawing hint colors to the framebuffer based on the current stencil value. - struct { - int multiplier; - GLenum func; - GLint ref; - SkColor color; - } stencil_tests[] = { - {1, GL_EQUAL, 2, 0x2f0000ff}, // Blue: Overdrawn once. - {2, GL_EQUAL, 3, 0x2f00ff00}, // Green: Overdrawn twice. - {3, GL_EQUAL, 4, 0x3fff0000}, // Pink: Overdrawn three times. - {4, GL_LESS, 4, 0x7fff0000}, // Red: Overdrawn four or more times. - }; - - for (const auto& test : stencil_tests) { - gl_->StencilFunc(test.func, test.ref, 0xffffffff); - // Transparent color unless color-coding of overdraw is enabled. - SetShaderColor(debug_settings_->show_overdraw_feedback ? test.color : 0, - 1.f); - gl_->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr); - } -} - -void GLRenderer::ProcessOverdrawFeedback(base::CheckedNumeric<int> surface_area, - unsigned occlusion_query) { - unsigned result = 0; - DCHECK(occlusion_query); - gl_->GetQueryObjectuivEXT(occlusion_query, GL_QUERY_RESULT_EXT, &result); - gl_->DeleteQueriesEXT(1, &occlusion_query); - - // Report GPU overdraw as a percentage of |surface_area|. - TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("viz.overdraw"), "GPU Overdraw", - (result * 100.0 / - static_cast<int>(surface_area.ValueOrDefault(INT_MAX)))); -} - -void GLRenderer::UpdateRenderPassTextures( - const AggregatedRenderPassList& render_passes_in_draw_order, - const base::flat_map<AggregatedRenderPassId, RenderPassRequirements>& - render_passes_in_frame) { - // Collect RenderPass textures that should be deleted. - std::vector<AggregatedRenderPassId> passes_to_delete; - for (const auto& pair : render_pass_textures_) { - auto render_pass_it = render_passes_in_frame.find(pair.first); - if (render_pass_it == render_passes_in_frame.end()) { - passes_to_delete.push_back(pair.first); - continue; - } - const RenderPassRequirements& requirements = render_pass_it->second; - const ScopedRenderPassTexture& texture = pair.second; - bool size_appropriate = - texture.size().width() >= requirements.size.width() && - texture.size().height() >= requirements.size.height(); - bool mipmap_appropriate = !requirements.generate_mipmap || texture.mipmap(); - if (!size_appropriate || !mipmap_appropriate) - passes_to_delete.push_back(pair.first); - } - // Delete RenderPass textures from the previous frame that will not be used - // again. - for (auto& pass_to_delete : passes_to_delete) { - auto rp_backdrop_texture_it = - render_pass_backdrop_textures_.find(pass_to_delete); - if (rp_backdrop_texture_it != render_pass_backdrop_textures_.end()) - render_pass_backdrop_textures_.erase(pass_to_delete); - render_pass_textures_.erase(pass_to_delete); - } -} - -ResourceFormat GLRenderer::CurrentRenderPassResourceFormat() const { - const auto& caps = output_surface_->context_provider()->ContextCapabilities(); - if (CurrentRenderPassColorSpace().IsHDR()) { - // If a platform does not support half-float renderbuffers then it should - // not should request HDR rendering. - DCHECK(caps.texture_half_float_linear); - DCHECK(caps.color_buffer_half_float_rgba); - return RGBA_F16; - } - return PlatformColor::BestSupportedTextureFormat(caps); -} - -bool GLRenderer::HasOutputColorMatrix() const { - const bool is_root_render_pass = - current_frame()->current_render_pass == current_frame()->root_render_pass; - const SkM44& output_color_matrix = output_surface_->color_matrix(); - return is_root_render_pass && output_color_matrix != SkM44(); -} - -bool GLRenderer::CanUseFastSolidColorDraw( - const SolidColorDrawQuad* quad) const { - const SharedQuadState* sqs = quad->shared_quad_state; - - if (!use_fast_path_solid_color_quad_) - return false; - - // Mask filters require blending with the background, which is not possible - // with the glClear draw method. - if (!sqs->mask_filter_info.IsEmpty()) - return false; - - // 3D transforms need vertex computation in 3D and cannot be handled using - // glClear(). - if (!sqs->quad_to_target_transform.IsFlat()) - return false; - - // glClear ignores stencil buffer. - if (stencil_shadow_) - return false; - - // Any non axis aligned transform cannot be handled by glClear. - if (!sqs->quad_to_target_transform.Preserves2dAxisAlignment()) - return false; - - // When no blending is needed, glClear can be used. - SkBlendMode blend_mode = quad->shared_quad_state->blend_mode; - if (blend_mode == SkBlendMode::kSrc) - return true; - - if (blend_mode == SkBlendMode::kSrcOver) { - // Blending will replace destination color and alpha if the quad is opaque. - if (SkColorGetA(quad->color) == 255 && - quad->shared_quad_state->opacity >= 1.0f) { - return true; - } - - // It is safe to use glClearColor with alpha blending when the render - // pass has transparent background and nothing has drawn to the same rect - // area because the blending happens against (0, 0, 0, 0) which is the same - // as replacing the destination color & alpha. - if (!current_frame()->current_render_pass->has_transparent_background) - return false; - - gfx::RectF quad_rect_in_target(quad->visible_rect); - sqs->quad_to_target_transform.TransformRect(&quad_rect_in_target); - const gfx::Rect quad_rect_in_target_rounded = - gfx::ToRoundedRect(quad_rect_in_target); - - // If the quad does not intersect any region that has already been drawn - // to, then blending is not an issue and fast draw path can be used. - for (const auto& rect : drawn_rects_) - if (quad_rect_in_target_rounded.Intersects(rect)) - return false; - return true; - } - - return false; -} - -void GLRenderer::AllocateRenderPassResourceIfNeeded( - const AggregatedRenderPassId& 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()) { - DCHECK(gfx::Rect(contents_texture_it->second.size()) - .Contains(gfx::Rect(requirements.size))); - return; - } - - ScopedRenderPassTexture contents_texture( - output_surface_->context_provider(), requirements.size, - CurrentRenderPassResourceFormat(), CurrentRenderPassColorSpace(), - requirements.generate_mipmap); - render_pass_textures_[render_pass_id] = std::move(contents_texture); -} - -bool GLRenderer::IsRenderPassResourceAllocated( - const AggregatedRenderPassId& render_pass_id) const { - auto texture_it = render_pass_textures_.find(render_pass_id); - return texture_it != render_pass_textures_.end(); -} - -gfx::Size GLRenderer::GetRenderPassBackingPixelSize( - const AggregatedRenderPassId& render_pass_id) { - auto texture_it = render_pass_textures_.find(render_pass_id); - DCHECK(texture_it != render_pass_textures_.end()); - return texture_it->second.size(); -} - -GLRenderer::OverlayTexture::OverlayTexture() = default; -GLRenderer::OverlayTexture::~OverlayTexture() = default; - -bool GLRenderer::ColorTransformKey::operator==( - const ColorTransformKey& other) const { - return src == other.src && dst == other.dst && - sdr_max_luminance_nits == other.sdr_max_luminance_nits; -} - -bool GLRenderer::ColorTransformKey::operator!=( - const ColorTransformKey& other) const { - return !(*this == other); -} - -bool GLRenderer::ColorTransformKey::operator<( - const ColorTransformKey& other) const { - return std::tie(src, dst, sdr_max_luminance_nits) < - std::tie(other.src, other.dst, other.sdr_max_luminance_nits); -} - -} // namespace viz diff --git a/chromium/components/viz/service/display/gl_renderer.h b/chromium/components/viz/service/display/gl_renderer.h deleted file mode 100644 index f3106e13715..00000000000 --- a/chromium/components/viz/service/display/gl_renderer.h +++ /dev/null @@ -1,525 +0,0 @@ -// Copyright 2010 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_GL_RENDERER_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_H_ - -#include <map> -#include <memory> -#include <string> -#include <unordered_map> -#include <utility> -#include <vector> - -#include "base/cancelable_callback.h" -#include "base/containers/circular_deque.h" -#include "base/containers/queue.h" -#include "base/memory/raw_ptr.h" -#include "build/build_config.h" -#include "components/viz/common/gpu/context_cache_controller.h" -#include "components/viz/common/quads/aggregated_render_pass_draw_quad.h" -#include "components/viz/common/quads/compositor_render_pass_draw_quad.h" -#include "components/viz/common/quads/debug_border_draw_quad.h" -#include "components/viz/common/quads/solid_color_draw_quad.h" -#include "components/viz/common/quads/tile_draw_quad.h" -#include "components/viz/common/quads/yuv_video_draw_quad.h" -#include "components/viz/service/display/direct_renderer.h" -#include "components/viz/service/display/display_resource_provider_gl.h" -#include "components/viz/service/display/gl_renderer_copier.h" -#include "components/viz/service/display/gl_renderer_draw_cache.h" -#include "components/viz/service/display/program_binding.h" -#include "components/viz/service/display/scoped_gpu_memory_buffer_texture.h" -#include "components/viz/service/display/sync_query_collection.h" -#include "components/viz/service/display/texture_deleter.h" -#include "components/viz/service/viz_service_export.h" -#include "ui/gfx/geometry/quad_f.h" -#include "ui/gfx/gpu_fence_handle.h" -#include "ui/latency/latency_info.h" - -#if BUILDFLAG(IS_APPLE) -#include "components/viz/service/display/ca_layer_overlay.h" -#endif - -#if BUILDFLAG(IS_WIN) -#include "components/viz/service/display/dc_layer_overlay.h" -#endif - -namespace base { -class SingleThreadTaskRunner; -} - -namespace gpu { -namespace gles2 { -class GLES2Interface; -} -} // namespace gpu - -namespace gfx { -class RRectF; -} - -namespace viz { - -class DynamicGeometryBinding; -class GLRendererShaderTest; -class OutputSurface; -class ScopedRenderPassTexture; -class StaticGeometryBinding; -class StreamVideoDrawQuad; -class TextureDrawQuad; - -// Class that handles drawing of composited render layers using GL. -class VIZ_SERVICE_EXPORT GLRenderer : public DirectRenderer { - public: - class ScopedUseGrContext; - - GLRenderer(const RendererSettings* settings, - const DebugRendererSettings* debug_settings, - OutputSurface* output_surface, - DisplayResourceProviderGL* resource_provider, - OverlayProcessorInterface* overlay_processor, - scoped_refptr<base::SingleThreadTaskRunner> current_task_runner); - - GLRenderer(const GLRenderer&) = delete; - GLRenderer& operator=(const GLRenderer&) = delete; - - ~GLRenderer() override; - - bool use_swap_with_bounds() const { return use_swap_with_bounds_; } - - void SwapBuffers(SwapFrameData swap_frame_data) override; - void SwapBuffersSkipped() override; - void SwapBuffersComplete(gfx::GpuFenceHandle release_fence) override; - void BuffersPresented() override; - - void DidReceiveTextureInUseResponses( - const gpu::TextureInUseResponses& responses) override; - - virtual bool IsContextLost(); - - protected: - void DidChangeVisibility() override; - - bool stencil_enabled() const { return stencil_shadow_; } - - bool CanPartialSwap() override; - void UpdateRenderPassTextures( - const AggregatedRenderPassList& render_passes_in_draw_order, - const base::flat_map<AggregatedRenderPassId, RenderPassRequirements>& - render_passes_in_frame) override; - void AllocateRenderPassResourceIfNeeded( - const AggregatedRenderPassId& render_pass_id, - const RenderPassRequirements& requirements) override; - bool IsRenderPassResourceAllocated( - const AggregatedRenderPassId& render_pass_id) const override; - gfx::Size GetRenderPassBackingPixelSize( - const AggregatedRenderPassId& render_pass_id) override; - void BindFramebufferToOutputSurface() override; - void BindFramebufferToTexture( - const AggregatedRenderPassId render_pass_id) override; - void SetScissorTestRect(const gfx::Rect& scissor_rect) override; - void PrepareSurfaceForPass(SurfaceInitializationMode initialization_mode, - const gfx::Rect& render_pass_scissor) override; - void DoDrawQuad(const class DrawQuad*, - const gfx::QuadF* draw_region) override; - void BeginDrawingFrame() override; - void FlushOverdrawFeedback(const gfx::Rect& output_rect) override; - void FinishDrawingFrame() override; - bool FlippedFramebuffer() const override; - bool FlippedRootFramebuffer() const; - void EnsureScissorTestEnabled() override; - void EnsureScissorTestDisabled() override; - void CopyDrawnRenderPass(const copy_output::RenderPassGeometry& geometry, - std::unique_ptr<CopyOutputRequest> request) override; - void FinishDrawingQuadList() override; - void GenerateMipmap() override; - - // Returns true if quad requires antialiasing and false otherwise. - static bool ShouldAntialiasQuad(const gfx::QuadF& device_layer_quad, - bool clipped, - bool force_aa); - - // Inflate the quad and fill edge array for fragment shader. - // |local_quad| is set to inflated quad. |edge| array is filled with - // inflated quad's edge data. - static void SetupQuadForClippingAndAntialiasing( - const gfx::Transform& device_transform, - const DrawQuad* quad, - const gfx::QuadF* device_layer_quad, - const gfx::QuadF* clip_region, - gfx::QuadF* local_quad, - float edge[24]); - static void SetupRenderPassQuadForClippingAndAntialiasing( - const gfx::Transform& device_transform, - const AggregatedRenderPassDrawQuad* quad, - const gfx::QuadF* device_layer_quad, - const gfx::QuadF* clip_region, - gfx::QuadF* local_quad, - float edge[24]); - - private: - friend class GLRendererCopierPixelTest; - friend class GLRendererShaderPixelTest; - friend class GLRendererShaderTest; - friend class GLRendererTest; - - using OverlayResourceLock = - std::unique_ptr<DisplayResourceProviderGL::ScopedOverlayLockGL>; - using OverlayResourceLockList = std::vector<OverlayResourceLock>; - - // If a RenderPass is used as an overlay, we render the RenderPass with any - // effects into a texture for overlay use. We must keep the texture alive past - // the execution of SwapBuffers, and such textures are more expensive to make - // so we want to reuse them. - struct OverlayTexture { - OverlayTexture(); - ~OverlayTexture(); - - AggregatedRenderPassId render_pass_id; - ScopedGpuMemoryBufferTexture texture; - int frames_waiting_for_reuse = 0; - }; - - struct DrawRenderPassDrawQuadParams; - - // Returns the format to use for storage if copying from the current - // framebuffer. If the root renderpass is current, it uses the best matching - // format from the OutputSurface, otherwise it uses the best matching format - // from the texture being drawn to as the backbuffer. - GLenum GetFramebufferCopyTextureFormat(); - void ReleaseRenderPassTextures(); - enum BoundGeometry { NO_BINDING, SHARED_BINDING, CLIPPED_BINDING }; - void PrepareGeometry(BoundGeometry geometry_to_bind); - void SetStencilEnabled(bool enabled); - void SetBlendEnabled(bool enabled); - bool blend_enabled() const { return blend_shadow_; } - - // If any of the following functions returns false, then it means that drawing - // is not possible. - bool InitializeRPDQParameters(DrawRenderPassDrawQuadParams* params); - void UpdateRPDQShadersForBlending(DrawRenderPassDrawQuadParams* params); - bool UpdateRPDQWithSkiaFilters(DrawRenderPassDrawQuadParams* params); - void UpdateRPDQTexturesForSampling(DrawRenderPassDrawQuadParams* params); - void UpdateRPDQBlendMode(DrawRenderPassDrawQuadParams* params); - void ChooseRPDQProgram(DrawRenderPassDrawQuadParams* params, - const gfx::ColorSpace& target_color_space); - void UpdateRPDQUniforms(DrawRenderPassDrawQuadParams* params); - void DrawRPDQ(const DrawRenderPassDrawQuadParams& params); - - static void ToGLMatrix(float* gl_matrix, const gfx::Transform& transform); - - void DiscardPixels(); - void ClearFramebuffer(); - void SetViewport(); - - void DrawDebugBorderQuad(const DebugBorderDrawQuad* quad); - static bool IsDefaultBlendMode(SkBlendMode blend_mode) { - return blend_mode == SkBlendMode::kSrcOver; - } - bool CanApplyBlendModeUsingBlendFunc(SkBlendMode blend_mode); - void ApplyBlendModeUsingBlendFunc(SkBlendMode blend_mode); - void RestoreBlendFuncToDefault(SkBlendMode blend_mode); - - // Returns the rect that should be sampled from the backdrop texture to be - // backdrop filtered. This rect lives in window pixel space. The - // |backdrop_filter_bounds| output lives in the space of the output rect - // returned by this function. It will be used to clip the sampled backdrop - // texture. The |unclipped_rect| output is the unclipped (full) rect that the - // backdrop_filter should be applied to, in window pixel space. - gfx::Rect GetBackdropBoundingBoxForRenderPassQuad( - DrawRenderPassDrawQuadParams* params, - gfx::Transform* backdrop_filter_bounds_transform, - absl::optional<gfx::RRectF>* backdrop_filter_bounds, - gfx::Rect* unclipped_rect) const; - - // Allocates and returns a texture id that contains a copy of the contents - // of the current RenderPass being drawn. - uint32_t GetBackdropTexture(const gfx::Rect& window_rect, - float scale, - GLenum* internal_format); - - static bool ShouldApplyBackdropFilters( - const DrawRenderPassDrawQuadParams* params); - - // Applies the backdrop filters to the backdrop that has been painted to this - // point, and returns it as an SkImage. Any opacity and/or "regular" - // (non-backdrop) filters will also be applied directly to the backdrop- - // filtered image at this point, so that the final result is as if the - // filtered backdrop image was painted as the starting point for this new - // stacking context, which would then be painted into its parent with opacity - // and filters applied. This is an approximation, but it should be close - // enough. - sk_sp<SkImage> ApplyBackdropFilters( - DrawRenderPassDrawQuadParams* params, - const gfx::Rect& unclipped_rect, - const absl::optional<gfx::RRectF>& backdrop_filter_bounds, - const gfx::Transform& backdrop_filter_bounds_transform); - - // gl_renderer can bypass TileDrawQuads that fill the RenderPass - const DrawQuad* CanPassBeDrawnDirectly( - const AggregatedRenderPass* pass) override; - - void DrawRenderPassQuad(const AggregatedRenderPassDrawQuad* quadi, - const gfx::QuadF* clip_region); - void DrawRenderPassQuadInternal(DrawRenderPassDrawQuadParams* params); - void DrawSolidColorQuad(const SolidColorDrawQuad* quad, - const gfx::QuadF* clip_region); - void DrawStreamVideoQuad(const StreamVideoDrawQuad* quad, - const gfx::QuadF* clip_region); - void EnqueueTextureQuad(const TextureDrawQuad* quad, - const gfx::QuadF* clip_region); - void FlushTextureQuadCache(BoundGeometry flush_binding); - void DrawTileQuad(const TileDrawQuad* quad, const gfx::QuadF* clip_region); - void DrawContentQuad(const ContentDrawQuadBase* quad, - ResourceId resource_id, - const gfx::QuadF* clip_region); - void DrawContentQuadAA(const ContentDrawQuadBase* quad, - ResourceId resource_id, - const gfx::Transform& device_transform, - const gfx::QuadF& aa_quad, - const gfx::QuadF* clip_region); - void DrawContentQuadNoAA(const ContentDrawQuadBase* quad, - ResourceId resource_id, - const gfx::QuadF* clip_region); - void DrawYUVVideoQuad(const YUVVideoDrawQuad* quad, - const gfx::QuadF* clip_region); - - void SetShaderOpacity(float opacity); - void SetShaderQuadF(const gfx::QuadF& quad); - void SetShaderMatrix(const gfx::Transform& transform); - void SetShaderColor(SkColor color, float opacity); - void SetShaderRoundedCorner(const gfx::RRectF& rounded_corner_bounds, - const gfx::Transform& screen_transform); - void DrawQuadGeometryClippedByQuadF(const gfx::Transform& draw_transform, - const gfx::RectF& quad_rect, - const gfx::QuadF& clipping_region_quad, - const float uv[8]); - void DrawQuadGeometry(const gfx::Transform& projection_matrix, - const gfx::Transform& draw_transform, - const gfx::RectF& quad_rect); - void DrawQuadGeometryWithAA(const DrawQuad* quad, - gfx::QuadF* local_quad, - const gfx::Rect& tile_rect); - - const gfx::QuadF& SharedGeometryQuad() const { return shared_geometry_quad_; } - const StaticGeometryBinding* SharedGeometry() const { - return shared_geometry_.get(); - } - - // If |dst_color_space| is invalid, then no color conversion (apart from YUV - // to RGB conversion) is performed. This explicit argument is available so - // that video color conversion can be enabled separately from general color - // conversion. If |adjust_src_white_level| is true, then the |src_color_space| - // white levels are adjusted to the display SDR white level so that no white - // level scaling happens. |src_hdr_metadata|, if available, is the mastering - // metadata associated to the source quad. - void SetUseProgram( - const ProgramKey& program_key, - const gfx::ColorSpace& src_color_space, - const gfx::ColorSpace& dst_color_space, - bool adjust_src_white_level = false, - absl::optional<gfx::HDRMetadata> src_hdr_metadata = absl::nullopt); - - bool MakeContextCurrent(); - - void InitializeSharedObjects(); - void CleanupSharedObjects(); - - void ReinitializeGLState(); - void RestoreGLState(); - void RestoreGLStateAfterSkia(); - - // TODO(weiliangc): Once the overlay processor could schedule overlays, remove - // these functions. - // Sends over output surface information as it is a overlay plane. This is - // used for BufferQueue. For non-BufferQueue cases, this function will do - // nothing. - void ScheduleOutputSurfaceAsOverlay(); - // Schedule overlays sends overlay candidate to the GPU. -#if BUILDFLAG(IS_ANDROID) || defined(USE_OZONE) - void ScheduleOverlays(); -#elif BUILDFLAG(IS_APPLE) - void ScheduleCALayers(); - - // Schedules the |ca_layer_overlay|, which is guaranteed to have a non-null - // |rpdq| parameter. Returns ownership of a GL texture that contains the - // output of the CompositorRenderPassDrawQuad. - std::unique_ptr<OverlayTexture> ScheduleRenderPassDrawQuad( - const CALayerOverlay* ca_layer_overlay); - - // Copies the contents of the render pass draw quad, including filter effects, - // to a GL texture, returned in |overlay_texture|. The resulting texture may - // be larger than the CompositorRenderPassDrawQuad's output, in order to reuse - // existing textures. The new size and position is placed in |new_bounds|. - void CopyRenderPassDrawQuadToOverlayResource( - const CALayerOverlay* ca_layer_overlay, - std::unique_ptr<OverlayTexture>* overlay_texture, - gfx::RectF* new_bounds); - std::unique_ptr<OverlayTexture> FindOrCreateOverlayTexture( - const AggregatedRenderPassId& render_pass_id, - int width, - int height, - const gfx::ColorSpace& color_space); - void ReduceAvailableOverlayTextures(); - -#elif BUILDFLAG(IS_WIN) - void ScheduleDCLayers(); -#endif - - // Setup/flush all pending overdraw feedback to framebuffer. - void SetupOverdrawFeedback(); - - // Process overdraw feedback from query. - void ProcessOverdrawFeedback(base::CheckedNumeric<int> surface_area, - unsigned query); - bool OverdrawTracingEnabled(); - - bool CompositeTimeTracingEnabled() override; - void AddCompositeTimeTraces(base::TimeTicks ready_timestamp) override; - - ResourceFormat CurrentRenderPassResourceFormat() const; - - bool HasOutputColorMatrix() const; - - // Returns true if the given solid color draw quad can be safely drawn using - // the glClear function call. - bool CanUseFastSolidColorDraw(const SolidColorDrawQuad* quad) const; - - DisplayResourceProviderGL* resource_provider() { - return static_cast<DisplayResourceProviderGL*>(resource_provider_); - } - - // A map from RenderPass id to the texture used to draw the RenderPass from. - base::flat_map<AggregatedRenderPassId, ScopedRenderPassTexture> - render_pass_textures_; - - // A map from RenderPass id to backdrop filter cache texture. - base::flat_map<AggregatedRenderPassId, sk_sp<SkImage>> - render_pass_backdrop_textures_; - - // OverlayTextures that are free to be used in the next frame. - std::vector<std::unique_ptr<OverlayTexture>> available_overlay_textures_; - // OverlayTextures that have been set up for use but are waiting for - // SwapBuffers. - std::vector<std::unique_ptr<OverlayTexture>> awaiting_swap_overlay_textures_; - // OverlayTextures that have been swapped for display on the gpu. Each vector - // represents a single frame, and may be empty if none were used in that - // frame. Ordered from oldest to most recent frame. - std::vector<std::vector<std::unique_ptr<OverlayTexture>>> - displayed_overlay_textures_; - // OverlayTextures that we have replaced on the gpu but are awaiting - // confirmation that we can reuse them. - std::vector<std::unique_ptr<OverlayTexture>> - awaiting_release_overlay_textures_; - - // Resources that have been sent to the GPU process, but not yet swapped. - OverlayResourceLockList pending_overlay_resources_; - // Resources that should be shortly swapped by the GPU process. - base::circular_deque<OverlayResourceLockList> swapping_overlay_resources_; - - // Locks for overlays that have release fences and read lock fences. - base::circular_deque<OverlayResourceLockList> - read_lock_release_fence_overlay_locks_; - - // Resources that the GPU process has finished swapping. The key is the - // texture id of the resource. - std::map<unsigned, OverlayResourceLock> swapped_and_acked_overlay_resources_; - - // Query object, used to determine the number of sample drawn during a render - // pass. - unsigned occlusion_query_ = 0u; - - unsigned offscreen_framebuffer_id_ = 0u; - - std::unique_ptr<StaticGeometryBinding> shared_geometry_; - std::unique_ptr<DynamicGeometryBinding> clipped_geometry_; - gfx::QuadF shared_geometry_quad_; - - // This will return nullptr if the requested program has not yet been - // initialized. - const Program* GetProgramIfInitialized(const ProgramKey& key) const; - - std::unordered_map<ProgramKey, std::unique_ptr<Program>, ProgramKeyHash> - program_cache_; - - const gfx::ColorTransform* GetColorTransform(const gfx::ColorSpace& src, - const gfx::ColorSpace& dst); - struct ColorTransformKey { - gfx::ColorSpace src; - gfx::ColorSpace dst; - float sdr_max_luminance_nits = 0.f; - bool operator==(const ColorTransformKey& other) const; - bool operator!=(const ColorTransformKey& other) const; - bool operator<(const ColorTransformKey& other) const; - }; - std::map<ColorTransformKey, std::unique_ptr<gfx::ColorTransform>> - color_transform_cache_; - - raw_ptr<gpu::gles2::GLES2Interface> gl_; - raw_ptr<gpu::ContextSupport> context_support_; - std::unique_ptr<ContextCacheController::ScopedVisibility> context_visibility_; - std::unique_ptr<ContextCacheController::ScopedBusy> context_busy_; - - TextureDeleter texture_deleter_; - GLRendererCopier copier_; - - gfx::Rect swap_buffer_rect_; - std::vector<gfx::Rect> swap_content_bounds_; - gfx::Rect scissor_rect_; - bool is_scissor_enabled_ = false; - bool stencil_shadow_ = false; - bool blend_shadow_ = false; - raw_ptr<const Program> current_program_ = nullptr; - TexturedQuadDrawCache draw_cache_; - int highp_threshold_cache_ = 0; - - raw_ptr<ScopedRenderPassTexture> current_framebuffer_texture_; - - SyncQueryCollection sync_queries_; - bool use_discard_framebuffer_ = false; - bool use_sync_query_ = false; - bool use_blend_equation_advanced_ = false; - bool use_blend_equation_advanced_coherent_ = false; - bool use_timer_query_ = false; - bool use_occlusion_query_ = false; - bool use_swap_with_bounds_ = false; - bool use_fast_path_solid_color_quad_ = false; - bool supports_multi_sampling_ = false; - - // If true, tints all the composited content to red. - bool tint_gl_composited_content_ = true; - -#if BUILDFLAG(IS_APPLE) - // The method FlippedFramebuffer determines whether the framebuffer associated - // with a DrawingFrame is flipped. It makes the assumption that the - // DrawingFrame is being used as part of a render pass. If a DrawingFrame is - // not being used as part of a render pass, setting it here forces - // FlippedFramebuffer to return |true|. - bool force_drawing_frame_framebuffer_unflipped_ = false; -#endif - - BoundGeometry bound_geometry_; - - unsigned offscreen_stencil_renderbuffer_id_ = 0; - gfx::Size offscreen_stencil_renderbuffer_size_; - - unsigned num_triangles_drawn_ = 0; - bool prefer_draw_to_copy_ = false; - - // A circular queue of to keep track of timer queries and their associated - // quad type as string. - base::queue<std::pair<unsigned, std::string>> timer_queries_; - - // Keeps track of areas that have been drawn to in the current render pass. - std::vector<gfx::Rect> drawn_rects_; - - // This may be null if the compositor is run on a thread without a - // MessageLoop. - scoped_refptr<base::SingleThreadTaskRunner> current_task_runner_; - base::WeakPtrFactory<GLRenderer> weak_ptr_factory_{this}; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_H_ diff --git a/chromium/components/viz/service/display/gl_renderer_copier.cc b/chromium/components/viz/service/display/gl_renderer_copier.cc deleted file mode 100644 index bbfe5a7515c..00000000000 --- a/chromium/components/viz/service/display/gl_renderer_copier.cc +++ /dev/null @@ -1,1298 +0,0 @@ -// Copyright 2017 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/gl_renderer_copier.h" - -#include <cstring> -#include <utility> - -#include "base/bind.h" -#include "base/containers/cxx20_erase.h" -#include "base/memory/raw_ptr.h" -#include "base/process/memory.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" -#include "components/viz/common/gl_i420_converter.h" -#include "components/viz/common/gl_nv12_converter.h" -#include "components/viz/common/gl_scaler.h" -#include "components/viz/common/gpu/context_provider.h" -#include "components/viz/service/display/texture_deleter.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "gpu/command_buffer/client/context_support.h" -#include "gpu/command_buffer/client/gles2_interface.h" -#include "gpu/command_buffer/client/shared_image_interface.h" -#include "gpu/command_buffer/common/mailbox.h" -#include "gpu/command_buffer/common/shared_image_usage.h" -#include "gpu/command_buffer/common/sync_token.h" -#include "third_party/libyuv/include/libyuv/planar_functions.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkColorSpace.h" -#include "third_party/skia/include/core/SkImageInfo.h" -#include "ui/gfx/geometry/size.h" - -// Syntactic sugar to DCHECK that two sizes are equal. -#define DCHECK_SIZE_EQ(a, b) \ - DCHECK((a) == (b)) << #a " != " #b ": " << (a).ToString() \ - << " != " << (b).ToString() - -namespace viz { - -using ResultFormat = CopyOutputRequest::ResultFormat; -using ResultDestination = CopyOutputRequest::ResultDestination; - -namespace { - -constexpr int kRGBABytesPerPixel = 4; - -// Returns the source property of the |request|, if it is set. Otherwise, -// returns an empty token. This is needed because CopyOutputRequest will crash -// if source() is called when !has_source(). -base::UnguessableToken SourceOf(const CopyOutputRequest& request) { - return request.has_source() ? request.source() : base::UnguessableToken(); -} - -// Creates a new texture, binds it to the GL_TEXTURE_2D target, and initializes -// its default parameters. -GLuint CreateDefaultTexture2D(gpu::gles2::GLES2Interface* gl) { - GLuint result = 0; - gl->GenTextures(1, &result); - gl->BindTexture(GL_TEXTURE_2D, result); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - return result; -} - -// Creates or re-creates a texture, only if needed, to ensure a texture of the -// given |required| size is defined. |texture| and |size| are I/O parameters, -// read to determine what to do and updated if any changes are made. -void EnsureTextureDefinedWithSize(gpu::gles2::GLES2Interface* gl, - const gfx::Size& required, - GLuint* texture, - gfx::Size* size) { - if (*texture != 0 && *size == required) - return; - if (*texture == 0) { - *texture = CreateDefaultTexture2D(gl); - } else { - gl->BindTexture(GL_TEXTURE_2D, *texture); - } - gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, required.width(), required.height(), - 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - *size = required; -} - -// Returns parameters for the scaler to scale/transform the image in the source -// framebuffer to meet the requirements of the |request|. -GLScaler::Parameters CreateScalerParameters( - const CopyOutputRequest& request, - const gfx::ColorSpace& source_color_space, - const gfx::ColorSpace& output_color_space, - bool flipped_source) { - GLScaler::Parameters params; - - params.scale_from = request.scale_from(); - params.scale_to = request.scale_to(); - params.source_color_space = source_color_space; - params.output_color_space = output_color_space; - // For downscaling, use the GOOD quality setting (appropriate for - // thumbnailing); and, for upscaling, use the BEST quality. - const bool is_downscale_in_both_dimensions = - request.scale_to().x() < request.scale_from().x() && - request.scale_to().y() < request.scale_from().y(); - params.quality = is_downscale_in_both_dimensions - ? GLScaler::Parameters::Quality::GOOD - : GLScaler::Parameters::Quality::BEST; - params.is_flipped_source = flipped_source; - - return params; -} - -// Returns the specified offset in the form of a pointer for OpenGL's -// `glReadPixels` call. This is not a valid memory address and the pointer -// should never be dereferenced. -uint8_t* GetOffsetPointer(int offset) { - uint8_t* result = reinterpret_cast<uint8_t*>(0); - result += offset; - return result; -} - -} // namespace - -GLRendererCopier::GLRendererCopier(ContextProvider* context_provider, - TextureDeleter* texture_deleter) - : context_provider_(context_provider), texture_deleter_(texture_deleter) {} - -GLRendererCopier::~GLRendererCopier() { - for (auto& entry : cache_) - entry.second->Free(context_provider_->ContextGL()); -} - -void GLRendererCopier::CopyFromTextureOrFramebuffer( - std::unique_ptr<CopyOutputRequest> request, - const copy_output::RenderPassGeometry& geometry, - GLenum internal_format, - GLuint framebuffer_texture, - const gfx::Size& framebuffer_texture_size, - bool flipped_source, - const gfx::ColorSpace& framebuffer_color_space) { - const gfx::Rect& result_rect = geometry.result_selection; - - // If we can't convert |color_space| to a SkColorSpace for SkBitmap copy - // requests (e.g. PIECEWISE_HDR), fallback to a color transform to sRGB - // before returning the copy result. - gfx::ColorSpace dest_color_space = framebuffer_color_space; - if (!framebuffer_color_space.ToSkColorSpace() && - request->result_format() == ResultFormat::RGBA && - request->result_destination() == ResultDestination::kSystemMemory) { - dest_color_space = gfx::ColorSpace::CreateSRGB(); - } - // Fast-Path: If no transformation is necessary and no new textures need to be - // generated, read-back directly from the currently-bound framebuffer. - if (request->result_format() == ResultFormat::RGBA && - request->result_destination() == ResultDestination::kSystemMemory && - framebuffer_color_space == dest_color_space && !request->is_scaled()) { - StartReadbackFromFramebuffer(std::move(request), geometry.readback_offset, - flipped_source, false, result_rect, - dest_color_space); - return; - } - - gfx::Rect sampling_rect = geometry.sampling_bounds; - - const base::UnguessableToken requester = SourceOf(*request); - std::unique_ptr<ReusableThings> things = - TakeReusableThingsOrCreate(requester); - - // Determine the source texture: This is either the one attached to the - // framebuffer, or a copy made from the framebuffer. Its format will be the - // same as |internal_format|. - // - // TODO(crbug/767221): All of this (including some texture copies) wouldn't be - // necessary if we could query whether the currently-bound framebuffer has a - // texture attached to it, and just source from that texture directly (i.e., - // using glGetFramebufferAttachmentParameteriv() and - // glGetTexLevelParameteriv(GL_TEXTURE_WIDTH/HEIGHT)). - GLuint source_texture; - gfx::Size source_texture_size; - if (framebuffer_texture != 0) { - source_texture = framebuffer_texture; - source_texture_size = framebuffer_texture_size; - } else { - auto* const gl = context_provider_->ContextGL(); - if (things->fb_copy_texture == 0) { - things->fb_copy_texture = CreateDefaultTexture2D(gl); - things->fb_copy_texture_internal_format = static_cast<GLenum>(GL_NONE); - things->fb_copy_texture_size = gfx::Size(); - } else { - gl->BindTexture(GL_TEXTURE_2D, things->fb_copy_texture); - } - if (things->fb_copy_texture_internal_format == internal_format && - things->fb_copy_texture_size == sampling_rect.size()) { - // Copy the framebuffer pixels without redefining the texture. - gl->CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sampling_rect.x(), - sampling_rect.y(), sampling_rect.width(), - sampling_rect.height()); - } else { - // Copy the framebuffer pixels into a newly-defined texture. - gl->CopyTexImage2D(GL_TEXTURE_2D, 0, internal_format, sampling_rect.x(), - sampling_rect.y(), sampling_rect.width(), - sampling_rect.height(), 0); - things->fb_copy_texture_internal_format = internal_format; - things->fb_copy_texture_size = sampling_rect.size(); - } - source_texture = things->fb_copy_texture; - source_texture_size = sampling_rect.size(); - sampling_rect.set_origin(gfx::Point()); - } - - // Revert the Y-flipping of the sampling rect coordinates for GLScaler, which - // always assumes the source offset is assuming a origin-at-top-left - // coordinate space. - if (flipped_source) { - sampling_rect.set_y(source_texture_size.height() - sampling_rect.bottom()); - } - - switch (request->result_format()) { - case ResultFormat::RGBA: - - switch (request->result_destination()) { - case ResultDestination::kSystemMemory: - EnsureTextureDefinedWithSize( - context_provider_->ContextGL(), result_rect.size(), - &things->result_texture, &things->result_texture_size); - RenderResultTexture(*request, flipped_source, framebuffer_color_space, - dest_color_space, source_texture, - source_texture_size, sampling_rect, result_rect, - things->result_texture, things.get()); - StartReadbackFromTexture(std::move(request), result_rect, - dest_color_space, things.get()); - break; - case ResultDestination::kNativeTextures: - RenderAndSendTextureResult(std::move(request), flipped_source, - framebuffer_color_space, dest_color_space, - source_texture, source_texture_size, - sampling_rect, result_rect, things.get()); - break; - } - - break; - - case ResultFormat::I420_PLANES: { - // The optimized single-copy path, provided by GLPixelBufferI420Result, - // 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. - if (!request->SendsResultsInCurrentSequence()) { - request->set_result_task_runner(base::SequencedTaskRunnerHandle::Get()); - } - - const gfx::Rect aligned_rect = RenderI420Textures( - *request, flipped_source, framebuffer_color_space, source_texture, - source_texture_size, sampling_rect, result_rect, things.get()); - StartI420ReadbackFromTextures(std::move(request), aligned_rect, - result_rect, things.get()); - break; - } - - case ResultFormat::NV12_PLANES: { - // The optimized single-copy path, provided by GLPixelBufferNV12Result, - // requires that the result be accessed via a task in the same task runner - // sequence as the GLRendererCopier. Since NV12_PLANES requests are meant - // to be VIZ-internal, this is an acceptable limitation to enforce. - if (!request->SendsResultsInCurrentSequence()) { - request->set_result_task_runner(base::SequencedTaskRunnerHandle::Get()); - } - - const gfx::Rect aligned_rect = RenderNV12Textures( - *request, flipped_source, framebuffer_color_space, source_texture, - source_texture_size, sampling_rect, result_rect, things.get()); - StartNV12ReadbackFromTextures(std::move(request), aligned_rect, - result_rect, things.get()); - - break; - } - } - - StashReusableThingsOrDelete(requester, std::move(things)); -} - -void GLRendererCopier::FreeUnusedCachedResources() { - ++purge_counter_; - - // Purge all cache entries that should no longer be kept alive, freeing any - // resources they held. - const auto IsTooOld = [this](const decltype(cache_)::value_type& entry) { - return static_cast<int32_t>(purge_counter_ - - entry.second->purge_count_at_last_use) >= - kKeepalivePeriod; - }; - for (auto& entry : cache_) { - if (IsTooOld(entry)) - entry.second->Free(context_provider_->ContextGL()); - } - base::EraseIf(cache_, IsTooOld); -} - -void GLRendererCopier::RenderResultTexture( - const CopyOutputRequest& request, - bool flipped_source, - const gfx::ColorSpace& source_color_space, - const gfx::ColorSpace& dest_color_space, - GLuint source_texture, - const gfx::Size& source_texture_size, - const gfx::Rect& sampling_rect, - const gfx::Rect& result_rect, - GLuint result_texture, - ReusableThings* things) { - DCHECK_EQ(request.result_format(), ResultFormat::RGBA); - - GLScaler::Parameters params = CreateScalerParameters( - request, source_color_space, dest_color_space, flipped_source); - - if (request.result_destination() == ResultDestination::kSystemMemory) { - // Render the result in top-down row order, and swizzle, within the GPU so - // these things don't have to be done, less efficiently, on the CPU later. - params.flip_output = flipped_source; - params.swizzle[0] = - ShouldSwapRedAndBlueForBitmapReadback() ? GL_BGRA_EXT : GL_RGBA; - } else { - // Texture results are always in bottom-up row order. - DCHECK_EQ(request.result_destination(), ResultDestination::kNativeTextures); - params.flip_output = !flipped_source; - DCHECK_EQ(params.swizzle[0], static_cast<GLenum>(GL_RGBA)); - } - - if (!things->scaler) - things->scaler = std::make_unique<GLScaler>(context_provider_); - if (!GLScaler::ParametersAreEquivalent(params, things->scaler->params())) { - const bool is_configured = things->scaler->Configure(params); - // GLRendererCopier should never use illegal or unsupported options, nor - // be using GLScaler with an invalid GL context. - DCHECK(is_configured); - } - - const bool success = things->scaler->Scale( - source_texture, source_texture_size, sampling_rect.OffsetFromOrigin(), - result_texture, result_rect); - DCHECK(success); -} - -gfx::Rect GLRendererCopier::RenderI420Textures( - const CopyOutputRequest& request, - bool flipped_source, - const gfx::ColorSpace& source_color_space, - GLuint source_texture, - const gfx::Size& source_texture_size, - const gfx::Rect& sampling_rect, - const gfx::Rect& result_rect, - ReusableThings* things) { - DCHECK_EQ(request.result_format(), ResultFormat::I420_PLANES); - - auto* const gl = context_provider_->ContextGL(); - - // Compute required Y/U/V texture sizes and re-define them, if necessary. See - // class comments for GLI420Converter for an explanation of how planar data is - // packed into RGBA textures. - const gfx::Rect aligned_rect = GLI420Converter::ToAlignedRect(result_rect); - const gfx::Size required_luma_size(aligned_rect.width() / kRGBABytesPerPixel, - aligned_rect.height()); - const gfx::Size required_chroma_size(required_luma_size.width() / 2, - required_luma_size.height() / 2); - - EnsureTextureDefinedWithSize(gl, required_luma_size, &things->yuv_textures[0], - &things->texture_sizes[0]); - EnsureTextureDefinedWithSize(gl, required_chroma_size, - &things->yuv_textures[1], - &things->texture_sizes[1]); - EnsureTextureDefinedWithSize(gl, required_chroma_size, - &things->yuv_textures[2], - &things->texture_sizes[2]); - - GLI420Converter::Parameters params = - CreateScalerParameters(request, source_color_space, - gfx::ColorSpace::CreateREC709(), flipped_source); - // I420 readback assumes content is in top-down row order. Also, set the - // output swizzle to match the readback format so that image bitmaps don't - // have to be byte-order-swizzled on the CPU later. - params.flip_output = flipped_source; - params.swizzle[0] = GetOptimalReadbackFormat(); - - if (!things->i420_converter) { - things->i420_converter = - std::make_unique<GLI420Converter>(context_provider_); - } - if (!GLI420Converter::ParametersAreEquivalent( - params, things->i420_converter->params())) { - const bool is_configured = things->i420_converter->Configure(params); - // GLRendererCopier should never use illegal or unsupported options, nor - // be using GLI420Converter with an invalid GL context. - DCHECK(is_configured); - } - - const bool success = things->i420_converter->Convert( - source_texture, source_texture_size, sampling_rect.OffsetFromOrigin(), - aligned_rect, things->yuv_textures.data()); - DCHECK(success); - - return aligned_rect; -} - -gfx::Rect GLRendererCopier::RenderNV12Textures( - const CopyOutputRequest& request, - bool flipped_source, - const gfx::ColorSpace& source_color_space, - GLuint source_texture, - const gfx::Size& source_texture_size, - const gfx::Rect& sampling_rect, - const gfx::Rect& result_rect, - ReusableThings* things) { - DCHECK_EQ(request.result_format(), ResultFormat::NV12_PLANES); - - auto* const gl = context_provider_->ContextGL(); - - // Compute required Y/UV texture sizes and re-define them, if necessary. See - // class comments for GLNV12Converter for an explanation of how planar data is - // packed into RGBA textures. - const gfx::Rect aligned_rect = GLNV12Converter::ToAlignedRect(result_rect); - - const gfx::Size required_luma_size(aligned_rect.width() / kRGBABytesPerPixel, - aligned_rect.height()); - const gfx::Size required_chroma_size(required_luma_size.width(), - required_luma_size.height() / 2); - - EnsureTextureDefinedWithSize(gl, required_luma_size, &things->yuv_textures[0], - &things->texture_sizes[0]); - - EnsureTextureDefinedWithSize(gl, required_chroma_size, - &things->yuv_textures[1], - &things->texture_sizes[1]); - - GLNV12Converter::Parameters params = - CreateScalerParameters(request, source_color_space, - gfx::ColorSpace::CreateREC709(), flipped_source); - - // NV12 readback assumes content is in top-down row order. Also, set the - // output swizzle to match the readback format so that image bitmaps don't - // have to be byte-order-swizzled on the CPU later. - params.flip_output = flipped_source; - params.swizzle[0] = GetOptimalReadbackFormat(); - - if (!things->nv12_converter) { - things->nv12_converter = - std::make_unique<GLNV12Converter>(context_provider_); - } - if (!GLNV12Converter::ParametersAreEquivalent( - params, things->nv12_converter->params())) { - const bool is_configured = things->nv12_converter->Configure(params); - // GLRendererCopier should never use illegal or unsupported options, nor - // be using GLNV12Converter with an invalid GL context. - DCHECK(is_configured); - } - - const bool success = things->nv12_converter->Convert( - source_texture, source_texture_size, sampling_rect.OffsetFromOrigin(), - aligned_rect, things->yuv_textures.data()); - DCHECK(success); - - return aligned_rect; -} - -void GLRendererCopier::StartReadbackFromTexture( - std::unique_ptr<CopyOutputRequest> request, - const gfx::Rect& result_rect, - const gfx::ColorSpace& color_space, - ReusableThings* things) { - DCHECK_EQ(request->result_format(), ResultFormat::RGBA); - DCHECK_EQ(request->result_destination(), ResultDestination::kSystemMemory); - - auto* const gl = context_provider_->ContextGL(); - if (things->readback_framebuffer == 0) { - gl->GenFramebuffers(1, &things->readback_framebuffer); - } - gl->BindFramebuffer(GL_FRAMEBUFFER, things->readback_framebuffer); - gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - things->result_texture, 0); - StartReadbackFromFramebuffer(std::move(request), gfx::Vector2d(), false, - ShouldSwapRedAndBlueForBitmapReadback(), - result_rect, color_space); -} - -namespace { - -// This is the type of CopyOutputResult we send for RGBA readback. The -// constructor is called during on GLRendererCopier::FinishReadPixelsWorkflow(), -// thus it always have access to the GLContext. The ReadRGBAPlane and destructor -// are called asynchronously, and thus might not have access to the GLContext if -// it has been destroyed in the meantime. We use the WeakPtr to the -// GLRendererCopier as an indicator that the GLContext is still alive. If the -// access to the GLContext is lost, we treat the copy output as failed. -class GLPixelBufferRGBAResult final : public CopyOutputResult { - public: - GLPixelBufferRGBAResult(const gfx::Rect& result_rect, - const gfx::ColorSpace& color_space, - base::WeakPtr<GLRendererCopier> copier_weak_ptr, - ContextProvider* context_provider, - GLuint transfer_buffer, - bool is_upside_down, - bool swap_red_and_blue) - : CopyOutputResult(CopyOutputResult::Format::RGBA, - CopyOutputResult::Destination::kSystemMemory, - result_rect, - /*needs_lock_for_bitmap=*/false), - color_space_(color_space), - copier_weak_ptr_(std::move(copier_weak_ptr)), - context_provider_(std::move(context_provider)), - transfer_buffer_(transfer_buffer), - is_upside_down_(is_upside_down), - swap_red_and_blue_(swap_red_and_blue) {} - - ~GLPixelBufferRGBAResult() final { - if (transfer_buffer_ && copier_weak_ptr_) { - context_provider_->ContextGL()->DeleteBuffers(1, &transfer_buffer_); - } - } - - bool ReadRGBAPlane(uint8_t* dest, int stride) const final { - // If the GLRendererCopier is gone, this implies the display compositor - // which contains the GLContext is gone. Regard this copy output readback as - // failed. - if (!copier_weak_ptr_) - return false; - - const int src_bytes_per_row = size().width() * kRGBABytesPerPixel; - DCHECK_GE(stride, src_bytes_per_row); - - // No need to read from GPU memory if a cached bitmap already exists. - if (rect().IsEmpty() || cached_bitmap()->readyToDraw()) - return CopyOutputResult::ReadRGBAPlane(dest, stride); - - auto* const gl = context_provider_->ContextGL(); - gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer_); - const uint8_t* pixels = static_cast<uint8_t*>(gl->MapBufferCHROMIUM( - GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY)); - if (pixels) { - if (is_upside_down_) { - dest += (size().height() - 1) * stride; - stride = -stride; - } - const uint8_t* src = pixels; - if (swap_red_and_blue_) { - for (int y = 0; y < size().height(); - ++y, src += src_bytes_per_row, dest += stride) { - for (int x = 0; x < kRGBABytesPerPixel * size().width(); - x += kRGBABytesPerPixel) { - dest[x + 2] = src[x + 0]; - dest[x + 1] = src[x + 1]; - dest[x + 0] = src[x + 2]; - dest[x + 3] = src[x + 3]; - } - } - } else { - libyuv::CopyPlane(src, src_bytes_per_row, dest, stride, - src_bytes_per_row, size().height()); - } - gl->UnmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM); - } - gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); - return !!pixels; - } - - gfx::ColorSpace GetRGBAColorSpace() const final { return color_space_; } - - // This method is always called on the same sequence as the GLRendererCopier. - // This method will be inside Viz and has access to the WeakPtr of the - // GLRendererCopier to check whether we still have the access to an alive - // GLContext. - const SkBitmap& AsSkBitmap() const final { - if (rect().IsEmpty()) - return *cached_bitmap(); // Return "null" bitmap for empty result. - - if (cached_bitmap()->readyToDraw()) - return *cached_bitmap(); - - if (!copier_weak_ptr_) - return *cached_bitmap(); - - SkBitmap result_bitmap; - // size() was clamped to render pass or framebuffer size. If we can't - // allocate it then OOM. - auto info = SkImageInfo::MakeN32Premul( - size().width(), size().height(), GetRGBAColorSpace().ToSkColorSpace()); - if (!result_bitmap.tryAllocPixels(info, info.minRowBytes())) - base::TerminateBecauseOutOfMemory(info.computeMinByteSize()); - - ReadRGBAPlane(static_cast<uint8_t*>(result_bitmap.getPixels()), - result_bitmap.rowBytes()); - *cached_bitmap() = result_bitmap; - // Now that we have a cached bitmap, no need to read from GPU memory - // anymore. - context_provider_->ContextGL()->DeleteBuffers(1, &transfer_buffer_); - transfer_buffer_ = 0; - - return *cached_bitmap(); - } - - private: - const gfx::ColorSpace color_space_; - base::WeakPtr<GLRendererCopier> copier_weak_ptr_; - raw_ptr<ContextProvider> context_provider_; - mutable GLuint transfer_buffer_; - const bool is_upside_down_; - const bool swap_red_and_blue_; -}; -} // namespace - -GLRendererCopier::ReadPixelsWorkflow::ReadPixelsWorkflow( - std::unique_ptr<CopyOutputRequest> copy_request, - const gfx::Vector2d& readback_offset, - bool flipped_source, - bool swap_red_and_blue, - const gfx::Rect& result_rect, - const gfx::ColorSpace& color_space, - ContextProvider* context_provider, - GLenum readback_format) - : copy_request(std::move(copy_request)), - flipped_source(flipped_source), - swap_red_and_blue(swap_red_and_blue), - result_rect(result_rect), - color_space(color_space), - context_provider_(context_provider) { - DCHECK(readback_format == GL_RGBA || readback_format == GL_BGRA_EXT); - DCHECK(context_provider_); - auto* const gl = context_provider_->ContextGL(); - - // 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, - (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. - gl->GenQueriesEXT(1, &query_); - gl->BeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, query_); - gl->ReadPixels(readback_offset.x(), readback_offset.y(), result_rect.width(), - result_rect.height(), readback_format, GL_UNSIGNED_BYTE, - nullptr); - gl->EndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM); - gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); -} - -GLRendererCopier::ReadPixelsWorkflow::~ReadPixelsWorkflow() { - auto* const gl = context_provider_->ContextGL(); - gl->DeleteQueriesEXT(1, &query_); - if (transfer_buffer) - gl->DeleteBuffers(1, &transfer_buffer); -} - -// Callback for the asynchronous glReadPixels(). The pixels are read from the -// transfer buffer, and a CopyOutputResult is sent to the requestor. This would -// mark this workflow as finished, and the workflow will be cleared later. -void GLRendererCopier::FinishReadPixelsWorkflow(ReadPixelsWorkflow* workflow) { - auto result = std::make_unique<GLPixelBufferRGBAResult>( - workflow->result_rect, workflow->color_space, weak_factory_.GetWeakPtr(), - context_provider_, workflow->transfer_buffer, workflow->flipped_source, - workflow->swap_red_and_blue); - workflow->transfer_buffer = 0; // Ownerhip was transferred to the result. - if (!workflow->copy_request->SendsResultsInCurrentSequence()) { - // Force readback into a SkBitmap now, because after PostTask we don't - // have access to |context_provider_|. - auto scoped_bitmap = result->ScopedAccessSkBitmap(); - auto bitmap = scoped_bitmap.bitmap(); - } - workflow->copy_request->SendResult(std::move(result)); - const auto it = - std::find_if(read_pixels_workflows_.begin(), read_pixels_workflows_.end(), - [workflow](auto& ptr) { return ptr.get() == workflow; }); - DCHECK(it != read_pixels_workflows_.end()); - read_pixels_workflows_.erase(it); -} - -void GLRendererCopier::StartReadbackFromFramebuffer( - std::unique_ptr<CopyOutputRequest> request, - const gfx::Vector2d& readback_offset, - bool flipped_source, - bool swapped_red_and_blue, - const gfx::Rect& result_rect, - const gfx::ColorSpace& color_space) { - DCHECK_EQ(request->result_format(), ResultFormat::RGBA); - DCHECK_EQ(request->result_destination(), ResultDestination::kSystemMemory); - - read_pixels_workflows_.emplace_back(std::make_unique<ReadPixelsWorkflow>( - std::move(request), readback_offset, flipped_source, - ShouldSwapRedAndBlueForBitmapReadback() != swapped_red_and_blue, - result_rect, color_space, context_provider_, GetOptimalReadbackFormat())); - context_provider_->ContextSupport()->SignalQuery( - read_pixels_workflows_.back()->query(), - base::BindOnce(&GLRendererCopier::FinishReadPixelsWorkflow, - weak_factory_.GetWeakPtr(), - read_pixels_workflows_.back().get())); -} - -void GLRendererCopier::RenderAndSendTextureResult( - std::unique_ptr<CopyOutputRequest> request, - bool flipped_source, - const gfx::ColorSpace& source_color_space, - const gfx::ColorSpace& dest_color_space, - GLuint source_texture, - const gfx::Size& source_texture_size, - const gfx::Rect& sampling_rect, - const gfx::Rect& result_rect, - ReusableThings* things) { - DCHECK_EQ(request->result_format(), ResultFormat::RGBA); - DCHECK_EQ(request->result_destination(), ResultDestination::kNativeTextures); - - auto* sii = context_provider_->SharedImageInterface(); - gpu::Mailbox mailbox = sii->CreateSharedImage( - ResourceFormat::RGBA_8888, result_rect.size(), dest_color_space, - kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, - gpu::SHARED_IMAGE_USAGE_GLES2, gpu::kNullSurfaceHandle); - auto* gl = context_provider_->ContextGL(); - gl->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData()); - GLuint texture = gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name); - gl->BeginSharedImageAccessDirectCHROMIUM( - texture, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM); - RenderResultTexture(*request, flipped_source, source_color_space, - dest_color_space, source_texture, source_texture_size, - sampling_rect, result_rect, texture, things); - gl->EndSharedImageAccessDirectCHROMIUM(texture); - gl->DeleteTextures(1, &texture); - gpu::SyncToken sync_token; - gl->GenSyncTokenCHROMIUM(sync_token.GetData()); - - // Create a callback that deletes what was created in this GL context. - // Note: There's no need to try to pool/re-use the result texture from here, - // since only clients that are trying to re-invent video capture would see any - // significant performance benefit. Instead, such clients should use the video - // capture services provided by VIZ. - CopyOutputResult::ReleaseCallbacks release_callbacks; - release_callbacks.push_back( - texture_deleter_->GetReleaseCallback(context_provider_.get(), mailbox)); - - request->SendResult(std::make_unique<CopyOutputTextureResult>( - CopyOutputResult::Format::RGBA, result_rect, - CopyOutputResult::TextureResult(mailbox, sync_token, dest_color_space), - std::move(release_callbacks))); -} - -namespace { - -// Specialization of CopyOutputResult which reads I420 plane data from a GL -// pixel buffer object, and automatically deletes the pixel buffer object at -// destruction time. This provides an optimal one-copy data flow, from the pixel -// buffer into client-provided memory. -class GLPixelBufferI420Result final : public CopyOutputResult { - public: - // |aligned_rect| identifies the region of result pixels in the pixel buffer, - // while the |result_rect| is the subregion that is exposed to the client. - GLPixelBufferI420Result(const gfx::Rect& aligned_rect, - const gfx::Rect& result_rect, - base::WeakPtr<GLRendererCopier> copier_weak_ptr, - ContextProvider* context_provider, - GLuint transfer_buffer) - : CopyOutputResult(CopyOutputResult::Format::I420_PLANES, - CopyOutputResult::Destination::kSystemMemory, - result_rect, - /*needs_lock_for_bitmap=*/false), - aligned_rect_(aligned_rect), - copier_weak_ptr_(copier_weak_ptr), - context_provider_(context_provider), - transfer_buffer_(transfer_buffer) { - auto* const gl = context_provider_->ContextGL(); - gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer_); - pixels_ = static_cast<uint8_t*>(gl->MapBufferCHROMIUM( - GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY)); - gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); - } - - ~GLPixelBufferI420Result() final { - if (copier_weak_ptr_) { - auto* const gl = context_provider_->ContextGL(); - gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer_); - gl->UnmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM); - gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); - gl->DeleteBuffers(1, &transfer_buffer_); - } - } - - bool ReadI420Planes(uint8_t* y_out, - int y_out_stride, - uint8_t* u_out, - int u_out_stride, - uint8_t* v_out, - int v_out_stride) const final { - DCHECK_GE(y_out_stride, size().width()); - const int chroma_row_bytes = (size().width() + 1) / 2; - - DCHECK_GE(u_out_stride, chroma_row_bytes); - DCHECK_GE(v_out_stride, chroma_row_bytes); - if (!copier_weak_ptr_) - return false; - - uint8_t* pixels = pixels_; - if (pixels) { - const int y_stride = aligned_rect_.width(); - const gfx::Vector2d result_offset = - rect().OffsetFromOrigin() - aligned_rect_.OffsetFromOrigin(); - const int y_start_offset = - result_offset.y() * y_stride + result_offset.x(); - libyuv::CopyPlane(pixels + y_start_offset, y_stride, y_out, y_out_stride, - size().width(), size().height()); - pixels += y_stride * aligned_rect_.height(); - const int chroma_stride = aligned_rect_.width() / 2; - const int chroma_start_offset = - ((result_offset.y() / 2) * chroma_stride) + (result_offset.x() / 2); - const int chroma_height = (size().height() + 1) / 2; - libyuv::CopyPlane(pixels + chroma_start_offset, chroma_stride, u_out, - u_out_stride, chroma_row_bytes, chroma_height); - pixels += chroma_stride * (aligned_rect_.height() / 2); - libyuv::CopyPlane(pixels + chroma_start_offset, chroma_stride, v_out, - v_out_stride, chroma_row_bytes, chroma_height); - } - return !!pixels; - } - - private: - const gfx::Rect aligned_rect_; - base::WeakPtr<GLRendererCopier> copier_weak_ptr_; - const raw_ptr<ContextProvider> context_provider_; - const GLuint transfer_buffer_; - raw_ptr<uint8_t> pixels_; -}; - -// Specialization of CopyOutputResult which reads NV12 plane data from a GL -// pixel buffer object, and automatically deletes the pixel buffer object at -// destruction time. This provides an optimal one-copy data flow, from the pixel -// buffer into client-provided memory. -class GLPixelBufferNV12Result final : public CopyOutputResult { - public: - // |aligned_rect| identifies the region of result pixels in the pixel buffer, - // while the |result_rect| is the subregion that is exposed to the client. - GLPixelBufferNV12Result(const gfx::Rect& aligned_rect, - const gfx::Rect& result_rect, - base::WeakPtr<GLRendererCopier> copier_weak_ptr, - ContextProvider* context_provider, - GLuint transfer_buffer) - : CopyOutputResult(CopyOutputResult::Format::NV12_PLANES, - CopyOutputResult::Destination::kSystemMemory, - result_rect, - /*needs_lock_for_bitmap=*/false), - aligned_rect_(aligned_rect), - copier_weak_ptr_(copier_weak_ptr), - context_provider_(context_provider), - transfer_buffer_(transfer_buffer) { - auto* const gl = context_provider_->ContextGL(); - gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer_); - pixels_ = static_cast<uint8_t*>(gl->MapBufferCHROMIUM( - GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY)); - gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); - } - - ~GLPixelBufferNV12Result() final { - if (copier_weak_ptr_) { - auto* const gl = context_provider_->ContextGL(); - gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer_); - gl->UnmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM); - gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); - gl->DeleteBuffers(1, &transfer_buffer_); - } - } - - bool ReadNV12Planes(uint8_t* y_out, - int y_out_stride, - uint8_t* uv_out, - int uv_out_stride) const final { - DCHECK_GE(y_out_stride, size().width()); - const int chroma_row_bytes = 2 * ((size().width() + 1) / 2); - DCHECK_GE(uv_out_stride, chroma_row_bytes); - if (!copier_weak_ptr_) - return false; - - uint8_t* pixels = pixels_; - - if (pixels) { - const int y_stride = aligned_rect_.width(); - const gfx::Vector2d result_offset = - rect().OffsetFromOrigin() - aligned_rect_.OffsetFromOrigin(); - const int y_start_offset = - result_offset.y() * y_stride + result_offset.x(); - libyuv::CopyPlane(pixels + y_start_offset, y_stride, y_out, y_out_stride, - size().width(), size().height()); - pixels += y_stride * aligned_rect_.height(); - const int chroma_stride = aligned_rect_.width(); - const int chroma_start_offset = - ((result_offset.y() / 2) * chroma_stride) + - 2 * (result_offset.x() / 2); - const int chroma_height = (size().height() + 1) / 2; - libyuv::CopyPlane(pixels + chroma_start_offset, chroma_stride, uv_out, - uv_out_stride, chroma_row_bytes, chroma_height); - } - return !!pixels; - } - - private: - const gfx::Rect aligned_rect_; - base::WeakPtr<GLRendererCopier> copier_weak_ptr_; - const raw_ptr<ContextProvider> context_provider_; - const GLuint transfer_buffer_; - raw_ptr<uint8_t> pixels_ = nullptr; -}; - -} // namespace - -GLRendererCopier::ReadI420PlanesWorkflow::ReadI420PlanesWorkflow( - std::unique_ptr<CopyOutputRequest> copy_request, - const gfx::Rect& aligned_rect, - const gfx::Rect& result_rect, - base::WeakPtr<GLRendererCopier> copier_weak_ptr, - ContextProvider* context_provider) - : copy_request(std::move(copy_request)), - aligned_rect(aligned_rect), - result_rect(result_rect), - copier_weak_ptr_(copier_weak_ptr), - context_provider_(context_provider) { - // Create a buffer for the pixel transfer: A single buffer is used and will - // contain the Y plane, then the U plane, then the V plane. - auto* const gl = context_provider_->ContextGL(); - gl->GenBuffers(1, &transfer_buffer); - gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer); - 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 + 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 - // readbacks has completed. - gl->GenQueriesEXT(3, queries.data()); -} - -void GLRendererCopier::ReadI420PlanesWorkflow::BindTransferBuffer() { - DCHECK_NE(transfer_buffer, 0u); - context_provider_->ContextGL()->BindBuffer( - GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer); -} - -void GLRendererCopier::ReadI420PlanesWorkflow::StartPlaneReadback( - int plane, - GLenum readback_format) { - DCHECK_NE(queries[plane], 0u); - auto* const gl = context_provider_->ContextGL(); - gl->BeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, queries[plane]); - const gfx::Size& size = plane == 0 ? y_texture_size() : chroma_texture_size(); - // Note: While a PIXEL_PACK_BUFFER is bound, OpenGL interprets the last - // argument to ReadPixels() as a byte offset within the buffer instead of - // an actual pointer in system memory. - uint8_t* offset_in_buffer = GetOffsetPointer(data_offsets_[plane]); - gl->ReadPixels(0, 0, size.width(), size.height(), readback_format, - GL_UNSIGNED_BYTE, offset_in_buffer); - gl->EndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM); - context_provider_->ContextSupport()->SignalQuery( - queries[plane], - base::BindOnce(&GLRendererCopier::FinishReadI420PlanesWorkflow, - copier_weak_ptr_, this, plane)); -} - -void GLRendererCopier::ReadI420PlanesWorkflow::UnbindTransferBuffer() { - context_provider_->ContextGL()->BindBuffer( - GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); -} - -GLRendererCopier::ReadI420PlanesWorkflow::~ReadI420PlanesWorkflow() { - auto* const gl = context_provider_->ContextGL(); - if (transfer_buffer != 0) - gl->DeleteBuffers(1, &transfer_buffer); - for (GLuint& query : queries) { - if (query != 0) - gl->DeleteQueriesEXT(1, &query); - } -} - -gfx::Size GLRendererCopier::ReadI420PlanesWorkflow::y_texture_size() const { - return gfx::Size(aligned_rect.width() / kRGBABytesPerPixel, - aligned_rect.height()); -} - -gfx::Size GLRendererCopier::ReadI420PlanesWorkflow::chroma_texture_size() - const { - return gfx::Size(aligned_rect.width() / kRGBABytesPerPixel / 2, - aligned_rect.height() / 2); -} - -GLRendererCopier::ReadNV12PlanesWorkflow::ReadNV12PlanesWorkflow( - std::unique_ptr<CopyOutputRequest> copy_request, - const gfx::Rect& aligned_rect, - const gfx::Rect& result_rect, - base::WeakPtr<GLRendererCopier> copier_weak_ptr, - ContextProvider* context_provider) - : copy_request_(std::move(copy_request)), - aligned_rect_(aligned_rect), - result_rect_(result_rect), - copier_weak_ptr_(copier_weak_ptr), - context_provider_(context_provider) { - // Create a buffer for the pixel transfer: A single buffer is used and will - // contain the Y plane, then the UV plane. - auto* const gl = context_provider_->ContextGL(); - gl->GenBuffers(1, &transfer_buffer_); - gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer_); - 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 + chroma_plane_bytes).ValueOrDie(), nullptr, - GL_STREAM_READ); - data_offsets_ = {0, y_plane_bytes.ValueOrDie()}; - gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); - - // Generate the two queries used for determining when each of the plane - // readbacks has completed. - gl->GenQueriesEXT(2, queries_.data()); -} - -void GLRendererCopier::ReadNV12PlanesWorkflow::BindTransferBuffer() { - DCHECK_NE(transfer_buffer_, 0u); - context_provider_->ContextGL()->BindBuffer( - GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer_); -} - -void GLRendererCopier::ReadNV12PlanesWorkflow::StartPlaneReadback( - int plane, - GLenum readback_format) { - DCHECK_NE(queries_[plane], 0u); - auto* const gl = context_provider_->ContextGL(); - gl->BeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, queries_[plane]); - const gfx::Size& size = plane == 0 ? y_texture_size() : chroma_texture_size(); - // Note: While a PIXEL_PACK_BUFFER is bound, OpenGL interprets the last - // argument to ReadPixels() as a byte offset within the buffer instead of - // an actual pointer in system memory. - uint8_t* offset_in_buffer = GetOffsetPointer(data_offsets_[plane]); - gl->ReadPixels(0, 0, size.width(), size.height(), readback_format, - GL_UNSIGNED_BYTE, offset_in_buffer); - gl->EndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM); - context_provider_->ContextSupport()->SignalQuery( - queries_[plane], - base::BindOnce(&GLRendererCopier::FinishReadNV12PlanesWorkflow, - copier_weak_ptr_, this, plane)); -} - -void GLRendererCopier::ReadNV12PlanesWorkflow::UnbindTransferBuffer() { - context_provider_->ContextGL()->BindBuffer( - GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); -} - -GLRendererCopier::ReadNV12PlanesWorkflow::~ReadNV12PlanesWorkflow() { - auto* const gl = context_provider_->ContextGL(); - if (transfer_buffer_ != 0) - gl->DeleteBuffers(1, &transfer_buffer_); - for (GLuint& query : queries_) { - if (query != 0) - gl->DeleteQueriesEXT(1, &query); - } -} - -gfx::Size GLRendererCopier::ReadNV12PlanesWorkflow::y_texture_size() const { - return gfx::Size(aligned_rect_.width() / kRGBABytesPerPixel, - aligned_rect_.height()); -} - -gfx::Size GLRendererCopier::ReadNV12PlanesWorkflow::chroma_texture_size() - const { - return gfx::Size(aligned_rect_.width() / kRGBABytesPerPixel, - aligned_rect_.height() / 2); -} - -void GLRendererCopier::StartI420ReadbackFromTextures( - std::unique_ptr<CopyOutputRequest> request, - const gfx::Rect& aligned_rect, - const gfx::Rect& result_rect, - ReusableThings* things) { - DCHECK_EQ(request->result_format(), ResultFormat::I420_PLANES); - - auto* const gl = context_provider_->ContextGL(); - if (things->yuv_readback_framebuffers[0] == 0) { - gl->GenFramebuffers(3, things->yuv_readback_framebuffers.data()); - } else if (things->yuv_readback_framebuffers[2] == 0) { - gl->GenFramebuffers(1, &things->yuv_readback_framebuffers[2]); - } - - // Execute three asynchronous read-pixels operations, one for each plane. The - // CopyOutputRequest is passed to the ReadI420PlanesWorkflow, which will send - // the CopyOutputResult once all readback operations are complete. - read_i420_workflows_.emplace_back(std::make_unique<ReadI420PlanesWorkflow>( - std::move(request), aligned_rect, result_rect, weak_factory_.GetWeakPtr(), - context_provider_)); - ReadI420PlanesWorkflow* workflow = read_i420_workflows_.back().get(); - workflow->BindTransferBuffer(); - for (int plane = 0; plane < 3; ++plane) { - gl->BindFramebuffer(GL_FRAMEBUFFER, - things->yuv_readback_framebuffers[plane]); - gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, things->yuv_textures[plane], 0); - workflow->StartPlaneReadback(plane, GetOptimalReadbackFormat()); - } - workflow->UnbindTransferBuffer(); -} - -void GLRendererCopier::FinishReadI420PlanesWorkflow( - ReadI420PlanesWorkflow* workflow, - int plane) { - context_provider_->ContextGL()->DeleteQueriesEXT(1, - &workflow->queries[plane]); - workflow->queries[plane] = 0; - - // If all three readbacks have completed, send the result. - if (workflow->queries == std::array<GLuint, 3>{{0, 0, 0}}) { - workflow->copy_request->SendResult( - std::make_unique<GLPixelBufferI420Result>( - workflow->aligned_rect, workflow->result_rect, - weak_factory_.GetWeakPtr(), context_provider_, - workflow->transfer_buffer)); - workflow->transfer_buffer = 0; // Ownership was transferred to the result. - const auto it = - std::find_if(read_i420_workflows_.begin(), read_i420_workflows_.end(), - [workflow](auto& ptr) { return ptr.get() == workflow; }); - DCHECK(it != read_i420_workflows_.end()); - read_i420_workflows_.erase(it); - } -} - -void GLRendererCopier::StartNV12ReadbackFromTextures( - std::unique_ptr<CopyOutputRequest> request, - const gfx::Rect& aligned_rect, - const gfx::Rect& result_rect, - ReusableThings* things) { - DCHECK_EQ(request->result_format(), ResultFormat::NV12_PLANES); - - auto* const gl = context_provider_->ContextGL(); - if (things->yuv_readback_framebuffers[0] == 0) - gl->GenFramebuffers(2, things->yuv_readback_framebuffers.data()); - - // Execute two asynchronous read-pixels operations, one for each plane. The - // CopyOutputRequest is passed to the ReadNV12PlanesWorkflow, which will send - // the CopyOutputResult once all readback operations are complete. - read_nv12_workflows_.push_back(std::make_unique<ReadNV12PlanesWorkflow>( - std::move(request), aligned_rect, result_rect, weak_factory_.GetWeakPtr(), - context_provider_)); - ReadNV12PlanesWorkflow* workflow = read_nv12_workflows_.back().get(); - workflow->BindTransferBuffer(); - for (int plane = 0; plane < 2; ++plane) { - gl->BindFramebuffer(GL_FRAMEBUFFER, - things->yuv_readback_framebuffers[plane]); - gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, things->yuv_textures[plane], 0); - workflow->StartPlaneReadback(plane, GetOptimalReadbackFormat()); - } - workflow->UnbindTransferBuffer(); -} - -void GLRendererCopier::FinishReadNV12PlanesWorkflow( - ReadNV12PlanesWorkflow* workflow, - int plane) { - GLuint query = workflow->query(plane); - context_provider_->ContextGL()->DeleteQueriesEXT(1, &query); - workflow->MarkQueryCompleted(plane); - - // If both readbacks have completed, send the result. - if (workflow->IsCompleted()) { - workflow->TakeRequest()->SendResult( - std::make_unique<GLPixelBufferNV12Result>( - workflow->aligned_rect(), workflow->result_rect(), - weak_factory_.GetWeakPtr(), context_provider_, - workflow->TakeTransferBuffer())); - const auto it = - std::find_if(read_nv12_workflows_.begin(), read_nv12_workflows_.end(), - [workflow](auto& ptr) { return ptr.get() == workflow; }); - DCHECK(it != read_nv12_workflows_.end()); - read_nv12_workflows_.erase(it); - } -} - -std::unique_ptr<GLRendererCopier::ReusableThings> -GLRendererCopier::TakeReusableThingsOrCreate( - const base::UnguessableToken& requester) { - if (!requester.is_empty()) { - const auto it = cache_.find(requester); - if (it != cache_.end()) { - auto things = std::move(it->second); - cache_.erase(it); - return things; - } - } - - return std::make_unique<ReusableThings>(); -} - -void GLRendererCopier::StashReusableThingsOrDelete( - const base::UnguessableToken& requester, - std::unique_ptr<ReusableThings> things) { - if (requester.is_empty()) { - things->Free(context_provider_->ContextGL()); - } else { - things->purge_count_at_last_use = purge_counter_; - cache_[requester] = std::move(things); - } -} - -GLenum GLRendererCopier::GetOptimalReadbackFormat() { - if (optimal_readback_format_ != GL_NONE) - return optimal_readback_format_; - - // Preconditions: GetOptimalReadbackFormat() requires a valid context and a - // complete framebuffer set up. The latter must be guaranteed by all possible - // callers of this method. - auto* const gl = context_provider_->ContextGL(); - if (gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR) - return GL_RGBA; // No context: Just return a sane default. - DCHECK(gl->CheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); - - // If the GL implementation internally uses the GL_BGRA_EXT+GL_UNSIGNED_BYTE - // format+type combination, then consider that the optimal readback - // format+type. Otherwise, use GL_RGBA+GL_UNSIGNED_BYTE, which all platforms - // must support, per the GLES 2.0 spec. - GLint type = 0; - GLint readback_format = 0; - gl->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &type); - if (type == GL_UNSIGNED_BYTE) - gl->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readback_format); - if (readback_format != GL_BGRA_EXT) - readback_format = GL_RGBA; - - optimal_readback_format_ = static_cast<GLenum>(readback_format); - return optimal_readback_format_; -} - -bool GLRendererCopier::ShouldSwapRedAndBlueForBitmapReadback() { - const bool skbitmap_is_bgra = (kN32_SkColorType == kBGRA_8888_SkColorType); - const bool readback_will_be_bgra = - (GetOptimalReadbackFormat() == GL_BGRA_EXT); - return skbitmap_is_bgra != readback_will_be_bgra; -} - -GLRendererCopier::ReusableThings::ReusableThings() = default; - -GLRendererCopier::ReusableThings::~ReusableThings() { - // Ensure all resources were freed by this point. Resources aren't explicity - // freed here, in the destructor, because some require access to the GL - // context. See Free(). - DCHECK_EQ(fb_copy_texture, 0u); - DCHECK(!scaler); - DCHECK_EQ(result_texture, 0u); - DCHECK_EQ(readback_framebuffer, 0u); - DCHECK(!i420_converter); - constexpr std::array<GLuint, 3> kAllZeros = {0, 0, 0}; - DCHECK(yuv_textures == kAllZeros); - DCHECK(yuv_readback_framebuffers == kAllZeros); -} - -void GLRendererCopier::ReusableThings::Free(gpu::gles2::GLES2Interface* gl) { - if (fb_copy_texture != 0) { - gl->DeleteTextures(1, &fb_copy_texture); - fb_copy_texture = 0; - fb_copy_texture_internal_format = static_cast<GLenum>(GL_NONE); - fb_copy_texture_size = gfx::Size(); - } - scaler.reset(); - if (result_texture != 0) { - gl->DeleteTextures(1, &result_texture); - result_texture = 0; - result_texture_size = gfx::Size(); - } - if (readback_framebuffer != 0) { - gl->DeleteFramebuffers(1, &readback_framebuffer); - readback_framebuffer = 0; - } - - i420_converter.reset(); - nv12_converter.reset(); - - if (yuv_textures[0] != 0) { - // We have some cached textures, check if there's 2 or 3 & delete them: - int num_textures = yuv_textures[2] != 0 ? 3 : 2; - gl->DeleteTextures(num_textures, yuv_textures.data()); - yuv_textures = {0, 0, 0}; - texture_sizes = {}; - } - if (yuv_readback_framebuffers[0] != 0) { - // We have some cached readback buffers, check if there's 2 or 3 & delete - // them: - int num_readback_buffers = yuv_readback_framebuffers[2] != 0 ? 3 : 2; - gl->DeleteFramebuffers(num_readback_buffers, - yuv_readback_framebuffers.data()); - yuv_readback_framebuffers = {0, 0, 0}; - } -} - -} // namespace viz diff --git a/chromium/components/viz/service/display/gl_renderer_copier.h b/chromium/components/viz/service/display/gl_renderer_copier.h deleted file mode 100644 index e0952de31f3..00000000000 --- a/chromium/components/viz/service/display/gl_renderer_copier.h +++ /dev/null @@ -1,485 +0,0 @@ -// Copyright 2017 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_GL_RENDERER_COPIER_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_COPIER_H_ - -#include <stdint.h> - -#include <array> -#include <memory> -#include <utility> -#include <vector> - -#include "base/callback.h" -#include "base/containers/flat_map.h" -#include "base/memory/raw_ptr.h" -#include "base/memory/ref_counted.h" -#include "base/task/task_runner.h" -#include "base/unguessable_token.h" -#include "components/viz/service/viz_service_export.h" -#include "ui/gfx/color_space.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/geometry/size.h" - -namespace gfx { -class ColorSpace; -class Rect; -class Vector2d; -} // namespace gfx - -namespace gpu { -namespace gles2 { -class GLES2Interface; -} -} // namespace gpu - -namespace viz { - -class ContextProvider; -class CopyOutputRequest; -class GLI420Converter; -class GLNV12Converter; -class GLScaler; -class TextureDeleter; - -namespace copy_output { -struct RenderPassGeometry; -} // namespace copy_output - -// Helper class for GLRenderer that executes CopyOutputRequests using GL, to -// perform texture copies/transformations and read back bitmaps. Also manages -// the caching of resources needed to ensure efficient video performance. -// -// GLRenderer calls CopyFromTextureOrFramebuffer() to execute a -// CopyOutputRequest. GLRendererCopier will examine the request and determine -// the minimal amount of work needed to satisfy all the requirements of the -// request. -// -// In many cases, interim GL objects (textures, framebuffers, etc.) must be -// created as part of a multi-step process. When considering video performance -// (i.e., a series of CopyOutputRequests from the same "source"), these interim -// objects must be cached to prevent a significant performance penalty on some -// GPU/drivers. GLRendererCopier manages such a cache and automatically frees -// the objects when it detects that a stream of CopyOutputRequests from a given -// "source" has ended. -class VIZ_SERVICE_EXPORT GLRendererCopier { - public: - // Define types to avoid pulling in command buffer GL headers, which conflict - // the ui/gl/gl_bindings.h - using GLuint = unsigned int; - using GLenum = unsigned int; - - // |context_provider| and |texture_deleter| must outlive this instance. - GLRendererCopier(ContextProvider* context_provider, - TextureDeleter* texture_deleter); - - GLRendererCopier(const GLRendererCopier&) = delete; - GLRendererCopier& operator=(const GLRendererCopier&) = delete; - - ~GLRendererCopier(); - - // Executes the |request|, copying from the currently-bound framebuffer of the - // given |internal_format|. |output_rect| is the RenderPass's output Rect in - // draw space, and is used to translate and clip the result selection Rect in - // the request. |framebuffer_texture| and |framebuffer_texture_size| are - // optional, but desired for performance: If provided, the texture might be - // used as the source, to avoid having to make a copy of the framebuffer. - // |flipped_source| is true (common case) if the framebuffer content is - // vertically flipped (bottom-up row order). |framebuffer_color_space| - // specifies the color space of the pixels in the framebuffer. - // - // This implementation may change a wide variety of GL state, such as texture - // and framebuffer bindings, shader programs, and related attributes; and so - // the caller must not make any assumptions about the state of the GL context - // after this call. - void CopyFromTextureOrFramebuffer( - std::unique_ptr<CopyOutputRequest> request, - const copy_output::RenderPassGeometry& geometry, - GLenum internal_format, - GLuint framebuffer_texture, - const gfx::Size& framebuffer_texture_size, - bool flipped_source, - const gfx::ColorSpace& framebuffer_color_space); - - // Checks whether cached resources should be freed because recent copy - // activity is no longer using them. This should be called after a frame has - // finished drawing (after all copy requests have been executed). - void FreeUnusedCachedResources(); - - private: - friend class GLRendererCopierTest; - - // The collection of resources that might be cached over multiple copy - // requests from the same source. While executing a CopyOutputRequest, this - // struct is also used to pass around intermediate objects between operations. - struct VIZ_SERVICE_EXPORT ReusableThings { - // This is used to determine whether these things aren't being used anymore. - uint32_t purge_count_at_last_use = 0; - - // Texture containing a copy of the source framebuffer, if the source - // framebuffer cannot be used directly. - GLuint fb_copy_texture = 0; - GLenum fb_copy_texture_internal_format = static_cast<GLenum>(0 /*GL_NONE*/); - gfx::Size fb_copy_texture_size; - - // RGBA requests: Scaling, and texture/framebuffer for readback. - std::unique_ptr<GLScaler> scaler; - GLuint result_texture = 0; - gfx::Size result_texture_size; - GLuint readback_framebuffer = 0; - - // I420_PLANES & NV12_PLANES requests: I420, NV12 scaling and format - // conversion, and textures+framebuffers for readback. - std::unique_ptr<GLI420Converter> i420_converter; - std::unique_ptr<GLNV12Converter> nv12_converter; - std::array<GLuint, 3> yuv_textures = {0, 0, 0}; - std::array<gfx::Size, 3> texture_sizes; - std::array<GLuint, 3> yuv_readback_framebuffers = {0, 0, 0}; - - ReusableThings(); - - ReusableThings(const ReusableThings&) = delete; - ReusableThings& operator=(const ReusableThings&) = delete; - - ~ReusableThings(); - - // Frees all the GL objects and scalers. This is in-lieu of a ReusableThings - // destructor because a valid GL context is required to free some of the - // objects. - void Free(gpu::gles2::GLES2Interface* gl); - }; - - // Manages the execution of one asynchronous framebuffer readback and contains - // all the relevant state needed to complete a copy request. The constructor - // initiates the operation, and the destructor cleans up all the GL objects - // created for this workflow. This class is owned by the GLRendererCopier, and - // GLRendererCopier is responsible for deleting this either after the workflow - // is finished, or when the GLRendererCopier is being destroyed. - struct ReadPixelsWorkflow { - public: - // Saves all revelant state and initiates the GL asynchronous read-pixels - // workflow. - ReadPixelsWorkflow(std::unique_ptr<CopyOutputRequest> copy_request, - const gfx::Vector2d& readback_offset, - bool flipped_source, - bool swap_red_and_blue, - const gfx::Rect& result_rect, - const gfx::ColorSpace& color_space, - ContextProvider* context_provider, - GLenum readback_format); - ReadPixelsWorkflow(const ReadPixelsWorkflow&) = delete; - - // The destructor is by the GLRendererCopier, either called after the - // workflow is finished or when GLRendererCopier is being destoryed. - ~ReadPixelsWorkflow(); - - GLuint query() const { return query_; } - - const std::unique_ptr<CopyOutputRequest> copy_request; - const bool flipped_source; - const bool swap_red_and_blue; - const gfx::Rect result_rect; - const gfx::ColorSpace color_space; - GLuint transfer_buffer = 0; - - private: - const raw_ptr<ContextProvider> context_provider_; - GLuint query_ = 0; - }; - - // Renders a scaled/transformed copy of a source texture according to the - // |request| parameters and other source characteristics. |result_texture| - // must be allocated/sized by the caller. For RGBA requests with destination - // set to system memory, the image content will be rendered in top-down row - // order and maybe red-blue swapped, to support efficient readback later on. - // For RGBA requests with ResultDestination::kNativeTextures set, the image - // content is always rendered Y-flipped (bottom-up row order). - void RenderResultTexture(const CopyOutputRequest& request, - bool flipped_source, - const gfx::ColorSpace& source_color_space, - const gfx::ColorSpace& dest_color_space, - GLuint source_texture, - const gfx::Size& source_texture_size, - const gfx::Rect& sampling_rect, - const gfx::Rect& result_rect, - GLuint result_texture, - ReusableThings* things); - - // Like the ReadPixelsWorkflow, except for I420 planes readback. Because there - // are three separate glReadPixels operations that may complete in any order, - // a ReadI420PlanesWorkflow will receive notifications from three separate "GL - // query" callbacks. It is only after all three operations have completed that - // a fully-assembled CopyOutputResult can be sent. - // - // See class comments for GLI420Converter for an explanation of how - // planar data is packed into RGBA textures. - struct ReadI420PlanesWorkflow { - public: - ReadI420PlanesWorkflow(std::unique_ptr<CopyOutputRequest> copy_request, - const gfx::Rect& aligned_rect, - const gfx::Rect& result_rect, - base::WeakPtr<GLRendererCopier> copier_weak_ptr, - ContextProvider* context_provider); - - void BindTransferBuffer(); - void StartPlaneReadback(int plane, GLenum readback_format); - void UnbindTransferBuffer(); - - ~ReadI420PlanesWorkflow(); - - const std::unique_ptr<CopyOutputRequest> copy_request; - const gfx::Rect aligned_rect; - const gfx::Rect result_rect; - GLuint transfer_buffer; - std::array<GLuint, 3> queries; - - private: - gfx::Size y_texture_size() const; - gfx::Size chroma_texture_size() const; - - base::WeakPtr<GLRendererCopier> copier_weak_ptr_; - const raw_ptr<ContextProvider> context_provider_; - std::array<int, 3> data_offsets_; - }; - - // Like the ReadPixelsWorkflow, except for NV12 planes readback. Because there - // are two separate glReadPixels operations that may complete in any order, - // a ReadNV12PlanesWorkflow will receive notifications from two separate "GL - // query" callbacks. It is only after all two operations have completed that - // a fully-assembled CopyOutputResult can be sent. - // - // See class comments for GLNV12Converter for an explanation of how planar - // data is packed into RGBA textures. - class ReadNV12PlanesWorkflow { - public: - ReadNV12PlanesWorkflow(std::unique_ptr<CopyOutputRequest> copy_request, - const gfx::Rect& aligned_rect, - const gfx::Rect& result_rect, - base::WeakPtr<GLRendererCopier> copier_weak_ptr, - ContextProvider* context_provider); - ~ReadNV12PlanesWorkflow(); - - void BindTransferBuffer(); - void StartPlaneReadback(int plane, GLenum readback_format); - void UnbindTransferBuffer(); - - gfx::Rect aligned_rect() const { return aligned_rect_; } - - gfx::Rect result_rect() const { return result_rect_; } - - std::unique_ptr<CopyOutputRequest> TakeRequest() { - DCHECK(copy_request_); - - return std::move(copy_request_); - } - - GLuint TakeTransferBuffer() { - DCHECK(transfer_buffer_); - - GLuint result = transfer_buffer_; - transfer_buffer_ = 0; - return result; - } - - // Returns true if the workflow has completed (i.e. readback requests for - // all planes have finished). - bool IsCompleted() const { - return queries_ == std::array<GLuint, 2>{{0, 0}}; - } - - GLuint query(int plane) { return queries_[plane]; } - - // Marks that a readback has completed for a given plane. - void MarkQueryCompleted(int plane) { queries_[plane] = 0; } - - private: - gfx::Size y_texture_size() const; - gfx::Size chroma_texture_size() const; - - std::unique_ptr<CopyOutputRequest> copy_request_; - const gfx::Rect aligned_rect_; - const gfx::Rect result_rect_; - GLuint transfer_buffer_; - std::array<GLuint, 2> queries_; - - base::WeakPtr<GLRendererCopier> copier_weak_ptr_; - const raw_ptr<ContextProvider> context_provider_; - std::array<int, 2> data_offsets_; - }; - - // Similar to RenderResultTexture(), except also transform the image into I420 - // format (a popular video format). Three textures, representing each of the - // Y/U/V planes (as described in GLI420Converter), are populated and their GL - // references placed in |things|. The image content is always rendered in - // top-down row order and swizzled (if needed), to support efficient readback - // later on. - // - // For alignment reasons, sometimes a slightly larger result will be provided, - // and the return Rect will indicate the actual bounds that were rendered - // (|result_rect|'s coordinate system). See StartI420ReadbackFromTextures() - // for more details. - gfx::Rect RenderI420Textures(const CopyOutputRequest& request, - bool flipped_source, - const gfx::ColorSpace& source_color_space, - GLuint source_texture, - const gfx::Size& source_texture_size, - const gfx::Rect& sampling_rect, - const gfx::Rect& result_rect, - ReusableThings* things); - - // Similar to RenderResultTexture(), except also transform the image into NV12 - // format (a popular video format). Two textures, representing each of the - // Y/UV planes (as described in GLNV12Converter), are populated and their GL - // references placed in |things|. The image content is always rendered in - // top-down row order and swizzled (if needed), to support efficient readback - // later on. - // - // For alignment reasons, sometimes a slightly larger result will be provided, - // and the return Rect will indicate the actual bounds that were rendered - // (|result_rect|'s coordinate system). See StartNV12ReadbackFromTextures() - // for more details. - gfx::Rect RenderNV12Textures(const CopyOutputRequest& request, - bool flipped_source, - const gfx::ColorSpace& source_color_space, - GLuint source_texture, - const gfx::Size& source_texture_size, - const gfx::Rect& sampling_rect, - const gfx::Rect& result_rect, - ReusableThings* things); - - // Binds the |things->result_texture| to a framebuffer and calls - // StartReadbackFromFramebuffer(). This is only for RGBA requests with - // destination set to kSystemMemory. - // Assumes the image content is in top-down row order (and is red-blue swapped - // iff RenderResultTexture() would have done that). - void StartReadbackFromTexture(std::unique_ptr<CopyOutputRequest> request, - const gfx::Rect& result_rect, - const gfx::ColorSpace& color_space, - ReusableThings* things); - - // Processes the next phase of the copy request by starting readback from the - // currently-bound framebuffer into a pixel transfer buffer. |readback_offset| - // is the origin of the readback rect within the framebuffer, with - // |result_rect| providing the size of the readback rect. |flipped_source| is - // true if the framebuffer content is in bottom-up row order, and - // |swapped_red_and_blue| specifies whether the red and blue channels have - // been swapped. This method kicks-off an asynchronous glReadPixels() - // workflow. - void StartReadbackFromFramebuffer(std::unique_ptr<CopyOutputRequest> request, - const gfx::Vector2d& readback_offset, - bool flipped_source, - bool swapped_red_and_blue, - const gfx::Rect& result_rect, - const gfx::ColorSpace& color_space); - - // Renders a scaled/transformed copy of a source texture similarly to - // RenderResultTexture, but packages up the result in a mailbox and sends it - // as the result to the CopyOutputRequest. - void RenderAndSendTextureResult(std::unique_ptr<CopyOutputRequest> request, - bool flipped_source, - const gfx::ColorSpace& source_color_space, - const gfx::ColorSpace& dest_color_space, - GLuint source_texture, - const gfx::Size& source_texture_size, - const gfx::Rect& sampling_rect, - const gfx::Rect& result_rect, - ReusableThings* things); - - // Like StartReadbackFromTexture(), except that this processes the three Y/U/V - // result textures in |things| by using three framebuffers and three - // asynchronous readback operations. A single pixel transfer buffer is used to - // hold the results of all three readbacks (i.e., each plane starts at a - // different offset in the transfer buffer). - // - // |aligned_rect| is the Rect returned from the RenderI420Textures() call, and - // is required so that the CopyOutputResult sent at the end of this workflow - // will access the correct region of pixels. - void StartI420ReadbackFromTextures(std::unique_ptr<CopyOutputRequest> request, - const gfx::Rect& aligned_rect, - const gfx::Rect& result_rect, - ReusableThings* things); - - // Like StartReadbackFromTexture(), except that this processes the two Y/UV - // result textures in |things| by using two framebuffers and two asynchronous - // readback operations. A single pixel transfer buffer is used to hold the - // results of both readbacks (i.e., each plane starts at a different offset in - // the transfer buffer). - // - // |aligned_rect| is the Rect returned from the RenderNV12Textures() call, and - // is required so that the CopyOutputResult sent at the end of this workflow - // will access the correct region of pixels. - void StartNV12ReadbackFromTextures(std::unique_ptr<CopyOutputRequest> request, - const gfx::Rect& aligned_rect, - const gfx::Rect& result_rect, - ReusableThings* things); - - // Retrieves a cached ReusableThings instance for the given CopyOutputRequest - // source, or creates a new instance. - std::unique_ptr<ReusableThings> TakeReusableThingsOrCreate( - const base::UnguessableToken& requester); - - // If |requester| is a valid UnguessableToken, this stashes the given - // ReusableThings instance in the cache for use in future CopyOutputRequests - // from the same requester. Otherwise, |things| is freed. - void StashReusableThingsOrDelete(const base::UnguessableToken& requester, - std::unique_ptr<ReusableThings> things); - - // Queries the GL implementation to determine which is the more performance- - // optimal supported readback format: GL_RGBA or GL_BGRA_EXT, and memoizes the - // result for all future calls. - // - // Precondition: The GL context has a complete, bound framebuffer ready for - // readback. - GLenum GetOptimalReadbackFormat(); - - // Returns true if the red and blue channels should be swapped within the GPU, - // where such an operation has negligible cost, so that later the red-blue - // swap does not need to happen on the CPU (non-negligible cost). - bool ShouldSwapRedAndBlueForBitmapReadback(); - - void FinishReadPixelsWorkflow(ReadPixelsWorkflow*); - void FinishReadI420PlanesWorkflow(ReadI420PlanesWorkflow*, int plane); - void FinishReadNV12PlanesWorkflow(ReadNV12PlanesWorkflow* workflow, - int plane); - - // Injected dependencies. - const raw_ptr<ContextProvider> context_provider_; - const raw_ptr<TextureDeleter> texture_deleter_; - - // This increments by one for every call to FreeUnusedCachedResources(). It - // is meant to determine when cached resources should be freed because they - // are unlikely to see further use. - uint32_t purge_counter_ = 0; - - // A cache of resources recently used in the execution of a stream of copy - // requests from the same source. Since this reflects the number of active - // video captures, it is expected to almost always be zero or one entry in - // size. - base::flat_map<base::UnguessableToken, std::unique_ptr<ReusableThings>> - cache_; - - // This specifies whether the GPU+driver combination executes readback more - // efficiently using GL_RGBA or GL_BGRA_EXT format. This starts out as - // GL_NONE, which means "unknown," and will be determined at the time the - // first readback request is made. - GLenum optimal_readback_format_ = static_cast<GLenum>(0 /*GL_NONE*/); - - // Purge cache entries that have not been used after this many calls to - // FreeUnusedCachedResources(). The choice of 60 is arbitrary, but on most - // platforms means that a somewhat-to-fully active compositor will cause - // things to be auto-purged after approx. 1-2 seconds of not being used. - static constexpr int kKeepalivePeriod = 60; - - std::vector<std::unique_ptr<ReadPixelsWorkflow>> read_pixels_workflows_; - std::vector<std::unique_ptr<ReadI420PlanesWorkflow>> read_i420_workflows_; - std::vector<std::unique_ptr<ReadNV12PlanesWorkflow>> read_nv12_workflows_; - - // Weak ptr to this class. - base::WeakPtrFactory<GLRendererCopier> weak_factory_{this}; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_COPIER_H_ diff --git a/chromium/components/viz/service/display/gl_renderer_copier_perftest.cc b/chromium/components/viz/service/display/gl_renderer_copier_perftest.cc deleted file mode 100644 index 853f61e9fe9..00000000000 --- a/chromium/components/viz/service/display/gl_renderer_copier_perftest.cc +++ /dev/null @@ -1,338 +0,0 @@ -// Copyright 2019 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 "base/memory/raw_ptr.h" -#include "components/viz/service/display/gl_renderer_copier.h" - -#include "base/bind.h" -#include "base/files/file_path.h" -#include "base/path_service.h" -#include "base/run_loop.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/timer/lap_timer.h" -#include "cc/test/pixel_test_utils.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" -#include "components/viz/service/display/gl_renderer.h" -#include "components/viz/test/paths.h" -#include "components/viz/test/test_in_process_context_provider.h" -#include "gpu/command_buffer/client/gles2_interface.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/perf/perf_result_reporter.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "ui/gfx/color_space.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/geometry/size.h" - -namespace viz { - -namespace { - -// The size of the source texture or framebuffer. -constexpr gfx::Size kSourceSize = gfx::Size(240, 120); - -// In order to test coordinate calculations and Y-flipping, the tests will issue -// copy requests for a small region just to the right and below the center of -// the entire source texture/framebuffer. -constexpr gfx::Rect kRequestArea = gfx::Rect(kSourceSize.width() / 2, - kSourceSize.height() / 2, - kSourceSize.width() / 4, - kSourceSize.height() / 4); - -constexpr char kMetricPrefixGLRendererCopier[] = "GLRendererCopier."; -constexpr char kMetricReadbackThroughputRunsPerS[] = "readback_throughput"; - -perf_test::PerfResultReporter SetUpGLRendererCopierReporter( - const std::string& story) { - perf_test::PerfResultReporter reporter(kMetricPrefixGLRendererCopier, story); - reporter.RegisterImportantMetric(kMetricReadbackThroughputRunsPerS, "runs/s"); - return reporter; -} - -base::FilePath GetTestFilePath(const base::FilePath::CharType* basename) { - base::FilePath test_dir; - base::PathService::Get(Paths::DIR_TEST_DATA, &test_dir); - return test_dir.Append(base::FilePath(basename)); -} - -} // namespace - -class GLRendererCopierPerfTest : public testing::Test { - public: - GLRendererCopierPerfTest() { - context_provider_ = base::MakeRefCounted<TestInProcessContextProvider>( - TestContextType::kGLES2, /*support_locking=*/false); - gpu::ContextResult result = context_provider_->BindToCurrentThread(); - DCHECK_EQ(result, gpu::ContextResult::kSuccess); - gl_ = context_provider_->ContextGL(); - texture_deleter_ = - std::make_unique<TextureDeleter>(base::ThreadTaskRunnerHandle::Get()); - copier_ = std::make_unique<GLRendererCopier>(context_provider_.get(), - texture_deleter_.get()); - } - - GLRendererCopierPerfTest(const GLRendererCopierPerfTest&) = delete; - GLRendererCopierPerfTest& operator=(const GLRendererCopierPerfTest&) = delete; - - void TearDown() override { - DeleteSourceFramebuffer(); - DeleteSourceTexture(); - copier_.reset(); - texture_deleter_.reset(); - } - - gfx::Rect DrawToWindowSpace(const gfx::Rect& draw_rect, bool flipped_source) { - gfx::Rect window_rect = draw_rect; - if (flipped_source) - window_rect.set_y(kSourceSize.height() - window_rect.bottom()); - return window_rect; - } - - // Creates a packed RGBA (bytes_per_pixel=4) bitmap in OpenGL byte/row order - // from the given SkBitmap. - std::unique_ptr<uint8_t[]> CreateGLPixelsFromSkBitmap(SkBitmap bitmap, - bool flipped_source) { - // |bitmap| could be of any color type (and is usually BGRA). Convert it to - // a RGBA bitmap in the GL byte order. - SkBitmap rgba_bitmap; - rgba_bitmap.allocPixels(SkImageInfo::Make(bitmap.width(), bitmap.height(), - kRGBA_8888_SkColorType, - kPremul_SkAlphaType)); - SkPixmap pixmap; - const bool success = - bitmap.peekPixels(&pixmap) && rgba_bitmap.writePixels(pixmap, 0, 0); - CHECK(success); - - // Copy the RGBA bitmap into a raw byte array, reversing the row order and - // maybe stripping-out the alpha channel. - const int bytes_per_pixel = 4; - std::unique_ptr<uint8_t[]> pixels( - new uint8_t[rgba_bitmap.width() * rgba_bitmap.height() * - bytes_per_pixel]); - for (int y = 0; y < rgba_bitmap.height(); ++y) { - const uint8_t* src = static_cast<uint8_t*>(rgba_bitmap.getAddr(0, y)); - const int flipped_y = flipped_source ? rgba_bitmap.height() - y - 1 : y; - uint8_t* dest = - pixels.get() + flipped_y * rgba_bitmap.width() * bytes_per_pixel; - for (int x = 0; x < rgba_bitmap.width(); ++x) { - *(dest++) = *(src++); - *(dest++) = *(src++); - *(dest++) = *(src++); - if (bytes_per_pixel == 4) - *(dest++) = *(src++); - else - ++src; - } - } - - return pixels; - } - - GLuint CreateSourceTexture(bool flipped_source) { - CHECK_EQ(0u, source_texture_); - SkBitmap source_bitmap; - cc::ReadPNGFile(GetTestFilePath(FILE_PATH_LITERAL("16_color_rects.png")), - &source_bitmap); - source_bitmap.setImmutable(); - gl_->GenTextures(1, &source_texture_); - gl_->BindTexture(GL_TEXTURE_2D, source_texture_); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - gl_->TexImage2D( - GL_TEXTURE_2D, 0, static_cast<GLenum>(GL_RGBA), kSourceSize.width(), - kSourceSize.height(), 0, static_cast<GLenum>(GL_RGBA), GL_UNSIGNED_BYTE, - CreateGLPixelsFromSkBitmap(source_bitmap, flipped_source).get()); - gl_->BindTexture(GL_TEXTURE_2D, 0); - return source_texture_; - } - - void DeleteSourceTexture() { - if (source_texture_ != 0) { - gl_->DeleteTextures(1, &source_texture_); - source_texture_ = 0; - } - } - - void CreateAndBindSourceFramebuffer(GLuint texture) { - ASSERT_EQ(0u, source_framebuffer_); - gl_->GenFramebuffers(1, &source_framebuffer_); - gl_->BindFramebuffer(GL_FRAMEBUFFER, source_framebuffer_); - gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, texture, 0); - } - - void DeleteSourceFramebuffer() { - if (source_framebuffer_ != 0) { - gl_->DeleteFramebuffers(1, &source_framebuffer_); - source_framebuffer_ = 0; - } - } - - void CopyFromTextureOrFramebuffer( - bool have_source_texture, - CopyOutputResult::Format result_format, - CopyOutputResult::Destination result_destination, - bool scale_by_half, - bool flipped_source, - const std::string& story) { - std::unique_ptr<CopyOutputResult> result; - - gfx::Rect result_selection(kRequestArea); - if (scale_by_half) - result_selection = gfx::ScaleToEnclosingRect(result_selection, 0.5f); - - copy_output::RenderPassGeometry geometry; - // geometry.result_bounds not used by GLRendererCopier - geometry.sampling_bounds = - DrawToWindowSpace(gfx::Rect(kSourceSize), flipped_source); - geometry.result_selection = result_selection; - geometry.readback_offset = - DrawToWindowSpace(geometry.result_selection, flipped_source) - .OffsetFromOrigin(); - - timer_.Reset(); - do { - base::RunLoop loop; - auto request = std::make_unique<CopyOutputRequest>( - result_format, result_destination, - base::BindOnce( - [](std::unique_ptr<CopyOutputResult>* result, - base::OnceClosure quit_closure, - std::unique_ptr<CopyOutputResult> result_from_copier) { - *result = std::move(result_from_copier); - std::move(quit_closure).Run(); - }, - &result, loop.QuitClosure())); - if (scale_by_half) - request->SetUniformScaleRatio(2, 1); - const GLuint source_texture = CreateSourceTexture(flipped_source); - CreateAndBindSourceFramebuffer(source_texture); - - copier_->CopyFromTextureOrFramebuffer( - std::move(request), geometry, static_cast<GLenum>(GL_RGBA), - have_source_texture ? source_texture : 0, kSourceSize, flipped_source, - gfx::ColorSpace::CreateSRGB()); - loop.Run(); - - // Check that a result was produced and is of the expected rect/size. - ASSERT_TRUE(result); - ASSERT_FALSE(result->IsEmpty()); - if (scale_by_half) - ASSERT_EQ(gfx::ScaleToEnclosingRect(kRequestArea, 0.5f), - result->rect()); - else - ASSERT_EQ(kRequestArea, result->rect()); - - if (result_format == CopyOutputResult::Format::RGBA && - result_destination == CopyOutputResult::Destination::kSystemMemory) { - auto scoped_bitmap = result->ScopedAccessSkBitmap(); - const SkBitmap& result_bitmap = scoped_bitmap.bitmap(); - ASSERT_TRUE(result_bitmap.readyToDraw()); - } else if (result_format == CopyOutputResult::Format::I420_PLANES) { - const int result_width = result->rect().width(); - const int result_height = result->rect().height(); - const int y_width = result_width; - const int y_stride = y_width; - std::unique_ptr<uint8_t[]> y_data( - new uint8_t[y_stride * result_height]); - const int chroma_width = (result_width + 1) / 2; - const int u_stride = chroma_width; - const int v_stride = chroma_width; - const int chroma_height = (result_height + 1) / 2; - std::unique_ptr<uint8_t[]> u_data( - new uint8_t[u_stride * chroma_height]); - std::unique_ptr<uint8_t[]> v_data( - new uint8_t[v_stride * chroma_height]); - - const bool success = - result->ReadI420Planes(y_data.get(), y_stride, u_data.get(), - u_stride, v_data.get(), v_stride); - ASSERT_TRUE(success); - } - - DeleteSourceFramebuffer(); - DeleteSourceTexture(); - timer_.NextLap(); - } while (!timer_.HasTimeLimitExpired()); - - auto reporter = SetUpGLRendererCopierReporter(story); - reporter.AddResult(kMetricReadbackThroughputRunsPerS, - timer_.LapsPerSecond()); - } - - private: - raw_ptr<gpu::gles2::GLES2Interface> gl_ = nullptr; - scoped_refptr<TestInProcessContextProvider> context_provider_; - std::unique_ptr<TextureDeleter> texture_deleter_; - std::unique_ptr<GLRendererCopier> copier_; - GLuint source_texture_ = 0; - GLuint source_framebuffer_ = 0; - base::LapTimer timer_; -}; - -// Fast-Path: If no transformation is necessary and no new textures need to be -// generated, read-back directly from the currently-bound framebuffer. -TEST_F(GLRendererCopierPerfTest, NoTransformNoNewTextures) { - CopyFromTextureOrFramebuffer( - /*have_source_texture=*/false, CopyOutputResult::Format::RGBA, - CopyOutputResult::Destination::kSystemMemory, - /*scale_by_half=*/false, /*flipped_source=*/false, - "no_transformation_and_no_new_textures"); -} - -// Source texture is the one attached to the framebuffer, better performance -// than having to make a copy of the framebuffer. -TEST_F(GLRendererCopierPerfTest, HaveTextureResultRGBABitmap) { - CopyFromTextureOrFramebuffer( - /*have_source_texture=*/true, CopyOutputResult::Format::RGBA, - CopyOutputResult::Destination::kSystemMemory, - /*scale_by_half=*/true, /*flipped_source=*/false, - "framebuffer_has_texture_and_result_is_RGBA_BITMAP"); -} -TEST_F(GLRendererCopierPerfTest, HaveTextureResultRGBATexture) { - CopyFromTextureOrFramebuffer( - /*have_source_texture=*/true, CopyOutputResult::Format::RGBA, - CopyOutputResult::Destination::kNativeTextures, - /*scale_by_half=*/true, /*flipped_source=*/false, - "framebuffer_has_texture_and_result_is_RGBA_TEXTURE"); -} -TEST_F(GLRendererCopierPerfTest, HaveTextureResultI420Planes) { - CopyFromTextureOrFramebuffer( - /*have_source_texture=*/true, CopyOutputResult::Format::I420_PLANES, - CopyOutputResult::Destination::kSystemMemory, - /*scale_by_half=*/true, /*flipped_source=*/false, - "framebuffer_has_texture_and_result_is_I420_PLANES"); -} - -// Have to make a copy of the framebuffer for the source texture. -TEST_F(GLRendererCopierPerfTest, NoTextureResultI420Planes) { - CopyFromTextureOrFramebuffer( - /*have_source_texture=*/false, CopyOutputResult::Format::I420_PLANES, - CopyOutputResult::Destination::kSystemMemory, - /*scale_by_half=*/true, /*flipped_source=*/false, - "framebuffer_doesn't_have_texture_and_result_is_I420_PLANES"); -} - -// Source content is vertically flipped. -TEST_F(GLRendererCopierPerfTest, SourceContentVerticallyFlipped) { - CopyFromTextureOrFramebuffer(/*have_source_texture=*/true, - CopyOutputResult::Format::I420_PLANES, - CopyOutputResult::Destination::kSystemMemory, - /*scale_by_half=*/true, /*flipped_source=*/true, - "source_content_is_vertically_flipped"); -} - -// Result is not scaled by half. -TEST_F(GLRendererCopierPerfTest, ResultNotScaled) { - CopyFromTextureOrFramebuffer(/*have_source_texture=*/true, - CopyOutputResult::Format::I420_PLANES, - CopyOutputResult::Destination::kSystemMemory, - /*scale_by_half=*/false, /*flipped_source=*/true, - "result_is_not_scaled_by_half"); -} - -} // namespace viz diff --git a/chromium/components/viz/service/display/gl_renderer_copier_pixeltest.cc b/chromium/components/viz/service/display/gl_renderer_copier_pixeltest.cc deleted file mode 100644 index 0f72f75a2aa..00000000000 --- a/chromium/components/viz/service/display/gl_renderer_copier_pixeltest.cc +++ /dev/null @@ -1,958 +0,0 @@ -// Copyright 2017 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/gl_renderer_copier.h" - -#include <stdint.h> - -#include <cstring> -#include <memory> -#include <tuple> - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/path_service.h" -#include "base/run_loop.h" -#include "base/test/test_switches.h" -#include "base/threading/thread_task_runner_handle.h" -#include "build/build_config.h" -#include "cc/test/pixel_test.h" -#include "cc/test/pixel_test_utils.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" -#include "components/viz/service/display/gl_renderer.h" -#include "components/viz/test/gl_scaler_test_util.h" -#include "components/viz/test/paths.h" -#include "gpu/command_buffer/client/gles2_interface.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/libyuv/include/libyuv/convert_argb.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkImageInfo.h" -#include "third_party/skia/include/core/SkPixmap.h" -#include "ui/gfx/color_space.h" -#include "ui/gfx/color_transform.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/geometry/size.h" - -#if BUILDFLAG(IS_ANDROID) -#include "base/android/build_info.h" -#endif - -namespace viz { - -namespace { - -base::FilePath GetTestFilePath(const base::FilePath::CharType* basename) { - base::FilePath test_dir; - base::PathService::Get(Paths::DIR_TEST_DATA, &test_dir); - return test_dir.Append(base::FilePath(basename)); -} - -// Creates a packed RGBA (bytes_per_pixel=4) or RGB (bytes_per_pixel=3) bitmap -// in OpenGL byte/row order from the given SkBitmap. -std::unique_ptr<uint8_t[]> CreateGLPixelsFromSkBitmap(SkBitmap bitmap, - GLuint source_format, - bool flip_source) { - // |bitmap| could be of any color type (and is usually BGRA). Convert it to - // a RGBA bitmap in the GL byte order. - SkBitmap rgba_bitmap; - rgba_bitmap.allocPixels(SkImageInfo::Make(bitmap.width(), bitmap.height(), - kRGBA_8888_SkColorType, - kPremul_SkAlphaType)); - SkPixmap pixmap; - const bool success = - bitmap.peekPixels(&pixmap) && rgba_bitmap.writePixels(pixmap, 0, 0); - CHECK(success); - - // Copy the RGBA bitmap into a raw byte array, reversing the row order and - // maybe stripping-out the alpha channel. - const int bytes_per_pixel = source_format == GL_RGBA ? 4 : 3; - std::unique_ptr<uint8_t[]> pixels( - new uint8_t[rgba_bitmap.width() * rgba_bitmap.height() * - bytes_per_pixel]); - for (int y = 0; y < rgba_bitmap.height(); ++y) { - const uint8_t* src = static_cast<uint8_t*>(rgba_bitmap.getAddr(0, y)); - const int flipped_y = flip_source ? rgba_bitmap.height() - y - 1 : y; - uint8_t* dest = - pixels.get() + flipped_y * rgba_bitmap.width() * bytes_per_pixel; - for (int x = 0; x < rgba_bitmap.width(); ++x) { - *(dest++) = *(src++); - *(dest++) = *(src++); - *(dest++) = *(src++); - if (bytes_per_pixel == 4) - *(dest++) = *(src++); - else - ++src; - } - } - - return pixels; -} - -// Returns a SkBitmap, given a packed RGBA bitmap in OpenGL byte/row order. -SkBitmap CreateSkBitmapFromGLPixels(const uint8_t* pixels, - const gfx::Size& size) { - SkBitmap bitmap; - bitmap.allocPixels( - SkImageInfo::Make(size.width(), size.height(), kRGBA_8888_SkColorType, - kPremul_SkAlphaType), - size.width() * 4); - for (int y = 0; y < size.height(); ++y) { - const int flipped_y = size.height() - y - 1; - const uint8_t* const src_row = pixels + flipped_y * size.width() * 4; - void* const dest_row = bitmap.getAddr(0, y); - std::memcpy(dest_row, src_row, size.width() * 4); - } - return bitmap; -} - -// Reads back the texture in the given |mailbox| to a SkBitmap in Skia-native -// format. -SkBitmap ReadbackToSkBitmap(gpu::gles2::GLES2Interface* gl, - const gpu::Mailbox& mailbox, - const gpu::SyncToken& sync_token, - const gfx::Size& texture_size) { - // Bind the texture to a framebuffer from which to read the pixels. - if (sync_token.HasData()) - gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); - GLuint texture = gl->CreateAndConsumeTextureCHROMIUM(mailbox.name); - GLuint framebuffer = 0; - gl->GenFramebuffers(1, &framebuffer); - gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer); - gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - texture, 0); - - // Read the pixels and convert to SkBitmap form for test comparisons. - std::unique_ptr<uint8_t[]> pixels(new uint8_t[texture_size.GetArea() * 4]); - gl->ReadPixels(0, 0, texture_size.width(), texture_size.height(), GL_RGBA, - GL_UNSIGNED_BYTE, pixels.get()); - gl->DeleteFramebuffers(1, &framebuffer); - gl->DeleteTextures(1, &texture); - return CreateSkBitmapFromGLPixels(pixels.get(), texture_size); -} - -// Validates whether all rows are identical (i.e. for each row r != 0, compares -// it with row 0. -void ValidateRows(uint8_t* pixel_data, size_t row_stride, size_t height) { - for (size_t row = 1; row < height; ++row) { - for (size_t col = 0; col < row_stride; ++col) { - EXPECT_NEAR(pixel_data[col], pixel_data[col + row * row_stride], 1) - << " mismatch in row " << row << ", column " << col; - } - } -} - -// Returns maximum allowed difference between the expected and actual pixel -// values. -int GetTolerance() { - return 1; -} - -} // namespace - -// -// All tests in this class follow roughly the same pattern:: -// - Construct a CopyOutputRequest, with arguments depending on the test -// parameters and the specific format that is being tested. -// - Upload source texture to GL. -// - Invoke GLRendererCopier::CopyFromTextureOrFramebuffer(), with arguments -// depending on the test parameters, passing the created CopyOutputRequest. -// - Load the result into memory and compare with baseline. -// -// Parameters: -// 0. GL format to use when uploading source texture. -// 1. True if the copier will also receive the texture in a call to -// `CopyFromTextureOrFramebuffer()`, false otherwise. -// 2. Destiation for the CopyOutputRequest (native textures or system memory). -// 3. True if the result should be scaled by half in each dimension, false -// otherwise. -// 4. True if the source texture will be flipped (bottom-up), false otherwise. -class GLRendererCopierPixelTest - : public cc::PixelTest, - public testing::WithParamInterface< - std::tuple<GLenum, bool, CopyOutputResult::Destination, bool, bool>> { - public: - // In order to test coordinate calculations and Y-flipping, the tests will - // issue copy requests for a small region just to the right and below the - // center of the entire source texture/framebuffer. - gfx::Rect GetRequestArea() const { - DCHECK(!source_size_.IsZero()); - - gfx::Rect result(source_size_.width() / 2, source_size_.height() / 2, - source_size_.width() / 4, source_size_.height() / 4); - - if (scale_by_half_) { - return gfx::ScaleToEnclosingRect(result, 0.5f); - } - - return result; - } - - void SetUp() override { - SetUpGLWithoutRenderer(gfx::SurfaceOrigin::kBottomLeft); - - texture_deleter_ = - std::make_unique<TextureDeleter>(base::ThreadTaskRunnerHandle::Get()); - - source_gl_format_ = std::get<0>(GetParam()); - have_source_texture_ = std::get<1>(GetParam()); - result_destination_ = std::get<2>(GetParam()); - scale_by_half_ = std::get<3>(GetParam()); - flipped_source_ = std::get<4>(GetParam()); - - gl_ = context_provider()->ContextGL(); - copier_ = std::make_unique<GLRendererCopier>(context_provider(), - texture_deleter_.get()); - - ASSERT_TRUE(cc::ReadPNGFile( - GetTestFilePath(FILE_PATH_LITERAL("16_color_rects.png")), - &source_bitmap_)); - source_bitmap_.setImmutable(); - - source_size_ = gfx::Size(source_bitmap_.width(), source_bitmap_.height()); - - source_bitmap_rgba_ = - GLScalerTestUtil::CopyAndConvertToRGBA(source_bitmap_); - source_bitmap_rgba_.setImmutable(); - - source_bitmap_yuv_ = source_bitmap_rgba_; - GLScalerTestUtil::ConvertRGBABitmapToYUV(&source_bitmap_yuv_); - source_bitmap_yuv_.setImmutable(); - } - - void TearDown() override { - DeleteSourceFramebuffer(); - DeleteSourceTexture(); - copier_.reset(); - texture_deleter_.reset(); - } - - gpu::gles2::GLES2Interface* gl() { return gl_; } - - GLRendererCopier* copier() { return copier_.get(); } - - gfx::Rect DrawToWindowSpace(const gfx::Size& source_size, - const gfx::Rect& draw_rect) { - gfx::Rect window_rect = draw_rect; - if (flipped_source_) - window_rect.set_y(source_size.height() - window_rect.bottom()); - return window_rect; - } - - GLuint CreateSourceTexture(SkBitmap source_bitmap) { - CHECK_EQ(0u, source_texture_); - gl_->GenTextures(1, &source_texture_); - gl_->BindTexture(GL_TEXTURE_2D, source_texture_); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - gl_->TexImage2D(GL_TEXTURE_2D, 0, source_gl_format_, source_bitmap.width(), - source_bitmap.height(), 0, source_gl_format_, - GL_UNSIGNED_BYTE, - CreateGLPixelsFromSkBitmap(source_bitmap, source_gl_format_, - flipped_source_) - .get()); - gl_->BindTexture(GL_TEXTURE_2D, 0); - return source_texture_; - } - - void DeleteSourceTexture() { - if (source_texture_ != 0) { - gl_->DeleteTextures(1, &source_texture_); - source_texture_ = 0; - } - } - - void CreateAndBindSourceFramebuffer(GLuint texture) { - ASSERT_EQ(0u, source_framebuffer_); - gl_->GenFramebuffers(1, &source_framebuffer_); - gl_->BindFramebuffer(GL_FRAMEBUFFER, source_framebuffer_); - gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, texture, 0); - } - - void DeleteSourceFramebuffer() { - if (source_framebuffer_ != 0) { - gl_->DeleteFramebuffers(1, &source_framebuffer_); - source_framebuffer_ = 0; - } - } - - protected: - // The size of the source texture or framebuffer. - gfx::Size source_size_; - - GLenum source_gl_format_; - bool have_source_texture_; - CopyOutputResult::Destination result_destination_; - bool scale_by_half_; - bool flipped_source_; - SkBitmap source_bitmap_; - SkBitmap source_bitmap_rgba_; - SkBitmap source_bitmap_yuv_; - - private: - gpu::gles2::GLES2Interface* gl_ = nullptr; - std::unique_ptr<TextureDeleter> texture_deleter_; - std::unique_ptr<GLRendererCopier> copier_; - GLuint source_texture_ = 0; - GLuint source_framebuffer_ = 0; -}; - -// On Android KitKat bots (but not newer ones), the left column of pixels in the -// result is off-by-one in the red channel. Use the off-by-one camparator as a -// workaround. -#if BUILDFLAG(IS_ANDROID) -#define PIXEL_COMPARATOR() cc::FuzzyPixelOffByOneComparator(false) -#else -#define PIXEL_COMPARATOR() cc::ExactPixelComparator(false) -#endif - -TEST_P(GLRendererCopierPixelTest, ExecutesCopyRequestRGBA) { - // Create and execute a CopyOutputRequest via the GLRendererCopier. - std::unique_ptr<CopyOutputResult> result; - { - base::RunLoop loop; - auto request = std::make_unique<CopyOutputRequest>( - CopyOutputRequest::ResultFormat::RGBA, result_destination_, - base::BindOnce( - [](std::unique_ptr<CopyOutputResult>* result, - base::OnceClosure quit_closure, - std::unique_ptr<CopyOutputResult> result_from_copier) { - *result = std::move(result_from_copier); - std::move(quit_closure).Run(); - }, - &result, loop.QuitClosure())); - if (scale_by_half_) { - request->SetUniformScaleRatio(2, 1); - } - - request->set_result_selection(GetRequestArea()); - - const GLuint source_texture = CreateSourceTexture(source_bitmap_); - CreateAndBindSourceFramebuffer(source_texture); - - copy_output::RenderPassGeometry geometry; - // geometry.result_bounds not used by GLRendererCopier - geometry.sampling_bounds = - DrawToWindowSpace(source_size_, gfx::Rect(source_size_)); - geometry.result_selection = request->result_selection(); - geometry.readback_offset = - DrawToWindowSpace(source_size_, geometry.result_selection) - .OffsetFromOrigin(); - - copier()->CopyFromTextureOrFramebuffer( - std::move(request), geometry, source_gl_format_, - have_source_texture_ ? source_texture : 0, source_size_, - flipped_source_, gfx::ColorSpace::CreateSRGB()); - loop.Run(); - } - - // Check that a result was produced and is of the expected rect/size. - ASSERT_TRUE(result); - ASSERT_FALSE(result->IsEmpty()); - ASSERT_EQ(GetRequestArea(), result->rect()); - - // Examine the image in the |result|, and compare it to the baseline PNG file. - absl::optional<CopyOutputResult::ScopedSkBitmap> scoped_bitmap; - SkBitmap actual; - if (result_destination_ == CopyOutputResult::Destination::kSystemMemory) { - scoped_bitmap = result->ScopedAccessSkBitmap(); - actual = scoped_bitmap->bitmap(); - } else { - actual = ReadbackToSkBitmap( - gl(), result->GetTextureResult()->planes[0].mailbox, - result->GetTextureResult()->planes[0].sync_token, result->size()); - } - const auto png_file_path = GetTestFilePath( - scale_by_half_ ? FILE_PATH_LITERAL("half_of_one_of_16_color_rects.png") - : FILE_PATH_LITERAL("one_of_16_color_rects.png")); - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kRebaselinePixelTests)) - EXPECT_TRUE(cc::WritePNGFile(actual, png_file_path, false)); - if (!cc::MatchesPNGFile(actual, png_file_path, PIXEL_COMPARATOR())) { - LOG(ERROR) << "Entire source: " << cc::GetPNGDataUrl(source_bitmap_); - ADD_FAILURE(); - } -} - -TEST_P(GLRendererCopierPixelTest, ExecutesCopyRequestNV12) { - if (result_destination_ == - CopyOutputRequest::ResultDestination::kNativeTextures) { - // TODO(https://crbug.com/1216287): Enable once textures are supported. - GTEST_SKIP() - << "Enable once the GLRenderer supports producing producing results to " - "a texture for NV12 format."; - } - - const gfx::Rect request_area = GetRequestArea(); - - // Check if request's width and height are even (required for NV12 format). - // The test case expects the result size to match the request size exactly, - // which is not possible with NV12 when the request size dimensions aren't - // even. - ASSERT_TRUE(request_area.width() % 2 == 0 && request_area.height() % 2 == 0) - << " request size is not even, request_area.size()=" - << request_area.size().ToString(); - - // Additionally, the test uses helpers that assume pixel data can be packed (4 - // 8-bit values in 1 32-bit pixel). - ASSERT_TRUE(request_area.width() % 4 == 0) - << " request width is not divisible by 4, request_area.width()=" - << request_area.width(); - - // Create and execute a CopyOutputRequest via the GLRendererCopier. - std::unique_ptr<CopyOutputResult> result; - { - base::RunLoop loop; - auto request = std::make_unique<CopyOutputRequest>( - CopyOutputRequest::ResultFormat::NV12_PLANES, result_destination_, - base::BindOnce( - [](std::unique_ptr<CopyOutputResult>* result, - base::OnceClosure quit_closure, - std::unique_ptr<CopyOutputResult> result_from_copier) { - *result = std::move(result_from_copier); - std::move(quit_closure).Run(); - }, - &result, loop.QuitClosure())); - if (scale_by_half_) { - request->SetUniformScaleRatio(2, 1); - } - - request->set_result_selection(request_area); - - // Upload source texture to GL - the texture will be converted to RGBA if - // necessary. - const GLuint source_texture = CreateSourceTexture(source_bitmap_); - CreateAndBindSourceFramebuffer(source_texture); - - copy_output::RenderPassGeometry geometry; - // geometry.result_bounds not used by GLRendererCopier - geometry.sampling_bounds = - DrawToWindowSpace(source_size_, gfx::Rect(source_size_)); - geometry.result_selection = request->result_selection(); - geometry.readback_offset = - DrawToWindowSpace(source_size_, geometry.result_selection) - .OffsetFromOrigin(); - - copier()->CopyFromTextureOrFramebuffer( - std::move(request), geometry, source_gl_format_, - have_source_texture_ ? source_texture : 0, source_size_, - flipped_source_, gfx::ColorSpace::CreateSRGB()); - loop.Run(); - } - - // Check that a result was produced and is of the expected rect/size. - ASSERT_TRUE(result); - ASSERT_FALSE(result->IsEmpty()); - ASSERT_EQ(request_area, result->rect()); - - // Examine the image in the |result|, and compare it to the baseline PNG file. - // Approach is the same as the one in GLNV12ConverterPixelTest. - - // Allocate new bitmap, it will then be populated with Y & UV data. - SkBitmap actual = GLScalerTestUtil::AllocateRGBABitmap(result->size()); - actual.eraseColor(SkColorSetARGB(0xff, 0x00, 0x00, 0x00)); - - SkBitmap luma_plane; - SkBitmap chroma_planes; - - if (result_destination_ == CopyOutputResult::Destination::kSystemMemory) { - // Create a bitmap with packed Y values: - luma_plane = GLScalerTestUtil::AllocateRGBABitmap( - gfx::Size(result->size().width() / 4, result->size().height())); - - chroma_planes = GLScalerTestUtil::AllocateRGBABitmap( - gfx::Size(luma_plane.width(), luma_plane.height() / 2)); - - result->ReadNV12Planes( - reinterpret_cast<uint8_t*>(luma_plane.getAddr(0, 0)), - result->size().width(), - reinterpret_cast<uint8_t*>(chroma_planes.getAddr(0, 0)), - result->size().width()); - } else { - LOG(ERROR) << "Texture results for NV12 are not supported yet!"; - ADD_FAILURE(); - } - - GLScalerTestUtil::UnpackPlanarBitmap(luma_plane, 0, &actual); - GLScalerTestUtil::UnpackUVBitmap(chroma_planes, &actual); - - const auto png_file_path = GetTestFilePath( - scale_by_half_ ? FILE_PATH_LITERAL("half_of_one_of_16_color_rects.png") - : FILE_PATH_LITERAL("one_of_16_color_rects.png")); - - SkBitmap expected; - if (!cc::ReadPNGFile(png_file_path, &expected)) { - LOG(ERROR) << "Cannot read reference image: " << png_file_path.value(); - ADD_FAILURE(); - return; - } - - expected = GLScalerTestUtil::CopyAndConvertToRGBA(expected); - GLScalerTestUtil::ConvertRGBABitmapToYUV(&expected); - - constexpr float kAvgAbsoluteErrorLimit = 16.f; - constexpr int kMaxAbsoluteErrorLimit = 0x80; - if (!cc::MatchesBitmap( - actual, expected, - cc::FuzzyPixelComparator(false, 100.f, 0.f, kAvgAbsoluteErrorLimit, - kMaxAbsoluteErrorLimit, 0))) { - ADD_FAILURE(); - return; - } -} - -#undef PIXEL_COMPARATOR - -// These tests work similarly to `GLRendererCopierPixelTest`, but test various -// request dimensions in more depth. -// -// Parameters: -// 0. Destiation for the CopyOutputRequest (native textures or system memory). -// 1. True if the result should be scaled by half in each dimension, false -// otherwise. -// 2. True if the request should specify odd coordinates for the result -// selection rectangle, false otherwise. -// 3. True if the request should specify odd dimensions for the result selection -// rectangle, false otherwise. -class GLRendererCopierDimensionsPixelTest - : public cc::PixelTest, - public testing::WithParamInterface< - std::tuple<CopyOutputResult::Destination, bool, bool>> { - public: - void SetUp() override { - SetUpGLWithoutRenderer(gfx::SurfaceOrigin::kBottomLeft); - - texture_deleter_ = - std::make_unique<TextureDeleter>(base::ThreadTaskRunnerHandle::Get()); - - result_destination_ = std::get<0>(GetParam()); - scale_by_half_ = std::get<1>(GetParam()); - use_odd_offset_ = std::get<2>(GetParam()); - - gl_ = context_provider()->ContextGL(); - copier_ = std::make_unique<GLRendererCopier>(context_provider(), - texture_deleter_.get()); - - // For this test, use a generated bitmap, with 4 groups of 4 pixels each. - const std::vector<SkColor> kCycle = { - // Red: - SkColorSetARGB(0xff, 0xff, 0x00, 0x00), - SkColorSetARGB(0xff, 0xff, 0x00, 0x00), - SkColorSetARGB(0xff, 0xff, 0x00, 0x00), - SkColorSetARGB(0xff, 0xff, 0x00, 0x00), - // Green: - SkColorSetARGB(0xff, 0x00, 0xff, 0x00), - SkColorSetARGB(0xff, 0x00, 0xff, 0x00), - SkColorSetARGB(0xff, 0x00, 0xff, 0x00), - SkColorSetARGB(0xff, 0x00, 0xff, 0x00), - // Blue: - SkColorSetARGB(0xff, 0x00, 0x00, 0xff), - SkColorSetARGB(0xff, 0x00, 0x00, 0xff), - SkColorSetARGB(0xff, 0x00, 0x00, 0xff), - SkColorSetARGB(0xff, 0x00, 0x00, 0xff), - // White: - SkColorSetARGB(0xff, 0xff, 0xff, 0xff), - SkColorSetARGB(0xff, 0xff, 0xff, 0xff), - SkColorSetARGB(0xff, 0xff, 0xff, 0xff), - SkColorSetARGB(0xff, 0xff, 0xff, 0xff), - }; - source_bitmap_ = GLScalerTestUtil::CreateCyclicalTestImage( - gfx::Size(800, 600), GLScalerTestUtil::VERTICAL_STRIPES, kCycle, 0); - // source_bitmap_.setImmutable(); - source_bitmap_size_ = - gfx::Size(source_bitmap_.width(), source_bitmap_.height()); - } - - void TearDown() override { - DeleteSourceFramebuffer(); - DeleteSourceTexture(); - copier_.reset(); - texture_deleter_.reset(); - } - - gpu::gles2::GLES2Interface* gl() { return gl_; } - - GLRendererCopier* copier() { return copier_.get(); } - - gfx::Rect DrawToWindowSpace(const gfx::Size& source_size, - const gfx::Rect& draw_rect) { - gfx::Rect window_rect = draw_rect; - if (flipped_source_) - window_rect.set_y(source_size.height() - window_rect.bottom()); - return window_rect; - } - - GLuint CreateSourceTexture(SkBitmap source_bitmap) { - CHECK_EQ(0u, source_texture_); - gl_->GenTextures(1, &source_texture_); - gl_->BindTexture(GL_TEXTURE_2D, source_texture_); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - gl_->TexImage2D(GL_TEXTURE_2D, 0, source_gl_format_, source_bitmap.width(), - source_bitmap.height(), 0, source_gl_format_, - GL_UNSIGNED_BYTE, - CreateGLPixelsFromSkBitmap(source_bitmap, source_gl_format_, - flipped_source_) - .get()); - gl_->BindTexture(GL_TEXTURE_2D, 0); - return source_texture_; - } - - void DeleteSourceTexture() { - if (source_texture_ != 0) { - gl_->DeleteTextures(1, &source_texture_); - source_texture_ = 0; - } - } - - void CreateAndBindSourceFramebuffer(GLuint texture) { - ASSERT_EQ(0u, source_framebuffer_); - gl_->GenFramebuffers(1, &source_framebuffer_); - gl_->BindFramebuffer(GL_FRAMEBUFFER, source_framebuffer_); - gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, texture, 0); - } - - void DeleteSourceFramebuffer() { - if (source_framebuffer_ != 0) { - gl_->DeleteFramebuffers(1, &source_framebuffer_); - source_framebuffer_ = 0; - } - } - - protected: - GLenum source_gl_format_ = GL_RGBA; - bool have_source_texture_ = false; - CopyOutputResult::Destination result_destination_; - bool scale_by_half_; - bool flipped_source_ = false; - bool use_odd_offset_; - SkBitmap source_bitmap_; - gfx::Size source_bitmap_size_; - - private: - gpu::gles2::GLES2Interface* gl_ = nullptr; - std::unique_ptr<TextureDeleter> texture_deleter_; - std::unique_ptr<GLRendererCopier> copier_; - GLuint source_texture_ = 0; - GLuint source_framebuffer_ = 0; -}; - -TEST_P(GLRendererCopierDimensionsPixelTest, ExecutesCopyRequestNV12) { - if (result_destination_ == - CopyOutputRequest::ResultDestination::kNativeTextures) { - // TODO(https://crbug.com/1216287): Enable once textures are supported. - GTEST_SKIP() << "Enable once the NV12 format supports producing results to " - "a texture."; - } - - // Result should contain 1px green strip at the beginning if the offset is - // supposed to be odd. - const gfx::Rect request_area = [this]() { - // Capture 2x2 or 4x4 blue strip fragment, depending on scaling. - gfx::Rect result = - scale_by_half_ ? gfx::Rect(4, 0, 2, 2) : gfx::Rect(8, 0, 4, 4); - - // If we are supposed to ask for a rect with odd offset, - // make sure that we capture 1 green pixel. - if (use_odd_offset_) { - result.set_x(result.x() - 1); - } - - return result; - }(); - - // Create and execute a CopyOutputRequest via the GLRendererCopier. - std::unique_ptr<CopyOutputResult> result; - { - base::RunLoop loop; - auto request = std::make_unique<CopyOutputRequest>( - CopyOutputRequest::ResultFormat::NV12_PLANES, result_destination_, - base::BindOnce( - [](std::unique_ptr<CopyOutputResult>* result, - base::OnceClosure quit_closure, - std::unique_ptr<CopyOutputResult> result_from_copier) { - *result = std::move(result_from_copier); - std::move(quit_closure).Run(); - }, - &result, loop.QuitClosure())); - - if (scale_by_half_) { - request->SetUniformScaleRatio(2, 1); - } - - request->set_result_selection(gfx::Rect(request_area)); - - // Upload source texture to GL: - const GLuint source_texture = CreateSourceTexture(source_bitmap_); - CreateAndBindSourceFramebuffer(source_texture); - - copy_output::RenderPassGeometry geometry; - // geometry.result_bounds not used by GLRendererCopier - geometry.sampling_bounds = - DrawToWindowSpace(source_bitmap_size_, gfx::Rect(source_bitmap_size_)); - geometry.result_selection = request->result_selection(); - geometry.readback_offset = - DrawToWindowSpace(source_bitmap_size_, geometry.result_selection) - .OffsetFromOrigin(); - - copier()->CopyFromTextureOrFramebuffer( - std::move(request), geometry, source_gl_format_, - have_source_texture_ ? source_texture : 0, source_bitmap_size_, - flipped_source_, gfx::ColorSpace::CreateSRGB()); - loop.Run(); - } - - // Check that a result was produced and is of the expected rect/size. - ASSERT_TRUE(result); - ASSERT_FALSE(result->IsEmpty()); - ASSERT_EQ(request_area, result->rect()); - - const auto luma_nbytes = copy_output::GetLumaPlaneSize(*result); - const auto luma_stride = copy_output::GetLumaPlaneStride(*result); - - const auto chroma_nbytes = copy_output::GetChromaPlaneSize(*result); - const auto chroma_stride = copy_output::GetChromaPlaneStride(*result); - - std::unique_ptr<uint8_t[]> luma_plane = - std::make_unique<uint8_t[]>(luma_nbytes); - std::unique_ptr<uint8_t[]> chroma_planes = - std::make_unique<uint8_t[]>(chroma_nbytes); - - // Examine the image in the |result|, and compare it to the baseline. - if (result_destination_ == CopyOutputResult::Destination::kSystemMemory) { - result->ReadNV12Planes(luma_plane.get(), luma_stride, chroma_planes.get(), - chroma_stride); - } else { - LOG(ERROR) << "Texture results for NV12 are not supported yet!"; - ADD_FAILURE(); - } - - SkBitmap source_bitmap_yuv = source_bitmap_; - GLScalerTestUtil::ConvertRGBABitmapToYUV(&source_bitmap_yuv); - - // We've asked for a region that starts with one green pixel that is followed - // by all blue pixels, grab the source colors after they have been converted - // to YUV to have something to validate against: - SkColor green_yuv = source_bitmap_yuv.getColor(4, 0); - SkColor blue_yuv = source_bitmap_yuv.getColor(8, 0); - - // Validate first row of luma (first color channel): - for (int col = 0; col < luma_stride; ++col) { - if (col == 0 && use_odd_offset_) { - EXPECT_NEAR(luma_plane[col], SkColorGetR(green_yuv), GetTolerance()); - continue; - } - - EXPECT_NEAR(luma_plane[col], SkColorGetR(blue_yuv), GetTolerance()); - } - - // All other luma rows must match the first row: - ValidateRows(luma_plane.get(), luma_stride, result->rect().height()); - - // Validate first row of chroma (second and third color channel): - for (int col = 0; col < chroma_stride; col += 2) { - if (col == 0 && use_odd_offset_) { - EXPECT_NEAR(chroma_planes[col], SkColorGetG(green_yuv), GetTolerance()); - EXPECT_NEAR(chroma_planes[col + 1], SkColorGetB(green_yuv), - GetTolerance()); - continue; - } - - EXPECT_NEAR(chroma_planes[col], SkColorGetG(blue_yuv), GetTolerance()); - EXPECT_NEAR(chroma_planes[col + 1], SkColorGetB(blue_yuv), GetTolerance()); - } - - // All other chroma rows must match the first row: - ValidateRows(chroma_planes.get(), chroma_stride, - (result->rect().height() + 1) / 2); -} - -TEST_P(GLRendererCopierDimensionsPixelTest, ExecutesCopyRequestI420) { - if (result_destination_ == - CopyOutputRequest::ResultDestination::kNativeTextures) { - // TODO(https://crbug.com/1216287): Enable once textures are supported. - GTEST_SKIP() << "Enable once the I420 format supports producing results to " - "a texture."; - } - - // Result should contain 1px green strip at the beginning if the offset is - // supposed to be odd. - const gfx::Rect request_area = [this]() { - // Capture 2x2 or 4x4 blue strip fragment, depending on scaling. - gfx::Rect result = - scale_by_half_ ? gfx::Rect(4, 0, 2, 2) : gfx::Rect(8, 0, 4, 4); - - // If we are supposed to ask for a rect with odd offset, - // make sure that we capture 1 green pixel. - if (use_odd_offset_) { - result.set_x(result.x() - 1); - } - - return result; - }(); - - // Create and execute a CopyOutputRequest via the GLRendererCopier. - std::unique_ptr<CopyOutputResult> result; - { - base::RunLoop loop; - auto request = std::make_unique<CopyOutputRequest>( - CopyOutputRequest::ResultFormat::I420_PLANES, result_destination_, - base::BindOnce( - [](std::unique_ptr<CopyOutputResult>* result, - base::OnceClosure quit_closure, - std::unique_ptr<CopyOutputResult> result_from_copier) { - *result = std::move(result_from_copier); - std::move(quit_closure).Run(); - }, - &result, loop.QuitClosure())); - - if (scale_by_half_) { - request->SetUniformScaleRatio(2, 1); - } - - request->set_result_selection(gfx::Rect(request_area)); - - // Upload source texture to GL: - const GLuint source_texture = CreateSourceTexture(source_bitmap_); - CreateAndBindSourceFramebuffer(source_texture); - - copy_output::RenderPassGeometry geometry; - // geometry.result_bounds not used by GLRendererCopier - geometry.sampling_bounds = - DrawToWindowSpace(source_bitmap_size_, gfx::Rect(source_bitmap_size_)); - geometry.result_selection = request->result_selection(); - geometry.readback_offset = - DrawToWindowSpace(source_bitmap_size_, geometry.result_selection) - .OffsetFromOrigin(); - - copier()->CopyFromTextureOrFramebuffer( - std::move(request), geometry, source_gl_format_, - have_source_texture_ ? source_texture : 0, source_bitmap_size_, - flipped_source_, gfx::ColorSpace::CreateSRGB()); - loop.Run(); - } - - // Check that a result was produced and is of the expected rect/size. - ASSERT_TRUE(result); - ASSERT_FALSE(result->IsEmpty()); - ASSERT_EQ(request_area, result->rect()); - - // Examine the image in the |result|, and compare it to the baseline PNG file. - - const auto luma_nbytes = copy_output::GetLumaPlaneSize(*result); - const auto luma_stride = copy_output::GetLumaPlaneStride(*result); - - const auto chroma_nbytes = copy_output::GetChromaPlaneSize(*result); - const auto chroma_stride = copy_output::GetChromaPlaneStride(*result); - - std::unique_ptr<uint8_t[]> luma_plane = - std::make_unique<uint8_t[]>(luma_nbytes); - std::unique_ptr<uint8_t[]> chroma_plane_1 = - std::make_unique<uint8_t[]>(chroma_nbytes); - std::unique_ptr<uint8_t[]> chroma_plane_2 = - std::make_unique<uint8_t[]>(chroma_nbytes); - - if (result_destination_ == CopyOutputResult::Destination::kSystemMemory) { - result->ReadI420Planes(luma_plane.get(), luma_stride, chroma_plane_1.get(), - chroma_stride, chroma_plane_2.get(), chroma_stride); - } else { - LOG(ERROR) << "Texture results for I420 are not supported yet!"; - ADD_FAILURE(); - } - - SkBitmap source_bitmap_yuv = source_bitmap_; - GLScalerTestUtil::ConvertRGBABitmapToYUV(&source_bitmap_yuv); - - // We've asked for a region that starts with one green pixel that is followed - // by all blue pixels, validate: - SkColor green_yuv = source_bitmap_yuv.getColor(7, 0); - SkColor blue_yuv = source_bitmap_yuv.getColor(8, 0); - - // Validate first row of luma (first channel): - for (int col = 0; col < luma_stride; ++col) { - if (col == 0 && use_odd_offset_) { - EXPECT_NEAR(luma_plane[col], SkColorGetR(green_yuv), GetTolerance()); - continue; - } - - EXPECT_NEAR(luma_plane[col], SkColorGetR(blue_yuv), GetTolerance()); - } - - // All other luma rows must match the first row: - ValidateRows(luma_plane.get(), luma_stride, result->rect().height()); - - // Validate first row of chroma_1 (second channel): - for (int col = 0; col < chroma_stride; ++col) { - if (col == 0 && use_odd_offset_) { - EXPECT_NEAR(chroma_plane_1[col], SkColorGetG(green_yuv), GetTolerance()); - continue; - } - - EXPECT_NEAR(chroma_plane_1[col], SkColorGetG(blue_yuv), GetTolerance()); - } - - // All other chroma_1 rows must match the first row: - ValidateRows(chroma_plane_1.get(), chroma_stride, - (result->rect().height() + 1) / 2); - - // Validate first row of chroma_2 (third channel): - for (int col = 0; col < chroma_stride; ++col) { - if (col == 0 && use_odd_offset_) { - EXPECT_NEAR(chroma_plane_2[col], SkColorGetB(green_yuv), GetTolerance()); - continue; - } - - EXPECT_NEAR(chroma_plane_2[col], SkColorGetB(blue_yuv), GetTolerance()); - } - - // All other chroma_2 rows must match the first row: - ValidateRows(chroma_plane_2.get(), chroma_stride, - (result->rect().height() + 1) / 2); -} - -// Instantiate parameter sets for all possible combinations of scenarios -// GLRendererCopier will encounter, which will cause it to follow different -// workflows. -INSTANTIATE_TEST_SUITE_P( - All, - GLRendererCopierPixelTest, - testing::Combine( - // Source framebuffer GL format. - testing::Values(static_cast<GLenum>(GL_RGBA), - static_cast<GLenum>(GL_RGB)), - // Source: Have texture too? - testing::Values(false, true), - // Result destination. - testing::Values(CopyOutputResult::Destination::kSystemMemory, - CopyOutputResult::Destination::kNativeTextures), - // Result scaling: Scale by half? - testing::Values(false, true), - // Source content is vertically flipped? - testing::Values(false, true))); - -// Instantiate parameter sets for all possible combinations of scenarios -// GLRendererCopier will encounter, which will cause it to follow different -// workflows. -INSTANTIATE_TEST_SUITE_P( - All, - GLRendererCopierDimensionsPixelTest, - testing::Combine( - // Result destination. - testing::Values(CopyOutputResult::Destination::kSystemMemory, - CopyOutputResult::Destination::kNativeTextures), - // Result scaling: Scale by half? - testing::Values(false, true), - // Use odd offset? - testing::Values(false, true))); - -} // namespace viz diff --git a/chromium/components/viz/service/display/gl_renderer_copier_unittest.cc b/chromium/components/viz/service/display/gl_renderer_copier_unittest.cc deleted file mode 100644 index 8ff8bca30b9..00000000000 --- a/chromium/components/viz/service/display/gl_renderer_copier_unittest.cc +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2017 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/gl_renderer_copier.h" - -#include <stdint.h> - -#include <iterator> -#include <memory> -#include <utility> - -#include "base/bind.h" -#include "base/run_loop.h" -#include "components/viz/common/frame_sinks/copy_output_request.h" -#include "components/viz/common/frame_sinks/copy_output_util.h" -#include "components/viz/test/test_context_provider.h" -#include "components/viz/test/test_gles2_interface.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/geometry/vector2d.h" - -namespace viz { - -namespace { - -class CopierTestGLES2Interface : public TestGLES2Interface { - public: - // Sets how GL will respond to queries regarding the implementation's internal - // read-back format. - void SetOptimalReadbackFormat(GLenum format, GLenum type) { - format_ = format; - type_ = type; - } - - // GLES2Interface override. - void GetIntegerv(GLenum pname, GLint* params) override { - switch (pname) { - case GL_IMPLEMENTATION_COLOR_READ_FORMAT: - ASSERT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), - CheckFramebufferStatus(GL_FRAMEBUFFER)); - params[0] = format_; - break; - case GL_IMPLEMENTATION_COLOR_READ_TYPE: - ASSERT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), - CheckFramebufferStatus(GL_FRAMEBUFFER)); - params[0] = type_; - break; - default: - TestGLES2Interface::GetIntegerv(pname, params); - break; - } - } - - private: - GLenum format_ = 0; - GLenum type_ = 0; -}; - -} // namespace - -class GLRendererCopierTest : public testing::Test { - public: - using ReusableThings = GLRendererCopier::ReusableThings; - - void SetUp() override { - context_provider_ = TestContextProvider::Create( - std::make_unique<CopierTestGLES2Interface>()); - context_provider_->BindToCurrentThread(); - copier_ = - std::make_unique<GLRendererCopier>(context_provider_.get(), nullptr); - } - - void TearDown() override { copier_.reset(); } - - GLRendererCopier* copier() const { return copier_.get(); } - - CopierTestGLES2Interface* test_gl() const { - return static_cast<CopierTestGLES2Interface*>( - copier_->context_provider_->ContextGL()); - } - - // These simply forward method calls to GLRendererCopier. - std::unique_ptr<ReusableThings> TakeReusableThingsOrCreate( - const base::UnguessableToken& requester) { - return copier_->TakeReusableThingsOrCreate(requester); - } - void StashReusableThingsOrDelete(const base::UnguessableToken& requester, - std::unique_ptr<ReusableThings> things) { - return copier_->StashReusableThingsOrDelete(requester, std::move(things)); - } - ReusableThings* PeekReusableThings(const base::UnguessableToken& requester) { - const auto it = copier_->cache_.find(requester); - if (it == copier_->cache_.end()) - return nullptr; - return it->second.get(); - } - size_t GetCopierCacheSize() { return copier_->cache_.size(); } - void FreeUnusedCachedResources() { copier_->FreeUnusedCachedResources(); } - GLenum GetOptimalReadbackFormat() const { - return copier_->GetOptimalReadbackFormat(); - } - - static constexpr int kKeepalivePeriod = GLRendererCopier::kKeepalivePeriod; - - private: - scoped_refptr<ContextProvider> context_provider_; - std::unique_ptr<GLRendererCopier> copier_; -}; - -// Tests that named objects, such as textures or framebuffers, are only cached -// when the CopyOutputRequest has specified a "source" of requests. -TEST_F(GLRendererCopierTest, ReusesThingsFromSameSource) { - // With no source set in a copy request, expect to never re-use any textures - // or framebuffers. - const base::UnguessableToken no_source; - EXPECT_EQ(0u, GetCopierCacheSize()); - auto things = TakeReusableThingsOrCreate(no_source); - EXPECT_TRUE(things); - StashReusableThingsOrDelete(no_source, std::move(things)); - EXPECT_EQ(nullptr, PeekReusableThings(no_source)); - EXPECT_EQ(0u, GetCopierCacheSize()); - - // With a source set in the request, objects should now be cached and re-used. - const auto source = base::UnguessableToken::Create(); - things = TakeReusableThingsOrCreate(source); - ReusableThings* things_raw_ptr = things.get(); - EXPECT_TRUE(things_raw_ptr); - StashReusableThingsOrDelete(source, std::move(things)); - EXPECT_EQ(things_raw_ptr, PeekReusableThings(source)); - EXPECT_EQ(1u, GetCopierCacheSize()); - - // A second, separate source gets its own cache entry. - const auto source2 = base::UnguessableToken::Create(); - things = TakeReusableThingsOrCreate(source2); - things_raw_ptr = things.get(); - EXPECT_TRUE(things_raw_ptr); - EXPECT_EQ(1u, GetCopierCacheSize()); - StashReusableThingsOrDelete(source2, std::move(things)); - EXPECT_EQ(things_raw_ptr, PeekReusableThings(source2)); - EXPECT_EQ(2u, GetCopierCacheSize()); -} - -// Tests that cached resources are freed if unused for a while. -TEST_F(GLRendererCopierTest, FreesUnusedResources) { - // Take and then stash a ReusableThings instance for a valid source. - const base::UnguessableToken source = base::UnguessableToken::Create(); - EXPECT_EQ(0u, GetCopierCacheSize()); - StashReusableThingsOrDelete(source, TakeReusableThingsOrCreate(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_EQ(1u, GetCopierCacheSize()); - if (HasFailure()) - break; - } - - // Calling FreeUnusedCachedResources() just one more time should cause the - // cache entry to be freed. - FreeUnusedCachedResources(); - EXPECT_FALSE(PeekReusableThings(source)); - EXPECT_EQ(0u, GetCopierCacheSize()); -} - -TEST_F(GLRendererCopierTest, DetectsBGRAForReadbackFormat) { - test_gl()->SetOptimalReadbackFormat(GL_BGRA_EXT, GL_UNSIGNED_BYTE); - EXPECT_EQ(static_cast<GLenum>(GL_BGRA_EXT), GetOptimalReadbackFormat()); -} - -TEST_F(GLRendererCopierTest, DetectsRGBAForReadbackFormat) { - test_gl()->SetOptimalReadbackFormat(GL_RGBA, GL_UNSIGNED_BYTE); - EXPECT_EQ(static_cast<GLenum>(GL_RGBA), GetOptimalReadbackFormat()); -} - -TEST_F(GLRendererCopierTest, FallsBackOnRGBAForReadbackFormat_BadFormat) { - test_gl()->SetOptimalReadbackFormat(GL_RGB, GL_UNSIGNED_BYTE); - EXPECT_EQ(static_cast<GLenum>(GL_RGBA), GetOptimalReadbackFormat()); -} - -TEST_F(GLRendererCopierTest, FallsBackOnRGBAForReadbackFormat_BadType) { - test_gl()->SetOptimalReadbackFormat(GL_BGRA_EXT, GL_UNSIGNED_SHORT); - EXPECT_EQ(static_cast<GLenum>(GL_RGBA), GetOptimalReadbackFormat()); -} - -// Tests that copying from a source with a color space that can't be converted -// to a SkColorSpace will fallback to a transform to sRGB. -TEST_F(GLRendererCopierTest, FallsBackToSRGBForInvalidSkColorSpaces) { - std::unique_ptr<CopyOutputResult> result; - base::RunLoop loop; - auto request = std::make_unique<CopyOutputRequest>( - CopyOutputRequest::ResultFormat::RGBA, - CopyOutputRequest::ResultDestination::kSystemMemory, - base::BindOnce( - [](std::unique_ptr<CopyOutputResult>* result_out, - base::OnceClosure quit_closure, - std::unique_ptr<CopyOutputResult> result_from_copier) { - *result_out = std::move(result_from_copier); - std::move(quit_closure).Run(); - }, - &result, loop.QuitClosure())); - gfx::Rect bounds(50, 50); - copy_output::RenderPassGeometry geometry; - geometry.result_bounds = bounds; - geometry.result_selection = bounds; - geometry.sampling_bounds = bounds; - gfx::ColorSpace hdr_color_space = gfx::ColorSpace::CreatePiecewiseHDR( - gfx::ColorSpace::PrimaryID::BT2020, 0.5, 1.5); - - copier()->CopyFromTextureOrFramebuffer(std::move(request), geometry, GL_RGBA, - 0, gfx::Size(50, 50), false, hdr_color_space); - loop.Run(); - - auto scoped_bitmap = result->ScopedAccessSkBitmap(); - SkBitmap result_bitmap = scoped_bitmap.bitmap(); - ASSERT_NE(nullptr, result_bitmap.colorSpace()); - EXPECT_TRUE(result_bitmap.colorSpace()->isSRGB()); -} - -} // namespace viz diff --git a/chromium/components/viz/service/display/gl_renderer_draw_cache.cc b/chromium/components/viz/service/display/gl_renderer_draw_cache.cc deleted file mode 100644 index 887eec8c2af..00000000000 --- a/chromium/components/viz/service/display/gl_renderer_draw_cache.cc +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2012 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/gl_renderer_draw_cache.h" - -namespace viz { - -TexturedQuadDrawCache::TexturedQuadDrawCache() = default; - -TexturedQuadDrawCache::~TexturedQuadDrawCache() = default; - -} // namespace viz diff --git a/chromium/components/viz/service/display/gl_renderer_draw_cache.h b/chromium/components/viz/service/display/gl_renderer_draw_cache.h deleted file mode 100644 index eb85528501d..00000000000 --- a/chromium/components/viz/service/display/gl_renderer_draw_cache.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2012 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_GL_RENDERER_DRAW_CACHE_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_DRAW_CACHE_H_ - -#include <vector> - -#include "components/viz/common/resources/resource_id.h" -#include "components/viz/service/display/program_binding.h" -#include "third_party/skia/include/core/SkColor.h" -#include "ui/gfx/geometry/mask_filter_info.h" - -namespace viz { - -// Collects 4 floats at a time for easy upload to GL. -struct Float4 { - float data[4]; -}; - -// Collects 16 floats at a time for easy upload to GL. -struct Float16 { - float data[16]; -}; - -// A cache for storing textured quads to be drawn. Stores the minimum required -// data to tell if two back to back draws only differ in their transform. Quads -// that only differ by transform may be coalesced into a single draw call. -struct TexturedQuadDrawCache { - TexturedQuadDrawCache(); - - TexturedQuadDrawCache(const TexturedQuadDrawCache&) = delete; - TexturedQuadDrawCache& operator=(const TexturedQuadDrawCache&) = delete; - - ~TexturedQuadDrawCache(); - - bool is_empty = true; - - // Values tracked to determine if textured quads may be coalesced. - ProgramKey program_key; - ResourceId resource_id = kInvalidResourceId; - bool needs_blending = false; - bool nearest_neighbor = false; - SkColor background_color = 0; - gfx::MaskFilterInfo mask_filter_info; - - // A cache for the coalesced quad data. - std::vector<Float4> uv_xform_data; - std::vector<float> vertex_opacity_data; - std::vector<Float16> matrix_data; - - // Don't batch if tex clamp rect is given. - Float4 tex_clamp_rect_data; - - // Video frames need special white level adjustment. - bool is_video_frame = false; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_DRAW_CACHE_H_ diff --git a/chromium/components/viz/service/display/gl_renderer_unittest.cc b/chromium/components/viz/service/display/gl_renderer_unittest.cc deleted file mode 100644 index 7981afdac9c..00000000000 --- a/chromium/components/viz/service/display/gl_renderer_unittest.cc +++ /dev/null @@ -1,5195 +0,0 @@ -// Copyright 2012 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/gl_renderer.h" - -#include <stdint.h> - -#include <memory> -#include <set> -#include <tuple> -#include <utility> -#include <vector> - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/containers/cxx20_erase.h" -#include "base/location.h" -#include "base/strings/stringprintf.h" -#include "base/task/single_thread_task_runner.h" -#include "base/test/scoped_feature_list.h" -#include "base/threading/thread_task_runner_handle.h" -#include "build/build_config.h" -#include "cc/base/math_util.h" -#include "cc/test/fake_impl_task_runner_provider.h" -#include "cc/test/fake_layer_tree_host_impl.h" -#include "cc/test/fake_output_surface_client.h" -#include "cc/test/pixel_test.h" -#include "cc/test/render_pass_test_utils.h" -#include "cc/test/resource_provider_test_utils.h" -#include "components/viz/client/client_resource_provider.h" -#include "components/viz/common/display/renderer_settings.h" -#include "components/viz/common/features.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/texture_draw_quad.h" -#include "components/viz/common/resources/platform_color.h" -#include "components/viz/common/resources/transferable_resource.h" -#include "components/viz/service/display/display_resource_provider_gl.h" -#include "components/viz/test/fake_output_surface.h" -#include "components/viz/test/test_gles2_interface.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" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/skia/include/core/SkMatrix.h" -#include "third_party/skia/include/effects/SkColorMatrixFilter.h" -#include "ui/base/ui_base_features.h" -#include "ui/gfx/color_transform.h" -#include "ui/gfx/geometry/transform.h" -#include "ui/latency/latency_info.h" - -#if BUILDFLAG(IS_WIN) -#include "components/viz/service/display/overlay_processor_win.h" -#elif BUILDFLAG(IS_APPLE) -#include "components/viz/service/display/overlay_processor_mac.h" -#elif BUILDFLAG(IS_ANDROID) || defined(USE_OZONE) -#include "components/viz/service/display/overlay_processor_strategy.h" -#include "components/viz/service/display/overlay_processor_using_strategy.h" -#include "components/viz/service/display/overlay_strategy_single_on_top.h" -#include "components/viz/service/display/overlay_strategy_underlay.h" -#else // Default -#include "components/viz/service/display/overlay_processor_stub.h" -#endif - -using testing::_; -using testing::AnyNumber; -using testing::Args; -using testing::AtLeast; -using testing::Contains; -using testing::ElementsAre; -using testing::Expectation; -using testing::InSequence; -using testing::Invoke; -using testing::Mock; -using testing::Not; -using testing::Pointee; -using testing::Return; -using testing::StrictMock; - -namespace viz { - -MATCHER_P(MatchesSyncToken, sync_token, "") { - gpu::SyncToken other; - memcpy(&other, arg, sizeof(other)); - return other == 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(); - } - AggregatedRenderPass* root_render_pass() { - return render_passes_in_draw_order_.back().get(); - } - void DrawFrame(GLRenderer* renderer, - const gfx::Size& viewport_size, - const gfx::DisplayColorSpaces& display_color_spaces = - gfx::DisplayColorSpaces()) { - SurfaceDamageRectList surface_damage_rect_list; - renderer->DrawFrame(&render_passes_in_draw_order_, 1.f, viewport_size, - display_color_spaces, - std::move(surface_damage_rect_list)); - } - - static const Program* current_program(GLRenderer* renderer) { - return renderer->current_program_; - } - - static TexCoordPrecision get_cached_tex_coord_precision( - GLRenderer* renderer) { - return renderer->draw_cache_.program_key.tex_coord_precision(); - } - - DebugRendererSettings debug_settings_; - AggregatedRenderPassList render_passes_in_draw_order_; -}; - -#define EXPECT_PROGRAM_VALID(program_binding) \ - do { \ - ASSERT_TRUE(program_binding); \ - EXPECT_TRUE((program_binding)->program()); \ - EXPECT_TRUE((program_binding)->initialized()); \ - } while (false) - -static inline SkBlendMode BlendModeToSkXfermode(BlendMode blend_mode) { - switch (blend_mode) { - case BLEND_MODE_NONE: - case BLEND_MODE_NORMAL: - return SkBlendMode::kSrcOver; - case BLEND_MODE_DESTINATION_IN: - return SkBlendMode::kDstIn; - case BLEND_MODE_SCREEN: - return SkBlendMode::kScreen; - case BLEND_MODE_OVERLAY: - return SkBlendMode::kOverlay; - case BLEND_MODE_DARKEN: - return SkBlendMode::kDarken; - case BLEND_MODE_LIGHTEN: - return SkBlendMode::kLighten; - case BLEND_MODE_COLOR_DODGE: - return SkBlendMode::kColorDodge; - case BLEND_MODE_COLOR_BURN: - return SkBlendMode::kColorBurn; - case BLEND_MODE_HARD_LIGHT: - return SkBlendMode::kHardLight; - case BLEND_MODE_SOFT_LIGHT: - return SkBlendMode::kSoftLight; - case BLEND_MODE_DIFFERENCE: - return SkBlendMode::kDifference; - case BLEND_MODE_EXCLUSION: - return SkBlendMode::kExclusion; - case BLEND_MODE_MULTIPLY: - return SkBlendMode::kMultiply; - case BLEND_MODE_HUE: - return SkBlendMode::kHue; - case BLEND_MODE_SATURATION: - return SkBlendMode::kSaturation; - case BLEND_MODE_COLOR: - return SkBlendMode::kColor; - case BLEND_MODE_LUMINOSITY: - return SkBlendMode::kLuminosity; - } - return SkBlendMode::kSrcOver; -} - -// Explicitly named to be a friend in GLRenderer for shader access. -class GLRendererShaderPixelTest : public cc::PixelTest { - public: - void SetUp() override { - SetUpGLRenderer(gfx::SurfaceOrigin::kBottomLeft); - ASSERT_FALSE(renderer()->IsContextLost()); - } - - void TearDown() override { - cc::PixelTest::TearDown(); - ASSERT_FALSE(renderer()); - } - - GLRenderer* renderer() { return static_cast<GLRenderer*>(renderer_.get()); } - - void TestShaderWithDrawingFrame( - const ProgramKey& program_key, - const DirectRenderer::DrawingFrame& drawing_frame, - bool validate_output_color_matrix) { - renderer()->SetCurrentFrameForTesting(drawing_frame); - const gfx::ColorSpace kSrcColorSpaces[] = { - gfx::ColorSpace::CreateSRGB(), - gfx::ColorSpace(gfx::ColorSpace::PrimaryID::ADOBE_RGB, - gfx::ColorSpace::TransferID::GAMMA28), - gfx::ColorSpace::CreateREC709(), - gfx::ColorSpace::CreateExtendedSRGB(), - // This will be adjusted to the display's SDR white level, because no - // level was specified. - gfx::ColorSpace::CreateSCRGBLinear(), - // This won't be, because it has a set SDR white level. - gfx::ColorSpace::CreateSCRGBLinear(123.0f), - // This will be adjusted to the display's SDR white level, because no - // level was specified. - gfx::ColorSpace::CreateHDR10(), - // This won't be, because it has a set SDR white level. - gfx::ColorSpace::CreateHDR10(123.0f), - }; - const gfx::ColorSpace kDstColorSpaces[] = { - gfx::ColorSpace::CreateSRGB(), - gfx::ColorSpace(gfx::ColorSpace::PrimaryID::ADOBE_RGB, - gfx::ColorSpace::TransferID::GAMMA18), - gfx::ColorSpace::CreateExtendedSRGB(), - gfx::ColorSpace::CreateSCRGBLinear(), - }; - // Note: Use ASSERT_XXX() and not EXPECT_XXX() below since the size of the - // loop will lead to useless timeout failures on the bots otherwise. - for (const auto& src_color_space : kSrcColorSpaces) { - for (const auto& dst_color_space : kDstColorSpaces) { - renderer()->SetUseProgram(program_key, src_color_space, dst_color_space, - /*adjust_src_white_level=*/true); - ASSERT_TRUE(renderer()->current_program_->initialized()); - - if (src_color_space != dst_color_space) { - auto adjusted_color_space = src_color_space; - if (src_color_space.IsHDR()) { - adjusted_color_space = src_color_space.GetWithSDRWhiteLevel( - drawing_frame.display_color_spaces.GetSDRMaxLuminanceNits()); - } - SCOPED_TRACE( - base::StringPrintf("adjusted_color_space=%s, dst_color_space=%s", - adjusted_color_space.ToString().c_str(), - dst_color_space.ToString().c_str())); - - gfx::ColorTransform::Options options; - options.tone_map_pq_and_hlg_to_sdr = !dst_color_space.IsHDR(); - options.sdr_max_luminance_nits = - drawing_frame.display_color_spaces.GetSDRMaxLuminanceNits(); - auto color_transform = gfx::ColorTransform::NewColorTransform( - adjusted_color_space, dst_color_space, options); - - ASSERT_EQ(color_transform->GetShaderSource(), - renderer() - ->current_program_->color_transform_for_testing() - ->GetShaderSource()); - } - - if (validate_output_color_matrix) { - if (program_key.type() == ProgramType::PROGRAM_TYPE_SOLID_COLOR) { - ASSERT_EQ( - -1, - renderer()->current_program_->output_color_matrix_location()); - } else { - ASSERT_NE( - -1, - renderer()->current_program_->output_color_matrix_location()); - } - } - } - } - } - - void TestShader(const ProgramKey& program_key) { - TestShaderWithDrawingFrame(program_key, GLRenderer::DrawingFrame(), false); - } - - void TestShadersWithOutputColorMatrix(const ProgramKey& program_key) { - GLRenderer::DrawingFrame frame; - - AggregatedRenderPassList render_passes_in_draw_order; - gfx::Size viewport_size(100, 100); - AggregatedRenderPassId root_pass_id{1}; - auto* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - root_pass->damage_rect = gfx::Rect(0, 0, 25, 25); - - frame.root_render_pass = root_pass; - frame.current_render_pass = root_pass; - frame.render_passes_in_draw_order = &render_passes_in_draw_order; - - // Set a non-identity color matrix on the output surface. - SkM44 color_matrix; - color_matrix.setRC(0, 0, 0.7f); - color_matrix.setRC(1, 1, 0.4f); - color_matrix.setRC(2, 2, 0.5f); - renderer()->output_surface_->set_color_matrix(color_matrix); - - TestShaderWithDrawingFrame(program_key, frame, true); - } - - void TestShadersWithSDRWhiteLevel(const ProgramKey& program_key, - float sdr_white_level) { - GLRenderer::DrawingFrame frame; - frame.display_color_spaces.SetSDRMaxLuminanceNits(sdr_white_level); - TestShaderWithDrawingFrame(program_key, frame, false); - } - - void TestBasicShaders() { - TestShader(ProgramKey::DebugBorder()); - TestShader(ProgramKey::SolidColor(NO_AA, false, false)); - TestShader(ProgramKey::SolidColor(USE_AA, false, false)); - TestShader(ProgramKey::SolidColor(NO_AA, true, false)); - - TestShadersWithOutputColorMatrix(ProgramKey::DebugBorder()); - TestShadersWithOutputColorMatrix( - ProgramKey::SolidColor(NO_AA, false, false)); - TestShadersWithOutputColorMatrix( - ProgramKey::SolidColor(USE_AA, false, false)); - TestShadersWithOutputColorMatrix( - ProgramKey::SolidColor(NO_AA, true, false)); - - TestShadersWithSDRWhiteLevel(ProgramKey::DebugBorder(), 200.f); - TestShadersWithSDRWhiteLevel(ProgramKey::SolidColor(NO_AA, false, false), - 200.f); - TestShadersWithSDRWhiteLevel(ProgramKey::SolidColor(USE_AA, false, false), - 200.f); - TestShadersWithSDRWhiteLevel(ProgramKey::SolidColor(NO_AA, true, false), - 200.f); - } - - void TestColorShaders() { - const size_t kNumTransferFns = 7; - skcms_TransferFunction transfer_fns[kNumTransferFns] = { - // The identity. - {1.f, 1.f, 0.f, 1.f, 0.f, 0.f, 0.f}, - // The identity, with an if statement. - {1.f, 1.f, 0.f, 1.f, 0.5f, 0.f, 0.f}, - // Just the power function. - {1.1f, 1.f, 0.f, 1.f, 0.f, 0.f, 0.f}, - // Everything but the power function, nonlinear only. - {1.f, 0.9f, 0.1f, 0.9f, 0.f, 0.1f, 0.1f}, - // Everything, nonlinear only. - {1.1f, 0.9f, 0.1f, 0.9f, 0.f, 0.1f, 0.1f}, - // Everything but the power function. - {1.f, 0.9f, 0.1f, 0.9f, 0.5f, 0.1f, 0.1f}, - // Everything. - {1.1f, 0.9f, 0.1f, 0.9f, 0.5f, 0.1f, 0.1f}, - }; - - for (size_t i = 0; i < kNumTransferFns; ++i) { - skcms_Matrix3x3 primaries; - gfx::ColorSpace::CreateSRGB().GetPrimaryMatrix(&primaries); - gfx::ColorSpace src = - gfx::ColorSpace::CreateCustom(primaries, transfer_fns[i]); - - renderer()->SetCurrentFrameForTesting(GLRenderer::DrawingFrame()); - renderer()->SetUseProgram(ProgramKey::SolidColor(NO_AA, false, false), - src, gfx::ColorSpace::CreateXYZD50()); - EXPECT_TRUE(renderer()->current_program_->initialized()); - } - } - - void TestShadersWithPrecision(TexCoordPrecision precision) { - // This program uses external textures and sampler, so it won't compile - // everywhere. - if (context_provider()->ContextCapabilities().egl_image_external) { - TestShader(ProgramKey::VideoStream(precision, false)); - } - } - - void TestShadersWithPrecisionAndBlend(TexCoordPrecision precision, - BlendMode blend_mode) { - TestShader(ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, - NO_AA, NO_MASK, false, false, false, - false)); - TestShader(ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, - USE_AA, NO_MASK, false, false, false, - false)); - } - - void TestShadersWithPrecisionAndSampler( - TexCoordPrecision precision, - SamplerType sampler, - PremultipliedAlphaMode premultipliedAlpha, - bool has_background_color, - bool has_tex_clamp_rect) { - TestShader(ProgramKey::Texture(precision, sampler, premultipliedAlpha, - has_background_color, has_tex_clamp_rect, - false, false)); - } - - void TestShadersWithPrecisionAndSamplerTiledAA( - TexCoordPrecision precision, - SamplerType sampler, - PremultipliedAlphaMode premultipliedAlpha) { - TestShader(ProgramKey::Tile(precision, sampler, USE_AA, premultipliedAlpha, - false, false, false, false)); - } - - void TestShadersWithPrecisionAndSamplerTiled( - TexCoordPrecision precision, - SamplerType sampler, - PremultipliedAlphaMode premultipliedAlpha, - bool is_opaque, - bool has_tex_clamp_rect) { - TestShader(ProgramKey::Tile(precision, sampler, NO_AA, premultipliedAlpha, - is_opaque, has_tex_clamp_rect, false, false)); - } - - void TestYUVShadersWithPrecisionAndSampler(TexCoordPrecision precision, - SamplerType sampler) { - // Iterate over alpha plane and nv12 parameters. - UVTextureMode uv_modes[2] = {UV_TEXTURE_MODE_UV, UV_TEXTURE_MODE_U_V}; - YUVAlphaTextureMode a_modes[2] = {YUV_NO_ALPHA_TEXTURE, - YUV_HAS_ALPHA_TEXTURE}; - for (auto uv_mode : uv_modes) { - SCOPED_TRACE(uv_mode); - for (auto a_mode : a_modes) { - SCOPED_TRACE(a_mode); - TestShader(ProgramKey::YUVVideo(precision, sampler, a_mode, uv_mode, - false, false)); - } - } - } - - void TestShadersWithMasks(TexCoordPrecision precision, - SamplerType sampler, - BlendMode blend_mode, - bool mask_for_background) { - TestShader(ProgramKey::RenderPass(precision, sampler, blend_mode, NO_AA, - HAS_MASK, mask_for_background, false, - false, false)); - TestShader(ProgramKey::RenderPass(precision, sampler, blend_mode, NO_AA, - HAS_MASK, mask_for_background, true, - false, false)); - TestShader(ProgramKey::RenderPass(precision, sampler, blend_mode, USE_AA, - HAS_MASK, mask_for_background, false, - false, false)); - TestShader(ProgramKey::RenderPass(precision, sampler, blend_mode, USE_AA, - HAS_MASK, mask_for_background, true, - false, false)); - } -}; - -namespace { - -#if !BUILDFLAG(IS_ANDROID) -static const TexCoordPrecision kPrecisionList[] = {TEX_COORD_PRECISION_MEDIUM, - TEX_COORD_PRECISION_HIGH}; - -static const BlendMode kBlendModeList[LAST_BLEND_MODE + 1] = { - BLEND_MODE_NONE, BLEND_MODE_NORMAL, BLEND_MODE_DESTINATION_IN, - BLEND_MODE_SCREEN, BLEND_MODE_OVERLAY, BLEND_MODE_DARKEN, - BLEND_MODE_LIGHTEN, BLEND_MODE_COLOR_DODGE, BLEND_MODE_COLOR_BURN, - BLEND_MODE_HARD_LIGHT, BLEND_MODE_SOFT_LIGHT, BLEND_MODE_DIFFERENCE, - BLEND_MODE_EXCLUSION, BLEND_MODE_MULTIPLY, BLEND_MODE_HUE, - BLEND_MODE_SATURATION, BLEND_MODE_COLOR, BLEND_MODE_LUMINOSITY, -}; - -static const SamplerType kSamplerList[] = { - SAMPLER_TYPE_2D, SAMPLER_TYPE_2D_RECT, SAMPLER_TYPE_EXTERNAL_OES, -}; - -static const PremultipliedAlphaMode kPremultipliedAlphaModeList[] = { - PREMULTIPLIED_ALPHA, NON_PREMULTIPLIED_ALPHA}; - -TEST_F(GLRendererShaderPixelTest, BasicShadersCompile) { - TestBasicShaders(); -} - -TEST_F(GLRendererShaderPixelTest, TestColorShadersCompile) { - TestColorShaders(); -} - -class PrecisionShaderPixelTest - : public GLRendererShaderPixelTest, - public ::testing::WithParamInterface<TexCoordPrecision> {}; - -TEST_P(PrecisionShaderPixelTest, ShadersCompile) { - TestShadersWithPrecision(GetParam()); -} - -INSTANTIATE_TEST_SUITE_P(PrecisionShadersCompile, - PrecisionShaderPixelTest, - ::testing::ValuesIn(kPrecisionList)); - -class PrecisionBlendShaderPixelTest - : public GLRendererShaderPixelTest, - public ::testing::WithParamInterface< - std::tuple<TexCoordPrecision, BlendMode>> {}; - -TEST_P(PrecisionBlendShaderPixelTest, ShadersCompile) { - TestShadersWithPrecisionAndBlend(std::get<0>(GetParam()), - std::get<1>(GetParam())); -} - -INSTANTIATE_TEST_SUITE_P( - PrecisionBlendShadersCompile, - PrecisionBlendShaderPixelTest, - ::testing::Combine(::testing::ValuesIn(kPrecisionList), - ::testing::ValuesIn(kBlendModeList))); - -class PrecisionSamplerShaderPixelTest - : public GLRendererShaderPixelTest, - public ::testing::WithParamInterface< - std::tuple<TexCoordPrecision, - SamplerType, - PremultipliedAlphaMode, - bool, // has_background_color - bool>> {}; // has_tex_clamp_rect - -TEST_P(PrecisionSamplerShaderPixelTest, ShadersCompile) { - SamplerType sampler = std::get<1>(GetParam()); - if (sampler != SAMPLER_TYPE_2D_RECT || - context_provider()->ContextCapabilities().texture_rectangle) { - TestShadersWithPrecisionAndSampler( - std::get<0>(GetParam()), // TexCoordPrecision - sampler, - std::get<2>(GetParam()), // PremultipliedAlphaMode - std::get<3>(GetParam()), // has_background_color - std::get<4>(GetParam())); // has_tex_clamp_rect - } -} - -INSTANTIATE_TEST_SUITE_P( - PrecisionSamplerShadersCompile, - PrecisionSamplerShaderPixelTest, - ::testing::Combine(::testing::ValuesIn(kPrecisionList), - ::testing::ValuesIn(kSamplerList), - ::testing::ValuesIn(kPremultipliedAlphaModeList), - ::testing::Bool(), // has_background_color - ::testing::Bool())); // has_tex_clamp_rect - -class PrecisionSamplerShaderPixelTestTiled - : public GLRendererShaderPixelTest, - public ::testing::WithParamInterface< - std::tuple<TexCoordPrecision, - SamplerType, - PremultipliedAlphaMode, - bool, // is_opaque - bool>> // has_tex_clamp_rect -{}; - -TEST_P(PrecisionSamplerShaderPixelTestTiled, ShadersCompile) { - SamplerType sampler = std::get<1>(GetParam()); - if (sampler != SAMPLER_TYPE_2D_RECT || - context_provider()->ContextCapabilities().texture_rectangle) { - TestShadersWithPrecisionAndSamplerTiled( - std::get<0>(GetParam()), // TexCoordPrecision - sampler, - std::get<2>(GetParam()), // PremultipliedAlphaMode - std::get<3>(GetParam()), // is_opaque - std::get<4>(GetParam())); // has_tex_clamp_rect - } -} - -INSTANTIATE_TEST_SUITE_P( - PrecisionSamplerShadersCompile, - PrecisionSamplerShaderPixelTestTiled, - ::testing::Combine(::testing::ValuesIn(kPrecisionList), - ::testing::ValuesIn(kSamplerList), - ::testing::ValuesIn(kPremultipliedAlphaModeList), - ::testing::Bool(), // is_opaque - ::testing::Bool())); // has_tex_clamp_rect - -class PrecisionSamplerShaderPixelTestTiledAA - : public GLRendererShaderPixelTest, - public ::testing::WithParamInterface< - std::tuple<TexCoordPrecision, SamplerType, PremultipliedAlphaMode>> { -}; - -TEST_P(PrecisionSamplerShaderPixelTestTiledAA, ShadersCompile) { - SamplerType sampler = std::get<1>(GetParam()); - if (sampler != SAMPLER_TYPE_2D_RECT || - context_provider()->ContextCapabilities().texture_rectangle) { - TestShadersWithPrecisionAndSamplerTiledAA( - std::get<0>(GetParam()), // TexCoordPrecision - sampler, - std::get<2>(GetParam())); // PremultipliedAlphaMode - } -} - -INSTANTIATE_TEST_SUITE_P( - PrecisionSamplerShadersCompile, - PrecisionSamplerShaderPixelTestTiledAA, - ::testing::Combine(::testing::ValuesIn(kPrecisionList), - ::testing::ValuesIn(kSamplerList), - ::testing::ValuesIn(kPremultipliedAlphaModeList))); - -class PrecisionSamplerYUVShaderPixelTest - : public GLRendererShaderPixelTest, - public ::testing::WithParamInterface< - std::tuple<TexCoordPrecision, SamplerType>> {}; - -TEST_P(PrecisionSamplerYUVShaderPixelTest, ShadersCompile) { - SamplerType sampler = std::get<1>(GetParam()); - if (sampler != SAMPLER_TYPE_2D_RECT || - context_provider()->ContextCapabilities().texture_rectangle) { - TestYUVShadersWithPrecisionAndSampler( - std::get<0>(GetParam()), // TexCoordPrecision - sampler); - } -} - -INSTANTIATE_TEST_SUITE_P(PrecisionSamplerShadersCompile, - PrecisionSamplerYUVShaderPixelTest, - ::testing::Combine(::testing::ValuesIn(kPrecisionList), - ::testing::ValuesIn(kSamplerList))); - -class MaskShaderPixelTest - : public GLRendererShaderPixelTest, - public ::testing::WithParamInterface< - std::tuple<TexCoordPrecision, SamplerType, BlendMode, bool>> {}; - -TEST_P(MaskShaderPixelTest, ShadersCompile) { - SamplerType sampler = std::get<1>(GetParam()); - if (sampler != SAMPLER_TYPE_2D_RECT || - context_provider()->ContextCapabilities().texture_rectangle) { - TestShadersWithMasks(std::get<0>(GetParam()), sampler, - std::get<2>(GetParam()), std::get<3>(GetParam())); - } -} - -INSTANTIATE_TEST_SUITE_P(MaskShadersCompile, - MaskShaderPixelTest, - ::testing::Combine(::testing::ValuesIn(kPrecisionList), - ::testing::ValuesIn(kSamplerList), - ::testing::ValuesIn(kBlendModeList), - ::testing::Bool())); - -#endif - -class FakeRendererGL : public GLRenderer { - public: - FakeRendererGL(const RendererSettings* settings, - const DebugRendererSettings* debug_settings, - OutputSurface* output_surface, - DisplayResourceProviderGL* resource_provider) - : GLRenderer(settings, - debug_settings, - output_surface, - resource_provider, - nullptr, - nullptr) {} - - FakeRendererGL(const RendererSettings* settings, - const DebugRendererSettings* debug_settings, - OutputSurface* output_surface, - DisplayResourceProviderGL* resource_provider, - OverlayProcessorInterface* overlay_processor) - : GLRenderer(settings, - debug_settings, - output_surface, - resource_provider, - overlay_processor, - nullptr) {} - - FakeRendererGL( - const RendererSettings* settings, - const DebugRendererSettings* debug_settings, - OutputSurface* output_surface, - DisplayResourceProviderGL* resource_provider, - OverlayProcessorInterface* overlay_processor, - scoped_refptr<base::SingleThreadTaskRunner> current_task_runner) - : GLRenderer(settings, - debug_settings, - output_surface, - resource_provider, - overlay_processor, - std::move(current_task_runner)) {} - - // GLRenderer methods. - - // Changing visibility to public. - using GLRenderer::stencil_enabled; -}; - -class GLRendererWithDefaultHarnessTest : public GLRendererTest { - protected: - GLRendererWithDefaultHarnessTest() { - output_surface_ = FakeOutputSurface::Create3d(); - output_surface_->BindToClient(&output_surface_client_); - - resource_provider_ = std::make_unique<DisplayResourceProviderGL>( - output_surface_->context_provider()); - renderer_ = std::make_unique<FakeRendererGL>(&settings_, &debug_settings_, - output_surface_.get(), - resource_provider_.get()); - renderer_->Initialize(); - renderer_->SetVisible(true); - } - - void SwapBuffers() { renderer_->SwapBuffers({}); } - - RendererSettings settings_; - cc::FakeOutputSurfaceClient output_surface_client_; - std::unique_ptr<FakeOutputSurface> output_surface_; - std::unique_ptr<DisplayResourceProviderGL> resource_provider_; - std::unique_ptr<FakeRendererGL> renderer_; -}; - -// Closing the namespace here so that GLRendererShaderTest can take advantage -// of the friend relationship with GLRenderer and all of the mock classes -// declared above it. -} // namespace - -class GLRendererShaderTest : public GLRendererTest { - protected: - GLRendererShaderTest() { - output_surface_ = FakeOutputSurface::Create3d(); - output_surface_->BindToClient(&output_surface_client_); - - resource_provider_ = std::make_unique<DisplayResourceProviderGL>( - output_surface_->context_provider()); - renderer_ = std::make_unique<FakeRendererGL>( - &settings_, &debug_settings_, output_surface_.get(), - resource_provider_.get(), nullptr); - renderer_->Initialize(); - renderer_->SetVisible(true); - - child_context_provider_ = TestContextProvider::Create(); - child_context_provider_->BindToCurrentThread(); - child_resource_provider_ = std::make_unique<ClientResourceProvider>(); - } - - ~GLRendererShaderTest() override { - child_resource_provider_->ShutdownAndReleaseAllResources(); - } - - void TestRenderPassProgram(TexCoordPrecision precision, - BlendMode blend_mode) { - const Program* program = renderer_->GetProgramIfInitialized( - ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, NO_AA, - NO_MASK, false, false, false, false)); - EXPECT_PROGRAM_VALID(program); - EXPECT_EQ(program, renderer_->current_program_); - } - - void TestRenderPassColorMatrixProgram(TexCoordPrecision precision, - BlendMode blend_mode) { - const Program* program = renderer_->GetProgramIfInitialized( - ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, NO_AA, - NO_MASK, false, true, false, false)); - EXPECT_PROGRAM_VALID(program); - EXPECT_EQ(program, renderer_->current_program_); - } - - void TestRenderPassMaskProgram(TexCoordPrecision precision, - SamplerType sampler, - BlendMode blend_mode) { - const Program* program = renderer_->GetProgramIfInitialized( - ProgramKey::RenderPass(precision, sampler, blend_mode, NO_AA, HAS_MASK, - false, false, false, false)); - EXPECT_PROGRAM_VALID(program); - EXPECT_EQ(program, renderer_->current_program_); - } - - void TestRenderPassMaskColorMatrixProgram(TexCoordPrecision precision, - SamplerType sampler, - BlendMode blend_mode) { - const Program* program = renderer_->GetProgramIfInitialized( - ProgramKey::RenderPass(precision, sampler, blend_mode, NO_AA, HAS_MASK, - false, true, false, false)); - EXPECT_PROGRAM_VALID(program); - EXPECT_EQ(program, renderer_->current_program_); - } - - void TestRenderPassProgramAA(TexCoordPrecision precision, - BlendMode blend_mode) { - const Program* program = renderer_->GetProgramIfInitialized( - ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, USE_AA, - NO_MASK, false, false, false, false)); - EXPECT_PROGRAM_VALID(program); - EXPECT_EQ(program, renderer_->current_program_); - } - - void TestRenderPassColorMatrixProgramAA(TexCoordPrecision precision, - BlendMode blend_mode) { - const Program* program = renderer_->GetProgramIfInitialized( - ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, USE_AA, - NO_MASK, false, true, false, false)); - EXPECT_PROGRAM_VALID(program); - EXPECT_EQ(program, renderer_->current_program_); - } - - void TestRenderPassMaskProgramAA(TexCoordPrecision precision, - SamplerType sampler, - BlendMode blend_mode) { - const Program* program = renderer_->GetProgramIfInitialized( - ProgramKey::RenderPass(precision, sampler, blend_mode, USE_AA, HAS_MASK, - false, false, false, false)); - EXPECT_PROGRAM_VALID(program); - EXPECT_EQ(program, renderer_->current_program_); - } - - void TestRenderPassMaskColorMatrixProgramAA(TexCoordPrecision precision, - SamplerType sampler, - BlendMode blend_mode) { - const Program* program = renderer_->GetProgramIfInitialized( - ProgramKey::RenderPass(precision, sampler, blend_mode, USE_AA, HAS_MASK, - false, true, false, false)); - EXPECT_PROGRAM_VALID(program); - EXPECT_EQ(program, renderer_->current_program_); - } - - void TestSolidColorProgramAA() { - const Program* program = renderer_->GetProgramIfInitialized( - ProgramKey::SolidColor(USE_AA, false, false)); - EXPECT_PROGRAM_VALID(program); - EXPECT_EQ(program, renderer_->current_program_); - } - - RendererSettings settings_; - cc::FakeOutputSurfaceClient output_surface_client_; - std::unique_ptr<FakeOutputSurface> output_surface_; - std::unique_ptr<DisplayResourceProviderGL> resource_provider_; - scoped_refptr<TestContextProvider> child_context_provider_; - std::unique_ptr<ClientResourceProvider> child_resource_provider_; - std::unique_ptr<FakeRendererGL> renderer_; -}; - -namespace { - -TEST_F(GLRendererWithDefaultHarnessTest, ExternalStencil) { - gfx::Size viewport_size(1, 1); - EXPECT_FALSE(renderer_->stencil_enabled()); - - output_surface_->set_has_external_stencil_test(true); - - auto* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{1}, - gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations()); - root_pass->has_transparent_background = false; - - DrawFrame(renderer_.get(), viewport_size); - EXPECT_TRUE(renderer_->stencil_enabled()); -} - -TEST_F(GLRendererWithDefaultHarnessTest, TextureDrawQuadShaderPrecisionHigh) { - // TestContextProvider, used inside FakeOuputSurfaceClient, redefines - // GetShaderPrecisionFormat() and sets the resolution of mediump with - // 10-bits (1024). So any value higher than 1024 should use highp. - // The goal is to make sure the fragment shaders used in DoDrawQuad() use - // the correct precision qualifier. - - const gfx::Size viewport_size(1, 1); - auto* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{1}, - gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations()); - - const bool needs_blending = false; - const bool premultiplied_alpha = false; - const bool flipped = false; - const bool nearest_neighbor = false; - const float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - const gfx::PointF uv_top_left(0, 0); - const gfx::PointF uv_bottom_right(1, 1); - - auto child_context_provider = TestContextProvider::Create(); - child_context_provider->BindToCurrentThread(); - - auto child_resource_provider = std::make_unique<ClientResourceProvider>(); - - // Here is where the texture is created. Any value bigger than 1024 should use - // a highp. - auto transfer_resource = TransferableResource::MakeGL( - gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(), - gfx::Size(1025, 1025), true); - ResourceId client_resource_id = child_resource_provider->ImportResource( - transfer_resource, base::DoNothing()); - - std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = - cc::SendResourceAndGetChildToParentMap( - {client_resource_id}, resource_provider_.get(), - child_resource_provider.get(), child_context_provider.get()); - ResourceId resource_id = resource_map[client_resource_id]; - - // The values defined here should not alter the size of the already created - // texture. - TextureDrawQuad* overlay_quad = - root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>(); - SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState(); - shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size), - gfx::Rect(1023, 1023), gfx::MaskFilterInfo(), - absl::nullopt, false, 1, SkBlendMode::kSrcOver, 0); - overlay_quad->SetNew(shared_state, gfx::Rect(1023, 1023), - gfx::Rect(1023, 1023), needs_blending, resource_id, - premultiplied_alpha, uv_top_left, uv_bottom_right, - SK_ColorTRANSPARENT, vertex_opacity, flipped, - nearest_neighbor, /*secure_output_only=*/false, - gfx::ProtectedVideoType::kClear); - - DrawFrame(renderer_.get(), viewport_size); - - TexCoordPrecision precision = get_cached_tex_coord_precision(renderer_.get()); - EXPECT_EQ(precision, TEX_COORD_PRECISION_HIGH); - - child_resource_provider->ShutdownAndReleaseAllResources(); -} - -TEST_F(GLRendererWithDefaultHarnessTest, TextureDrawQuadShaderPrecisionMedium) { - // TestContextProvider, used inside FakeOuputSurfaceClient, redefines - // GetShaderPrecisionFormat() and sets the resolution of mediump with - // 10-bits (1024). So any value higher than 1024 should use highp. - // The goal is to make sure the fragment shaders used in DoDrawQuad() use - // the correct precision qualifier. - - const gfx::Size viewport_size(1, 1); - auto* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{1}, - gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations()); - - const bool needs_blending = false; - const bool premultiplied_alpha = false; - const bool flipped = false; - const bool nearest_neighbor = false; - const float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - const gfx::PointF uv_top_left(0, 0); - const gfx::PointF uv_bottom_right(1, 1); - - auto child_context_provider = TestContextProvider::Create(); - child_context_provider->BindToCurrentThread(); - - auto child_resource_provider = std::make_unique<ClientResourceProvider>(); - - // Here is where the texture is created. Any value smaller than 1024 should - // use a mediump. - auto transfer_resource = TransferableResource::MakeGL( - gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(), - gfx::Size(1023, 1023), true); - ResourceId client_resource_id = child_resource_provider->ImportResource( - transfer_resource, base::DoNothing()); - - std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = - cc::SendResourceAndGetChildToParentMap( - {client_resource_id}, resource_provider_.get(), - child_resource_provider.get(), child_context_provider.get()); - ResourceId resource_id = resource_map[client_resource_id]; - - // The values defined here should not alter the size of the already created - // texture. - TextureDrawQuad* overlay_quad = - root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>(); - SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState(); - shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size), - gfx::Rect(1025, 1025), gfx::MaskFilterInfo(), - absl::nullopt, false, 1, SkBlendMode::kSrcOver, 0); - overlay_quad->SetNew(shared_state, gfx::Rect(1025, 1025), - gfx::Rect(1025, 1025), needs_blending, resource_id, - premultiplied_alpha, uv_top_left, uv_bottom_right, - SK_ColorTRANSPARENT, vertex_opacity, flipped, - nearest_neighbor, /*secure_output_only=*/false, - gfx::ProtectedVideoType::kClear); - - DrawFrame(renderer_.get(), viewport_size); - - TexCoordPrecision precision = get_cached_tex_coord_precision(renderer_.get()); - EXPECT_EQ(precision, TEX_COORD_PRECISION_MEDIUM); - - child_resource_provider->ShutdownAndReleaseAllResources(); -} - -class GLRendererTextureDrawQuadHDRTest - : public GLRendererWithDefaultHarnessTest { - protected: - void RunTest(bool is_video_frame) { - const gfx::Size viewport_size(10, 10); - auto* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{1}, - gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations()); - - const bool needs_blending = false; - const bool premultiplied_alpha = false; - const bool flipped = false; - const bool nearest_neighbor = false; - const float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - const gfx::PointF uv_top_left(0, 0); - const gfx::PointF uv_bottom_right(1, 1); - - auto child_context_provider = TestContextProvider::Create(); - child_context_provider->BindToCurrentThread(); - - auto child_resource_provider = std::make_unique<ClientResourceProvider>(); - - constexpr gfx::Size kTextureSize = gfx::Size(10, 10); - auto transfer_resource = TransferableResource::MakeGL( - gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(), - kTextureSize, true); - transfer_resource.color_space = gfx::ColorSpace::CreateSCRGBLinear(); - ResourceId client_resource_id = child_resource_provider->ImportResource( - transfer_resource, base::DoNothing()); - - std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = - cc::SendResourceAndGetChildToParentMap( - {client_resource_id}, resource_provider_.get(), - child_resource_provider.get(), child_context_provider.get()); - ResourceId resource_id = resource_map[client_resource_id]; - - TextureDrawQuad* overlay_quad = - root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>(); - SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState(); - shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size), - gfx::Rect(kTextureSize), gfx::MaskFilterInfo(), - absl::nullopt, false, 1, SkBlendMode::kSrcOver, 0); - overlay_quad->SetNew(shared_state, gfx::Rect(kTextureSize), - gfx::Rect(kTextureSize), needs_blending, resource_id, - premultiplied_alpha, uv_top_left, uv_bottom_right, - SK_ColorTRANSPARENT, vertex_opacity, flipped, - nearest_neighbor, /*secure_output_only=*/false, - gfx::ProtectedVideoType::kClear); - overlay_quad->is_video_frame = is_video_frame; - - constexpr float kSDRWhiteLevel = 123.0f; - gfx::DisplayColorSpaces display_color_spaces; - display_color_spaces.SetSDRMaxLuminanceNits(kSDRWhiteLevel); - - DrawFrame(renderer_.get(), viewport_size, display_color_spaces); - - const Program* program = current_program(renderer_.get()); - DCHECK(program); - DCHECK(program->color_transform_for_testing()) - << program->fragment_shader().GetShaderString(); - - const gfx::ColorSpace expected_src_color_space = - is_video_frame - ? gfx::ColorSpace::CreateSCRGBLinear().GetWithSDRWhiteLevel( - kSDRWhiteLevel) - : gfx::ColorSpace::CreateSCRGBLinear(); - EXPECT_EQ(program->color_transform_for_testing()->GetSrcColorSpace(), - expected_src_color_space); - - child_resource_provider->ShutdownAndReleaseAllResources(); - } -}; - -TEST_F(GLRendererTextureDrawQuadHDRTest, VideoFrame) { - RunTest(/*is_video_frame=*/true); -} - -TEST_F(GLRendererTextureDrawQuadHDRTest, NotVideoFrame) { - RunTest(/*is_video_frame=*/false); -} - -class ForbidSynchronousCallGLES2Interface : public TestGLES2Interface { - public: - ForbidSynchronousCallGLES2Interface() = default; - - void GetAttachedShaders(GLuint program, - GLsizei max_count, - GLsizei* count, - GLuint* shaders) override { - ADD_FAILURE(); - } - - GLint GetAttribLocation(GLuint program, const GLchar* name) override { - ADD_FAILURE(); - return 0; - } - - void GetBooleanv(GLenum pname, GLboolean* value) override { ADD_FAILURE(); } - - void GetBufferParameteriv(GLenum target, - GLenum pname, - GLint* value) override { - ADD_FAILURE(); - } - - GLenum GetError() override { - ADD_FAILURE(); - return GL_NO_ERROR; - } - - void GetFloatv(GLenum pname, GLfloat* value) override { ADD_FAILURE(); } - - void GetFramebufferAttachmentParameteriv(GLenum target, - GLenum attachment, - GLenum pname, - GLint* value) override { - ADD_FAILURE(); - } - - void GetIntegerv(GLenum pname, GLint* value) override { - if (pname == GL_MAX_TEXTURE_SIZE) { - // MAX_TEXTURE_SIZE is cached client side, so it's OK to query. - *value = 1024; - } else { - ADD_FAILURE(); - } - } - - // We allow querying the shader compilation and program link status in debug - // mode, but not release. - void GetProgramiv(GLuint program, GLenum pname, GLint* value) override { - ADD_FAILURE(); - } - - void GetShaderiv(GLuint shader, GLenum pname, GLint* value) override { - ADD_FAILURE(); - } - - void GetRenderbufferParameteriv(GLenum target, - GLenum pname, - GLint* value) override { - ADD_FAILURE(); - } - - void GetShaderPrecisionFormat(GLenum shadertype, - GLenum precisiontype, - GLint* range, - GLint* precision) override { - ADD_FAILURE(); - } - - void GetTexParameterfv(GLenum target, GLenum pname, GLfloat* value) override { - ADD_FAILURE(); - } - - void GetTexParameteriv(GLenum target, GLenum pname, GLint* value) override { - ADD_FAILURE(); - } - - void GetUniformfv(GLuint program, GLint location, GLfloat* value) override { - ADD_FAILURE(); - } - - void GetUniformiv(GLuint program, GLint location, GLint* value) override { - ADD_FAILURE(); - } - - GLint GetUniformLocation(GLuint program, const GLchar* name) override { - ADD_FAILURE(); - return 0; - } - - void GetVertexAttribfv(GLuint index, GLenum pname, GLfloat* value) override { - ADD_FAILURE(); - } - - void GetVertexAttribiv(GLuint index, GLenum pname, GLint* value) override { - ADD_FAILURE(); - } - - void GetVertexAttribPointerv(GLuint index, - GLenum pname, - void** pointer) override { - ADD_FAILURE(); - } -}; - -TEST_F(GLRendererTest, InitializationDoesNotMakeSynchronousCalls) { - auto gl_owned = std::make_unique<ForbidSynchronousCallGLES2Interface>(); - auto provider = TestContextProvider::Create(std::move(gl_owned)); - provider->BindToCurrentThread(); - - cc::FakeOutputSurfaceClient output_surface_client; - std::unique_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(std::move(provider))); - output_surface->BindToClient(&output_surface_client); - - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - RendererSettings settings; - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - resource_provider.get()); -} - -class LoseContextOnFirstGetGLES2Interface : public TestGLES2Interface { - public: - LoseContextOnFirstGetGLES2Interface() {} - - void GetProgramiv(GLuint program, GLenum pname, GLint* value) override { - LoseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB, - GL_INNOCENT_CONTEXT_RESET_ARB); - *value = 0; - } - - void GetShaderiv(GLuint shader, GLenum pname, GLint* value) override { - LoseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB, - GL_INNOCENT_CONTEXT_RESET_ARB); - *value = 0; - } -}; - -TEST_F(GLRendererTest, InitializationWithQuicklyLostContextDoesNotAssert) { - auto gl_owned = std::make_unique<LoseContextOnFirstGetGLES2Interface>(); - auto provider = TestContextProvider::Create(std::move(gl_owned)); - provider->BindToCurrentThread(); - - cc::FakeOutputSurfaceClient output_surface_client; - std::unique_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(std::move(provider))); - output_surface->BindToClient(&output_surface_client); - - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - RendererSettings settings; - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - resource_provider.get()); -} - -class ClearCountingGLES2Interface : public TestGLES2Interface { - public: - ClearCountingGLES2Interface() = default; - - MOCK_METHOD3(DiscardFramebufferEXT, - void(GLenum target, - GLsizei numAttachments, - const GLenum* attachments)); - MOCK_METHOD1(Clear, void(GLbitfield mask)); -}; - -TEST_F(GLRendererTest, OpaqueBackground) { - auto gl_owned = std::make_unique<ClearCountingGLES2Interface>(); - gl_owned->set_have_discard_framebuffer(true); - - auto* gl = gl_owned.get(); - - auto provider = TestContextProvider::Create(std::move(gl_owned)); - provider->BindToCurrentThread(); - - cc::FakeOutputSurfaceClient output_surface_client; - std::unique_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(std::move(provider))); - output_surface->BindToClient(&output_surface_client); - - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - RendererSettings settings; - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - resource_provider.get()); - renderer.Initialize(); - renderer.SetVisible(true); - - gfx::Size viewport_size(1, 1); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{1}, - gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations()); - root_pass->has_transparent_background = false; - - // On DEBUG builds, render passes with opaque background clear to blue to - // easily see regions that were not drawn on the screen. - EXPECT_CALL(*gl, DiscardFramebufferEXT(GL_FRAMEBUFFER, _, _)) - .With(Args<2, 1>(ElementsAre(GL_COLOR_EXT))) - .Times(1); -#ifdef NDEBUG - EXPECT_CALL(*gl, Clear(_)).Times(0); -#else - EXPECT_CALL(*gl, Clear(_)).Times(1); -#endif - DrawFrame(&renderer, viewport_size); - Mock::VerifyAndClearExpectations(gl); -} - -TEST_F(GLRendererTest, TransparentBackground) { - auto gl_owned = std::make_unique<ClearCountingGLES2Interface>(); - auto* gl = gl_owned.get(); - gl_owned->set_have_discard_framebuffer(true); - - auto provider = TestContextProvider::Create(std::move(gl_owned)); - provider->BindToCurrentThread(); - - cc::FakeOutputSurfaceClient output_surface_client; - std::unique_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(std::move(provider))); - output_surface->BindToClient(&output_surface_client); - - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - RendererSettings settings; - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - resource_provider.get()); - renderer.Initialize(); - renderer.SetVisible(true); - - gfx::Size viewport_size(1, 1); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{1}, - gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations()); - root_pass->has_transparent_background = true; - - EXPECT_CALL(*gl, DiscardFramebufferEXT(GL_FRAMEBUFFER, 1, _)).Times(1); - EXPECT_CALL(*gl, Clear(_)).Times(1); - DrawFrame(&renderer, viewport_size); - - Mock::VerifyAndClearExpectations(gl); -} - -TEST_F(GLRendererTest, OffscreenOutputSurface) { - auto gl_owned = std::make_unique<ClearCountingGLES2Interface>(); - auto* gl = gl_owned.get(); - gl_owned->set_have_discard_framebuffer(true); - - auto provider = TestContextProvider::Create(std::move(gl_owned)); - provider->BindToCurrentThread(); - - cc::FakeOutputSurfaceClient output_surface_client; - std::unique_ptr<OutputSurface> output_surface( - FakeOutputSurface::CreateOffscreen(std::move(provider))); - output_surface->BindToClient(&output_surface_client); - - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - RendererSettings settings; - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - resource_provider.get()); - renderer.Initialize(); - renderer.SetVisible(true); - - gfx::Size viewport_size(1, 1); - cc::AddRenderPass(&render_passes_in_draw_order_, AggregatedRenderPassId{1}, - gfx::Rect(viewport_size), gfx::Transform(), - cc::FilterOperations()); - - EXPECT_CALL(*gl, DiscardFramebufferEXT(GL_FRAMEBUFFER, _, _)) - .With(Args<2, 1>(ElementsAre(GL_COLOR_ATTACHMENT0))) - .Times(1); - EXPECT_CALL(*gl, Clear(_)).Times(AnyNumber()); - DrawFrame(&renderer, viewport_size); - Mock::VerifyAndClearExpectations(gl); -} - -class TextureStateTrackingGLES2Interface : public TestGLES2Interface { - public: - TextureStateTrackingGLES2Interface() : active_texture_(GL_INVALID_ENUM) {} - - MOCK_METHOD1(WaitSyncTokenCHROMIUM, void(const GLbyte* sync_token)); - MOCK_METHOD3(TexParameteri, void(GLenum target, GLenum pname, GLint param)); - MOCK_METHOD4( - DrawElements, - void(GLenum mode, GLsizei count, GLenum type, const void* indices)); - - void ActiveTexture(GLenum texture) override { - EXPECT_NE(texture, active_texture_); - active_texture_ = texture; - } - - GLenum active_texture() const { return active_texture_; } - - private: - GLenum active_texture_; -}; - -#define EXPECT_FILTER_CALL(filter) \ - EXPECT_CALL(*gl, \ - TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter)); \ - EXPECT_CALL(*gl, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter)); - -TEST_F(GLRendererTest, ActiveTextureState) { - auto child_gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>(); - auto child_context_provider = - TestContextProvider::Create(std::move(child_gl_owned)); - child_context_provider->BindToCurrentThread(); - auto child_resource_provider = std::make_unique<ClientResourceProvider>(); - - auto gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>(); - gl_owned->set_have_extension_egl_image(true); - auto* gl = gl_owned.get(); - - auto provider = TestContextProvider::Create(std::move(gl_owned)); - provider->BindToCurrentThread(); - - cc::FakeOutputSurfaceClient output_surface_client; - std::unique_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(std::move(provider))); - output_surface->BindToClient(&output_surface_client); - - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - RendererSettings settings; - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - resource_provider.get()); - renderer.Initialize(); - renderer.SetVisible(true); - - // During initialization we are allowed to set any texture parameters. - EXPECT_CALL(*gl, TexParameteri(_, _, _)).Times(AnyNumber()); - - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{1}, - gfx::Rect(100, 100), gfx::Transform(), cc::FilterOperations()); - gpu::SyncToken mailbox_sync_token; - cc::AddOneOfEveryQuadTypeInDisplayResourceProvider( - root_pass, resource_provider.get(), child_resource_provider.get(), - child_context_provider.get(), AggregatedRenderPassId{0}, - &mailbox_sync_token); - - EXPECT_EQ(12u, resource_provider->num_resources()); - renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - - // Set up expected texture filter state transitions that match the quads - // created in AppendOneOfEveryQuadType(). - Mock::VerifyAndClearExpectations(gl); - { - InSequence sequence; - // The verified flush flag will be set by - // ClientResourceProvider::PrepareSendToParent. Before checking if - // the gpu::SyncToken matches, set this flag first. - mailbox_sync_token.SetVerifyFlush(); - // In AddOneOfEveryQuadTypeInDisplayResourceProvider, resources are added - // into RenderPass with the below order: resource6, resource1, resource8 - // (with mailbox), resource2, resource3, resource4, resource9, resource10, - // resource11, resource12. resource8 has its own mailbox mailbox_sync_token. - // The rest resources share a common default sync token. - EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(2); - EXPECT_CALL(*gl, - WaitSyncTokenCHROMIUM(MatchesSyncToken(mailbox_sync_token))) - .Times(1); - EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(7); - - // yuv_quad is drawn with the default linear filter. - for (int i = 0; i < 4; ++i) { - EXPECT_FILTER_CALL(GL_LINEAR); - } - EXPECT_CALL(*gl, DrawElements(_, _, _, _)); - - // tile_quad is drawn with GL_NEAREST because it is not transformed or - // scaled. - EXPECT_FILTER_CALL(GL_NEAREST); - EXPECT_CALL(*gl, DrawElements(_, _, _, _)); - - // transformed tile_quad - EXPECT_FILTER_CALL(GL_LINEAR); - EXPECT_CALL(*gl, DrawElements(_, _, _, _)); - - // scaled tile_quad - EXPECT_FILTER_CALL(GL_LINEAR); - EXPECT_CALL(*gl, DrawElements(_, _, _, _)); - - // texture_quad without nearest neighbor - EXPECT_FILTER_CALL(GL_LINEAR); - EXPECT_CALL(*gl, DrawElements(_, _, _, _)); - - // texture_quad without nearest neighbor - EXPECT_FILTER_CALL(GL_LINEAR); - EXPECT_CALL(*gl, DrawElements(_, _, _, _)); - - if (features::IsUsingFastPathForSolidColorQuad()) { - // stream video and debug draw quads - EXPECT_CALL(*gl, DrawElements(_, _, _, _)).Times(2); - } else { - // stream video, solid color, and debug draw quads - EXPECT_CALL(*gl, DrawElements(_, _, _, _)).Times(3); - } - } - - gfx::Size viewport_size(100, 100); - DrawFrame(&renderer, viewport_size); - Mock::VerifyAndClearExpectations(gl); - - child_resource_provider->ShutdownAndReleaseAllResources(); -} - -class BufferSubDataTrackingGLES2Interface : public TestGLES2Interface { - public: - BufferSubDataTrackingGLES2Interface() = default; - ~BufferSubDataTrackingGLES2Interface() override = default; - - void BufferSubData(GLenum target, - GLintptr offset, - GLsizeiptr size, - const void* data) override { - if (target != GL_ARRAY_BUFFER) - return; - DCHECK_EQ(0, offset); - last_array_data.resize(size); - memcpy(last_array_data.data(), data, size); - } - - std::vector<uint8_t> last_array_data; -}; - -TEST_F(GLRendererTest, DrawYUVVideoDrawQuadWithVisibleRect) { - gfx::Size viewport_size(100, 100); - - auto mock_gl_owned = std::make_unique<BufferSubDataTrackingGLES2Interface>(); - BufferSubDataTrackingGLES2Interface* mock_gl = mock_gl_owned.get(); - auto provider = TestContextProvider::Create(std::move(mock_gl_owned)); - provider->BindToCurrentThread(); - - cc::FakeOutputSurfaceClient output_surface_client; - std::unique_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(std::move(provider))); - output_surface->BindToClient(&output_surface_client); - - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - RendererSettings settings; - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - resource_provider.get()); - renderer.Initialize(); - renderer.SetVisible(true); - - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{1}, - gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations()); - root_pass->has_transparent_background = false; - - gfx::Rect rect(viewport_size); - gfx::Rect visible_rect(rect); - gfx::RectF tex_coord_rect(0, 0, 1, 1); - visible_rect.Inset(gfx::Insets::TLBR(20, 10, 40, 30)); - - SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState(); - shared_state->SetAll(gfx::Transform(), gfx::Rect(), rect, - gfx::MaskFilterInfo(), absl::nullopt, false, 1, - SkBlendMode::kSrcOver, 0); - - YUVVideoDrawQuad* quad = - root_pass->CreateAndAppendDrawQuad<YUVVideoDrawQuad>(); - quad->SetNew(shared_state, rect, visible_rect, /*needs_blending=*/false, - tex_coord_rect, tex_coord_rect, rect.size(), rect.size(), - ResourceId(1), ResourceId(1), ResourceId(1), ResourceId(1), - gfx::ColorSpace(), 0, 1.0, 8); - - DrawFrame(&renderer, viewport_size); - - ASSERT_EQ(96u, mock_gl->last_array_data.size()); - float* geometry_binding_vertexes = - reinterpret_cast<float*>(mock_gl->last_array_data.data()); - - const double kEpsilon = 1e-6; - EXPECT_NEAR(-0.4f, geometry_binding_vertexes[0], kEpsilon); - EXPECT_NEAR(-0.3f, geometry_binding_vertexes[1], kEpsilon); - EXPECT_NEAR(0.1f, geometry_binding_vertexes[3], kEpsilon); - EXPECT_NEAR(0.2f, geometry_binding_vertexes[4], kEpsilon); - - EXPECT_NEAR(0.2f, geometry_binding_vertexes[12], kEpsilon); - EXPECT_NEAR(0.1f, geometry_binding_vertexes[13], kEpsilon); - EXPECT_NEAR(0.7f, geometry_binding_vertexes[15], kEpsilon); - EXPECT_NEAR(0.6f, geometry_binding_vertexes[16], kEpsilon); -} - -class NoClearRootRenderPassMockGLES2Interface : public TestGLES2Interface { - public: - MOCK_METHOD1(Clear, void(GLbitfield mask)); - MOCK_METHOD4( - DrawElements, - void(GLenum mode, GLsizei count, GLenum type, const void* indices)); -}; - -TEST_F(GLRendererTest, ShouldClearRootRenderPass) { - auto mock_gl_owned = - std::make_unique<NoClearRootRenderPassMockGLES2Interface>(); - NoClearRootRenderPassMockGLES2Interface* mock_gl = mock_gl_owned.get(); - - auto provider = TestContextProvider::Create(std::move(mock_gl_owned)); - provider->BindToCurrentThread(); - - cc::FakeOutputSurfaceClient output_surface_client; - std::unique_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(std::move(provider))); - output_surface->BindToClient(&output_surface_client); - - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - RendererSettings settings; - settings.should_clear_root_render_pass = false; - - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - resource_provider.get()); - renderer.Initialize(); - renderer.SetVisible(true); - - gfx::Size viewport_size(10, 10); - - AggregatedRenderPassId child_pass_id{2}; - AggregatedRenderPass* child_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, child_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddQuad(child_pass, gfx::Rect(viewport_size), SK_ColorBLUE); - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN); - - cc::AddRenderPassQuad(root_pass, child_pass); - -#ifdef NDEBUG - GLint clear_bits = GL_COLOR_BUFFER_BIT; -#else - GLint clear_bits = GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; -#endif - - // First render pass is not the root one, clearing should happen. - EXPECT_CALL(*mock_gl, Clear(clear_bits)).Times(AtLeast(1)); - - Expectation first_render_pass = - EXPECT_CALL(*mock_gl, DrawElements(_, _, _, _)).Times(1); - - if (features::IsUsingFastPathForSolidColorQuad()) { - // The second render pass is the root one, clearing should be prevented. The - // one call is expected due to the solid color draw quad which uses glClear - // to draw the quad. - EXPECT_CALL(*mock_gl, Clear(clear_bits)).Times(1).After(first_render_pass); - } else { - // The second render pass is the root one, clearing should be prevented. - EXPECT_CALL(*mock_gl, Clear(clear_bits)).Times(0).After(first_render_pass); - } - - EXPECT_CALL(*mock_gl, DrawElements(_, _, _, _)) - .Times(AnyNumber()) - .After(first_render_pass); - - renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(&renderer, viewport_size); - - // In multiple render passes all but the root pass should clear the - // framebuffer. - Mock::VerifyAndClearExpectations(&mock_gl); -} - -class ScissorTestOnClearCheckingGLES2Interface : public TestGLES2Interface { - public: - ScissorTestOnClearCheckingGLES2Interface() = default; - - void ClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) override { - // RGBA - {0, 0, 0, 0} is used to clear the buffer before drawing onto the - // render target. Any other color means a solid color draw quad is being - // drawn. - if (features::IsUsingFastPathForSolidColorQuad()) - is_drawing_solid_color_quad_ = !(r == 0 && g == 0 && b == 0 && a == 0); - } - - void Clear(GLbitfield bits) override { - // GL clear is also used to draw solid color draw quads. - if ((bits & GL_COLOR_BUFFER_BIT) && is_drawing_solid_color_quad_) - return; - EXPECT_FALSE(scissor_enabled_); - } - - void Enable(GLenum cap) override { - if (cap == GL_SCISSOR_TEST) - scissor_enabled_ = true; - } - - void Disable(GLenum cap) override { - if (cap == GL_SCISSOR_TEST) - scissor_enabled_ = false; - } - - private: - bool scissor_enabled_ = false; - bool is_drawing_solid_color_quad_ = false; -}; - -TEST_F(GLRendererTest, ScissorTestWhenClearing) { - auto gl_owned = std::make_unique<ScissorTestOnClearCheckingGLES2Interface>(); - - auto provider = TestContextProvider::Create(std::move(gl_owned)); - provider->BindToCurrentThread(); - - cc::FakeOutputSurfaceClient output_surface_client; - std::unique_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(std::move(provider))); - output_surface->BindToClient(&output_surface_client); - - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - RendererSettings settings; - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - resource_provider.get()); - renderer.Initialize(); - EXPECT_FALSE(renderer.use_partial_swap()); - renderer.SetVisible(true); - - gfx::Size viewport_size(100, 100); - - gfx::Rect grand_child_rect(25, 25); - AggregatedRenderPassId grand_child_pass_id{3}; - AggregatedRenderPass* grand_child_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, grand_child_pass_id, grand_child_rect, - gfx::Transform(), cc::FilterOperations()); - cc::AddClippedQuad(grand_child_pass, grand_child_rect, SK_ColorYELLOW); - - gfx::Rect child_rect(50, 50); - AggregatedRenderPassId child_pass_id{2}; - AggregatedRenderPass* child_pass = - cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - child_rect, gfx::Transform(), cc::FilterOperations()); - cc::AddQuad(child_pass, child_rect, SK_ColorBLUE); - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN); - - cc::AddRenderPassQuad(root_pass, child_pass); - cc::AddRenderPassQuad(child_pass, grand_child_pass); - - renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(&renderer, viewport_size); -} - -class DiscardCheckingGLES2Interface : public TestGLES2Interface { - public: - DiscardCheckingGLES2Interface() = default; - - void DiscardFramebufferEXT(GLenum target, - GLsizei numAttachments, - const GLenum* attachments) override { - ++discarded_; - } - - int discarded() const { return discarded_; } - void reset_discarded() { discarded_ = 0; } - - private: - int discarded_ = 0; -}; - -TEST_F(GLRendererTest, NoDiscardOnPartialUpdates) { - auto gl_owned = std::make_unique<DiscardCheckingGLES2Interface>(); - gl_owned->set_have_post_sub_buffer(true); - gl_owned->set_have_discard_framebuffer(true); - - auto* gl = gl_owned.get(); - - auto provider = TestContextProvider::Create(std::move(gl_owned)); - provider->BindToCurrentThread(); - - cc::FakeOutputSurfaceClient output_surface_client; - auto output_surface = FakeOutputSurface::Create3d(std::move(provider)); - output_surface->BindToClient(&output_surface_client); - - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - RendererSettings settings; - settings.partial_swap_enabled = true; - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - resource_provider.get()); - renderer.Initialize(); - EXPECT_TRUE(renderer.use_partial_swap()); - renderer.SetVisible(true); - - gfx::Size viewport_size(100, 100); - { - // Draw one black frame to make sure the output surface is reshaped before - // testes. - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorBLACK); - root_pass->damage_rect = gfx::Rect(viewport_size); - - renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(&renderer, viewport_size); - gl->reset_discarded(); - } - { - // Partial frame, should not discard. - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN); - root_pass->damage_rect = gfx::Rect(2, 2, 3, 3); - - renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(&renderer, viewport_size); - EXPECT_EQ(0, gl->discarded()); - gl->reset_discarded(); - } - { - // Full frame, should discard. - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN); - root_pass->damage_rect = root_pass->output_rect; - - renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(&renderer, viewport_size); - EXPECT_EQ(1, gl->discarded()); - gl->reset_discarded(); - } - { - // Full frame, external scissor is set, should not discard. - output_surface->set_has_external_stencil_test(true); - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN); - root_pass->damage_rect = root_pass->output_rect; - root_pass->has_transparent_background = false; - - renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(&renderer, viewport_size); - EXPECT_EQ(0, gl->discarded()); - gl->reset_discarded(); - output_surface->set_has_external_stencil_test(false); - } -} - -class ResourceTrackingGLES2Interface : public TestGLES2Interface { - public: - ResourceTrackingGLES2Interface() = default; - ~ResourceTrackingGLES2Interface() override { CheckNoResources(); } - - void CheckNoResources() { - EXPECT_TRUE(textures_.empty()); - EXPECT_TRUE(buffers_.empty()); - EXPECT_TRUE(framebuffers_.empty()); - EXPECT_TRUE(renderbuffers_.empty()); - EXPECT_TRUE(queries_.empty()); - EXPECT_TRUE(shaders_.empty()); - EXPECT_TRUE(programs_.empty()); - } - - void GenTextures(GLsizei n, GLuint* textures) override { - GenIds(&textures_, n, textures); - } - - void GenBuffers(GLsizei n, GLuint* buffers) override { - GenIds(&buffers_, n, buffers); - } - - void GenFramebuffers(GLsizei n, GLuint* framebuffers) override { - GenIds(&framebuffers_, n, framebuffers); - } - - void GenRenderbuffers(GLsizei n, GLuint* renderbuffers) override { - GenIds(&renderbuffers_, n, renderbuffers); - } - - void GenQueriesEXT(GLsizei n, GLuint* queries) override { - GenIds(&queries_, n, queries); - } - - GLuint CreateProgram() override { return GenId(&programs_); } - - GLuint CreateShader(GLenum type) override { return GenId(&shaders_); } - - void BindTexture(GLenum target, GLuint texture) override { - CheckId(&textures_, texture); - } - - void BindBuffer(GLenum target, GLuint buffer) override { - CheckId(&buffers_, buffer); - } - - void BindRenderbuffer(GLenum target, GLuint renderbuffer) override { - CheckId(&renderbuffers_, renderbuffer); - } - - void BindFramebuffer(GLenum target, GLuint framebuffer) override { - CheckId(&framebuffers_, framebuffer); - } - - void UseProgram(GLuint program) override { CheckId(&programs_, program); } - - void DeleteTextures(GLsizei n, const GLuint* textures) override { - DeleteIds(&textures_, n, textures); - } - - void DeleteBuffers(GLsizei n, const GLuint* buffers) override { - DeleteIds(&buffers_, n, buffers); - } - - void DeleteFramebuffers(GLsizei n, const GLuint* framebuffers) override { - DeleteIds(&framebuffers_, n, framebuffers); - } - - void DeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) override { - DeleteIds(&renderbuffers_, n, renderbuffers); - } - - void DeleteQueriesEXT(GLsizei n, const GLuint* queries) override { - DeleteIds(&queries_, n, queries); - } - - void DeleteProgram(GLuint program) override { DeleteId(&programs_, program); } - - void DeleteShader(GLuint shader) override { DeleteId(&shaders_, shader); } - - void BufferData(GLenum target, - GLsizeiptr size, - const void* data, - GLenum usage) override {} - - private: - GLuint GenId(std::set<GLuint>* resource_set) { - GLuint id = next_id_++; - resource_set->insert(id); - return id; - } - - void GenIds(std::set<GLuint>* resource_set, GLsizei n, GLuint* ids) { - for (GLsizei i = 0; i < n; ++i) - ids[i] = GenId(resource_set); - } - - void CheckId(std::set<GLuint>* resource_set, GLuint id) { - if (id == 0) - return; - EXPECT_TRUE(resource_set->find(id) != resource_set->end()); - } - - void DeleteId(std::set<GLuint>* resource_set, GLuint id) { - if (id == 0) - return; - size_t num_erased = resource_set->erase(id); - EXPECT_EQ(1u, num_erased); - } - - void DeleteIds(std::set<GLuint>* resource_set, GLsizei n, const GLuint* ids) { - for (GLsizei i = 0; i < n; ++i) - DeleteId(resource_set, ids[i]); - } - - GLuint next_id_ = 1; - std::set<GLuint> textures_; - std::set<GLuint> buffers_; - std::set<GLuint> framebuffers_; - std::set<GLuint> renderbuffers_; - std::set<GLuint> queries_; - std::set<GLuint> shaders_; - std::set<GLuint> programs_; -}; - -TEST_F(GLRendererTest, NoResourceLeak) { - auto gl_owned = std::make_unique<ResourceTrackingGLES2Interface>(); - auto* gl = gl_owned.get(); - - auto provider = TestContextProvider::Create(std::move(gl_owned)); - provider->BindToCurrentThread(); - - cc::FakeOutputSurfaceClient output_surface_client; - auto output_surface = FakeOutputSurface::Create3d(std::move(provider)); - output_surface->BindToClient(&output_surface_client); - - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - { - RendererSettings settings; - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - resource_provider.get()); - renderer.Initialize(); - renderer.SetVisible(true); - - gfx::Size viewport_size(100, 100); - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN); - root_pass->damage_rect = gfx::Rect(2, 2, 3, 3); - - renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(&renderer, viewport_size); - } - gl->CheckNoResources(); -} - -class DrawElementsGLES2Interface : public TestGLES2Interface { - public: - MOCK_METHOD4( - DrawElements, - void(GLenum mode, GLsizei count, GLenum type, const void* indices)); -}; - -class GLRendererSkipTest : public GLRendererTest { - protected: - GLRendererSkipTest() { - auto gl_owned = std::make_unique<StrictMock<DrawElementsGLES2Interface>>(); - gl_owned->set_have_post_sub_buffer(true); - gl_ = gl_owned.get(); - - auto provider = TestContextProvider::Create(std::move(gl_owned)); - provider->BindToCurrentThread(); - - output_surface_ = FakeOutputSurface::Create3d(std::move(provider)); - output_surface_->BindToClient(&output_surface_client_); - - resource_provider_ = std::make_unique<DisplayResourceProviderGL>( - output_surface_->context_provider()); - settings_.partial_swap_enabled = true; - renderer_ = std::make_unique<FakeRendererGL>(&settings_, &debug_settings_, - output_surface_.get(), - resource_provider_.get()); - renderer_->Initialize(); - renderer_->SetVisible(true); - } - - void DrawBlackFrame(const gfx::Size& viewport_size) { - // The feature enables a faster path to draw solid color quads that does not - // use GL draw calls but instead uses glClear. - if (!features::IsUsingFastPathForSolidColorQuad()) - EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(1); - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - root_pass->damage_rect = gfx::Rect(viewport_size); - cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorBLACK); - renderer_->DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - DrawFrame(renderer_.get(), viewport_size); - Mock::VerifyAndClearExpectations(gl_); - } - - StrictMock<DrawElementsGLES2Interface>* gl_; - RendererSettings settings_; - cc::FakeOutputSurfaceClient output_surface_client_; - std::unique_ptr<FakeOutputSurface> output_surface_; - std::unique_ptr<DisplayResourceProviderGL> resource_provider_; - std::unique_ptr<FakeRendererGL> renderer_; -}; - -TEST_F(GLRendererSkipTest, DrawQuad) { - gfx::Size viewport_size(100, 100); - gfx::Rect quad_rect = gfx::Rect(20, 20, 20, 20); - - // Draw the a black frame to make sure output surface is reshaped before - // tests. - DrawBlackFrame(viewport_size); - - EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(1); - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - root_pass->damage_rect = gfx::Rect(0, 0, 25, 25); - cc::AddQuad(root_pass, quad_rect, SK_ColorGREEN); - - // Add rounded corners to the solid color draw quad so that the fast path - // of drawing using glClear is not used. - root_pass->shared_quad_state_list.front()->mask_filter_info = - gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(quad_rect), 2.f)); - - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(renderer_.get(), viewport_size); -} - -TEST_F(GLRendererSkipTest, SkipVisibleRect) { - gfx::Size viewport_size(100, 100); - gfx::Rect quad_rect = gfx::Rect(0, 0, 40, 40); - - // Draw the a black frame to make sure output surface is reshaped before - // tests. - DrawBlackFrame(viewport_size); - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - root_pass->damage_rect = gfx::Rect(0, 0, 10, 10); - cc::AddQuad(root_pass, quad_rect, SK_ColorGREEN); - root_pass->shared_quad_state_list.front()->clip_rect = - gfx::Rect(0, 0, 40, 40); - root_pass->quad_list.front()->visible_rect = gfx::Rect(20, 20, 20, 20); - - // Add rounded corners to the solid color draw quad so that the fast path - // of drawing using glClear is not used. - root_pass->shared_quad_state_list.front()->mask_filter_info = - gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(quad_rect), 1.f)); - - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(renderer_.get(), viewport_size); - // DrawElements should not be called because the visible rect is outside the - // scissor, even though the clip rect and quad rect intersect the scissor. -} - -TEST_F(GLRendererSkipTest, SkipClippedQuads) { - gfx::Size viewport_size(100, 100); - gfx::Rect quad_rect = gfx::Rect(25, 25, 90, 90); - - // Draw the a black frame to make sure output surface is reshaped before - // tests. - DrawBlackFrame(viewport_size); - - AggregatedRenderPassId root_pass_id{1}; - - auto* root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, - root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - root_pass->damage_rect = gfx::Rect(0, 0, 25, 25); - cc::AddClippedQuad(root_pass, quad_rect, SK_ColorGREEN); - root_pass->quad_list.front()->rect = gfx::Rect(20, 20, 20, 20); - - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(renderer_.get(), viewport_size); - // DrawElements should not be called because the clip rect is outside the - // scissor. -} - -TEST_F(GLRendererTest, DrawFramePreservesFramebuffer) { - // When using render-to-FBO to display the surface, all rendering is done - // to a non-zero FBO. Make sure that the framebuffer is always restored to - // the correct framebuffer during rendering, if changed. - // Note: there is one path that will set it to 0, but that is after the render - // has finished. - cc::FakeOutputSurfaceClient output_surface_client; - std::unique_ptr<FakeOutputSurface> output_surface( - FakeOutputSurface::Create3d()); - output_surface->BindToClient(&output_surface_client); - - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - RendererSettings settings; - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - resource_provider.get()); - renderer.Initialize(); - EXPECT_FALSE(renderer.use_partial_swap()); - renderer.SetVisible(true); - - gfx::Size viewport_size(100, 100); - gfx::Rect quad_rect = gfx::Rect(20, 20, 20, 20); - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddClippedQuad(root_pass, quad_rect, SK_ColorGREEN); - - unsigned fbo; - gpu::gles2::GLES2Interface* gl = - output_surface->context_provider()->ContextGL(); - gl->GenFramebuffers(1, &fbo); - output_surface->set_framebuffer(fbo, GL_RGB); - - renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(&renderer, viewport_size); - - int bound_fbo; - gl->GetIntegerv(GL_FRAMEBUFFER_BINDING, &bound_fbo); - EXPECT_EQ(static_cast<int>(fbo), bound_fbo); -} - -TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) { - gfx::Size viewport_size(60, 75); - - gfx::Rect child_rect(50, 50); - AggregatedRenderPassId child_pass_id{2}; - AggregatedRenderPass* child_pass; - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass; - - auto transfer_resource = TransferableResource::MakeGL( - gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(), - child_rect.size(), false /* is_overlay_candidate */); - ResourceId mask = child_resource_provider_->ImportResource(transfer_resource, - base::DoNothing()); - - // Return the mapped resource id. - std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = - cc::SendResourceAndGetChildToParentMap({mask}, resource_provider_.get(), - child_resource_provider_.get(), - child_context_provider_.get()); - ResourceId mapped_mask = resource_map[mask]; - - float matrix[20]; - float amount = 0.5f; - matrix[0] = 0.213f + 0.787f * amount; - matrix[1] = 0.715f - 0.715f * amount; - matrix[2] = 1.f - (matrix[0] + matrix[1]); - matrix[3] = matrix[4] = 0; - matrix[5] = 0.213f - 0.213f * amount; - matrix[6] = 0.715f + 0.285f * amount; - matrix[7] = 1.f - (matrix[5] + matrix[6]); - matrix[8] = matrix[9] = 0; - matrix[10] = 0.213f - 0.213f * amount; - matrix[11] = 0.715f - 0.715f * amount; - matrix[12] = 1.f - (matrix[10] + matrix[11]); - matrix[13] = matrix[14] = 0; - matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0; - matrix[18] = 1; - cc::FilterOperations filters; - filters.Append(cc::FilterOperation::CreateReferenceFilter( - sk_make_sp<cc::ColorFilterPaintFilter>(SkColorFilters::Matrix(matrix), - nullptr))); - - gfx::Transform transform_causing_aa; - transform_causing_aa.Rotate(20.0); - - for (int i = 0; i <= LAST_BLEND_MODE; ++i) { - BlendMode blend_mode = static_cast<BlendMode>(i); - SkBlendMode xfer_mode = BlendModeToSkXfermode(blend_mode); - settings_.force_blending_with_shaders = (blend_mode != BLEND_MODE_NONE); - // RenderPassProgram - render_passes_in_draw_order_.clear(); - child_pass = - cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - child_rect, gfx::Transform(), cc::FilterOperations()); - - root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id, - gfx::Rect(viewport_size), gfx::Transform(), - cc::FilterOperations()); - - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), xfer_mode); - - renderer_->DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - DrawFrame(renderer_.get(), viewport_size); - TestRenderPassProgram(TEX_COORD_PRECISION_MEDIUM, blend_mode); - - // RenderPassColorMatrixProgram - render_passes_in_draw_order_.clear(); - - child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - child_rect, transform_causing_aa, filters); - - root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id, - gfx::Rect(viewport_size), gfx::Transform(), - cc::FilterOperations()); - - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), xfer_mode); - - renderer_->DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - DrawFrame(renderer_.get(), viewport_size); - TestRenderPassColorMatrixProgram(TEX_COORD_PRECISION_MEDIUM, blend_mode); - - // RenderPassMaskProgram - render_passes_in_draw_order_.clear(); - - child_pass = - cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - child_rect, gfx::Transform(), cc::FilterOperations()); - - root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id, - gfx::Rect(viewport_size), gfx::Transform(), - cc::FilterOperations()); - - cc::AddRenderPassQuad(root_pass, child_pass, mapped_mask, gfx::Transform(), - xfer_mode); - - renderer_->DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - DrawFrame(renderer_.get(), viewport_size); - TestRenderPassMaskProgram(TEX_COORD_PRECISION_MEDIUM, SAMPLER_TYPE_2D, - blend_mode); - - // RenderPassMaskColorMatrixProgram - render_passes_in_draw_order_.clear(); - - child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - child_rect, gfx::Transform(), filters); - - root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id, - gfx::Rect(viewport_size), gfx::Transform(), - cc::FilterOperations()); - - cc::AddRenderPassQuad(root_pass, child_pass, mapped_mask, gfx::Transform(), - xfer_mode); - - renderer_->DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - DrawFrame(renderer_.get(), viewport_size); - TestRenderPassMaskColorMatrixProgram(TEX_COORD_PRECISION_MEDIUM, - SAMPLER_TYPE_2D, blend_mode); - - // RenderPassProgramAA - render_passes_in_draw_order_.clear(); - - child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - child_rect, transform_causing_aa, - cc::FilterOperations()); - - root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id, - gfx::Rect(viewport_size), gfx::Transform(), - cc::FilterOperations()); - - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - transform_causing_aa, xfer_mode); - - renderer_->DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - DrawFrame(renderer_.get(), viewport_size); - TestRenderPassProgramAA(TEX_COORD_PRECISION_MEDIUM, blend_mode); - - // RenderPassColorMatrixProgramAA - render_passes_in_draw_order_.clear(); - - child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - child_rect, transform_causing_aa, filters); - - root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id, - gfx::Rect(viewport_size), gfx::Transform(), - cc::FilterOperations()); - - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - transform_causing_aa, xfer_mode); - - renderer_->DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - DrawFrame(renderer_.get(), viewport_size); - TestRenderPassColorMatrixProgramAA(TEX_COORD_PRECISION_MEDIUM, blend_mode); - - // RenderPassMaskProgramAA - render_passes_in_draw_order_.clear(); - - child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - child_rect, transform_causing_aa, - cc::FilterOperations()); - - root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id, - gfx::Rect(viewport_size), gfx::Transform(), - cc::FilterOperations()); - - cc::AddRenderPassQuad(root_pass, child_pass, mapped_mask, - transform_causing_aa, xfer_mode); - - renderer_->DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - DrawFrame(renderer_.get(), viewport_size); - TestRenderPassMaskProgramAA(TEX_COORD_PRECISION_MEDIUM, SAMPLER_TYPE_2D, - blend_mode); - - // RenderPassMaskColorMatrixProgramAA - render_passes_in_draw_order_.clear(); - - child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - child_rect, transform_causing_aa, filters); - - root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id, - gfx::Rect(viewport_size), - transform_causing_aa, cc::FilterOperations()); - - cc::AddRenderPassQuad(root_pass, child_pass, mapped_mask, - transform_causing_aa, xfer_mode); - - renderer_->DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - DrawFrame(renderer_.get(), viewport_size); - TestRenderPassMaskColorMatrixProgramAA(TEX_COORD_PRECISION_MEDIUM, - SAMPLER_TYPE_2D, blend_mode); - } -} - -// At this time, the AA code path cannot be taken if the surface's rect would -// project incorrectly by the given transform, because of w<0 clipping. -TEST_F(GLRendererShaderTest, DrawRenderPassQuadSkipsAAForClippingTransform) { - gfx::Rect child_rect(50, 50); - AggregatedRenderPassId child_pass_id{2}; - AggregatedRenderPass* child_pass; - - gfx::Size viewport_size(100, 100); - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass; - - gfx::Transform transform_preventing_aa; - transform_preventing_aa.ApplyPerspectiveDepth(40.0); - transform_preventing_aa.RotateAboutYAxis(-20.0); - transform_preventing_aa.Scale(30.0, 1.0); - - // Verify that the test transform and test rect actually do cause the clipped - // flag to trigger. Otherwise we are not testing the intended scenario. - bool clipped = false; - cc::MathUtil::MapQuad(transform_preventing_aa, - gfx::QuadF(gfx::RectF(child_rect)), &clipped); - ASSERT_TRUE(clipped); - - child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - child_rect, transform_preventing_aa, - cc::FilterOperations()); - - root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id, - gfx::Rect(viewport_size), gfx::Transform(), - cc::FilterOperations()); - - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - transform_preventing_aa, SkBlendMode::kSrcOver); - - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(renderer_.get(), viewport_size); - - // If use_aa incorrectly ignores clipping, it will use the - // RenderPassProgramAA shader instead of the RenderPassProgram. - TestRenderPassProgram(TEX_COORD_PRECISION_MEDIUM, BLEND_MODE_NONE); -} - -TEST_F(GLRendererShaderTest, DrawSolidColorShader) { - gfx::Size viewport_size(30, 30); // Don't translate out of the viewport. - gfx::Size quad_size(3, 3); - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass; - - gfx::Transform pixel_aligned_transform_causing_aa; - pixel_aligned_transform_causing_aa.Translate(25.5f, 25.5f); - pixel_aligned_transform_causing_aa.Scale(0.5f, 0.5f); - - root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id, - gfx::Rect(viewport_size), gfx::Transform(), - cc::FilterOperations()); - cc::AddTransformedQuad(root_pass, gfx::Rect(quad_size), SK_ColorYELLOW, - pixel_aligned_transform_causing_aa); - - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(renderer_.get(), viewport_size); - - TestSolidColorProgramAA(); -} - -class OutputSurfaceMockGLES2Interface : public TestGLES2Interface { - public: - OutputSurfaceMockGLES2Interface() = default; - - // Specifically override methods even if they are unused (used in conjunction - // with StrictMock). We need to make sure that GLRenderer does not issue - // framebuffer-related GLuint calls directly. Instead these are supposed to go - // through the OutputSurface abstraction. - MOCK_METHOD2(BindFramebuffer, void(GLenum target, GLuint framebuffer)); - MOCK_METHOD5(ResizeCHROMIUM, - void(GLuint width, - GLuint height, - float device_scale, - GLcolorSpace color_space, - GLboolean has_alpha)); - MOCK_METHOD4( - DrawElements, - void(GLenum mode, GLsizei count, GLenum type, const void* indices)); -}; - -class MockOutputSurface : public OutputSurface { - public: - explicit MockOutputSurface(scoped_refptr<ContextProvider> provider) - : OutputSurface(std::move(provider)) {} - ~MockOutputSurface() override {} - - void BindToClient(OutputSurfaceClient*) override {} - unsigned UpdateGpuFence() override { return 0; } - - MOCK_METHOD0(EnsureBackbuffer, void()); - MOCK_METHOD0(DiscardBackbuffer, void()); - MOCK_METHOD5(Reshape, - void(const gfx::Size& size, - float scale_factor, - const gfx::ColorSpace& color_space, - gfx::BufferFormat format, - bool use_stencil)); - MOCK_METHOD0(BindFramebuffer, void()); - MOCK_METHOD1(SetDrawRectangle, void(const gfx::Rect&)); - MOCK_METHOD1(SetEnableDCLayers, void(bool)); - MOCK_METHOD0(GetFramebufferCopyTextureFormat, GLenum()); - MOCK_METHOD1(SwapBuffers_, void(OutputSurfaceFrame& frame)); // NOLINT - void SwapBuffers(OutputSurfaceFrame frame) override { SwapBuffers_(frame); } - MOCK_CONST_METHOD0(IsDisplayedAsOverlayPlane, bool()); - MOCK_CONST_METHOD0(GetOverlayTextureId, unsigned()); - MOCK_CONST_METHOD0(HasExternalStencilTest, bool()); - MOCK_METHOD0(ApplyExternalStencil, void()); - MOCK_METHOD1(SetUpdateVSyncParametersCallback, - void(UpdateVSyncParametersCallback)); - MOCK_METHOD1(SetDisplayTransformHint, void(gfx::OverlayTransform)); - - gfx::OverlayTransform GetDisplayTransform() override { - return gfx::OVERLAY_TRANSFORM_NONE; - } -}; - -class MockOutputSurfaceTest : public GLRendererTest { - protected: - void SetUp() override { - auto gl = std::make_unique<StrictMock<OutputSurfaceMockGLES2Interface>>(); - gl->set_have_post_sub_buffer(true); - gl_ = gl.get(); - auto provider = TestContextProvider::Create(std::move(gl)); - provider->BindToCurrentThread(); - output_surface_ = - std::make_unique<StrictMock<MockOutputSurface>>(std::move(provider)); - - output_surface_->BindToClient(&output_surface_client_); - - resource_provider_ = std::make_unique<DisplayResourceProviderGL>( - output_surface_->context_provider()); - - renderer_ = std::make_unique<FakeRendererGL>(&settings_, &debug_settings_, - output_surface_.get(), - resource_provider_.get()); - renderer_->Initialize(); - - EXPECT_CALL(*output_surface_, EnsureBackbuffer()).Times(1); - renderer_->SetVisible(true); - Mock::VerifyAndClearExpectations(output_surface_.get()); - } - - void SwapBuffers() { - renderer_->SwapBuffers(DirectRenderer::SwapFrameData()); - } - - void DrawFrame(float device_scale_factor, - const gfx::Size& viewport_size, - bool transparent) { - gfx::BufferFormat format = transparent ? gfx::BufferFormat::RGBA_8888 - : gfx::BufferFormat::RGBX_8888; - AggregatedRenderPassId render_pass_id{1}; - AggregatedRenderPass* render_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, render_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddQuad(render_pass, gfx::Rect(viewport_size), SK_ColorGREEN); - render_pass->has_transparent_background = transparent; - - EXPECT_CALL(*output_surface_, EnsureBackbuffer()).WillRepeatedly(Return()); - - EXPECT_CALL(*output_surface_, - Reshape(viewport_size, device_scale_factor, _, format, _)) - .Times(1); - - EXPECT_CALL(*output_surface_, BindFramebuffer()).Times(1); - - EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(1); - - renderer_->DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - SurfaceDamageRectList surface_damage_rect_list; - renderer_->DrawFrame(&render_passes_in_draw_order_, device_scale_factor, - viewport_size, gfx::DisplayColorSpaces(), - std::move(surface_damage_rect_list)); - } - - RendererSettings settings_; - cc::FakeOutputSurfaceClient output_surface_client_; - OutputSurfaceMockGLES2Interface* gl_ = nullptr; - std::unique_ptr<StrictMock<MockOutputSurface>> output_surface_; - std::unique_ptr<DisplayResourceProviderGL> resource_provider_; - std::unique_ptr<FakeRendererGL> renderer_; -}; - -TEST_F(MockOutputSurfaceTest, BackbufferDiscard) { - // Drop backbuffer on hide. - EXPECT_CALL(*output_surface_, DiscardBackbuffer()).Times(1); - renderer_->SetVisible(false); - Mock::VerifyAndClearExpectations(output_surface_.get()); - - // Restore backbuffer on show. - EXPECT_CALL(*output_surface_, EnsureBackbuffer()).Times(1); - renderer_->SetVisible(true); - Mock::VerifyAndClearExpectations(output_surface_.get()); -} - -#if BUILDFLAG(IS_WIN) -class MockDCLayerOverlayProcessor : public DCLayerOverlayProcessor { - public: - MockDCLayerOverlayProcessor() - : DCLayerOverlayProcessor(&debug_settings_, - /*allowed_yuv_overlay_count=*/1, - true) {} - ~MockDCLayerOverlayProcessor() override = default; - MOCK_METHOD8(Process, - void(DisplayResourceProvider* resource_provider, - const gfx::RectF& display_rect, - const FilterOperationsMap& render_pass_filters, - const FilterOperationsMap& render_pass_backdrop_filters, - AggregatedRenderPassList* render_passes, - gfx::Rect* damage_rect, - SurfaceDamageRectList surface_damage_rect_list, - DCLayerOverlayList* dc_layer_overlays)); - - protected: - DebugRendererSettings debug_settings_; -}; -class TestOverlayProcessor : public OverlayProcessorWin { - public: - explicit TestOverlayProcessor(OutputSurface* output_surface) - : OverlayProcessorWin(output_surface, - std::make_unique<MockDCLayerOverlayProcessor>()) {} - ~TestOverlayProcessor() override = default; - - MockDCLayerOverlayProcessor* GetTestProcessor() { - return static_cast<MockDCLayerOverlayProcessor*>(GetOverlayProcessor()); - } -}; -#elif BUILDFLAG(IS_APPLE) -class MockCALayerOverlayProcessor : public CALayerOverlayProcessor { - public: - MockCALayerOverlayProcessor() : CALayerOverlayProcessor(true) {} - ~MockCALayerOverlayProcessor() override = default; - - MOCK_METHOD6( - ProcessForCALayerOverlays, - bool(AggregatedRenderPass* render_pass, - DisplayResourceProvider* resource_provider, - const gfx::RectF& display_rect, - const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>& - render_pass_filters, - const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>& - render_pass_backdrop_filters, - CALayerOverlayList* ca_layer_overlays)); -}; - -class TestOverlayProcessor : public OverlayProcessorMac { - public: - explicit TestOverlayProcessor(OutputSurface* output_surface) - : OverlayProcessorMac(std::make_unique<MockCALayerOverlayProcessor>()) {} - ~TestOverlayProcessor() override = default; - - MockCALayerOverlayProcessor* GetTestProcessor() { - return static_cast<MockCALayerOverlayProcessor*>(GetOverlayProcessor()); - } -}; - -#elif BUILDFLAG(IS_ANDROID) || defined(USE_OZONE) - -class TestOverlayProcessor : public OverlayProcessorUsingStrategy { - public: - class TestStrategy : public OverlayProcessorStrategy { - public: - TestStrategy() = default; - ~TestStrategy() override = default; - - MOCK_METHOD8( - Attempt, - bool(const SkM44& output_color_matrix, - const OverlayProcessorInterface::FilterOperationsMap& - render_pass_backdrop_filters, - DisplayResourceProvider* resource_provider, - AggregatedRenderPassList* render_pass_list, - SurfaceDamageRectList* surface_damage_rect_list, - const OverlayProcessorInterface::OutputSurfaceOverlayPlane* - primary_surface, - OverlayCandidateList* candidates, - std::vector<gfx::Rect>* content_bounds)); - - void ProposePrioritized( - const SkM44& output_color_matrix, - const FilterOperationsMap& render_pass_backdrop_filters, - DisplayResourceProvider* resource_provider, - AggregatedRenderPassList* render_pass_list, - SurfaceDamageRectList* surface_damage_rect_list, - const PrimaryPlane* primary_plane, - std::vector<OverlayProposedCandidate>* candidates, - std::vector<gfx::Rect>* content_bounds) override { - auto* render_pass = render_pass_list->back().get(); - QuadList& quad_list = render_pass->quad_list; - OverlayCandidate candidate; - candidates->push_back({quad_list.end(), candidate, this}); - } - - MOCK_METHOD9(AttemptPrioritized, - bool(const SkM44& output_color_matrix, - const FilterOperationsMap& render_pass_backdrop_filters, - DisplayResourceProvider* resource_provider, - AggregatedRenderPassList* render_pass_list, - SurfaceDamageRectList* surface_damage_rect_list, - const PrimaryPlane* primary_plane, - OverlayCandidateList* candidates, - std::vector<gfx::Rect>* content_bounds, - const OverlayProposedCandidate& proposed_candidate)); - - MOCK_METHOD2(CommitCandidate, - void(const OverlayProposedCandidate& proposed_candidate, - AggregatedRenderPass* render_pass)); - }; - - bool IsOverlaySupported() const override { return true; } - - // A list of possible overlay candidates is presented to this function. - // The expected result is that those candidates that can be in a separate - // plane are marked with |overlay_handled| set to true, otherwise they are - // to be traditionally composited. Candidates with |overlay_handled| set to - // true must also have their |display_rect| converted to integer - // coordinates if necessary. - void CheckOverlaySupportImpl( - const OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane, - OverlayCandidateList* surfaces) override {} - - TestStrategy& strategy() { - auto* strategy = strategies_.back().get(); - return *(static_cast<TestStrategy*>(strategy)); - } - - MOCK_CONST_METHOD0(NeedsSurfaceDamageRectList, bool()); - explicit TestOverlayProcessor(OutputSurface* output_surface) { - strategies_.push_back(std::make_unique<TestStrategy>()); - prioritization_config_.changing_threshold = false; - prioritization_config_.damage_rate_threshold = false; - } - ~TestOverlayProcessor() override = default; -}; -#else // Default to no overlay. -class TestOverlayProcessor : public OverlayProcessorStub { - public: - explicit TestOverlayProcessor(OutputSurface* output_surface) - : OverlayProcessorStub() {} - ~TestOverlayProcessor() override = default; -}; -#endif - -void MailboxReleased(const gpu::SyncToken& sync_token, bool lost_resource) {} - -static void CollectResources(std::vector<ReturnedResource>* array, - std::vector<ReturnedResource> returned) { - array->insert(array->end(), std::make_move_iterator(returned.begin()), - std::make_move_iterator(returned.end())); -} - -TEST_F(GLRendererTest, DontOverlayWithCopyRequests) { - cc::FakeOutputSurfaceClient output_surface_client; - std::unique_ptr<FakeOutputSurface> output_surface( - FakeOutputSurface::Create3d()); -#if BUILDFLAG(IS_WIN) - output_surface->set_supports_dc_layers(true); -#endif - output_surface->BindToClient(&output_surface_client); - - auto parent_resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - auto child_context_provider = TestContextProvider::Create(); - child_context_provider->BindToCurrentThread(); - auto child_resource_provider = std::make_unique<ClientResourceProvider>(); - - auto transfer_resource = TransferableResource::MakeGL( - gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(), - gfx::Size(256, 256), true); - auto release_callback = base::BindOnce(&MailboxReleased); - ResourceId resource_id = child_resource_provider->ImportResource( - transfer_resource, std::move(release_callback)); - - std::vector<ReturnedResource> returned_to_child; - int child_id = parent_resource_provider->CreateChild( - base::BindRepeating(&CollectResources, &returned_to_child), SurfaceId()); - - // Transfer resource to the parent. - std::vector<ResourceId> resource_ids_to_transfer; - resource_ids_to_transfer.push_back(resource_id); - std::vector<TransferableResource> list; - child_resource_provider->PrepareSendToParent( - resource_ids_to_transfer, &list, - static_cast<RasterContextProvider*>(child_context_provider.get())); - parent_resource_provider->ReceiveFromChild(child_id, list); - - // In DisplayResourceProvider's namespace, use the mapped resource id. - std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = - parent_resource_provider->GetChildToParentMap(child_id); - ResourceId parent_resource_id = resource_map[list[0].id]; - - auto processor = std::make_unique<TestOverlayProcessor>(output_surface.get()); - - RendererSettings settings; - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - parent_resource_provider.get(), processor.get(), - base::ThreadTaskRunnerHandle::Get()); - renderer.Initialize(); - renderer.SetVisible(true); - -#if BUILDFLAG(IS_APPLE) - MockCALayerOverlayProcessor* mock_ca_processor = - processor->GetTestProcessor(); -#elif BUILDFLAG(IS_WIN) - MockDCLayerOverlayProcessor* dc_processor = processor->GetTestProcessor(); -#endif - - gfx::Size viewport_size(1, 1); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{1}, - gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations()); - root_pass->has_transparent_background = false; - root_pass->copy_requests.push_back(CopyOutputRequest::CreateStubForTesting()); - - bool needs_blending = false; - bool premultiplied_alpha = false; - bool flipped = false; - bool nearest_neighbor = false; - float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - - TextureDrawQuad* overlay_quad = - root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>(); - overlay_quad->SetNew( - root_pass->CreateAndAppendSharedQuadState(), gfx::Rect(viewport_size), - gfx::Rect(viewport_size), needs_blending, parent_resource_id, - premultiplied_alpha, gfx::PointF(0, 0), gfx::PointF(1, 1), - SK_ColorTRANSPARENT, vertex_opacity, flipped, nearest_neighbor, - /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear); - - // DirectRenderer::DrawFrame calls into OverlayProcessor::ProcessForOverlays. - // Attempt will be called for each strategy in OverlayProcessor. We have - // added a fake strategy, so checking for Attempt calls checks if there was - // any attempt to overlay, which there shouldn't be. We can't use the quad - // list because the render pass is cleaned up by DrawFrame. -#if defined(USE_OZONE) || BUILDFLAG(IS_ANDROID) - if (features::IsOverlayPrioritizationEnabled()) { - EXPECT_CALL(processor->strategy(), - AttemptPrioritized(_, _, _, _, _, _, _, _, _)) - .Times(0); - } else { - EXPECT_CALL(processor->strategy(), Attempt(_, _, _, _, _, _, _, _)) - .Times(0); - } -#elif BUILDFLAG(IS_APPLE) - EXPECT_CALL(*mock_ca_processor, ProcessForCALayerOverlays(_, _, _, _, _, _)) - .WillOnce(Return(false)); -#elif BUILDFLAG(IS_WIN) - EXPECT_CALL(*dc_processor, Process(_, _, _, _, _, _, _, _)).Times(0); -#endif - DrawFrame(&renderer, viewport_size); -#if defined(USE_OZONE) || BUILDFLAG(IS_ANDROID) - Mock::VerifyAndClearExpectations(&processor->strategy()); -#elif BUILDFLAG(IS_APPLE) - Mock::VerifyAndClearExpectations( - const_cast<MockCALayerOverlayProcessor*>(mock_ca_processor)); -#elif BUILDFLAG(IS_WIN) - Mock::VerifyAndClearExpectations( - const_cast<MockDCLayerOverlayProcessor*>(dc_processor)); -#endif - - // Without a copy request Attempt() should be called once. - root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{1}, - gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations()); - root_pass->has_transparent_background = false; - - overlay_quad = root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>(); - overlay_quad->SetNew( - root_pass->CreateAndAppendSharedQuadState(), gfx::Rect(viewport_size), - gfx::Rect(viewport_size), needs_blending, parent_resource_id, - premultiplied_alpha, gfx::PointF(0, 0), gfx::PointF(1, 1), - SK_ColorTRANSPARENT, vertex_opacity, flipped, nearest_neighbor, - /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear); -#if defined(USE_OZONE) || BUILDFLAG(IS_ANDROID) - if (features::IsOverlayPrioritizationEnabled()) { - EXPECT_CALL(processor->strategy(), - AttemptPrioritized(_, _, _, _, _, _, _, _, _)) - .Times(1); - } else { - EXPECT_CALL(processor->strategy(), Attempt(_, _, _, _, _, _, _, _)) - .Times(1); - } -#elif BUILDFLAG(IS_APPLE) - EXPECT_CALL(*mock_ca_processor, ProcessForCALayerOverlays(_, _, _, _, _, _)) - .WillOnce(Return(true)); -#elif BUILDFLAG(IS_WIN) - EXPECT_CALL(*dc_processor, Process(_, _, _, _, _, _, _, _)).Times(1); -#endif - DrawFrame(&renderer, viewport_size); - - // Transfer resources back from the parent to the child. Set no resources as - // being in use. - parent_resource_provider->DeclareUsedResourcesFromChild(child_id, - ResourceIdSet()); - - child_resource_provider->RemoveImportedResource(resource_id); - child_resource_provider->ShutdownAndReleaseAllResources(); -} - -#if BUILDFLAG(IS_ANDROID) || defined(USE_OZONE) -class SingleOverlayOnTopProcessor : public OverlayProcessorUsingStrategy { - public: - SingleOverlayOnTopProcessor() : OverlayProcessorUsingStrategy() { - strategies_.push_back(std::make_unique<OverlayStrategySingleOnTop>(this)); - strategies_.push_back(std::make_unique<OverlayStrategyUnderlay>(this)); - prioritization_config_.changing_threshold = false; - prioritization_config_.damage_rate_threshold = false; - } - - bool NeedsSurfaceDamageRectList() const override { return true; } - bool IsOverlaySupported() const override { return true; } - - void CheckOverlaySupportImpl( - const OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane, - OverlayCandidateList* surfaces) override { - if (!multiple_candidates_) - ASSERT_EQ(1U, surfaces->size()); - OverlayCandidate& candidate = surfaces->back(); - candidate.overlay_handled = true; - } - - void AllowMultipleCandidates() { multiple_candidates_ = true; } - - private: - bool multiple_candidates_ = false; -}; - -class WaitSyncTokenCountingGLES2Interface : public TestGLES2Interface { - public: - MOCK_METHOD1(WaitSyncTokenCHROMIUM, void(const GLbyte* sync_token)); -}; - -class MockOverlayScheduler { - public: - MOCK_METHOD7(Schedule, - void(int plane_z_order, - gfx::OverlayTransform plane_transform, - unsigned overlay_texture_id, - const gfx::Rect& display_bounds, - const gfx::RectF& uv_rect, - bool enable_blend, - unsigned gpu_fence_id)); -}; - -TEST_F(GLRendererTest, OverlaySyncTokensAreProcessed) { - auto gl_owned = std::make_unique<WaitSyncTokenCountingGLES2Interface>(); - WaitSyncTokenCountingGLES2Interface* gl = gl_owned.get(); - - auto provider = TestContextProvider::Create(std::move(gl_owned)); - provider->BindToCurrentThread(); - - MockOverlayScheduler overlay_scheduler; - provider->support()->SetScheduleOverlayPlaneCallback(base::BindRepeating( - &MockOverlayScheduler::Schedule, base::Unretained(&overlay_scheduler))); - - cc::FakeOutputSurfaceClient output_surface_client; - std::unique_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(std::move(provider))); - output_surface->BindToClient(&output_surface_client); - - auto parent_resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - auto child_context_provider = TestContextProvider::Create(); - child_context_provider->BindToCurrentThread(); - auto child_resource_provider = std::make_unique<ClientResourceProvider>(); - - gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO, - gpu::CommandBufferId::FromUnsafeValue(0x123), 29); - auto transfer_resource = TransferableResource::MakeGL( - gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, sync_token, - gfx::Size(256, 256), true); - auto release_callback = base::BindOnce(&MailboxReleased); - ResourceId resource_id = child_resource_provider->ImportResource( - transfer_resource, std::move(release_callback)); - - std::vector<ReturnedResource> returned_to_child; - int child_id = parent_resource_provider->CreateChild( - base::BindRepeating(&CollectResources, &returned_to_child), SurfaceId()); - - // Transfer resource to the parent. - std::vector<ResourceId> resource_ids_to_transfer; - resource_ids_to_transfer.push_back(resource_id); - std::vector<TransferableResource> list; - child_resource_provider->PrepareSendToParent( - resource_ids_to_transfer, &list, - static_cast<RasterContextProvider*>(child_context_provider.get())); - parent_resource_provider->ReceiveFromChild(child_id, list); - - // In DisplayResourceProvider's namespace, use the mapped resource id. - std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = - parent_resource_provider->GetChildToParentMap(child_id); - ResourceId parent_resource_id = resource_map[list[0].id]; - - RendererSettings settings; - auto processor = std::make_unique<SingleOverlayOnTopProcessor>(); - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - parent_resource_provider.get(), processor.get(), - base::ThreadTaskRunnerHandle::Get()); - renderer.Initialize(); - renderer.SetVisible(true); - - gfx::Size viewport_size(1, 1); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{1}, - gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations()); - root_pass->has_transparent_background = false; - - bool needs_blending = false; - bool premultiplied_alpha = false; - bool flipped = false; - bool nearest_neighbor = false; - float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - gfx::PointF uv_top_left(0, 0); - gfx::PointF uv_bottom_right(1, 1); - - TextureDrawQuad* overlay_quad = - root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>(); - SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState(); - shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size), - gfx::Rect(viewport_size), gfx::MaskFilterInfo(), - absl::nullopt, false, 1, SkBlendMode::kSrcOver, 0); - overlay_quad->SetNew(shared_state, gfx::Rect(viewport_size), - gfx::Rect(viewport_size), needs_blending, - parent_resource_id, premultiplied_alpha, uv_top_left, - uv_bottom_right, SK_ColorTRANSPARENT, vertex_opacity, - flipped, nearest_neighbor, /*secure_output_only=*/false, - gfx::ProtectedVideoType::kClear); - - // The verified flush flag will be set by - // ClientResourceProvider::PrepareSendToParent. Before checking if the - // gpu::SyncToken matches, set this flag first. - sync_token.SetVerifyFlush(); - - // Verify that overlay_quad actually gets turned into an overlay, and even - // though it's not drawn, that its sync point is waited on. - EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(MatchesSyncToken(sync_token))) - .Times(1); - - EXPECT_CALL( - overlay_scheduler, - Schedule(1, gfx::OVERLAY_TRANSFORM_NONE, _, gfx::Rect(viewport_size), - BoundingRect(uv_top_left, uv_bottom_right), _, _)) - .Times(1); - - DrawFrame(&renderer, viewport_size); - - // Transfer resources back from the parent to the child. Set no resources as - // being in use. - parent_resource_provider->DeclareUsedResourcesFromChild(child_id, - ResourceIdSet()); - - child_resource_provider->RemoveImportedResource(resource_id); - child_resource_provider->ShutdownAndReleaseAllResources(); -} -#endif // defined(USE_OZONE) || BUILDFLAG(IS_ANDROID) - -class OutputColorMatrixMockGLES2Interface : public TestGLES2Interface { - public: - OutputColorMatrixMockGLES2Interface() = default; - - MOCK_METHOD4(UniformMatrix4fv, - void(GLint location, - GLsizei count, - GLboolean transpose, - const GLfloat* value)); -}; - -TEST_F(GLRendererTest, OutputColorMatrixTest) { - // Initialize the mock GL interface, the output surface and the renderer. - auto gl_owned = std::make_unique<OutputColorMatrixMockGLES2Interface>(); - auto* gl = gl_owned.get(); - auto provider = TestContextProvider::Create(std::move(gl_owned)); - provider->BindToCurrentThread(); - std::unique_ptr<FakeOutputSurface> output_surface( - FakeOutputSurface::Create3d(std::move(provider))); - cc::FakeOutputSurfaceClient output_surface_client; - output_surface->BindToClient(&output_surface_client); - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - RendererSettings settings; - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - resource_provider.get()); - renderer.Initialize(); - renderer.SetVisible(true); - - // Set a non-identity color matrix on the output surface. - SkM44 color_matrix; - color_matrix.setRC(0, 0, 0.7f); - color_matrix.setRC(1, 1, 0.4f); - color_matrix.setRC(2, 2, 0.5f); - output_surface->set_color_matrix(color_matrix); - - // Create a root and a child passes to test that the output color matrix is - // registered only for the root pass. - gfx::Size viewport_size(100, 100); - AggregatedRenderPassId child_pass_id{2}; - AggregatedRenderPass* child_pass = - cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - gfx::Rect(viewport_size) + gfx::Vector2d(1, 2), - gfx::Transform(), cc::FilterOperations()); - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - root_pass->damage_rect = gfx::Rect(0, 0, 25, 25); - cc::AddRenderPassQuad(root_pass, child_pass); - - // Verify that UniformMatrix4fv() is called only once on the root pass with - // the correct matrix values. - int call_count = 0; - bool output_color_matrix_invoked = false; - EXPECT_CALL(*gl, UniformMatrix4fv(_, 1, false, _)) - .WillRepeatedly(testing::WithArgs<0, 3>(testing::Invoke( - [&color_matrix, &renderer, &call_count, &output_color_matrix_invoked]( - int matrix_location, const GLfloat* gl_matrix) { - DCHECK(current_program(&renderer)); - const int color_matrix_location = - current_program(&renderer)->output_color_matrix_location(); - - if (matrix_location != color_matrix_location) - return; - - call_count++; - output_color_matrix_invoked = true; - float expected_matrix[16]; - color_matrix.getColMajor(expected_matrix); - for (int i = 0; i < 16; ++i) - EXPECT_FLOAT_EQ(expected_matrix[i], gl_matrix[i]); - }))); - - renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(&renderer, viewport_size); - - EXPECT_EQ(1, call_count); - EXPECT_TRUE(output_color_matrix_invoked); -} - -class GenerateMipmapMockGLESInterface : public TestGLES2Interface { - public: - GenerateMipmapMockGLESInterface() = default; - - MOCK_METHOD3(TexParameteri, void(GLenum target, GLenum pname, GLint param)); - MOCK_METHOD1(GenerateMipmap, void(GLenum target)); -}; - -// TODO(crbug.com/803286): Currently npot texture always return false on ubuntu -// desktop. The npot texture check is probably failing on desktop GL. This test -// crashes DCHECK npot texture to catch this. When -// GLRendererPixelTest.DISABLED_TrilinearFiltering got passed, can remove this. -TEST_F(GLRendererTest, GenerateMipmap) { - // Initialize the mock GL interface, the output surface and the renderer. - auto gl_owned = std::make_unique<GenerateMipmapMockGLESInterface>(); - gl_owned->set_support_texture_npot(true); - - auto* gl = gl_owned.get(); - auto provider = TestContextProvider::Create(std::move(gl_owned)); - provider->BindToCurrentThread(); - - std::unique_ptr<FakeOutputSurface> output_surface( - FakeOutputSurface::Create3d(std::move(provider))); - cc::FakeOutputSurfaceClient output_surface_client; - output_surface->BindToClient(&output_surface_client); - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - RendererSettings settings; - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - resource_provider.get()); - renderer.Initialize(); - renderer.SetVisible(true); - - gfx::Size viewport_size(100, 100); - AggregatedRenderPassId child_pass_id{2}; - // Create a child pass with mipmap to verify that npot texture is enabled. - AggregatedRenderPass* child_pass = - cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - gfx::Rect(viewport_size) + gfx::Vector2d(1, 2), - gfx::Transform(), cc::FilterOperations()); - child_pass->generate_mipmap = true; - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - root_pass->damage_rect = gfx::Rect(0, 0, 25, 25); - cc::AddRenderPassQuad(root_pass, child_pass); - renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - - EXPECT_CALL(*gl, TexParameteri(_, _, _)).Times(4); - EXPECT_CALL(*gl, GenerateMipmap(GL_TEXTURE_2D)).Times(1); - // When generate_mipmap enabled, the GL_TEXTURE_MIN_FILTER should be - // GL_LINEAR_MIPMAP_LINEAR. - EXPECT_CALL(*gl, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, - GL_LINEAR_MIPMAP_LINEAR)); - DrawFrame(&renderer, viewport_size); -} - -class FastSolidColorMockGLES2Interface : public TestGLES2Interface { - public: - FastSolidColorMockGLES2Interface() = default; - - MOCK_METHOD1(Enable, void(GLenum cap)); - MOCK_METHOD1(Disable, void(GLenum cap)); - MOCK_METHOD4(ClearColor, - void(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)); - MOCK_METHOD4(Scissor, void(GLint x, GLint y, GLsizei width, GLsizei height)); -}; - -class GLRendererFastSolidColorTest : public GLRendererTest { - public: - void SetUp() override { - feature_list_.InitAndEnableFeature(features::kFastSolidColorDraw); - GLRendererTest::SetUp(); - - auto gl_owned = std::make_unique<FastSolidColorMockGLES2Interface>(); - gl_owned->set_have_post_sub_buffer(true); - gl_ = gl_owned.get(); - - auto provider = TestContextProvider::Create(std::move(gl_owned)); - provider->BindToCurrentThread(); - - output_surface_ = FakeOutputSurface::Create3d(std::move(provider)); - output_surface_->BindToClient(&output_surface_client_); - - resource_provider_ = std::make_unique<DisplayResourceProviderGL>( - output_surface_->context_provider()); - - settings_.partial_swap_enabled = true; - settings_.slow_down_compositing_scale_factor = 1; - settings_.allow_antialiasing = true; - - fake_renderer_ = std::make_unique<FakeRendererGL>( - &settings_, &debug_settings_, output_surface_.get(), - resource_provider_.get()); - fake_renderer_->Initialize(); - EXPECT_TRUE(fake_renderer_->use_partial_swap()); - fake_renderer_->SetVisible(true); - } - - void TearDown() override { - resource_provider_.reset(); - fake_renderer_.reset(); - output_surface_.reset(); - gl_ = nullptr; - - GLRendererTest::TearDown(); - } - - FastSolidColorMockGLES2Interface* gl_ptr() { return gl_; } - - FakeOutputSurface* output_surface() { return output_surface_.get(); } - - protected: - void AddExpectations(bool use_fast_path, - const gfx::Rect& scissor_rect, - SkColor color = SK_ColorBLACK, - bool enable_stencil = false) { - auto* gl = gl_ptr(); - - InSequence seq; - - // Restore GL state method calls - EXPECT_CALL(*gl, Disable(GL_DEPTH_TEST)); - EXPECT_CALL(*gl, Disable(GL_CULL_FACE)); - EXPECT_CALL(*gl, Disable(GL_STENCIL_TEST)); - EXPECT_CALL(*gl, Enable(GL_BLEND)); - EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)); - EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)); - - if (!enable_stencil) - EXPECT_CALL(*gl, ClearColor(0, 0, 0, 0)); - - if (use_fast_path) { - EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)); - EXPECT_CALL(*gl, Scissor(scissor_rect.x(), scissor_rect.y(), - scissor_rect.width(), scissor_rect.height())); - - SkColor4f color_f = SkColor4f::FromColor(color); - EXPECT_CALL(*gl, - ClearColor(color_f.fR, color_f.fG, color_f.fB, color_f.fA)); - - EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)); - EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)); - } - - if (enable_stencil) { - EXPECT_CALL(*gl, Enable(GL_STENCIL_TEST)); - EXPECT_CALL(*gl, Disable(GL_BLEND)); - } - - EXPECT_CALL(*gl, Disable(GL_BLEND)); - } - - void RunTest(const gfx::Size& viewport_size) { - fake_renderer_->DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - DrawFrame(fake_renderer_.get(), viewport_size); - - auto* gl = gl_ptr(); - ASSERT_TRUE(gl); - Mock::VerifyAndClearExpectations(gl); - } - - private: - FastSolidColorMockGLES2Interface* gl_ = nullptr; - std::unique_ptr<FakeRendererGL> fake_renderer_; - std::unique_ptr<FakeOutputSurface> output_surface_; - std::unique_ptr<DisplayResourceProviderGL> resource_provider_; - cc::FakeOutputSurfaceClient output_surface_client_; - RendererSettings settings_; - base::test::ScopedFeatureList feature_list_; -}; - -TEST_F(GLRendererFastSolidColorTest, RoundedCorners) { - gfx::Size viewport_size(500, 500); - gfx::Rect root_pass_output_rect(400, 400); - gfx::Rect root_pass_damage_rect(10, 20, 300, 200); - gfx::Rect quad_rect(0, 50, 100, 100); - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPassWithDamage( - &render_passes_in_draw_order_, root_pass_id, root_pass_output_rect, - root_pass_damage_rect, gfx::Transform(), cc::FilterOperations()); - root_pass->damage_rect = root_pass_damage_rect; - cc::AddQuad(root_pass, quad_rect, SK_ColorRED); - - root_pass->shared_quad_state_list.front()->mask_filter_info = - gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(quad_rect), 5.f)); - - // Fast Solid color draw quads should not be executed. - AddExpectations(false /*use_fast_path*/, gfx::Rect()); - - RunTest(viewport_size); -} - -TEST_F(GLRendererFastSolidColorTest, Transform3DSlowPath) { - gfx::Size viewport_size(500, 500); - gfx::Rect root_pass_damage_rect(10, 20, 300, 200); - gfx::Rect quad_rect(0, 50, 100, 100); - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - root_pass->damage_rect = root_pass_damage_rect; - cc::AddQuad(root_pass, quad_rect, SK_ColorRED); - - gfx::Transform tm_3d; - tm_3d.RotateAboutYAxis(30.0); - ASSERT_FALSE(tm_3d.IsFlat()); - - root_pass->shared_quad_state_list.front()->quad_to_target_transform = tm_3d; - - AddExpectations(false /*use_fast_path*/, gfx::Rect()); - - RunTest(viewport_size); -} - -TEST_F(GLRendererFastSolidColorTest, NonTransform3DFastPath) { - gfx::Size viewport_size(500, 500); - gfx::Rect root_pass_damage_rect(10, 20, 300, 200); - gfx::Rect quad_rect(0, 0, 200, 200); - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - root_pass->damage_rect = root_pass_damage_rect; - cc::AddQuad(root_pass, quad_rect, SK_ColorRED); - - gfx::Transform tm_non_3d; - tm_non_3d.Translate(10.f, 10.f); - ASSERT_TRUE(tm_non_3d.IsFlat()); - - root_pass->shared_quad_state_list.front()->quad_to_target_transform = - tm_non_3d; - - AddExpectations(true /*use_fast_path*/, gfx::Rect(10, 290, 200, 200), - SK_ColorRED); - - RunTest(viewport_size); -} - -TEST_F(GLRendererFastSolidColorTest, NonAxisAlignSlowPath) { - gfx::Size viewport_size(500, 500); - gfx::Rect root_pass_damage_rect(10, 20, 300, 200); - gfx::Rect quad_rect(0, 0, 200, 200); - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - root_pass->damage_rect = root_pass_damage_rect; - cc::AddQuad(root_pass, quad_rect, SK_ColorRED); - - gfx::Transform tm_non_axis_align; - tm_non_axis_align.RotateAboutZAxis(45.0); - ASSERT_TRUE(tm_non_axis_align.IsFlat()); - - root_pass->shared_quad_state_list.front()->quad_to_target_transform = - tm_non_axis_align; - - AddExpectations(false /*use_fast_path*/, gfx::Rect()); - - RunTest(viewport_size); -} - -TEST_F(GLRendererFastSolidColorTest, StencilSlowPath) { - gfx::Size viewport_size(500, 500); - gfx::Rect root_pass_damage_rect(10, 20, 300, 200); - gfx::Rect quad_rect(0, 0, 200, 200); - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - root_pass->damage_rect = root_pass_damage_rect; - root_pass->has_transparent_background = false; - - cc::AddQuad(root_pass, quad_rect, SK_ColorRED); - - AddExpectations(false /*use_fast_path*/, gfx::Rect(), SK_ColorRED, - true /*enable_stencil*/); - output_surface()->set_has_external_stencil_test(true); - - RunTest(viewport_size); -} - -TEST_F(GLRendererFastSolidColorTest, NeedsBlendingSlowPath) { - gfx::Size viewport_size(500, 500); - gfx::Rect root_pass_damage_rect(2, 3, 300, 200); - gfx::Rect full_quad_rect(0, 0, 50, 50); - gfx::Rect quad_rect_1(0, 0, 20, 20); - gfx::Rect quad_rect_2(20, 0, 20, 20); - gfx::Rect quad_rect_3(0, 20, 20, 20); - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - root_pass->damage_rect = root_pass_damage_rect; - - cc::AddQuad(root_pass, quad_rect_1, SkColorSetARGB(0x33, 0xFF, 0, 0)); - - cc::AddQuad(root_pass, quad_rect_2, SK_ColorBLUE); - root_pass->shared_quad_state_list.back()->opacity = 0.5f; - - cc::AddQuad(root_pass, quad_rect_3, SK_ColorGREEN); - root_pass->shared_quad_state_list.back()->blend_mode = SkBlendMode::kDstIn; - - cc::AddQuad(root_pass, full_quad_rect, SK_ColorBLACK); - - // The first solid color quad would use a fast path, but the other quads that - // require blending will use the slower method. - AddExpectations(true /*use_fast_path*/, gfx::Rect(0, 450, 50, 50), - SK_ColorBLACK, false /*enable_stencil*/); - - RunTest(viewport_size); -} - -TEST_F(GLRendererFastSolidColorTest, NeedsBlendingFastPath) { - gfx::Size viewport_size(500, 500); - gfx::Rect root_pass_damage_rect(2, 3, 300, 200); - gfx::Rect quad_rect_1(0, 0, 20, 20); - gfx::Rect quad_rect_2(20, 0, 20, 20); - gfx::Rect quad_rect_3(0, 20, 20, 20); - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - root_pass->damage_rect = root_pass_damage_rect; - - cc::AddQuad(root_pass, quad_rect_1, SkColorSetARGB(0x33, 0xFF, 0, 0)); - - cc::AddQuad(root_pass, quad_rect_2, SK_ColorBLUE); - root_pass->shared_quad_state_list.back()->opacity = 0.5f; - - cc::AddQuad(root_pass, quad_rect_3, SK_ColorGREEN); - root_pass->shared_quad_state_list.back()->blend_mode = SkBlendMode::kSrc; - - auto* gl = gl_ptr(); - - // The quads here despite having blend requirements can still use fast path - // because they do not intersect with any other quad that has already been - // drawn onto the render target. - InSequence seq; - - // // Restore GL state method calls - EXPECT_CALL(*gl, Disable(GL_DEPTH_TEST)); - EXPECT_CALL(*gl, Disable(GL_CULL_FACE)); - EXPECT_CALL(*gl, Disable(GL_STENCIL_TEST)); - EXPECT_CALL(*gl, Enable(GL_BLEND)); - EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)); - EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)); - EXPECT_CALL(*gl, ClearColor(0, 0, 0, 0)); - - // Fast path draw used for green quad. - EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)); - EXPECT_CALL(*gl, Scissor(0, 460, 20, 20)); - EXPECT_CALL(*gl, ClearColor(0, 1, 0, 1)); - EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)); - EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)); - - // Fast path draw used for blue quad. - EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)); - EXPECT_CALL(*gl, Scissor(20, 480, 20, 20)); - EXPECT_CALL(*gl, ClearColor(0, 0, 0.5f, 0.5f)); - EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)); - EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)); - - // Fast path draw used for red quad. - EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)); - EXPECT_CALL(*gl, Scissor(0, 480, 20, 20)); - EXPECT_CALL(*gl, ClearColor(::testing::FloatEq(0.2f), 0, 0, - ::testing::FloatEq(0.2f))); - EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)); - EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)); - - EXPECT_CALL(*gl, Disable(GL_BLEND)); - - RunTest(viewport_size); -} - -TEST_F(GLRendererFastSolidColorTest, AntiAliasSlowPath) { - gfx::Size viewport_size(500, 500); - gfx::Rect root_pass_damage_rect(10, 20, 300, 200); - gfx::Rect quad_rect(0, 0, 200, 200); - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - root_pass->damage_rect = root_pass_damage_rect; - cc::AddQuad(root_pass, quad_rect, SK_ColorRED); - - gfx::Transform tm_aa; - tm_aa.Translate(0.1f, 0.1f); - ASSERT_TRUE(tm_aa.IsFlat()); - - root_pass->shared_quad_state_list.front()->quad_to_target_transform = tm_aa; - - AddExpectations(false /*use_fast_path*/, gfx::Rect()); - - RunTest(viewport_size); -} - -class PartialSwapMockGLES2Interface : public TestGLES2Interface { - public: - PartialSwapMockGLES2Interface() = default; - - MOCK_METHOD1(Enable, void(GLenum cap)); - MOCK_METHOD1(Disable, void(GLenum cap)); - MOCK_METHOD4(Scissor, void(GLint x, GLint y, GLsizei width, GLsizei height)); - MOCK_METHOD1(SetEnableDCLayersCHROMIUM, void(GLboolean enable)); -}; - -class GLRendererPartialSwapTest : public GLRendererTest { - public: - void SetUp() override { - // Force enable fast solid color draw path. - scoped_feature_list_.InitAndEnableFeature(features::kFastSolidColorDraw); - GLRendererTest::SetUp(); - } - - protected: - void RunTest(bool partial_swap, bool set_draw_rectangle) { - auto gl_owned = std::make_unique<PartialSwapMockGLES2Interface>(); - gl_owned->set_have_post_sub_buffer(true); - - auto* gl = gl_owned.get(); - - auto provider = TestContextProvider::Create(std::move(gl_owned)); - provider->BindToCurrentThread(); - - cc::FakeOutputSurfaceClient output_surface_client; - std::unique_ptr<FakeOutputSurface> output_surface( - FakeOutputSurface::Create3d(std::move(provider))); - output_surface->set_supports_dc_layers(set_draw_rectangle); - output_surface->BindToClient(&output_surface_client); - - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - RendererSettings settings; - settings.partial_swap_enabled = partial_swap; - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - resource_provider.get()); - renderer.Initialize(); - EXPECT_EQ(partial_swap, renderer.use_partial_swap()); - renderer.SetVisible(true); - - gfx::Size viewport_size(100, 100); - gfx::Rect root_pass_output_rect(80, 80); - gfx::Rect root_pass_damage_rect(2, 2, 3, 3); - - // Draw one black frame to make sure the output surface is reshaped before - // tests. - EXPECT_CALL(*gl, Disable(GL_DEPTH_TEST)).Times(1); - EXPECT_CALL(*gl, Disable(GL_CULL_FACE)).Times(1); - EXPECT_CALL(*gl, Disable(GL_STENCIL_TEST)).Times(1); - EXPECT_CALL(*gl, Enable(GL_BLEND)).Times(1); - - if (output_surface->capabilities().supports_dc_layers) { - EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)).Times(1); - EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)).Times(1); - - // Root render pass requires a scissor if the output surface supports - // dc layers. - EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)).Times(3); - EXPECT_CALL(*gl, Scissor(0, 0, 100, 100)).Times(3); - } else { - EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)).Times(2); - EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)).Times(2); - if (set_draw_rectangle) { - EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)).Times(2); - EXPECT_CALL(*gl, Scissor(0, 0, 100, 100)).Times(2); - } else { - EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)).Times(1); - EXPECT_CALL(*gl, Scissor(0, 0, 100, 100)).Times(1); - } - } - - EXPECT_CALL(*gl, Disable(GL_BLEND)).Times(1); - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - root_pass->damage_rect = gfx::Rect(viewport_size); - cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorBLACK); - - renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(&renderer, viewport_size); - Mock::VerifyAndClearExpectations(gl); - - for (int i = 0; i < 2; ++i) { - root_pass = cc::AddRenderPassWithDamage( - &render_passes_in_draw_order_, root_pass_id, root_pass_output_rect, - root_pass_damage_rect, gfx::Transform(), cc::FilterOperations()); - cc::AddQuad(root_pass, gfx::Rect(root_pass_output_rect), SK_ColorGREEN); - - InSequence seq; - - // A bunch of initialization that happens. - EXPECT_CALL(*gl, Disable(GL_DEPTH_TEST)); - EXPECT_CALL(*gl, Disable(GL_CULL_FACE)); - EXPECT_CALL(*gl, Disable(GL_STENCIL_TEST)); - EXPECT_CALL(*gl, Enable(GL_BLEND)); - EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)); - EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)); - - // Partial frame, we should use a scissor to swap only that part when - // partial swap is enabled. - gfx::Rect output_rectangle = - partial_swap ? root_pass_damage_rect : gfx::Rect(viewport_size); - - // The scissor is flipped, so subtract the y coord and height from the - // bottom of the GL viewport. - gfx::Rect scissor_rect(output_rectangle.x(), - viewport_size.height() - output_rectangle.y() - - output_rectangle.height(), - output_rectangle.width(), - output_rectangle.height()); - - // Drawing the solid color quad using glClear and scissor rect. - EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)); - EXPECT_CALL(*gl, Scissor(scissor_rect.x(), scissor_rect.y(), - scissor_rect.width(), scissor_rect.height())); - - if (partial_swap || set_draw_rectangle) { - EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)); - EXPECT_CALL(*gl, Scissor(scissor_rect.x(), scissor_rect.y(), - scissor_rect.width(), scissor_rect.height())); - } - - // Restore GL state after solid color draw quad. - if (partial_swap || set_draw_rectangle) { - EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)); - EXPECT_CALL(*gl, Scissor(scissor_rect.x(), scissor_rect.y(), - scissor_rect.width(), scissor_rect.height())); - } else { - EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)); - EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)); - } - - // Blending is disabled at the end of the frame. - EXPECT_CALL(*gl, Disable(GL_BLEND)); - - renderer.DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - DrawFrame(&renderer, viewport_size); - - if (set_draw_rectangle) { - EXPECT_EQ(output_rectangle, output_surface->last_set_draw_rectangle()); - } - - Mock::VerifyAndClearExpectations(gl); - } - } - - private: - base::test::ScopedFeatureList scoped_feature_list_; -}; - -TEST_F(GLRendererPartialSwapTest, PartialSwap) { - RunTest(true, false); -} - -TEST_F(GLRendererPartialSwapTest, NoPartialSwap) { - RunTest(false, false); -} - -#if BUILDFLAG(IS_WIN) -TEST_F(GLRendererPartialSwapTest, SetDrawRectangle_PartialSwap) { - RunTest(true, true); -} - -TEST_F(GLRendererPartialSwapTest, SetDrawRectangle_NoPartialSwap) { - RunTest(false, true); -} - -// Test that SetEnableDCLayersCHROMIUM is properly called when enabling -// and disabling DC layers. -TEST_F(GLRendererTest, DCLayerOverlaySwitch) { - auto gl_owned = std::make_unique<PartialSwapMockGLES2Interface>(); - gl_owned->set_have_post_sub_buffer(true); - auto* gl = gl_owned.get(); - - auto provider = TestContextProvider::Create(std::move(gl_owned)); - provider->BindToCurrentThread(); - - cc::FakeOutputSurfaceClient output_surface_client; - std::unique_ptr<FakeOutputSurface> output_surface( - FakeOutputSurface::Create3d(std::move(provider))); - output_surface->set_supports_dc_layers(true); - output_surface->BindToClient(&output_surface_client); - - auto parent_resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - auto child_context_provider = TestContextProvider::Create(); - child_context_provider->BindToCurrentThread(); - auto child_resource_provider = std::make_unique<ClientResourceProvider>(); - - auto transfer_resource = TransferableResource::MakeGL( - gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(), - gfx::Size(256, 256), true); - auto release_callback = base::BindOnce(&MailboxReleased); - ResourceId resource_id = child_resource_provider->ImportResource( - transfer_resource, std::move(release_callback)); - - std::vector<ReturnedResource> returned_to_child; - int child_id = parent_resource_provider->CreateChild( - base::BindRepeating(&CollectResources, &returned_to_child), SurfaceId()); - - // Transfer resource to the parent. - std::vector<ResourceId> resource_ids_to_transfer; - resource_ids_to_transfer.push_back(resource_id); - std::vector<TransferableResource> list; - child_resource_provider->PrepareSendToParent( - resource_ids_to_transfer, &list, - static_cast<RasterContextProvider*>(child_context_provider.get())); - parent_resource_provider->ReceiveFromChild(child_id, list); - // In DisplayResourceProvider's namespace, use the mapped resource id. - std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = - parent_resource_provider->GetChildToParentMap(child_id); - ResourceId parent_resource_id = resource_map[list[0].id]; - - auto processor = std::make_unique<OverlayProcessorWin>( - output_surface.get(), - std::make_unique<DCLayerOverlayProcessor>( - &debug_settings_, /*allowed_yuv_overlay_count=*/1, true)); - - RendererSettings settings; - settings.partial_swap_enabled = true; - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - parent_resource_provider.get(), processor.get()); - renderer.Initialize(); - renderer.SetVisible(true); - - gfx::Size viewport_size(100, 100); - - for (int i = 0; i < 65; i++) { - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - if (i == 0) { - gfx::Rect rect(0, 0, 100, 100); - bool needs_blending = false; - gfx::RectF tex_coord_rect(0, 0, 1, 1); - SharedQuadState* shared_state = - root_pass->CreateAndAppendSharedQuadState(); - shared_state->SetAll(gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), - absl::nullopt, false, 1, SkBlendMode::kSrcOver, 0); - YUVVideoDrawQuad* quad = - root_pass->CreateAndAppendDrawQuad<YUVVideoDrawQuad>(); - quad->SetNew(shared_state, rect, rect, needs_blending, tex_coord_rect, - tex_coord_rect, rect.size(), rect.size(), parent_resource_id, - parent_resource_id, parent_resource_id, parent_resource_id, - gfx::ColorSpace(), 0, 1.0, 8); - } - - // A bunch of initialization that happens. - EXPECT_CALL(*gl, Disable(_)).Times(AnyNumber()); - EXPECT_CALL(*gl, Enable(_)).Times(AnyNumber()); - EXPECT_CALL(*gl, Scissor(_, _, _, _)).Times(AnyNumber()); - - // Partial frame, we should use a scissor to swap only that part when - // partial swap is enabled. - root_pass->damage_rect = gfx::Rect(2, 2, 3, 3); - // Frame 0 should be completely damaged because it's the first. - // Frame 1 should be because it changed. Frame 60 should be - // because it's disabling DC layers. - gfx::Rect output_rectangle = (i == 0 || i == 1 || i == 60) - ? root_pass->output_rect - : root_pass->damage_rect; - - // Frame 0 should have DC Layers enabled because of the overlay. - // After 60 frames of no overlays DC layers should be disabled again. - if (i == 0) - EXPECT_CALL(*gl, SetEnableDCLayersCHROMIUM(GL_TRUE)); - else if (i == 60) - EXPECT_CALL(*gl, SetEnableDCLayersCHROMIUM(GL_FALSE)); - - renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(&renderer, viewport_size); - EXPECT_EQ(output_rectangle, output_surface->last_set_draw_rectangle()); - testing::Mock::VerifyAndClearExpectations(gl); - } - - // Transfer resources back from the parent to the child. Set no resources as - // being in use. - parent_resource_provider->DeclareUsedResourcesFromChild(child_id, - ResourceIdSet()); - - child_resource_provider->RemoveImportedResource(resource_id); - child_resource_provider->ShutdownAndReleaseAllResources(); -} -#endif - -class GLRendererWithMockContextTest : public ::testing::Test { - protected: - class MockContextSupport : public TestContextSupport { - public: - MockContextSupport() {} - MOCK_METHOD1(SetAggressivelyFreeResources, - void(bool aggressively_free_resources)); - }; - - void SetUp() override { - auto context_support = std::make_unique<MockContextSupport>(); - context_support_ptr_ = context_support.get(); - auto context_provider = - TestContextProvider::Create(std::move(context_support)); - ASSERT_EQ(context_provider->BindToCurrentThread(), - gpu::ContextResult::kSuccess); - output_surface_ = FakeOutputSurface::Create3d(std::move(context_provider)); - output_surface_->BindToClient(&output_surface_client_); - resource_provider_ = std::make_unique<DisplayResourceProviderGL>( - output_surface_->context_provider()); - renderer_ = std::make_unique<GLRenderer>( - &settings_, &debug_settings_, output_surface_.get(), - resource_provider_.get(), nullptr, nullptr); - renderer_->Initialize(); - } - - RendererSettings settings_; - DebugRendererSettings debug_settings_; - cc::FakeOutputSurfaceClient output_surface_client_; - MockContextSupport* context_support_ptr_; - std::unique_ptr<OutputSurface> output_surface_; - std::unique_ptr<DisplayResourceProviderGL> resource_provider_; - std::unique_ptr<GLRenderer> renderer_; -}; - -TEST_F(GLRendererWithMockContextTest, - ContextPurgedWhenRendererBecomesInvisible) { - EXPECT_CALL(*context_support_ptr_, SetAggressivelyFreeResources(false)); - renderer_->SetVisible(true); - Mock::VerifyAndClearExpectations(context_support_ptr_); - - EXPECT_CALL(*context_support_ptr_, SetAggressivelyFreeResources(true)); - renderer_->SetVisible(false); - Mock::VerifyAndClearExpectations(context_support_ptr_); -} - -#if defined(USE_OZONE) || BUILDFLAG(IS_ANDROID) -class ContentBoundsOverlayProcessor : public OverlayProcessorUsingStrategy { - public: - class TestStrategy : public OverlayProcessorStrategy { - public: - explicit TestStrategy(const std::vector<gfx::Rect>& content_bounds) - : content_bounds_(content_bounds) {} - ~TestStrategy() override = default; - - bool Attempt(const SkM44& output_color_matrix, - const OverlayProcessorInterface::FilterOperationsMap& - render_pass_backdrop_filters, - DisplayResourceProvider* resource_provider, - AggregatedRenderPassList* render_pass_list, - SurfaceDamageRectList* surface_damage_rect_list, - const PrimaryPlane* primary_plane, - OverlayCandidateList* candidates, - std::vector<gfx::Rect>* content_bounds) override { - content_bounds->insert(content_bounds->end(), content_bounds_.begin(), - content_bounds_.end()); - return true; - } - - void ProposePrioritized( - const SkM44& output_color_matrix, - const FilterOperationsMap& render_pass_backdrop_filters, - DisplayResourceProvider* resource_provider, - AggregatedRenderPassList* render_pass_list, - SurfaceDamageRectList* surface_damage_rect_list, - const PrimaryPlane* primary_plane, - std::vector<OverlayProposedCandidate>* candidates, - std::vector<gfx::Rect>* content_bounds) override { - auto* render_pass = render_pass_list->back().get(); - QuadList& quad_list = render_pass->quad_list; - OverlayCandidate candidate; - // Adding a mock candidate to the propose list so that - // 'AttemptPrioritized' will be called. - candidates->push_back({quad_list.end(), candidate, this}); - } - - bool AttemptPrioritized( - const SkM44& output_color_matrix, - const FilterOperationsMap& render_pass_backdrop_filters, - DisplayResourceProvider* resource_provider, - AggregatedRenderPassList* render_pass_list, - SurfaceDamageRectList* surface_damage_rect_list, - const PrimaryPlane* primary_plane, - OverlayCandidateList* candidates, - std::vector<gfx::Rect>* content_bounds, - const OverlayProposedCandidate& proposed_candidate) override { - content_bounds->insert(content_bounds->end(), content_bounds_.begin(), - content_bounds_.end()); - return true; - } - - void CommitCandidate(const OverlayProposedCandidate& proposed_candidate, - AggregatedRenderPass* render_pass) override {} - - private: - const std::vector<gfx::Rect> content_bounds_; - }; - - explicit ContentBoundsOverlayProcessor( - const std::vector<gfx::Rect>& content_bounds) - : OverlayProcessorUsingStrategy(), content_bounds_(content_bounds) { - strategies_.push_back( - std::make_unique<TestStrategy>(std::move(content_bounds_))); - prioritization_config_.changing_threshold = false; - prioritization_config_.damage_rate_threshold = false; - } - - TestStrategy& strategy() { - return static_cast<TestStrategy&>(*strategies_.back()); - } - // Empty mock methods since this test set up uses strategies, which are only - // for ozone and android. - MOCK_CONST_METHOD0(NeedsSurfaceDamageRectList, bool()); - bool IsOverlaySupported() const override { return true; } - - // A list of possible overlay candidates is presented to this function. - // The expected result is that those candidates that can be in a separate - // plane are marked with |overlay_handled| set to true, otherwise they are - // to be traditionally composited. Candidates with |overlay_handled| set to - // true must also have their |display_rect| converted to integer - // coordinates if necessary. - void CheckOverlaySupportImpl( - const OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane, - OverlayCandidateList* surfaces) override {} - - private: - std::vector<gfx::Rect> content_bounds_; -}; - -class GLRendererSwapWithBoundsTest : public GLRendererTest { - protected: - void RunTest(const std::vector<gfx::Rect>& content_bounds) { - auto gl_owned = std::make_unique<TestGLES2Interface>(); - gl_owned->set_have_swap_buffers_with_bounds(true); - - auto provider = TestContextProvider::Create(std::move(gl_owned)); - provider->BindToCurrentThread(); - - cc::FakeOutputSurfaceClient output_surface_client; - std::unique_ptr<FakeOutputSurface> output_surface( - FakeOutputSurface::Create3d(std::move(provider))); - output_surface->BindToClient(&output_surface_client); - - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - RendererSettings settings; - auto processor = - std::make_unique<ContentBoundsOverlayProcessor>(content_bounds); - FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(), - resource_provider.get(), processor.get()); - renderer.Initialize(); - EXPECT_EQ(true, renderer.use_swap_with_bounds()); - renderer.SetVisible(true); - - gfx::Size viewport_size(100, 100); - - { - AggregatedRenderPassId root_pass_id{1}; - cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id, - gfx::Rect(viewport_size), gfx::Transform(), - cc::FilterOperations()); - - renderer.DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - DrawFrame(&renderer, viewport_size); - renderer.SwapBuffers({}); - - std::vector<gfx::Rect> expected_content_bounds; - EXPECT_EQ(content_bounds, - output_surface->last_sent_frame()->content_bounds); - } - } -}; - -TEST_F(GLRendererSwapWithBoundsTest, EmptyContent) { - std::vector<gfx::Rect> content_bounds; - RunTest(content_bounds); -} - -TEST_F(GLRendererSwapWithBoundsTest, NonEmpty) { - std::vector<gfx::Rect> content_bounds; - content_bounds.push_back(gfx::Rect(0, 0, 10, 10)); - content_bounds.push_back(gfx::Rect(20, 20, 30, 30)); - RunTest(content_bounds); -} -#endif // defined(USE_OZONE) || BUILDFLAG(IS_ANDROID) - -#if BUILDFLAG(IS_APPLE) -class MockCALayerGLES2Interface : public TestGLES2Interface { - public: - MOCK_METHOD6(ScheduleCALayerSharedStateCHROMIUM, - void(GLfloat opacity, - GLboolean is_clipped, - const GLfloat* clip_rect, - const GLfloat* rounded_corner_bounds, - GLint sorting_context_id, - const GLfloat* transform)); - MOCK_METHOD6(ScheduleCALayerCHROMIUM, - void(GLuint contents_texture_id, - const GLfloat* contents_rect, - GLuint background_color, - GLuint edge_aa_mask, - const GLfloat* bounds_rect, - GLuint filter)); - MOCK_METHOD2(ScheduleCALayerInUseQueryCHROMIUM, - void(GLsizei count, const GLuint* textures)); - MOCK_METHOD5( - Uniform4f, - void(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w)); -}; - -class CALayerGLRendererTest : public GLRendererTest { - protected: - void SetUp() override { - // A mock GLES2Interface that can watch CALayer stuff happen. - auto gles2_interface = std::make_unique<MockCALayerGLES2Interface>(); - // Support image storage for GpuMemoryBuffers, needed for - // CALayers/IOSurfaces backed by textures. - gles2_interface->set_support_texture_storage_image(true); - // Allow the renderer to make an empty SwapBuffers - skipping even the - // root RenderPass. - gles2_interface->set_have_commit_overlay_planes(true); - - gl_ = gles2_interface.get(); - - auto provider = TestContextProvider::Create(std::move(gles2_interface)); - provider->BindToCurrentThread(); - - cc::FakeOutputSurfaceClient output_surface_client; - output_surface_ = FakeOutputSurface::Create3d(std::move(provider)); - output_surface_->BindToClient(&output_surface_client); - - display_resource_provider_ = std::make_unique<DisplayResourceProviderGL>( - output_surface_->context_provider()); - - settings_ = std::make_unique<RendererSettings>(); - // This setting is enabled to use CALayer overlays. - settings_->release_overlay_resources_after_gpu_query = true; - // The Mac TestOverlayProcessor default to enable CALayer overlays, then all - // damage is removed and we can skip the root RenderPass, swapping empty. - overlay_processor_ = std::make_unique<OverlayProcessorMac>( - std::make_unique<CALayerOverlayProcessor>()); - renderer_ = std::make_unique<FakeRendererGL>( - settings_.get(), &debug_settings_, output_surface_.get(), - display_resource_provider_.get(), overlay_processor_.get(), - base::ThreadTaskRunnerHandle::Get()); - renderer_->Initialize(); - renderer_->SetVisible(true); - } - - void TearDown() override { - renderer_.reset(); - display_resource_provider_.reset(); - output_surface_.reset(); - } - - void DrawBlackFrame(const gfx::Size& viewport_size) { - AggregatedRenderPassId root_pass_id{1}; - - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorBLACK); - - renderer().DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - - DrawFrame(&renderer(), viewport_size); - renderer().SwapBuffers(DirectRenderer::SwapFrameData()); - renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle()); - Mock::VerifyAndClearExpectations(&gl()); - } - - MockCALayerGLES2Interface& gl() const { return *gl_; } - FakeRendererGL& renderer() const { return *renderer_; } - FakeOutputSurface& output_surface() const { return *output_surface_; } - - private: - MockCALayerGLES2Interface* gl_; - std::unique_ptr<FakeOutputSurface> output_surface_; - std::unique_ptr<DisplayResourceProviderGL> display_resource_provider_; - std::unique_ptr<RendererSettings> settings_; - std::unique_ptr<OverlayProcessorInterface> overlay_processor_; - std::unique_ptr<FakeRendererGL> renderer_; -}; - -TEST_F(CALayerGLRendererTest, CALayerOverlaysWithAllQuadsPromoted) { - gfx::Size viewport_size(10, 10); - - // Draw an empty frame to make sure output surface is reshaped before tests. - DrawBlackFrame(viewport_size); - - // This frame has a root pass with a CompositorRenderPassDrawQuad pointing to - // a child pass that is at 1,2 to make it identifiable. - AggregatedRenderPassId child_pass_id{2}; - AggregatedRenderPassId root_pass_id{1}; - { - AggregatedRenderPass* child_pass = - cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - gfx::Rect(viewport_size) + gfx::Vector2d(1, 2), - gfx::Transform(), cc::FilterOperations()); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), SkBlendMode::kSrcOver); - } - - renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - - // The child pass is drawn, promoted to an overlay, and scheduled as a - // CALayer. - { - InSequence sequence; - EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)) - .WillOnce( - Invoke([](GLuint contents_texture_id, const GLfloat* contents_rect, - GLuint background_color, GLuint edge_aa_mask, - const GLfloat* bounds_rect, GLuint filter) { - // This is the child CompositorRenderPassDrawQuad. - EXPECT_EQ(1, bounds_rect[0]); - EXPECT_EQ(2, bounds_rect[1]); - })); - } - DrawFrame(&renderer(), viewport_size); - Mock::VerifyAndClearExpectations(&gl()); - - renderer().SwapBuffers(DirectRenderer::SwapFrameData()); - - // The damage was eliminated when everything was promoted to CALayers. - ASSERT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect); - EXPECT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect->IsEmpty()); - - // Frame number 2. Same inputs, except... - { - AggregatedRenderPass* child_pass = - cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - gfx::Rect(viewport_size) + gfx::Vector2d(1, 2), - gfx::Transform(), cc::FilterOperations()); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), SkBlendMode::kSrcOver); - - // Use a cached RenderPass for the child. - child_pass->cache_render_pass = true; - } - - renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - - // The child CompositorRenderPassDrawQuad gets promoted again, but importantly - // it did not itself have to be drawn this time as it can use the cached - // texture. Because we can skip the child pass, and the root pass (all quads - // were promoted), this exposes edge cases in GLRenderer if it assumes we draw - // at least one RenderPass. This still works, doesn't crash, etc, and the - // CompositorRenderPassDrawQuad is emitted. - { - InSequence sequence; - EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)); - } - DrawFrame(&renderer(), viewport_size); - Mock::VerifyAndClearExpectations(&gl()); - - renderer().SwapBuffers(DirectRenderer::SwapFrameData()); -} - -TEST_F(CALayerGLRendererTest, CALayerRoundRects) { - gfx::Size viewport_size(10, 10); - - // Draw an empty frame to make sure output surface is reshaped before tests. - DrawBlackFrame(viewport_size); - - for (size_t subtest = 0; subtest < 3; ++subtest) { - AggregatedRenderPass* child_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{1}, - gfx::Rect(250, 250), gfx::Transform(), cc::FilterOperations()); - - AggregatedRenderPassId root_pass_id{1}; - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - auto* quad = cc::AddRenderPassQuad(root_pass, child_pass); - SharedQuadState* sqs = - const_cast<SharedQuadState*>(quad->shared_quad_state); - - sqs->clip_rect = gfx::Rect(2, 2, 6, 6); - const float radius = 2; - sqs->mask_filter_info = - gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(*sqs->clip_rect), radius)); - - switch (subtest) { - case 0: - // Subtest 0 is a simple round rect that matches the clip rect, and - // should be handled by CALayers. - EXPECT_CALL(gl(), Uniform4f(_, _, _, _, _)).Times(1); - EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)) - .Times(1); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)).Times(1); - break; - case 1: - // Subtest 1 doesn't match clip and rounded rect, but we can still - // use CALayers. - sqs->clip_rect = gfx::Rect(3, 3, 4, 4); - EXPECT_CALL(gl(), Uniform4f(_, _, _, _, _)).Times(1); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)).Times(1); - break; - case 2: - // Subtest 2 has a non-simple rounded rect. - gfx::RRectF rounded_corner_bounds = - sqs->mask_filter_info.rounded_corner_bounds(); - rounded_corner_bounds.SetCornerRadii(gfx::RRectF::Corner::kUpperLeft, 1, - 1); - sqs->mask_filter_info = gfx::MaskFilterInfo(rounded_corner_bounds); - // Called 2 extra times in order to set up the rounded corner - // parameters in the shader, because the CALayer is not handling - // the rounded corners. - EXPECT_CALL(gl(), Uniform4f(_, _, _, _, _)).Times(3); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)).Times(0); - break; - } - - renderer().DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - DrawFrame(&renderer(), viewport_size); - Mock::VerifyAndClearExpectations(&gl()); - } -} - -TEST_F(CALayerGLRendererTest, CALayerOverlaysReusesTextureWithDifferentSizes) { - gfx::Size viewport_size(300, 300); - - // Draw an empty frame to make sure output surface is reshaped before tests. - DrawBlackFrame(viewport_size); - - // This frame has a root pass with a CompositorRenderPassDrawQuad pointing to - // a child pass that is at 1,2 to make it identifiable. The child's size is - // 250x251, but it will be rounded up to a multiple of 64 in order to promote - // easier texture reuse. See https://crbug.com/146070. - AggregatedRenderPassId child_pass_id{2}; - AggregatedRenderPassId root_pass_id{1}; - { - AggregatedRenderPass* child_pass = - cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - gfx::Rect(250, 251) + gfx::Vector2d(1, 2), - gfx::Transform(), cc::FilterOperations()); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), SkBlendMode::kSrcOver); - } - - renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - - // The child pass is drawn, promoted to an overlay, and scheduled as a - // CALayer. The bounds of the texture are rounded up to 256x256. We save the - // texture ID to make sure we reuse it correctly. - uint32_t saved_texture_id = 0; - { - InSequence sequence; - EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)) - .WillOnce( - Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect, - GLuint background_color, GLuint edge_aa_mask, - const GLfloat* bounds_rect, GLuint filter) { - // This is the child CompositorRenderPassDrawQuad. - EXPECT_EQ(1, bounds_rect[0]); - EXPECT_EQ(2, bounds_rect[1]); - // The size is rounded to a multiple of 64. - EXPECT_EQ(256, bounds_rect[2]); - EXPECT_EQ(256, bounds_rect[3]); - saved_texture_id = contents_texture_id; - })); - } - DrawFrame(&renderer(), viewport_size); - Mock::VerifyAndClearExpectations(&gl()); - renderer().SwapBuffers(DirectRenderer::SwapFrameData()); - - // ScheduleCALayerCHROMIUM happened and used a non-0 texture. - EXPECT_NE(saved_texture_id, 0u); - - // The damage was eliminated when everything was promoted to CALayers. - ASSERT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect); - EXPECT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect->IsEmpty()); - - // The texture will be checked to verify if it is free yet. - EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(1, _)); - renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle()); - Mock::VerifyAndClearExpectations(&gl()); - - // Frame number 2. We change the size of the child RenderPass to be smaller - // than the next multiple of 64, but larger than half the previous size so - // that our texture reuse heuristics will reuse the texture if it is free. - // For now, it is not. - { - AggregatedRenderPass* child_pass = - cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - gfx::Rect(190, 191) + gfx::Vector2d(1, 2), - gfx::Transform(), cc::FilterOperations()); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), SkBlendMode::kSrcOver); - } - - renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - - // The child RenderPass will use a new 192x192 texture, since the last texture - // is still in use. - { - InSequence sequence; - EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)) - .WillOnce( - Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect, - GLuint background_color, GLuint edge_aa_mask, - const GLfloat* bounds_rect, GLuint filter) { - // New texture id. - EXPECT_NE(saved_texture_id, contents_texture_id); - EXPECT_EQ(1, bounds_rect[0]); - EXPECT_EQ(2, bounds_rect[1]); - // The texture is 192x192 since we snap up to multiples of 64. - EXPECT_EQ(192, bounds_rect[2]); - EXPECT_EQ(192, bounds_rect[3]); - })); - } - DrawFrame(&renderer(), viewport_size); - Mock::VerifyAndClearExpectations(&gl()); - renderer().SwapBuffers(DirectRenderer::SwapFrameData()); - - // There are now 2 textures to check if they are free. - EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(2, _)); - renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle()); - Mock::VerifyAndClearExpectations(&gl()); - - // The first (256x256) texture is returned to the GLRenderer. - renderer().DidReceiveTextureInUseResponses({{saved_texture_id, false}}); - - // Frame number 3 looks just like frame number 2. The child RenderPass is - // smaller than the next multiple of 64 from the released texture, but larger - // than half of its size so that our texture reuse heuristics will kick in. - { - AggregatedRenderPass* child_pass = - cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - gfx::Rect(190, 191) + gfx::Vector2d(1, 2), - gfx::Transform(), cc::FilterOperations()); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), SkBlendMode::kSrcOver); - } - - renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - - // The child RenderPass would try to use a 192x192 texture, but since we have - // an existing 256x256 texture, we can reuse that. - { - InSequence sequence; - EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)) - .WillOnce( - Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect, - GLuint background_color, GLuint edge_aa_mask, - const GLfloat* bounds_rect, GLuint filter) { - // The first texture is reused. - EXPECT_EQ(saved_texture_id, contents_texture_id); - // This is the child CompositorRenderPassDrawQuad. - EXPECT_EQ(1, bounds_rect[0]); - EXPECT_EQ(2, bounds_rect[1]); - // The size here is the size of the texture being used, not - // the size we tried to use (192x192). - EXPECT_EQ(256, bounds_rect[2]); - EXPECT_EQ(256, bounds_rect[3]); - })); - } - DrawFrame(&renderer(), viewport_size); - Mock::VerifyAndClearExpectations(&gl()); - renderer().SwapBuffers(DirectRenderer::SwapFrameData()); -} - -TEST_F(CALayerGLRendererTest, CALayerOverlaysDontReuseTooBigTexture) { - gfx::Size viewport_size(300, 300); - - // Draw an empty frame to make sure output surface is reshaped before tests. - DrawBlackFrame(viewport_size); - - // This frame has a root pass with a CompositorRenderPassDrawQuad pointing to - // a child pass that is at 1,2 to make it identifiable. The child's size is - // 250x251, but it will be rounded up to a multiple of 64 in order to promote - // easier texture reuse. See https://crbug.com/146070. - AggregatedRenderPassId child_pass_id{2}; - AggregatedRenderPassId root_pass_id{1}; - { - AggregatedRenderPass* child_pass = - cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - gfx::Rect(250, 251) + gfx::Vector2d(1, 2), - gfx::Transform(), cc::FilterOperations()); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), SkBlendMode::kSrcOver); - } - - renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - - // The child pass is drawn, promoted to an overlay, and scheduled as a - // CALayer. The bounds of the texture are rounded up to 256x256. We save the - // texture ID to make sure we reuse it correctly. - uint32_t saved_texture_id = 0; - { - InSequence sequence; - EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)) - .WillOnce( - Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect, - GLuint background_color, GLuint edge_aa_mask, - const GLfloat* bounds_rect, GLuint filter) { - // This is the child CompositorRenderPassDrawQuad. - EXPECT_EQ(1, bounds_rect[0]); - EXPECT_EQ(2, bounds_rect[1]); - // The size is rounded to a multiple of 64. - EXPECT_EQ(256, bounds_rect[2]); - EXPECT_EQ(256, bounds_rect[3]); - saved_texture_id = contents_texture_id; - })); - } - DrawFrame(&renderer(), viewport_size); - Mock::VerifyAndClearExpectations(&gl()); - renderer().SwapBuffers(DirectRenderer::SwapFrameData()); - - // ScheduleCALayerCHROMIUM happened and used a non-0 texture. - EXPECT_NE(saved_texture_id, 0u); - - // The damage was eliminated when everything was promoted to CALayers. - ASSERT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect); - EXPECT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect->IsEmpty()); - - // The texture will be checked to verify if it is free yet. - EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(1, _)); - renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle()); - Mock::VerifyAndClearExpectations(&gl()); - - // Frame number 2. We change the size of the child RenderPass to be much - // smaller. - { - AggregatedRenderPass* child_pass = - cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - gfx::Rect(20, 21) + gfx::Vector2d(1, 2), - gfx::Transform(), cc::FilterOperations()); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), SkBlendMode::kSrcOver); - } - - renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - - // The child RenderPass will use a new 64x64 texture, since the last texture - // is still in use. - { - InSequence sequence; - EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)) - .WillOnce( - Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect, - GLuint background_color, GLuint edge_aa_mask, - const GLfloat* bounds_rect, GLuint filter) { - // New texture id. - EXPECT_NE(saved_texture_id, contents_texture_id); - EXPECT_EQ(1, bounds_rect[0]); - EXPECT_EQ(2, bounds_rect[1]); - // The texture is 64x64 since we snap up to multiples of 64. - EXPECT_EQ(64, bounds_rect[2]); - EXPECT_EQ(64, bounds_rect[3]); - })); - } - DrawFrame(&renderer(), viewport_size); - Mock::VerifyAndClearExpectations(&gl()); - renderer().SwapBuffers(DirectRenderer::SwapFrameData()); - - // There are now 2 textures to check if they are free. - EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(2, _)); - renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle()); - Mock::VerifyAndClearExpectations(&gl()); - - // The first (256x256) texture is returned to the GLRenderer. - renderer().DidReceiveTextureInUseResponses({{saved_texture_id, false}}); - - // Frame number 3 looks just like frame number 2. The child RenderPass is - // too small to reuse the old texture. - { - AggregatedRenderPass* child_pass = - cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - gfx::Rect(20, 21) + gfx::Vector2d(1, 2), - gfx::Transform(), cc::FilterOperations()); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), SkBlendMode::kSrcOver); - } - - renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - - // The child RenderPass would try to use a 64x64 texture. We have a free and - // existing 256x256 texture, but it's too large for us to reuse it. - { - InSequence sequence; - EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)) - .WillOnce( - Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect, - GLuint background_color, GLuint edge_aa_mask, - const GLfloat* bounds_rect, GLuint filter) { - // The first texture is not reused. - EXPECT_NE(saved_texture_id, contents_texture_id); - // This is the child CompositorRenderPassDrawQuad. - EXPECT_EQ(1, bounds_rect[0]); - EXPECT_EQ(2, bounds_rect[1]); - // The new texture has a smaller size. - EXPECT_EQ(64, bounds_rect[2]); - EXPECT_EQ(64, bounds_rect[3]); - })); - } - DrawFrame(&renderer(), viewport_size); - Mock::VerifyAndClearExpectations(&gl()); - renderer().SwapBuffers(DirectRenderer::SwapFrameData()); -} - -TEST_F(CALayerGLRendererTest, CALayerOverlaysReuseAfterNoSwapBuffers) { - gfx::Size viewport_size(300, 300); - - // This frame has a root pass with a CompositorRenderPassDrawQuad pointing to - // a child pass that is at 1,2 to make it identifiable. - AggregatedRenderPassId child_pass_id{2}; - AggregatedRenderPassId root_pass_id{1}; - { - AggregatedRenderPass* child_pass = - cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - gfx::Rect(100, 100) + gfx::Vector2d(1, 2), - gfx::Transform(), cc::FilterOperations()); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), SkBlendMode::kSrcOver); - } - - renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - - // The child pass is drawn, promoted to an overlay, and scheduled as a - // CALayer. We save the texture ID to make sure we reuse it correctly. - uint32_t saved_texture_id = 0; - { - InSequence sequence; - EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)) - .WillOnce( - Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect, - GLuint background_color, GLuint edge_aa_mask, - const GLfloat* bounds_rect, GLuint filter) { - // This is the child CompositorRenderPassDrawQuad. - EXPECT_EQ(1, bounds_rect[0]); - EXPECT_EQ(2, bounds_rect[1]); - saved_texture_id = contents_texture_id; - })); - } - DrawFrame(&renderer(), viewport_size); - Mock::VerifyAndClearExpectations(&gl()); - - // ScheduleCALayerCHROMIUM happened and used a non-0 texture. - EXPECT_NE(saved_texture_id, 0u); - - // SwapBuffers() is *not* called though! Display can do this sometimes. - - // Frame number 2. We can not reuse the texture since the last one isn't - // returned yet. We use a different size so we can control which texture gets - // reused later. - { - AggregatedRenderPass* child_pass = - cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - gfx::Rect(200, 200) + gfx::Vector2d(1, 2), - gfx::Transform(), cc::FilterOperations()); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), SkBlendMode::kSrcOver); - } - - renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - - uint32_t second_saved_texture_id = 0; - { - InSequence sequence; - EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)) - .WillOnce( - Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect, - GLuint background_color, GLuint edge_aa_mask, - const GLfloat* bounds_rect, GLuint filter) { - // New texture id. - EXPECT_NE(saved_texture_id, contents_texture_id); - EXPECT_EQ(1, bounds_rect[0]); - EXPECT_EQ(2, bounds_rect[1]); - second_saved_texture_id = contents_texture_id; - })); - } - DrawFrame(&renderer(), viewport_size); - Mock::VerifyAndClearExpectations(&gl()); - - // SwapBuffers() *does* happen this time. - renderer().SwapBuffers(DirectRenderer::SwapFrameData()); - - // There are 2 textures to check if they are free. - EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(2, _)); - renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle()); - Mock::VerifyAndClearExpectations(&gl()); - - // Both textures get returned and the 2nd one can be reused. - renderer().DidReceiveTextureInUseResponses( - {{saved_texture_id, false}, {second_saved_texture_id, false}}); - - // Frame number 3 looks just like frame number 2. - { - AggregatedRenderPass* child_pass = - cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - gfx::Rect(200, 200) + gfx::Vector2d(1, 2), - gfx::Transform(), cc::FilterOperations()); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), SkBlendMode::kSrcOver); - } - - renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - - // The 2nd texture that we sent has been returned so we can reuse it. We - // verify that happened. - { - InSequence sequence; - EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)) - .WillOnce( - Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect, - GLuint background_color, GLuint edge_aa_mask, - const GLfloat* bounds_rect, GLuint filter) { - // The second texture is reused. - EXPECT_EQ(second_saved_texture_id, contents_texture_id); - // This is the child CompositorRenderPassDrawQuad. - EXPECT_EQ(1, bounds_rect[0]); - EXPECT_EQ(2, bounds_rect[1]); - })); - } - DrawFrame(&renderer(), viewport_size); - Mock::VerifyAndClearExpectations(&gl()); - renderer().SwapBuffers(DirectRenderer::SwapFrameData()); -} - -TEST_F(CALayerGLRendererTest, CALayerOverlaysReuseManyIfReturnedSlowly) { - gfx::Size viewport_size(300, 300); - - // Draw an empty frame to make sure output surface is reshaped before tests. - DrawBlackFrame(viewport_size); - - // Each frame has a root pass with a CompositorRenderPassDrawQuad pointing to - // a child pass. We generate a bunch of frames and swap them, each with a - // different child RenderPass id, without getting any of the resources back - // from the OS. - AggregatedRenderPassId root_pass_id{1}; - - // The number is at least 2 larger than the number of textures we expect to - // reuse, so that we can leave one in the OS, and have 1 texture returned but - // not reused. - const int kNumSendManyTextureIds = 7; - uint32_t sent_texture_ids[kNumSendManyTextureIds]; - for (int i = 0; i < kNumSendManyTextureIds; ++i) { - AggregatedRenderPass* child_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{i + 2}, - gfx::Rect(250, 251) + gfx::Vector2d(1, 2), gfx::Transform(), - cc::FilterOperations()); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), SkBlendMode::kSrcOver); - - renderer().DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - - InSequence sequence; - EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)) - .WillOnce( - Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect, - GLuint background_color, GLuint edge_aa_mask, - const GLfloat* bounds_rect, GLuint filter) { - // This is the child CompositorRenderPassDrawQuad. - EXPECT_EQ(1, bounds_rect[0]); - EXPECT_EQ(2, bounds_rect[1]); - sent_texture_ids[i] = contents_texture_id; - })); - DrawFrame(&renderer(), viewport_size); - Mock::VerifyAndClearExpectations(&gl()); - renderer().SwapBuffers(DirectRenderer::SwapFrameData()); - - // ScheduleCALayerCHROMIUM happened and used a non-0 texture. - EXPECT_NE(sent_texture_ids[i], 0u); - - // The damage was eliminated when everything was promoted to CALayers. - ASSERT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect); - EXPECT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect->IsEmpty()); - - // All sent textures will be checked to verify if they are free yet. - EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(i + 1, _)); - renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle()); - Mock::VerifyAndClearExpectations(&gl()); - } - - // Now all but 1 texture get returned by the OS, so they are all inserted - // into the cache for reuse. - std::vector<uint32_t> returned_texture_ids; - for (int i = 0; i < kNumSendManyTextureIds - 1; ++i) { - uint32_t id = sent_texture_ids[i]; - renderer().DidReceiveTextureInUseResponses({{id, false}}); - returned_texture_ids.push_back(id); - } - - // We should keep *some* of these textures around to reuse them across - // multiple frames. https://crbug.com/146070 motivates this, and empirical - // testing found 5 to be a good number. - const int kNumSendReusedTextures = 5; - // See comment on |kNumSendManyTextureIds|. - ASSERT_LT(kNumSendReusedTextures, kNumSendManyTextureIds - 1); - - for (int i = 0; i < kNumSendReusedTextures + 1; ++i) { - // We use different RenderPass ids to ensure that the cache allows reuse - // even if they don't match. - AggregatedRenderPass* child_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{i + 100}, - gfx::Rect(250, 251) + gfx::Vector2d(1, 2), gfx::Transform(), - cc::FilterOperations()); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), SkBlendMode::kSrcOver); - - renderer().DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - - InSequence sequence; - EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)) - .WillOnce(Invoke([&](GLuint contents_texture_id, - const GLfloat* contents_rect, - GLuint background_color, GLuint edge_aa_mask, - const GLfloat* bounds_rect, GLuint filter) { - // This is the child CompositorRenderPassDrawQuad. - EXPECT_EQ(1, bounds_rect[0]); - EXPECT_EQ(2, bounds_rect[1]); - - if (i < kNumSendReusedTextures) { - // The texture id should be from the set of returned ones. - EXPECT_THAT(returned_texture_ids, Contains(contents_texture_id)); - base::Erase(returned_texture_ids, contents_texture_id); - } else { - // More textures were returned at once than we expect to reuse - // so eventually we should be making a new texture to show we're - // not just keeping infinity textures in the cache. - EXPECT_THAT(returned_texture_ids, - Not(Contains(contents_texture_id))); - // This shows that there was some returned id that we didn't use. - EXPECT_FALSE(returned_texture_ids.empty()); - } - })); - DrawFrame(&renderer(), viewport_size); - Mock::VerifyAndClearExpectations(&gl()); - renderer().SwapBuffers(DirectRenderer::SwapFrameData()); - - // All sent textures will be checked to verify if they are free yet. There's - // also 1 outstanding texture to check for that wasn't returned yet from the - // above loop. - EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(i + 2, _)); - renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle()); - Mock::VerifyAndClearExpectations(&gl()); - } -} - -TEST_F(CALayerGLRendererTest, CALayerOverlaysCachedTexturesAreFreed) { - gfx::Size viewport_size(300, 300); - - // Draw an empty frame to make sure output surface is reshaped before tests. - DrawBlackFrame(viewport_size); - - // Each frame has a root pass with a CompositorRenderPassDrawQuad pointing to - // a child pass. We generate a bunch of frames and swap them, each with a - // different child RenderPass id, without getting any of the resources back - // from the OS. - AggregatedRenderPassId child_pass_id{2}; - AggregatedRenderPassId root_pass_id{1}; - - // We send a whole bunch of textures as overlays to the OS. - const int kNumSendManyTextureIds = 7; - uint32_t sent_texture_ids[kNumSendManyTextureIds]; - for (int i = 0; i < kNumSendManyTextureIds; ++i) { - AggregatedRenderPass* child_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{i + 2}, - gfx::Rect(250, 251) + gfx::Vector2d(1, 2), gfx::Transform(), - cc::FilterOperations()); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), SkBlendMode::kSrcOver); - - renderer().DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - - InSequence sequence; - EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)) - .WillOnce( - Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect, - GLuint background_color, GLuint edge_aa_mask, - const GLfloat* bounds_rect, GLuint filter) { - // This is the child CompositorRenderPassDrawQuad. - EXPECT_EQ(1, bounds_rect[0]); - EXPECT_EQ(2, bounds_rect[1]); - sent_texture_ids[i] = contents_texture_id; - })); - DrawFrame(&renderer(), viewport_size); - Mock::VerifyAndClearExpectations(&gl()); - renderer().SwapBuffers(DirectRenderer::SwapFrameData()); - - // ScheduleCALayerCHROMIUM happened and used a non-0 texture. - EXPECT_NE(sent_texture_ids[i], 0u); - - // The damage was eliminated when everything was promoted to CALayers. - ASSERT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect); - EXPECT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect->IsEmpty()); - - // All sent textures will be checked to verify if they are free yet. - EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(i + 1, _)); - renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle()); - Mock::VerifyAndClearExpectations(&gl()); - } - - // Now all but 1 texture get returned by the OS, so they are all inserted - // into the cache for reuse. - std::vector<uint32_t> returned_texture_ids; - for (int i = 0; i < kNumSendManyTextureIds - 1; ++i) { - uint32_t id = sent_texture_ids[i]; - renderer().DidReceiveTextureInUseResponses({{id, false}}); - returned_texture_ids.push_back(id); - } - - // We generate a bunch of frames that don't use the cache, one less than the - // number of textures returned. - for (int i = 0; i < kNumSendManyTextureIds - 2; ++i) { - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddQuad(root_pass, gfx::Rect(100, 100), SK_ColorRED); - - renderer().DecideRenderPassAllocationsForFrame( - render_passes_in_draw_order_); - - InSequence sequence; - EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)); - DrawFrame(&renderer(), viewport_size); - Mock::VerifyAndClearExpectations(&gl()); - renderer().SwapBuffers(DirectRenderer::SwapFrameData()); - - // There's just 1 outstanding RenderPass texture to query for. - EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(1, _)); - renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle()); - Mock::VerifyAndClearExpectations(&gl()); - } - - // By now the cache should be empty, to show that we don't keep cached - // textures that won't be used forever. We generate a frame with a - // CompositorRenderPassDrawQuad and verify that it does not reuse a texture - // from the (empty) cache. - { - AggregatedRenderPass* child_pass = - cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id, - gfx::Rect(250, 251) + gfx::Vector2d(1, 2), - gfx::Transform(), cc::FilterOperations()); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), - gfx::Transform(), cc::FilterOperations()); - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), SkBlendMode::kSrcOver); - } - - renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - - InSequence sequence; - EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)) - .WillOnce(Invoke([&](GLuint contents_texture_id, - const GLfloat* contents_rect, - GLuint background_color, GLuint edge_aa_mask, - const GLfloat* bounds_rect, GLuint filter) { - // This is the child CompositorRenderPassDrawQuad. - EXPECT_EQ(1, bounds_rect[0]); - EXPECT_EQ(2, bounds_rect[1]); - - // More textures were returned at once than we expect to reuse - // so eventually we should be making a new texture to show we're - // not just keeping infinity textures in the cache. - EXPECT_THAT(returned_texture_ids, Not(Contains(contents_texture_id))); - // This shows that there was some returned id that we didn't use. - EXPECT_FALSE(returned_texture_ids.empty()); - })); - DrawFrame(&renderer(), viewport_size); - Mock::VerifyAndClearExpectations(&gl()); - renderer().SwapBuffers(DirectRenderer::SwapFrameData()); -} -#endif - -class FramebufferWatchingGLRenderer : public FakeRendererGL { - public: - FramebufferWatchingGLRenderer(RendererSettings* settings, - const DebugRendererSettings* debug_settings, - OutputSurface* output_surface, - DisplayResourceProviderGL* resource_provider) - : FakeRendererGL(settings, - debug_settings, - output_surface, - resource_provider) {} - - void BindFramebufferToOutputSurface() override { - ++bind_root_framebuffer_calls_; - FakeRendererGL::BindFramebufferToOutputSurface(); - } - - void BindFramebufferToTexture( - const AggregatedRenderPassId render_pass_id) override { - ++bind_child_framebuffer_calls_; - FakeRendererGL::BindFramebufferToTexture(render_pass_id); - } - - int bind_root_framebuffer_calls() const { - return bind_root_framebuffer_calls_; - } - int bind_child_framebuffer_calls() const { - return bind_child_framebuffer_calls_; - } - - void ResetBindCalls() { - bind_root_framebuffer_calls_ = bind_child_framebuffer_calls_ = 0; - } - - private: - int bind_root_framebuffer_calls_ = 0; - int bind_child_framebuffer_calls_ = 0; -}; - -TEST_F(GLRendererTest, UndamagedRenderPassStillDrawnWhenNoPartialSwap) { - auto provider = TestContextProvider::Create(); - provider->UnboundTestContextGL()->set_have_post_sub_buffer(true); - provider->BindToCurrentThread(); - - cc::FakeOutputSurfaceClient output_surface_client; - auto output_surface = FakeOutputSurface::Create3d(std::move(provider)); - output_surface->BindToClient(&output_surface_client); - - auto resource_provider = std::make_unique<DisplayResourceProviderGL>( - output_surface->context_provider()); - - for (int i = 0; i < 2; ++i) { - bool use_partial_swap = i == 0; - SCOPED_TRACE(use_partial_swap); - - RendererSettings settings; - settings.partial_swap_enabled = use_partial_swap; - FramebufferWatchingGLRenderer renderer(&settings, &debug_settings_, - output_surface.get(), - resource_provider.get()); - renderer.Initialize(); - EXPECT_EQ(use_partial_swap, renderer.use_partial_swap()); - renderer.SetVisible(true); - - gfx::Size viewport_size(100, 100); - gfx::Rect child_rect(10, 10); - - // First frame, the child and root RenderPass each have damage. - AggregatedRenderPass* child_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{2}, child_rect, - gfx::Transform(), cc::FilterOperations()); - cc::AddQuad(child_pass, child_rect, SK_ColorGREEN); - child_pass->damage_rect = child_rect; - - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{1}, - gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations()); - cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorRED); - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), SkBlendMode::kSrcOver); - root_pass->damage_rect = gfx::Rect(viewport_size); - - EXPECT_EQ(0, renderer.bind_root_framebuffer_calls()); - EXPECT_EQ(0, renderer.bind_child_framebuffer_calls()); - - renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(&renderer, viewport_size); - - // We had to draw the root, and the child. - EXPECT_EQ(1, renderer.bind_child_framebuffer_calls()); - // When the CompositorRenderPassDrawQuad in the root is drawn, we may - // re-bind the root framebuffer. So it can be bound more than once. - EXPECT_GE(renderer.bind_root_framebuffer_calls(), 1); - - // Reset counting. - renderer.ResetBindCalls(); - - // Second frame, the child RenderPass has no damage in it. - child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, - AggregatedRenderPassId{2}, child_rect, - gfx::Transform(), cc::FilterOperations()); - cc::AddQuad(child_pass, child_rect, SK_ColorGREEN); - child_pass->damage_rect = gfx::Rect(); - - // Root RenderPass has some damage that doesn't intersect the child. - root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{1}, - gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations()); - cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorRED); - cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId, - gfx::Transform(), SkBlendMode::kSrcOver); - root_pass->damage_rect = gfx::Rect(child_rect.right(), 0, 10, 10); - - EXPECT_EQ(0, renderer.bind_root_framebuffer_calls()); - EXPECT_EQ(0, renderer.bind_child_framebuffer_calls()); - - renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - DrawFrame(&renderer, viewport_size); - - if (use_partial_swap) { - // Without damage overlapping the child, it didn't need to be drawn (it - // may choose to anyway but that'd be a waste). So we don't check for - // |bind_child_framebuffer_calls|. But the root should have been drawn. - EXPECT_EQ(renderer.bind_root_framebuffer_calls(), 1); - } else { - // Without partial swap, we have to draw the child still, this means - // the child is bound as the framebuffer. - EXPECT_EQ(1, renderer.bind_child_framebuffer_calls()); - // When the CompositorRenderPassDrawQuad in the root is drawn, as it must - // be since we must draw the entire output, we may re-bind the root - // framebuffer. So it can be bound more than once. - EXPECT_GE(renderer.bind_root_framebuffer_calls(), 1); - } - } -} - -#if defined(USE_OZONE) || BUILDFLAG(IS_ANDROID) -class GLRendererWithGpuFenceTest : public GLRendererTest { - protected: - GLRendererWithGpuFenceTest() { - auto provider = TestContextProvider::Create(); - provider->BindToCurrentThread(); - provider->TestContextGL()->set_have_commit_overlay_planes(true); - test_context_support_ = provider->support(); - - output_surface_ = FakeOutputSurface::Create3d(std::move(provider)); - output_surface_->set_overlay_texture_id(kSurfaceOverlayTextureId); - output_surface_->set_gpu_fence_id(kGpuFenceId); - resource_provider_ = std::make_unique<DisplayResourceProviderGL>( - output_surface_->context_provider()); - overlay_processor_ = std::make_unique<SingleOverlayOnTopProcessor>(); - overlay_processor_->AllowMultipleCandidates(); - renderer_ = std::make_unique<FakeRendererGL>( - &settings_, &debug_settings_, output_surface_.get(), - resource_provider_.get(), overlay_processor_.get(), - base::ThreadTaskRunnerHandle::Get()); - renderer_->Initialize(); - renderer_->SetVisible(true); - - test_context_support_->SetScheduleOverlayPlaneCallback( - base::BindRepeating(&MockOverlayScheduler::Schedule, - base::Unretained(&overlay_scheduler_))); - } - - ~GLRendererWithGpuFenceTest() override { - if (child_resource_provider_) - child_resource_provider_->ShutdownAndReleaseAllResources(); - } - - ResourceId create_overlay_resource() { - child_context_provider_ = TestContextProvider::Create(); - child_context_provider_->BindToCurrentThread(); - - child_resource_provider_ = std::make_unique<ClientResourceProvider>(); - auto transfer_resource = TransferableResource::MakeGL( - gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(), - gfx::Size(256, 256), true); - ResourceId client_resource_id = child_resource_provider_->ImportResource( - transfer_resource, base::DoNothing()); - - std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = - cc::SendResourceAndGetChildToParentMap( - {client_resource_id}, resource_provider_.get(), - child_resource_provider_.get(), child_context_provider_.get()); - return resource_map[client_resource_id]; - } - - static constexpr unsigned kSurfaceOverlayTextureId = 33; - static constexpr unsigned kGpuFenceId = 66; - static constexpr unsigned kGpuNoFenceId = 0; - - TestContextSupport* test_context_support_; - - cc::FakeOutputSurfaceClient output_surface_client_; - std::unique_ptr<FakeOutputSurface> output_surface_; - std::unique_ptr<DisplayResourceProviderGL> resource_provider_; - scoped_refptr<TestContextProvider> child_context_provider_; - std::unique_ptr<ClientResourceProvider> child_resource_provider_; - RendererSettings settings_; - std::unique_ptr<SingleOverlayOnTopProcessor> overlay_processor_; - std::unique_ptr<FakeRendererGL> renderer_; - MockOverlayScheduler overlay_scheduler_; -}; - -TEST_F(GLRendererWithGpuFenceTest, GpuFenceIdIsUsedWithRootRenderPassOverlay) { - gfx::Size viewport_size(100, 100); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{1}, - gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations()); - root_pass->has_transparent_background = false; - - EXPECT_CALL(overlay_scheduler_, - Schedule(0, gfx::OVERLAY_TRANSFORM_NONE, kSurfaceOverlayTextureId, - gfx::Rect(viewport_size), _, _, kGpuFenceId)) - .Times(1); - DrawFrame(renderer_.get(), viewport_size); -} - -TEST_F(GLRendererWithGpuFenceTest, - GpuFenceIdIsUsedOnlyForRootRenderPassOverlay) { - gfx::Size viewport_size(100, 100); - AggregatedRenderPass* root_pass = cc::AddRenderPass( - &render_passes_in_draw_order_, AggregatedRenderPassId{1}, - gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations()); - root_pass->has_transparent_background = false; - - bool needs_blending = false; - bool premultiplied_alpha = false; - bool flipped = false; - bool nearest_neighbor = false; - float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - gfx::PointF uv_top_left(0, 0); - gfx::PointF uv_bottom_right(1, 1); - - TextureDrawQuad* overlay_quad = - root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>(); - SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState(); - shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size), - gfx::Rect(50, 50), gfx::MaskFilterInfo(), absl::nullopt, - false, 1, SkBlendMode::kSrcOver, 0); - overlay_quad->SetNew( - shared_state, gfx::Rect(viewport_size), gfx::Rect(viewport_size), - needs_blending, create_overlay_resource(), premultiplied_alpha, - uv_top_left, uv_bottom_right, SK_ColorTRANSPARENT, vertex_opacity, - flipped, nearest_neighbor, - /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear); - - EXPECT_CALL(overlay_scheduler_, - Schedule(0, gfx::OVERLAY_TRANSFORM_NONE, kSurfaceOverlayTextureId, - gfx::Rect(viewport_size), _, _, kGpuFenceId)) - .Times(1); - EXPECT_CALL(overlay_scheduler_, - Schedule(1, gfx::OVERLAY_TRANSFORM_NONE, _, - gfx::Rect(viewport_size), _, _, kGpuNoFenceId)) - .Times(1); - DrawFrame(renderer_.get(), viewport_size); -} -#endif // defined(USE_OZONE) || BUILDFLAG(IS_ANDROID) - -} // namespace -} // namespace viz diff --git a/chromium/components/viz/service/display/layer_quad.cc b/chromium/components/viz/service/display/layer_quad.cc deleted file mode 100644 index d7349d86d79..00000000000 --- a/chromium/components/viz/service/display/layer_quad.cc +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2011 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/layer_quad.h" - -#include <stddef.h> - -#include "base/check.h" -#include "ui/gfx/geometry/quad_f.h" - -namespace viz { - -LayerQuad::Edge::Edge(const gfx::PointF& p, const gfx::PointF& q) { - if (p == q) { - degenerate_ = true; - return; - } - degenerate_ = false; - gfx::Vector2dF tangent(p.y() - q.y(), q.x() - p.x()); - float cross2 = p.x() * q.y() - q.x() * p.y(); - - set(tangent.x(), tangent.y(), cross2); - scale(1.0f / tangent.Length()); -} - -gfx::PointF LayerQuad::Edge::Intersect(const LayerQuad::Edge& e) const { - DCHECK(!degenerate()); - DCHECK(!e.degenerate()); - - return gfx::PointF((y() * e.z() - e.y() * z()) / (x() * e.y() - e.x() * y()), - (x() * e.z() - e.x() * z()) / (e.x() * y() - x() * e.y())); -} - -LayerQuad::LayerQuad(const gfx::QuadF& quad) { - // Create edges. - left_ = Edge(quad.p4(), quad.p1()); - right_ = Edge(quad.p2(), quad.p3()); - top_ = Edge(quad.p1(), quad.p2()); - bottom_ = Edge(quad.p3(), quad.p4()); - - float sign = quad.IsCounterClockwise() ? -1 : 1; - left_.scale(sign); - right_.scale(sign); - top_.scale(sign); - bottom_.scale(sign); -} - -LayerQuad::LayerQuad(const Edge& left, - const Edge& top, - const Edge& right, - const Edge& bottom) - : left_(left), top_(top), right_(right), bottom_(bottom) {} - -gfx::QuadF LayerQuad::ToQuadF() const { - size_t num_degenerate_edges = left_.degenerate() + right_.degenerate() + - top_.degenerate() + bottom_.degenerate(); - if (num_degenerate_edges > 1) { - return gfx::QuadF(); - } - - if (left_.degenerate()) { - return gfx::QuadF(top_.Intersect(bottom_), top_.Intersect(right_), - right_.Intersect(bottom_), bottom_.Intersect(top_)); - } - if (right_.degenerate()) { - return gfx::QuadF(left_.Intersect(top_), top_.Intersect(bottom_), - bottom_.Intersect(top_), bottom_.Intersect(left_)); - } - if (top_.degenerate()) { - return gfx::QuadF(left_.Intersect(right_), right_.Intersect(left_), - right_.Intersect(bottom_), bottom_.Intersect(left_)); - } - if (bottom_.degenerate()) { - return gfx::QuadF(left_.Intersect(top_), top_.Intersect(right_), - right_.Intersect(left_), left_.Intersect(right_)); - } - return gfx::QuadF(left_.Intersect(top_), top_.Intersect(right_), - right_.Intersect(bottom_), bottom_.Intersect(left_)); -} - -void LayerQuad::ToFloatArray(float flattened[12]) const { - if (left_.degenerate()) { - flattened[0] = bottom_.x(); - flattened[1] = bottom_.y(); - flattened[2] = bottom_.z(); - } else { - flattened[0] = left_.x(); - flattened[1] = left_.y(); - flattened[2] = left_.z(); - } - if (top_.degenerate()) { - flattened[3] = left_.x(); - flattened[4] = left_.y(); - flattened[5] = left_.z(); - } else { - flattened[3] = top_.x(); - flattened[4] = top_.y(); - flattened[5] = top_.z(); - } - if (right_.degenerate()) { - flattened[6] = top_.x(); - flattened[7] = top_.y(); - flattened[8] = top_.z(); - } else { - flattened[6] = right_.x(); - flattened[7] = right_.y(); - flattened[8] = right_.z(); - } - if (bottom_.degenerate()) { - flattened[9] = right_.x(); - flattened[10] = right_.y(); - flattened[11] = right_.z(); - } else { - flattened[9] = bottom_.x(); - flattened[10] = bottom_.y(); - flattened[11] = bottom_.z(); - } -} - -} // namespace viz diff --git a/chromium/components/viz/service/display/layer_quad.h b/chromium/components/viz/service/display/layer_quad.h deleted file mode 100644 index e6cd61fa457..00000000000 --- a/chromium/components/viz/service/display/layer_quad.h +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2011 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_LAYER_QUAD_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_LAYER_QUAD_H_ - -#include "components/viz/service/viz_service_export.h" -#include "ui/gfx/geometry/point_f.h" - -namespace gfx { -class QuadF; -} - -namespace viz { - -constexpr float kAntiAliasingInflateDistance = 0.5f; - -class VIZ_SERVICE_EXPORT LayerQuad { - public: - class VIZ_SERVICE_EXPORT Edge { - public: - Edge() : x_(0), y_(0), z_(0), degenerate_(false) {} - Edge(const gfx::PointF& p, const gfx::PointF& q); - - float x() const { return x_; } - float y() const { return y_; } - float z() const { return z_; } - - void set_x(float x) { x_ = x; } - void set_y(float y) { y_ = y; } - void set_z(float z) { z_ = z; } - void set(float x, float y, float z) { - x_ = x; - y_ = y; - z_ = z; - } - - void move_x(float dx) { x_ += dx; } - void move_y(float dy) { y_ += dy; } - void move_z(float dz) { z_ += dz; } - void move(float dx, float dy, float dz) { - x_ += dx; - y_ += dy; - z_ += dz; - } - - void scale_x(float sx) { x_ *= sx; } - void scale_y(float sy) { y_ *= sy; } - void scale_z(float sz) { z_ *= sz; } - void scale(float sx, float sy, float sz) { - x_ *= sx; - y_ *= sy; - z_ *= sz; - } - void scale(float s) { scale(s, s, s); } - - bool degenerate() const { return degenerate_; } - - gfx::PointF Intersect(const Edge& e) const; - - private: - float x_; - float y_; - float z_; - bool degenerate_; - }; - - LayerQuad(const Edge& left, - const Edge& top, - const Edge& right, - const Edge& bottom); - explicit LayerQuad(const gfx::QuadF& quad); - - LayerQuad(const LayerQuad&) = delete; - LayerQuad& operator=(const LayerQuad&) = delete; - - Edge left() const { return left_; } - Edge top() const { return top_; } - Edge right() const { return right_; } - Edge bottom() const { return bottom_; } - - void InflateX(float dx) { - left_.move_z(dx); - right_.move_z(dx); - } - void InflateY(float dy) { - top_.move_z(dy); - bottom_.move_z(dy); - } - void Inflate(float d) { - InflateX(d); - InflateY(d); - } - void InflateAntiAliasingDistance() { Inflate(kAntiAliasingInflateDistance); } - - gfx::QuadF ToQuadF() const; - - void ToFloatArray(float flattened[12]) const; - - private: - Edge left_; - Edge top_; - Edge right_; - Edge bottom_; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_LAYER_QUAD_H_ diff --git a/chromium/components/viz/service/display/layer_quad_unittest.cc b/chromium/components/viz/service/display/layer_quad_unittest.cc deleted file mode 100644 index 6de10f5b62a..00000000000 --- a/chromium/components/viz/service/display/layer_quad_unittest.cc +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2011 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/layer_quad.h" - -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/geometry/quad_f.h" - -namespace viz { -namespace { - -TEST(LayerQuadTest, QuadFConversion) { - gfx::PointF p1(-0.5f, -0.5f); - gfx::PointF p2(0.5f, -0.5f); - gfx::PointF p3(0.5f, 0.5f); - gfx::PointF p4(-0.5f, 0.5f); - - gfx::QuadF quad_cw(p1, p2, p3, p4); - LayerQuad layer_quad_cw(quad_cw); - EXPECT_EQ(layer_quad_cw.ToQuadF(), quad_cw); - - gfx::QuadF quad_ccw(p1, p4, p3, p2); - LayerQuad layer_quad_ccw(quad_ccw); - EXPECT_EQ(layer_quad_ccw.ToQuadF(), quad_ccw); -} - -TEST(LayerQuadTest, Inflate) { - gfx::PointF p1(-0.5f, -0.5f); - gfx::PointF p2(0.5f, -0.5f); - gfx::PointF p3(0.5f, 0.5f); - gfx::PointF p4(-0.5f, 0.5f); - - gfx::QuadF quad(p1, p2, p3, p4); - LayerQuad layer_quad(quad); - quad.Scale(2.f, 2.f); - layer_quad.Inflate(0.5f); - EXPECT_EQ(layer_quad.ToQuadF(), quad); -} - -TEST(LayerQuadTest, Degenerate) { - gfx::QuadF quad; - gfx::PointF p1(1.0f, 1.0f); - gfx::PointF p2(0.0f, 1.0f); - gfx::PointF p3(1.0f, 0.0f); - gfx::QuadF triangle(p1, p2, p3, p1); - - LayerQuad::Edge e1d(p1, p1); - LayerQuad::Edge e2d(p2, p2); - LayerQuad::Edge e2(p1, p2); - LayerQuad::Edge e3(p2, p3); - LayerQuad::Edge e4(p3, p1); - EXPECT_TRUE(e1d.degenerate()); - EXPECT_TRUE(e2d.degenerate()); - EXPECT_FALSE(e2.degenerate()); - EXPECT_FALSE(e3.degenerate()); - EXPECT_FALSE(e4.degenerate()); - - LayerQuad degenerate_quad(e1d, e2d, e2, e3); - // With more than one degenerate edge, we expect the quad to be zero. - EXPECT_EQ(quad, degenerate_quad.ToQuadF()); - - LayerQuad triangle_quad(e1d, e2, e3, e4); - // With only one degenerate edge, we expect the quad to be a triangle. - EXPECT_EQ(triangle, triangle_quad.ToQuadF()); -} - -} // namespace -} // namespace viz diff --git a/chromium/components/viz/service/display/null_renderer.h b/chromium/components/viz/service/display/null_renderer.h index fa291a47ae7..99935df341c 100644 --- a/chromium/components/viz/service/display/null_renderer.h +++ b/chromium/components/viz/service/display/null_renderer.h @@ -45,7 +45,6 @@ class VIZ_SERVICE_EXPORT NullRenderer : public DirectRenderer { void DoDrawQuad(const DrawQuad* quad, const gfx::QuadF* clip_region) override {} void BeginDrawingFrame() override; - void FlushOverdrawFeedback(const gfx::Rect& output_rect) override {} void FinishDrawingFrame() override {} bool FlippedFramebuffer() const override; void EnsureScissorTestEnabled() override {} diff --git a/chromium/components/viz/service/display/output_surface.cc b/chromium/components/viz/service/display/output_surface.cc index 6088ff13f26..643a191a333 100644 --- a/chromium/components/viz/service/display/output_surface.cc +++ b/chromium/components/viz/service/display/output_surface.cc @@ -15,8 +15,6 @@ #include "components/viz/service/display/output_surface_client.h" #include "components/viz/service/display/output_surface_frame.h" #include "gpu/GLES2/gl2extchromium.h" -#include "gpu/command_buffer/client/context_support.h" -#include "gpu/command_buffer/client/gles2_interface.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/swap_result.h" @@ -30,14 +28,9 @@ OutputSurface::Capabilities& OutputSurface::Capabilities::operator=( OutputSurface::OutputSurface(Type type) : type_(type) {} -OutputSurface::OutputSurface(scoped_refptr<ContextProvider> context_provider) - : context_provider_(std::move(context_provider)), type_(Type::kOpenGL) { - DCHECK(context_provider_); -} - OutputSurface::OutputSurface( std::unique_ptr<SoftwareOutputDevice> software_device) - : software_device_(std::move(software_device)), type_(Type::kSoftware) { + : type_(Type::kSoftware), software_device_(std::move(software_device)) { DCHECK(software_device_); } diff --git a/chromium/components/viz/service/display/output_surface.h b/chromium/components/viz/service/display/output_surface.h index 9c854b56b21..40a1cc2b7d1 100644 --- a/chromium/components/viz/service/display/output_surface.h +++ b/chromium/components/viz/service/display/output_surface.h @@ -12,7 +12,6 @@ #include "base/memory/ref_counted.h" #include "base/threading/thread_checker.h" #include "components/viz/common/display/update_vsync_parameters_callback.h" -#include "components/viz/common/gpu/context_provider.h" #include "components/viz/common/gpu/gpu_vsync_callback.h" #include "components/viz/common/resources/resource_format.h" #include "components/viz/common/resources/returned_resource.h" @@ -20,12 +19,12 @@ #include "components/viz/service/display/software_output_device.h" #include "components/viz/service/viz_service_export.h" #include "gpu/command_buffer/common/mailbox.h" -#include "gpu/command_buffer/common/texture_in_use_response.h" #include "gpu/ipc/common/surface_handle.h" #include "gpu/ipc/gpu_task_scheduler_helper.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkM44.h" +#include "ui/gfx/buffer_types.h" #include "ui/gfx/color_space.h" #include "ui/gfx/overlay_transform.h" #include "ui/gfx/surface_origin.h" @@ -77,10 +76,6 @@ class VIZ_SERVICE_EXPORT OutputSurface { bool uses_default_gl_framebuffer = true; // Where (0,0) is on this OutputSurface. gfx::SurfaceOrigin output_surface_origin = gfx::SurfaceOrigin::kBottomLeft; - // Whether this OutputSurface supports stencil operations or not. - // Note: HasExternalStencilTest() must return false when an output surface - // has been configured for stencil usage. - bool supports_stencil = false; // Whether this OutputSurface supports post sub buffer or not. bool supports_post_sub_buffer = false; // Whether this OutputSurface supports commit overlay planes. @@ -145,8 +140,6 @@ class VIZ_SERVICE_EXPORT OutputSurface { // Constructor for skia-based compositing. explicit OutputSurface(Type type); - // Constructor for GL-based compositing. - explicit OutputSurface(scoped_refptr<ContextProvider> context_provider); // Constructor for software compositing. explicit OutputSurface(std::unique_ptr<SoftwareOutputDevice> software_device); @@ -158,11 +151,9 @@ class VIZ_SERVICE_EXPORT OutputSurface { const Capabilities& capabilities() const { return capabilities_; } Type type() const { return type_; } - // Obtain the 3d context or the software device associated with this output - // surface. Either of these may return a null pointer, but not both. - // In the event of a lost context, the entire output surface should be - // recreated. - ContextProvider* context_provider() const { return context_provider_.get(); } + // Obtain the software device associated with this output surface. This will + // return non-null for a software output surface and null for skia output + // surface. SoftwareOutputDevice* software_device() const { return software_device_.get(); } @@ -183,12 +174,14 @@ class VIZ_SERVICE_EXPORT OutputSurface { virtual void EnsureBackbuffer() = 0; virtual void DiscardBackbuffer() = 0; - // Bind the default framebuffer for drawing to, only valid for GL backed - // OutputSurfaces. - virtual void BindFramebuffer() = 0; - // Marks that the given rectangle will be drawn to on the default, bound - // framebuffer. Only valid if |capabilities().supports_dc_layers| is true. + // framebuffer. The contents of the framebuffer are undefined after this + // command and must be filled in completely before a swap happens. Drawing + // outside this rectangle causes undefined behavior. + // + // Note: This is only valid to call if `capabilities().supports_dc_layers` is + // true. It can only be called once per swap and must be called before + // drawing to the default framebuffer. virtual void SetDrawRectangle(const gfx::Rect& rect); // Enable or disable DC layers. Must be called before DC layers are scheduled. @@ -198,24 +191,28 @@ class VIZ_SERVICE_EXPORT OutputSurface { // Returns true if a main image overlay plane should be scheduled. virtual bool IsDisplayedAsOverlayPlane() const = 0; - // Get the texture for the main image's overlay. - virtual unsigned GetOverlayTextureId() const = 0; - // Returns the |mailbox| corresponding to the main image's overlay. virtual gpu::Mailbox GetOverlayMailbox() const; - virtual void Reshape(const gfx::Size& size, - float device_scale_factor, - const gfx::ColorSpace& color_space, - gfx::BufferFormat format, - bool use_stencil) = 0; - - virtual bool HasExternalStencilTest() const = 0; - virtual void ApplyExternalStencil() = 0; - - // Gives the GL internal format that should be used for calling CopyTexImage2D - // when the framebuffer is bound via BindFramebuffer(). - virtual uint32_t GetFramebufferCopyTextureFormat() = 0; + // Reshape the output surface. + struct ReshapeParams { + gfx::Size size; + float device_scale_factor = 1.f; + gfx::ColorSpace color_space; + float sdr_white_level = gfx::ColorSpace::kDefaultSDRWhiteLevel; + gfx::BufferFormat format = gfx::BufferFormat::RGBX_8888; + + bool operator==(const ReshapeParams& other) const { + return size == other.size && + device_scale_factor == other.device_scale_factor && + color_space == other.color_space && + sdr_white_level == other.sdr_white_level; + } + bool operator!=(const ReshapeParams& other) const { + return !(*this == other); + } + }; + virtual void Reshape(const ReshapeParams& params) = 0; // Swaps the current backbuffer to the screen. For successful swaps, the // implementation must call OutputSurfaceClient::DidReceiveSwapBuffersAck() @@ -232,14 +229,6 @@ class VIZ_SERVICE_EXPORT OutputSurface { // TODO(dcastagna): Consider making the following pure virtual. virtual gfx::Rect GetCurrentFramebufferDamage() const; - // Updates the GpuFence associated with this surface. The id of a newly - // created GpuFence is returned, or if an error occurs, or fences are not - // supported, the special id of 0 (meaning "no fence") is returned. In all - // cases, any previously associated fence is destroyed. The returned fence id - // corresponds to the GL id used by the CHROMIUM_gpu_fence GL extension and - // can be passed directly to any related extension functions. - virtual unsigned UpdateGpuFence() = 0; - // Sets callback to receive updated vsync parameters after SwapBuffers() if // supported. virtual void SetUpdateVSyncParametersCallback( @@ -298,11 +287,10 @@ class VIZ_SERVICE_EXPORT OutputSurface { protected: struct OutputSurface::Capabilities capabilities_; - scoped_refptr<ContextProvider> context_provider_; - std::unique_ptr<SoftwareOutputDevice> software_device_; private: const Type type_; + std::unique_ptr<SoftwareOutputDevice> software_device_; SkM44 color_matrix_; }; diff --git a/chromium/components/viz/service/display/output_surface_client.h b/chromium/components/viz/service/display/output_surface_client.h index 3aa54a8e1d1..505beb8c0c0 100644 --- a/chromium/components/viz/service/display/output_surface_client.h +++ b/chromium/components/viz/service/display/output_surface_client.h @@ -13,7 +13,6 @@ #include "components/viz/common/resources/returned_resource.h" #include "components/viz/service/viz_service_export.h" #include "gpu/command_buffer/common/mailbox.h" -#include "gpu/command_buffer/common/texture_in_use_response.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/gpu_fence_handle.h" #include "ui/latency/latency_info.h" @@ -37,11 +36,6 @@ class VIZ_SERVICE_EXPORT OutputSurfaceClient { // For surfaceless/ozone implementations to create damage for the next frame. virtual void SetNeedsRedrawRect(const gfx::Rect& damage_rect) = 0; - // For synchronizing IOSurface use with the macOS WindowServer with - // GLRenderer. - virtual void DidReceiveTextureInUseResponses( - const gpu::TextureInUseResponses& responses) = 0; - // For displaying a swapped frame's contents on macOS. virtual void DidReceiveCALayerParams( const gfx::CALayerParams& ca_layer_params) = 0; diff --git a/chromium/components/viz/service/display/overlay_ca_unittest.cc b/chromium/components/viz/service/display/overlay_ca_unittest.cc index 2a1b231bb45..b8020578253 100644 --- a/chromium/components/viz/service/display/overlay_ca_unittest.cc +++ b/chromium/components/viz/service/display/overlay_ca_unittest.cc @@ -24,14 +24,15 @@ #include "components/viz/common/quads/stream_video_draw_quad.h" #include "components/viz/common/quads/texture_draw_quad.h" #include "components/viz/common/quads/video_hole_draw_quad.h" +#include "components/viz/common/quads/yuv_video_draw_quad.h" #include "components/viz/common/resources/transferable_resource.h" #include "components/viz/service/display/ca_layer_overlay.h" -#include "components/viz/service/display/display_resource_provider_gl.h" -#include "components/viz/service/display/gl_renderer.h" +#include "components/viz/service/display/display_resource_provider_skia.h" #include "components/viz/service/display/output_surface.h" #include "components/viz/service/display/output_surface_client.h" #include "components/viz/service/display/output_surface_frame.h" #include "components/viz/service/display/overlay_processor_mac.h" +#include "components/viz/test/fake_skia_output_surface.h" #include "components/viz/test/test_context_provider.h" #include "components/viz/test/test_gles2_interface.h" #include "gpu/config/gpu_finch_features.h" @@ -52,45 +53,6 @@ const gfx::PointF kUVBottomRight(1.0f, 1.0f); const gfx::Rect kRenderPassOutputRect(0, 0, 256, 256); const gfx::Rect kOverlayDamageRect(0, 0, 100, 100); -class OverlayOutputSurface : public OutputSurface { - public: - explicit OverlayOutputSurface( - scoped_refptr<TestContextProvider> context_provider) - : OutputSurface(std::move(context_provider)) {} - - // OutputSurface implementation. - void BindToClient(OutputSurfaceClient* client) override {} - void EnsureBackbuffer() override {} - void DiscardBackbuffer() override {} - void BindFramebuffer() override { bind_framebuffer_count_ += 1; } - void Reshape(const gfx::Size& size, - float device_scale_factor, - const gfx::ColorSpace& color_space, - gfx::BufferFormat format, - bool use_stencil) override {} - void SwapBuffers(OutputSurfaceFrame frame) override {} - uint32_t GetFramebufferCopyTextureFormat() override { - // TestContextProvider has no real framebuffer, just use RGB. - return GL_RGB; - } - bool HasExternalStencilTest() const override { return false; } - void ApplyExternalStencil() override {} - bool IsDisplayedAsOverlayPlane() const override { return false; } - unsigned GetOverlayTextureId() const override { return 10000; } - unsigned UpdateGpuFence() override { return 0; } - void SetUpdateVSyncParametersCallback( - UpdateVSyncParametersCallback callback) override {} - void SetDisplayTransformHint(gfx::OverlayTransform transform) override {} - gfx::OverlayTransform GetDisplayTransform() override { - return gfx::OVERLAY_TRANSFORM_NONE; - } - - unsigned bind_framebuffer_count() const { return bind_framebuffer_count_; } - - private: - unsigned bind_framebuffer_count_ = 0; -}; - class CATestOverlayProcessor : public OverlayProcessorMac { public: CATestOverlayProcessor() : OverlayProcessorMac() {} @@ -211,13 +173,12 @@ SkM44 GetIdentityColorMatrix() { class CALayerOverlayTest : public testing::Test { protected: void SetUp() override { - provider_ = TestContextProvider::Create(); - provider_->BindToCurrentThread(); - output_surface_ = std::make_unique<OverlayOutputSurface>(provider_); - output_surface_->BindToClient(&client_); + output_surface_ = FakeSkiaOutputSurface::Create3d(); + output_surface_->BindToClient(&output_surface_client_); - resource_provider_ = - std::make_unique<DisplayResourceProviderGL>(provider_.get()); + resource_provider_ = std::make_unique<DisplayResourceProviderSkia>(); + lock_set_for_external_use_.emplace(resource_provider_.get(), + output_surface_.get()); child_provider_ = TestContextProvider::Create(); child_provider_->BindToCurrentThread(); @@ -231,15 +192,16 @@ class CALayerOverlayTest : public testing::Test { child_resource_provider_->ShutdownAndReleaseAllResources(); child_resource_provider_ = nullptr; child_provider_ = nullptr; + lock_set_for_external_use_.reset(); resource_provider_ = nullptr; output_surface_ = nullptr; - provider_ = nullptr; } - scoped_refptr<TestContextProvider> provider_; - std::unique_ptr<OverlayOutputSurface> output_surface_; - cc::FakeOutputSurfaceClient client_; - std::unique_ptr<DisplayResourceProviderGL> resource_provider_; + std::unique_ptr<SkiaOutputSurface> output_surface_; + cc::FakeOutputSurfaceClient output_surface_client_; + std::unique_ptr<DisplayResourceProviderSkia> resource_provider_; + absl::optional<DisplayResourceProviderSkia::LockSetForExternalUse> + lock_set_for_external_use_; scoped_refptr<TestContextProvider> child_provider_; std::unique_ptr<ClientResourceProvider> child_resource_provider_; std::unique_ptr<CATestOverlayProcessor> overlay_processor_; @@ -271,7 +233,6 @@ TEST_F(CALayerOverlayTest, AllowNonAxisAlignedTransform) { EXPECT_EQ(1U, ca_layer_list.size()); gfx::Rect overlay_damage = overlay_processor_->GetAndResetOverlayDamage(); EXPECT_EQ(kRenderPassOutputRect, overlay_damage); - EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); } TEST_F(CALayerOverlayTest, ThreeDTransform) { @@ -301,7 +262,6 @@ TEST_F(CALayerOverlayTest, ThreeDTransform) { expected_transform.RotateAboutXAxis(45.f); gfx::Transform actual_transform(ca_layer_list.back().shared_state->transform); EXPECT_EQ(expected_transform.ToString(), actual_transform.ToString()); - EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); } TEST_F(CALayerOverlayTest, AllowContainingClip) { @@ -325,7 +285,6 @@ TEST_F(CALayerOverlayTest, AllowContainingClip) { &damage_rect_, &content_bounds_); EXPECT_EQ(gfx::Rect(), damage_rect_); EXPECT_EQ(1U, ca_layer_list.size()); - EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); } TEST_F(CALayerOverlayTest, NontrivialClip) { @@ -351,7 +310,6 @@ TEST_F(CALayerOverlayTest, NontrivialClip) { EXPECT_EQ(1U, ca_layer_list.size()); EXPECT_EQ(gfx::RectF(64, 64, 128, 128), ca_layer_list.back().shared_state->clip_rect); - EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); } TEST_F(CALayerOverlayTest, SkipTransparent) { @@ -375,7 +333,6 @@ TEST_F(CALayerOverlayTest, SkipTransparent) { &damage_rect_, &content_bounds_); EXPECT_EQ(gfx::Rect(), damage_rect_); EXPECT_EQ(0U, ca_layer_list.size()); - EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); } TEST_F(CALayerOverlayTest, SkipNonVisible) { @@ -399,7 +356,6 @@ TEST_F(CALayerOverlayTest, SkipNonVisible) { &damage_rect_, &content_bounds_); EXPECT_EQ(gfx::Rect(), damage_rect_); EXPECT_EQ(0U, ca_layer_list.size()); - EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); } TEST_F(CALayerOverlayTest, YUVDrawQuadOverlay) { @@ -484,7 +440,6 @@ TEST_F(CALayerOverlayTest, YUVDrawQuadOverlay) { &damage_rect_, &content_bounds_); EXPECT_EQ(gfx::Rect(), damage_rect_); EXPECT_EQ(0U, ca_layer_list.size()); - EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); } } diff --git a/chromium/components/viz/service/display/overlay_candidate.cc b/chromium/components/viz/service/display/overlay_candidate.cc index 41ae9ef7547..c33317bc9d0 100644 --- a/chromium/components/viz/service/display/overlay_candidate.cc +++ b/chromium/components/viz/service/display/overlay_candidate.cc @@ -93,39 +93,6 @@ gfx::OverlayTransform GetOverlayTransform(const gfx::Transform& quad_transform, return gfx::OVERLAY_TRANSFORM_INVALID; } -gfx::Rect GetDamageRect(const DrawQuad* quad, - SurfaceDamageRectList* surface_damage_rect_list) { - const SharedQuadState* sqs = quad->shared_quad_state; - auto& transform = sqs->quad_to_target_transform; - gfx::RectF display_rect = gfx::RectF(quad->rect); - transform.TransformRect(&display_rect); - if (!sqs->overlay_damage_index.has_value()) { - gfx::Rect display_rect_int = gfx::ToRoundedRect(display_rect); - // This is a special case where an overlay candidate may have damage but it - // does not have a damage index since it was not the only quad in the - // original surface. Here the union of all |surface_damage_rect_list| will - // be in effect the full damage for this display. - auto full_display_damage = gfx::Rect(); - for (auto& each : *surface_damage_rect_list) { - full_display_damage.Union(each); - } - - // We limit the damage to the candidates quad rect in question. - gfx::Rect intersection = display_rect_int; - intersection.Intersect(full_display_damage); - return intersection; - } - - size_t overlay_damage_index = sqs->overlay_damage_index.value(); - // Invalid index. - if (overlay_damage_index >= surface_damage_rect_list->size()) { - DCHECK(false); - return gfx::Rect(); - } - - return (*surface_damage_rect_list)[overlay_damage_index]; -} - } // namespace OverlayCandidate::OverlayCandidate() = default; @@ -134,19 +101,13 @@ OverlayCandidate::OverlayCandidate(const OverlayCandidate& other) = default; OverlayCandidate::~OverlayCandidate() = default; -// static -OverlayCandidate::CandidateStatus OverlayCandidate::FromDrawQuad( - DisplayResourceProvider* resource_provider, - SurfaceDamageRectList* surface_damage_rect_list, - const SkM44& output_color_matrix, +OverlayCandidate::CandidateStatus OverlayCandidateFactory::FromDrawQuad( const DrawQuad* quad, - const gfx::RectF& primary_rect, - OverlayCandidate* candidate, - bool is_delegated_context) { + OverlayCandidate& candidate) const { // It is currently not possible to set a color conversion matrix on an HW // overlay plane. // TODO(https://crbug.com/792757): Remove this check once the bug is resolved. - if (output_color_matrix != SkM44()) + if (*output_color_matrix_ != SkM44()) return CandidateStatus::kFailColorMatrix; const SharedQuadState* sqs = quad->shared_quad_state; @@ -154,12 +115,12 @@ OverlayCandidate::CandidateStatus OverlayCandidate::FromDrawQuad( // We don't support an opacity value different than one for an overlay plane. // Render pass quads should have their |sqs| opacity integrated directly into // their final output buffers. - if (!cc::MathUtil::IsWithinEpsilon(sqs->opacity, 1.0f) && - !is_delegated_context) { + if (!is_delegated_context_ && + !cc::MathUtil::IsWithinEpsilon(sqs->opacity, 1.0f)) { return CandidateStatus::kFailOpacity; } - candidate->opacity = sqs->opacity; - candidate->rounded_corners = sqs->mask_filter_info.rounded_corner_bounds(); + candidate.opacity = sqs->opacity; + candidate.rounded_corners = sqs->mask_filter_info.rounded_corner_bounds(); // We support only kSrc (no blending) and kSrcOver (blending with premul). if (!(sqs->blend_mode == SkBlendMode::kSrc || @@ -167,43 +128,33 @@ OverlayCandidate::CandidateStatus OverlayCandidate::FromDrawQuad( return CandidateStatus::kFailBlending; } - candidate->requires_overlay = - OverlayCandidate::RequiresOverlay(quad); - candidate->overlay_damage_index = - sqs->overlay_damage_index.value_or(kInvalidDamageIndex); + candidate.requires_overlay = OverlayCandidate::RequiresOverlay(quad); + candidate.overlay_damage_index = + sqs->overlay_damage_index.value_or(OverlayCandidate::kInvalidDamageIndex); switch (quad->material) { case DrawQuad::Material::kTextureContent: - return FromTextureQuad(resource_provider, surface_damage_rect_list, - TextureDrawQuad::MaterialCast(quad), primary_rect, - candidate, is_delegated_context); + return FromTextureQuad(TextureDrawQuad::MaterialCast(quad), candidate); case DrawQuad::Material::kVideoHole: - return FromVideoHoleQuad(resource_provider, surface_damage_rect_list, - VideoHoleDrawQuad::MaterialCast(quad), + return FromVideoHoleQuad(VideoHoleDrawQuad::MaterialCast(quad), candidate); case DrawQuad::Material::kStreamVideoContent: - return FromStreamVideoQuad(resource_provider, surface_damage_rect_list, - StreamVideoDrawQuad::MaterialCast(quad), - candidate, is_delegated_context, primary_rect); + return FromStreamVideoQuad(StreamVideoDrawQuad::MaterialCast(quad), + candidate); case DrawQuad::Material::kSolidColor: - if (!is_delegated_context) + if (!is_delegated_context_) return CandidateStatus::kFailQuadNotSupported; - return candidate->FromSolidColorQuad( - resource_provider, surface_damage_rect_list, - SolidColorDrawQuad::MaterialCast(quad), primary_rect, candidate); + return FromSolidColorQuad(SolidColorDrawQuad::MaterialCast(quad), + candidate); case DrawQuad::Material::kAggregatedRenderPass: - if (!is_delegated_context) + if (!is_delegated_context_) return CandidateStatus::kFailQuadNotSupported; - return candidate->FromAggregateQuad( - resource_provider, surface_damage_rect_list, - AggregatedRenderPassDrawQuad::MaterialCast(quad), primary_rect, - candidate); + return FromAggregateQuad(AggregatedRenderPassDrawQuad::MaterialCast(quad), + candidate); case DrawQuad::Material::kTiledContent: - if (!is_delegated_context) + if (!is_delegated_context_) return CandidateStatus::kFailQuadNotSupported; - return candidate->FromTileQuad( - resource_provider, surface_damage_rect_list, - TileDrawQuad::MaterialCast(quad), primary_rect, candidate); + return FromTileQuad(TileDrawQuad::MaterialCast(quad), candidate); default: break; } @@ -218,8 +169,8 @@ bool OverlayCandidate::IsInvisibleQuad(const DrawQuad* quad) { return true; if (quad->material != DrawQuad::Material::kSolidColor) return false; - const SkColor color = SolidColorDrawQuad::MaterialCast(quad)->color; - const float alpha = (SkColorGetA(color) * (1.f / 255.f)) * opacity; + const float alpha = + SolidColorDrawQuad::MaterialCast(quad)->color.fA * opacity; return quad->ShouldDrawWithBlending() && cc::MathUtil::IsWithinEpsilon(alpha, 0.f); } @@ -247,17 +198,52 @@ bool OverlayCandidate::IsOccluded(const OverlayCandidate& candidate, return false; } -// static -int OverlayCandidate::EstimateVisibleDamage( +OverlayCandidateFactory::OverlayCandidateFactory( + const AggregatedRenderPass* render_pass, + DisplayResourceProvider* resource_provider, + const SurfaceDamageRectList* surface_damage_rect_list, + const SkM44* output_color_matrix, + const gfx::RectF primary_rect, + bool is_delegated_context) + : render_pass_(render_pass), + resource_provider_(resource_provider), + surface_damage_rect_list_(surface_damage_rect_list), + output_color_matrix_(output_color_matrix), + primary_rect_(primary_rect), + is_delegated_context_(is_delegated_context) { + // TODO(crbug.com/1323002): Replace this set with a simple ordered linear + // search when this bug is resolved. + base::flat_set<size_t> indices_with_quad_damage; + for (auto* sqs : render_pass_->shared_quad_state_list) { + // If a |sqs| has a damage index it will only be associated with a single + // draw quad. + if (sqs->overlay_damage_index.has_value()) { + indices_with_quad_damage.insert(sqs->overlay_damage_index.value()); + } + } + + for (size_t i = 0; i < (*surface_damage_rect_list_).size(); i++) { + // Add this damage only if it does not correspond to a specific quad. + // Ideally any damage that we might want to separate out (think overlays) + // will not end up in this |unassigned_surface_damage_| rect. + if (!indices_with_quad_damage.contains(i)) { + unassigned_surface_damage_.Union((*surface_damage_rect_list_)[i]); + } + } +} + +OverlayCandidateFactory::~OverlayCandidateFactory() = default; + +float OverlayCandidateFactory::EstimateVisibleDamage( const DrawQuad* quad, - SurfaceDamageRectList* surface_damage_rect_list, + const OverlayCandidate& candidate, QuadList::ConstIterator quad_list_begin, - QuadList::ConstIterator quad_list_end) { - gfx::Rect quad_damage = GetDamageRect(quad, surface_damage_rect_list); - int occluded_damage_estimate_total = 0; + QuadList::ConstIterator quad_list_end) const { + gfx::Rect quad_damage = gfx::ToEnclosingRect(GetDamageRect(quad, candidate)); + float occluded_damage_estimate_total = 0.f; for (auto overlap_iter = quad_list_begin; overlap_iter != quad_list_end; ++overlap_iter) { - gfx::Rect overlap_rect = gfx::ToRoundedRect(cc::MathUtil::MapClippedRect( + gfx::Rect overlap_rect = gfx::ToEnclosingRect(cc::MathUtil::MapClippedRect( overlap_iter->shared_quad_state->quad_to_target_transform, gfx::RectF(overlap_iter->rect))); @@ -273,7 +259,7 @@ int OverlayCandidate::EstimateVisibleDamage( // reason why this computation is an estimate and why we have the max clamping // below. return std::max( - 0, quad_damage.size().GetArea() - occluded_damage_estimate_total); + 0.f, quad_damage.size().GetArea() - occluded_damage_estimate_total); } // static @@ -295,13 +281,12 @@ bool OverlayCandidate::RequiresOverlay(const DrawQuad* quad) { } } -// static -bool OverlayCandidate::IsOccludedByFilteredQuad( +bool OverlayCandidateFactory::IsOccludedByFilteredQuad( const OverlayCandidate& candidate, QuadList::ConstIterator quad_list_begin, QuadList::ConstIterator quad_list_end, const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>& - render_pass_backdrop_filters) { + render_pass_backdrop_filters) const { for (auto overlap_iter = quad_list_begin; overlap_iter != quad_list_end; ++overlap_iter) { if (overlap_iter->material == DrawQuad::Material::kAggregatedRenderPass) { @@ -320,29 +305,24 @@ bool OverlayCandidate::IsOccludedByFilteredQuad( return false; } -// static -OverlayCandidate::CandidateStatus OverlayCandidate::FromDrawQuadResource( - DisplayResourceProvider* resource_provider, - SurfaceDamageRectList* surface_damage_rect_list, +OverlayCandidate::CandidateStatus OverlayCandidateFactory::FromDrawQuadResource( const DrawQuad* quad, ResourceId resource_id, bool y_flipped, - OverlayCandidate* candidate, - bool is_delegated_context, - const gfx::RectF& primary_rect) { + OverlayCandidate& candidate) const { if (resource_id != kInvalidResourceId && - !resource_provider->IsOverlayCandidate(resource_id)) + !resource_provider_->IsOverlayCandidate(resource_id)) return CandidateStatus::kFailNotOverlay; if (quad->visible_rect.IsEmpty()) return CandidateStatus::kFailVisible; if (resource_id != kInvalidResourceId) { - candidate->format = resource_provider->GetBufferFormat(resource_id); - candidate->color_space = resource_provider->GetColorSpace(resource_id); - candidate->hdr_metadata = resource_provider->GetHDRMetadata(resource_id); + candidate.format = resource_provider_->GetBufferFormat(resource_id); + candidate.color_space = resource_provider_->GetColorSpace(resource_id); + candidate.hdr_metadata = resource_provider_->GetHDRMetadata(resource_id); - if (!base::Contains(kOverlayFormats, candidate->format)) + if (!base::Contains(kOverlayFormats, candidate.format)) return CandidateStatus::kFailBufferFormat; } @@ -352,28 +332,28 @@ OverlayCandidate::CandidateStatus OverlayCandidate::FromDrawQuadResource( GetOverlayTransform(sqs->quad_to_target_transform, y_flipped); if (overlay_transform == gfx::OVERLAY_TRANSFORM_INVALID) return CandidateStatus::kFailNotAxisAligned; - candidate->transform = overlay_transform; + candidate.transform = overlay_transform; auto& transform = sqs->quad_to_target_transform; - candidate->display_rect = gfx::RectF(quad->rect); - transform.TransformRect(&candidate->display_rect); + candidate.display_rect = gfx::RectF(quad->rect); + transform.TransformRect(&candidate.display_rect); - candidate->clip_rect = sqs->clip_rect; - candidate->is_opaque = + candidate.clip_rect = sqs->clip_rect; + candidate.is_opaque = !quad->ShouldDrawWithBlendingForReasonOtherThanMaskFilter(); - candidate->has_mask_filter = !sqs->mask_filter_info.IsEmpty(); + candidate.has_mask_filter = !sqs->mask_filter_info.IsEmpty(); if (resource_id != kInvalidResourceId) { - candidate->resource_size_in_pixels = - resource_provider->GetResourceBackedSize(resource_id); + candidate.resource_size_in_pixels = + resource_provider_->GetResourceBackedSize(resource_id); } else { - candidate->resource_size_in_pixels = - gfx::Size(candidate->display_rect.size().width(), - candidate->display_rect.size().height()); + candidate.resource_size_in_pixels = + gfx::Size(candidate.display_rect.size().width(), + candidate.display_rect.size().height()); } - AssignDamage(quad, surface_damage_rect_list, candidate); - candidate->resource_id = resource_id; + AssignDamage(quad, candidate); + candidate.resource_id = resource_id; struct TrackingIdData { gfx::Rect rect; @@ -382,118 +362,106 @@ OverlayCandidate::CandidateStatus OverlayCandidate::FromDrawQuadResource( TrackingIdData track_data{quad->rect, FrameSinkId()}; if (resource_id != kInvalidResourceId) { - candidate->mailbox = resource_provider->GetMailbox(resource_id); + candidate.mailbox = resource_provider_->GetMailbox(resource_id); track_data.frame_sink_id = - resource_provider->GetSurfaceId(resource_id).frame_sink_id(); + resource_provider_->GetSurfaceId(resource_id).frame_sink_id(); } // Delegated compositing does not yet support |clip_rect| so it is applied // here to the |display_rect| and |uv_rect| directly. - if (is_delegated_context) { - if (candidate->clip_rect.has_value()) - ApplyClip(candidate, gfx::RectF(*candidate->clip_rect)); + if (is_delegated_context_) { + if (candidate.clip_rect.has_value()) + OverlayCandidate::ApplyClip(candidate, gfx::RectF(*candidate.clip_rect)); + + if (quad->visible_rect != quad->rect) { + auto visible_rect = gfx::RectF(quad->visible_rect); + transform.TransformRect(&visible_rect); + OverlayCandidate::ApplyClip(candidate, gfx::RectF(visible_rect)); + } // TODO(https://crbug.com/1300552) : Tile quads can overlay other quads and // the window by one pixel. Exo does not yet clip these quads so we need to // clip here with the |primary_rect|. - ApplyClip(candidate, primary_rect); + OverlayCandidate::ApplyClip(candidate, primary_rect_); + + if (candidate.display_rect.IsEmpty()) + return CandidateStatus::kFailVisible; } - candidate->tracking_id = base::Hash(&track_data, sizeof(track_data)); + candidate.tracking_id = base::Hash(&track_data, sizeof(track_data)); return CandidateStatus::kSuccess; } -// static -OverlayCandidate::CandidateStatus OverlayCandidate::FromAggregateQuad( - DisplayResourceProvider* resource_provider, - SurfaceDamageRectList* surface_damage_rect_list, +OverlayCandidate::CandidateStatus OverlayCandidateFactory::FromAggregateQuad( const AggregatedRenderPassDrawQuad* quad, - const gfx::RectF& primary_rect, - OverlayCandidate* candidate) { - auto rtn = FromDrawQuadResource(resource_provider, surface_damage_rect_list, - quad, kInvalidResourceId, false, candidate, - true, primary_rect); + OverlayCandidate& candidate) const { + auto rtn = FromDrawQuadResource(quad, kInvalidResourceId, false, candidate); if (rtn == CandidateStatus::kSuccess) { - candidate->rpdq = quad; + candidate.rpdq = quad; } return rtn; } -// static -OverlayCandidate::CandidateStatus OverlayCandidate::FromSolidColorQuad( - DisplayResourceProvider* resource_provider, - SurfaceDamageRectList* surface_damage_rect_list, +OverlayCandidate::CandidateStatus OverlayCandidateFactory::FromSolidColorQuad( const SolidColorDrawQuad* quad, - const gfx::RectF& primary_rect, - OverlayCandidate* candidate) { - auto rtn = FromDrawQuadResource(resource_provider, surface_damage_rect_list, - quad, kInvalidResourceId, false, candidate, - true, primary_rect); + OverlayCandidate& candidate) const { + auto rtn = FromDrawQuadResource(quad, kInvalidResourceId, false, candidate); if (rtn == CandidateStatus::kSuccess) { - candidate->solid_color = quad->color; + // TODO(crbug/1308932) remove toSkColor and make all SkColor4f + candidate.color = quad->color.toSkColor(); + // Mark this candidate a solid color as the |color| member can be either a + // background of the overlay or a color of the solid color quad. + candidate.is_solid_color = true; } return rtn; } -// static -// For VideoHoleDrawQuad, only calculate geometry information -// and put it in the |candidate|. -OverlayCandidate::CandidateStatus OverlayCandidate::FromVideoHoleQuad( - DisplayResourceProvider* resource_provider, - SurfaceDamageRectList* surface_damage_rect_list, +// For VideoHoleDrawQuad, only calculate geometry information and put it in the +// |candidate|. +OverlayCandidate::CandidateStatus OverlayCandidateFactory::FromVideoHoleQuad( const VideoHoleDrawQuad* quad, - OverlayCandidate* candidate) { + OverlayCandidate& candidate) const { gfx::OverlayTransform overlay_transform = GetOverlayTransform( quad->shared_quad_state->quad_to_target_transform, false); if (overlay_transform == gfx::OVERLAY_TRANSFORM_INVALID) return CandidateStatus::kFailNotAxisAligned; auto& transform = quad->shared_quad_state->quad_to_target_transform; - candidate->display_rect = gfx::RectF(quad->rect); - transform.TransformRect(&candidate->display_rect); - candidate->transform = overlay_transform; - candidate->is_opaque = + candidate.display_rect = gfx::RectF(quad->rect); + transform.TransformRect(&candidate.display_rect); + candidate.transform = overlay_transform; + candidate.is_opaque = !quad->ShouldDrawWithBlendingForReasonOtherThanMaskFilter(); - candidate->has_mask_filter = + candidate.has_mask_filter = !quad->shared_quad_state->mask_filter_info.IsEmpty(); - AssignDamage(quad, surface_damage_rect_list, candidate); - candidate->tracking_id = base::FastHash(quad->overlay_plane_id.AsBytes()); + AssignDamage(quad, candidate); + candidate.tracking_id = base::FastHash(quad->overlay_plane_id.AsBytes()); return CandidateStatus::kSuccess; } -OverlayCandidate::CandidateStatus OverlayCandidate::FromTileQuad( - DisplayResourceProvider* resource_provider, - SurfaceDamageRectList* surface_damage_rect_list, +OverlayCandidate::CandidateStatus OverlayCandidateFactory::FromTileQuad( const TileDrawQuad* quad, - const gfx::RectF& primary_rect, - OverlayCandidate* candidate) { + OverlayCandidate& candidate) const { if (quad->nearest_neighbor) return CandidateStatus::kFailNearFilter; - candidate->resource_size_in_pixels = - resource_provider->GetResourceBackedSize(quad->resource_id()); - candidate->uv_rect = gfx::ScaleRect( - quad->tex_coord_rect, 1.f / candidate->resource_size_in_pixels.width(), - 1.f / candidate->resource_size_in_pixels.height()); + candidate.resource_size_in_pixels = + resource_provider_->GetResourceBackedSize(quad->resource_id()); + candidate.uv_rect = gfx::ScaleRect( + quad->tex_coord_rect, 1.f / candidate.resource_size_in_pixels.width(), + 1.f / candidate.resource_size_in_pixels.height()); - auto rtn = FromDrawQuadResource(resource_provider, surface_damage_rect_list, - quad, quad->resource_id(), false, candidate, - true, primary_rect); + auto rtn = FromDrawQuadResource(quad, quad->resource_id(), false, candidate); return rtn; } -// static -OverlayCandidate::CandidateStatus OverlayCandidate::FromTextureQuad( - DisplayResourceProvider* resource_provider, - SurfaceDamageRectList* surface_damage_rect_list, +OverlayCandidate::CandidateStatus OverlayCandidateFactory::FromTextureQuad( const TextureDrawQuad* quad, - const gfx::RectF& primary_rect, - OverlayCandidate* candidate, - bool is_delegated_context) { - if (!is_delegated_context && + OverlayCandidate& candidate) const { + if (!is_delegated_context_ && quad->overlay_priority_hint == OverlayPriority::kLow) { // For current implementation low priority means this does not promote to // overlay. @@ -503,91 +471,88 @@ OverlayCandidate::CandidateStatus OverlayCandidate::FromTextureQuad( if (quad->nearest_neighbor) return CandidateStatus::kFailNearFilter; - if (quad->background_color != SK_ColorTRANSPARENT && - (quad->background_color != SK_ColorBLACK || - quad->ShouldDrawWithBlending())) - return CandidateStatus::kFailBlending; + if (quad->background_color != SkColors::kTransparent && + (quad->background_color != SkColors::kBlack || + quad->ShouldDrawWithBlending())) { + // This path can also be used by other platforms like Ash/Chrome, which does + // not support overlays with background color. Only LaCros/Wayland supports + // that. + if (!is_delegated_context_) + return CandidateStatus::kFailBlending; + // TODO(crbug/1308932) remove toSkColor and make all SkColor4f + candidate.color = quad->background_color.toSkColor(); + } - candidate->uv_rect = BoundingRect(quad->uv_top_left, quad->uv_bottom_right); + candidate.uv_rect = BoundingRect(quad->uv_top_left, quad->uv_bottom_right); - auto rtn = FromDrawQuadResource( - resource_provider, surface_damage_rect_list, quad, quad->resource_id(), - quad->y_flipped, candidate, is_delegated_context, primary_rect); + auto rtn = FromDrawQuadResource(quad, quad->resource_id(), quad->y_flipped, + candidate); if (rtn == CandidateStatus::kSuccess) { // Only handle clip rect for required overlays - if (!is_delegated_context && candidate->requires_overlay) - HandleClipAndSubsampling(candidate, primary_rect); + if (!is_delegated_context_ && candidate.requires_overlay) + HandleClipAndSubsampling(candidate); // Texture quads for UI elements like scroll bars have empty // |size_in_pixels| as 'set_resource_size_in_pixels' is not called as these // quads are not intended to become overlays. if (!quad->resource_size_in_pixels().IsEmpty()) - candidate->priority_hint = gfx::OverlayPriorityHint::kRegular; + candidate.priority_hint = gfx::OverlayPriorityHint::kRegular; } return rtn; } -// static -OverlayCandidate::CandidateStatus OverlayCandidate::FromStreamVideoQuad( - DisplayResourceProvider* resource_provider, - SurfaceDamageRectList* surface_damage_rect_list, +OverlayCandidate::CandidateStatus OverlayCandidateFactory::FromStreamVideoQuad( const StreamVideoDrawQuad* quad, - OverlayCandidate* candidate, - bool is_delegated_context, - const gfx::RectF& primary_rect) { - auto rtn = FromDrawQuadResource(resource_provider, surface_damage_rect_list, - quad, quad->resource_id(), false, candidate, - is_delegated_context, primary_rect); + OverlayCandidate& candidate) const { + auto rtn = FromDrawQuadResource(quad, quad->resource_id(), false, candidate); if (rtn == CandidateStatus::kSuccess) { - candidate->resource_size_in_pixels = quad->resource_size_in_pixels(); - candidate->uv_rect = BoundingRect(quad->uv_top_left, quad->uv_bottom_right); + candidate.resource_size_in_pixels = quad->resource_size_in_pixels(); + candidate.uv_rect = BoundingRect(quad->uv_top_left, quad->uv_bottom_right); #if BUILDFLAG(IS_ANDROID) - candidate->is_backed_by_surface_texture = - resource_provider->IsBackedBySurfaceTexture(quad->resource_id()); + candidate.is_backed_by_surface_texture = + resource_provider_->IsBackedBySurfaceTexture(quad->resource_id()); #endif } return rtn; } -// static -void OverlayCandidate::HandleClipAndSubsampling( - OverlayCandidate* candidate, - const gfx::RectF& primary_rect) { +void OverlayCandidateFactory::HandleClipAndSubsampling( + OverlayCandidate& candidate) const { // The purpose of this is to enable overlays that are required (i.e. protected // content) to be able to be shown in all cases. This will allow them to pass // the clipping check and also the 2x alignment requirement for subsampling in // the Intel DRM driver. This should not be used in cases where the surface // will not always be promoted to an overlay as it will lead to shifting of // the content when it switches between composition and overlay. - if (!candidate->clip_rect) + if (!candidate.clip_rect) return; // Make sure it's in a format we can deal with, we only support YUV and P010. - if (candidate->format != gfx::BufferFormat::YUV_420_BIPLANAR && - candidate->format != gfx::BufferFormat::P010) { + if (candidate.format != gfx::BufferFormat::YUV_420_BIPLANAR && + candidate.format != gfx::BufferFormat::P010) { return; } // Clip the clip rect to the primary plane. An overlay will only be shown on // a single display, so we want to perform our calculations within the bounds // of that display. - if (!primary_rect.IsEmpty()) - candidate->clip_rect->Intersect(gfx::ToNearestRect(primary_rect)); + if (!primary_rect_.IsEmpty()) + candidate.clip_rect->Intersect(gfx::ToNearestRect(primary_rect_)); // Calculate |uv_rect| of |clip_rect| in |display_rect| gfx::RectF uv_rect = cc::MathUtil::ScaleRectProportional( - candidate->uv_rect, candidate->display_rect, - gfx::RectF(*candidate->clip_rect)); + candidate.uv_rect, candidate.display_rect, + gfx::RectF(*candidate.clip_rect)); // In case that |uv_rect| of candidate is not (0, 0, 1, 1) - candidate->uv_rect.Intersect(uv_rect); + candidate.uv_rect.Intersect(uv_rect); // Update |display_rect| to avoid unexpected scaling and the candidate should // not be regarded as clippped after this. - candidate->display_rect.Intersect(gfx::RectF(*candidate->clip_rect)); - candidate->clip_rect.reset(); - gfx::Rect rounded_display_rect = gfx::ToRoundedRect(candidate->display_rect); - candidate->display_rect.SetRect( + candidate.display_rect.Intersect(gfx::RectF(*candidate.clip_rect)); + candidate.clip_rect.reset(); + gfx::Rect rounded_display_rect = gfx::ToRoundedRect(candidate.display_rect); + candidate.display_rect.SetRect( rounded_display_rect.x(), rounded_display_rect.y(), rounded_display_rect.width(), rounded_display_rect.height()); @@ -596,8 +561,8 @@ void OverlayCandidate::HandleClipAndSubsampling( // Get the rect for the source coordinates. gfx::RectF src_rect = gfx::ScaleRect( - candidate->uv_rect, candidate->resource_size_in_pixels.width(), - candidate->resource_size_in_pixels.height()); + candidate.uv_rect, candidate.resource_size_in_pixels.width(), + candidate.resource_size_in_pixels.height()); // Make it an integral multiple of the subsampling factor. auto subsample_round = [](float val) { constexpr int kSubsamplingFactor = 2; @@ -609,19 +574,16 @@ void OverlayCandidate::HandleClipAndSubsampling( src_rect.set_width(subsample_round(src_rect.width())); src_rect.set_height(subsample_round(src_rect.height())); // Scale it back into UV space and set it in the candidate. - candidate->uv_rect = gfx::ScaleRect( - src_rect, 1.0f / candidate->resource_size_in_pixels.width(), - 1.0f / candidate->resource_size_in_pixels.height()); + candidate.uv_rect = + gfx::ScaleRect(src_rect, 1.0f / candidate.resource_size_in_pixels.width(), + 1.0f / candidate.resource_size_in_pixels.height()); } -// static -void OverlayCandidate::AssignDamage( - const DrawQuad* quad, - SurfaceDamageRectList* surface_damage_rect_list, - OverlayCandidate* candidate) { +void OverlayCandidateFactory::AssignDamage(const DrawQuad* quad, + OverlayCandidate& candidate) const { auto& transform = quad->shared_quad_state->quad_to_target_transform; - const auto damage_rect = GetDamageRect(quad, surface_damage_rect_list); - auto transformed_damage = gfx::RectF(damage_rect); + auto damage_rect = GetDamageRect(quad, candidate); + auto transformed_damage = damage_rect; gfx::Transform inv; if (transform.GetInverse(&inv)) { inv.TransformRect(&transformed_damage); @@ -640,37 +602,65 @@ void OverlayCandidate::AssignDamage( // The normalization above is not enough if the |uv_rect| is not 0,0-1x1. // This is because texture uvs can effectively magnify damage. - if (!candidate->uv_rect.IsEmpty()) { - transformed_damage.Scale(candidate->uv_rect.width(), - candidate->uv_rect.height()); - transformed_damage.Offset(candidate->uv_rect.OffsetFromOrigin()); + if (!candidate.uv_rect.IsEmpty()) { + transformed_damage.Scale(candidate.uv_rect.width(), + candidate.uv_rect.height()); + transformed_damage.Offset(candidate.uv_rect.OffsetFromOrigin()); } // Buffer damage is in texels not UVs so scale by resource size. - transformed_damage.Scale(candidate->resource_size_in_pixels.width(), - candidate->resource_size_in_pixels.height()); + transformed_damage.Scale(candidate.resource_size_in_pixels.width(), + candidate.resource_size_in_pixels.height()); } else { // If not invertible, set to full damage. // TODO(https://crbug.com/1279965): |resource_size_in_pixels| might not be // properly initialized at this stage. transformed_damage = - gfx::RectF(gfx::SizeF(candidate->resource_size_in_pixels)); + gfx::RectF(gfx::SizeF(candidate.resource_size_in_pixels)); } // For underlays the function 'EstimateVisibleDamage()' is called to update // |damage_area_estimate| to more accurately reflect the actual visible // damage. - candidate->damage_area_estimate = damage_rect.size().GetArea(); - candidate->damage_rect = transformed_damage; + candidate.damage_area_estimate = damage_rect.size().GetArea(); + candidate.damage_rect = transformed_damage; } -void OverlayCandidate::ApplyClip(OverlayCandidate* candidate, +// static +void OverlayCandidate::ApplyClip(OverlayCandidate& candidate, const gfx::RectF& clip_rect) { - gfx::RectF intersect_clip_display = clip_rect; - intersect_clip_display.Intersect(candidate->display_rect); - gfx::RectF uv_rect = cc::MathUtil::ScaleRectProportional( - candidate->uv_rect, candidate->display_rect, intersect_clip_display); - candidate->display_rect = intersect_clip_display; - candidate->uv_rect = uv_rect; + if (!clip_rect.Contains(candidate.display_rect)) { + gfx::RectF intersect_clip_display = clip_rect; + intersect_clip_display.Intersect(candidate.display_rect); + gfx::RectF uv_rect = cc::MathUtil::ScaleRectProportional( + candidate.uv_rect, candidate.display_rect, intersect_clip_display); + candidate.display_rect = intersect_clip_display; + candidate.uv_rect = uv_rect; + } +} + +gfx::RectF OverlayCandidateFactory::GetDamageRect( + const DrawQuad* quad, + const OverlayCandidate& candidate) const { + const SharedQuadState* sqs = quad->shared_quad_state; + if (!sqs->overlay_damage_index.has_value()) { + // This is a special case where an overlay candidate may have damage but it + // does not have a damage index since it was not the only quad in the + // original surface. Here the |unassigned_surface_damage_| will contain all + // unassigned damage and we use it to conservatively estimate the damage for + // this quad. We limit the damage to the candidates quad rect in question. + gfx::RectF intersection = candidate.display_rect; + intersection.Intersect(gfx::RectF(unassigned_surface_damage_)); + return intersection; + } + + size_t overlay_damage_index = sqs->overlay_damage_index.value(); + // Invalid index. + if (overlay_damage_index >= surface_damage_rect_list_->size()) { + DCHECK(false); + return gfx::RectF(); + } + + return gfx::RectF((*surface_damage_rect_list_)[overlay_damage_index]); } } // namespace viz diff --git a/chromium/components/viz/service/display/overlay_candidate.h b/chromium/components/viz/service/display/overlay_candidate.h index 2585bd1f4a7..a205623d385 100644 --- a/chromium/components/viz/service/display/overlay_candidate.h +++ b/chromium/components/viz/service/display/overlay_candidate.h @@ -9,6 +9,7 @@ #include <vector> #include "base/containers/flat_map.h" +#include "base/memory/raw_ptr.h" #include "build/build_config.h" #include "components/viz/common/quads/aggregated_render_pass.h" #include "components/viz/common/quads/tile_draw_quad.h" @@ -60,17 +61,6 @@ class VIZ_SERVICE_EXPORT OverlayCandidate { using TrackingId = uint32_t; static constexpr TrackingId kDefaultTrackingId{0}; - // Returns true and fills in |candidate| if |draw_quad| is of a known quad - // type and contains an overlayable resource. |primary_rect| can be empty in - // the case of a null primary plane. - static CandidateStatus FromDrawQuad( - DisplayResourceProvider* resource_provider, - SurfaceDamageRectList* surface_damage_rect_list, - const SkM44& output_color_matrix, - const DrawQuad* quad, - const gfx::RectF& primary_rect, - OverlayCandidate* candidate, - bool is_delegated_context = false); // Returns true if |quad| will not block quads underneath from becoming // an overlay. static bool IsInvisibleQuad(const DrawQuad* quad); @@ -81,25 +71,11 @@ class VIZ_SERVICE_EXPORT OverlayCandidate { QuadList::ConstIterator quad_list_begin, QuadList::ConstIterator quad_list_end); - // Returns an estimate of this |quad|'s actual visible damage area. This - // visible damage is computed by combining from input - // |surface_damage_rect_list| with the occluding rects in the quad_list. - // This is an estimate since the occluded damage area is calculated on a per - // quad basis. - static int EstimateVisibleDamage( - const DrawQuad* quad, - SurfaceDamageRectList* surface_damage_rect_list, - QuadList::ConstIterator quad_list_begin, - QuadList::ConstIterator quad_list_end); - - // Returns true if any of the quads in the list given by |quad_list_begin| - // and |quad_list_end| have a filter associated and occlude |candidate|. - static bool IsOccludedByFilteredQuad( - const OverlayCandidate& candidate, - QuadList::ConstIterator quad_list_begin, - QuadList::ConstIterator quad_list_end, - const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>& - render_pass_backdrop_filters); + // Modifies the |candidate|'s |display_rect| to be clipped within |clip_rect|. + // This function will also update the |uv_rect| based on what clipping was + // applied to |display_rect|. + static void ApplyClip(OverlayCandidate& candidate, + const gfx::RectF& clip_rect); // Returns true if the |quad| cannot be displayed on the main plane. This is // used in conjuction with protected content that can't be GPU composited and @@ -163,7 +139,7 @@ class VIZ_SERVICE_EXPORT OverlayCandidate { // The total area in square pixels of damage for this candidate's quad. This // is an estimate when 'EstimateOccludedDamage' function is used. - int damage_area_estimate = 0; + float damage_area_estimate = 0.f; // Damage in buffer space (extents bound by |resource_size_in_pixels|). gfx::RectF damage_rect; @@ -175,8 +151,12 @@ class VIZ_SERVICE_EXPORT OverlayCandidate { // Is true if an HW overlay is required for the quad content. bool requires_overlay = false; - // for solid color quads only - absl::optional<SkColor> solid_color; + // Represents either a background of this overlay candidate or a color of a + // solid color quad, which can be checked via the |is_solid_color|. + absl::optional<SkColor> color; + + // Helps to identify whether this is a solid color quad or not. + bool is_solid_color = false; // If |rpdq| is present, then the renderer must draw the filter effects and // copy the result into the buffer backing of a render pass. @@ -207,72 +187,103 @@ class VIZ_SERVICE_EXPORT OverlayCandidate { // surface and have the same |DrawQuad::rect| they will have the same // |tracking_id|. TrackingId tracking_id = kDefaultTrackingId; - - private: - static CandidateStatus FromDrawQuadResource( - DisplayResourceProvider* resource_provider, - SurfaceDamageRectList* surface_damage_rect_list, - const DrawQuad* quad, - ResourceId resource_id, - bool y_flipped, - OverlayCandidate* candidate, - bool is_delegated_context, - const gfx::RectF& primary_rect); - - static CandidateStatus FromTextureQuad( - DisplayResourceProvider* resource_provider, - SurfaceDamageRectList* surface_damage_rect_list, - const TextureDrawQuad* quad, - const gfx::RectF& primary_rect, - OverlayCandidate* candidate, - bool is_delegated_context); - - static CandidateStatus FromTileQuad( - DisplayResourceProvider* resource_provider, - SurfaceDamageRectList* surface_damage_rect_list, - const TileDrawQuad* quad, - const gfx::RectF& primary_rect, - OverlayCandidate* candidate); - - static CandidateStatus FromAggregateQuad( - DisplayResourceProvider* resource_provider, - SurfaceDamageRectList* surface_damage_rect_list, - const AggregatedRenderPassDrawQuad* quad, - const gfx::RectF& primary_rect, - OverlayCandidate* candidate); - - static CandidateStatus FromSolidColorQuad( - DisplayResourceProvider* resource_provider, - SurfaceDamageRectList* surface_damage_rect_list, - const SolidColorDrawQuad* quad, - const gfx::RectF& primary_rect, - OverlayCandidate* candidate); - - static CandidateStatus FromStreamVideoQuad( - DisplayResourceProvider* resource_provider, - SurfaceDamageRectList* surface_damage_rect_list, - const StreamVideoDrawQuad* quad, - OverlayCandidate* candidate, - bool is_delegated_context, - const gfx::RectF& primary_rect); - - static CandidateStatus FromVideoHoleQuad( - DisplayResourceProvider* resource_provider, - SurfaceDamageRectList* surface_damage_rect_list, - const VideoHoleDrawQuad* quad, - OverlayCandidate* candidate); - static void HandleClipAndSubsampling(OverlayCandidate* candidate, - const gfx::RectF& primary_rect); - static void AssignDamage(const DrawQuad* quad, - SurfaceDamageRectList* surface_damage_rect_list, - OverlayCandidate* candidate); - - static void ApplyClip(OverlayCandidate* candidate, - const gfx::RectF& clip_rect); }; using OverlayCandidateList = std::vector<OverlayCandidate>; +// This is a factory to help with the creation of |OverlayCandidates|. On +// construction, this factory captures the required objects to create candidates +// from a draw quad. Common computations for all possible candidates can be +// made at construction time. This class is const after construction and not +// copy/moveable to avoid capture ownership issues. +class VIZ_SERVICE_EXPORT OverlayCandidateFactory { + public: + using CandidateStatus = OverlayCandidate::CandidateStatus; + + OverlayCandidateFactory(const AggregatedRenderPass* render_pass, + DisplayResourceProvider* resource_provider, + const SurfaceDamageRectList* surface_damage_rect_list, + const SkM44* output_color_matrix, + const gfx::RectF primary_rect, + bool is_delegated_context = false); + + OverlayCandidateFactory(const OverlayCandidateFactory&) = delete; + OverlayCandidateFactory& operator=(const OverlayCandidateFactory&) = delete; + + ~OverlayCandidateFactory(); + + // Returns |kSuccess| and fills in |candidate| if |draw_quad| is of a known + // quad type and contains an overlayable resource. |primary_rect| can be empty + // in the case of a null primary plane. |candidate| is expected to be a + // freshly constructed |OverlayCandidate| object. + CandidateStatus FromDrawQuad(const DrawQuad* quad, + OverlayCandidate& candidate) const; + + // Returns an estimate of this |quad|'s actual visible damage area as float + // pixels squared. This visible damage is computed by combining from input + // |surface_damage_rect_list_| with the occluding rects in the quad_list. This + // is an estimate since the occluded damage area is calculated on a per quad + // basis. The |quad_list_begin| and |quad_list_end| provide the range of valid + // occluders of this |candidate|. + // TODO(petermcneeley): Can we replace this with |visible_rect| in |DrawQuad|? + float EstimateVisibleDamage(const DrawQuad* quad, + const OverlayCandidate& candidate, + QuadList::ConstIterator quad_list_begin, + QuadList::ConstIterator quad_list_end) const; + + // Returns true if any of the quads in the list given by |quad_list_begin| + // and |quad_list_end| have an associated filter and occlude |candidate|. + bool IsOccludedByFilteredQuad( + const OverlayCandidate& candidate, + QuadList::ConstIterator quad_list_begin, + QuadList::ConstIterator quad_list_end, + const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>& + render_pass_backdrop_filters) const; + + private: + CandidateStatus FromDrawQuadResource(const DrawQuad* quad, + ResourceId resource_id, + bool y_flipped, + OverlayCandidate& candidate) const; + + CandidateStatus FromTextureQuad(const TextureDrawQuad* quad, + OverlayCandidate& candidate) const; + + CandidateStatus FromTileQuad(const TileDrawQuad* quad, + OverlayCandidate& candidate) const; + + CandidateStatus FromAggregateQuad(const AggregatedRenderPassDrawQuad* quad, + OverlayCandidate& candidate) const; + + CandidateStatus FromSolidColorQuad(const SolidColorDrawQuad* quad, + OverlayCandidate& candidate) const; + + CandidateStatus FromStreamVideoQuad(const StreamVideoDrawQuad* quad, + OverlayCandidate& candidate) const; + + CandidateStatus FromVideoHoleQuad(const VideoHoleDrawQuad* quad, + OverlayCandidate& candidate) const; + + void HandleClipAndSubsampling(OverlayCandidate& candidate) const; + + void AssignDamage(const DrawQuad* quad, OverlayCandidate& candidate) const; + + // Damage returned from this function is in target content space. + gfx::RectF GetDamageRect(const DrawQuad* quad, + const OverlayCandidate& candidate) const; + + raw_ptr<const AggregatedRenderPass> render_pass_; + raw_ptr<DisplayResourceProvider> resource_provider_; + raw_ptr<const SurfaceDamageRectList> surface_damage_rect_list_; + raw_ptr<const SkM44> output_color_matrix_; + const gfx::RectF primary_rect_; + bool is_delegated_context_; + + // The union of all surface damages that are not specifically assigned to a + // draw quad. + gfx::Rect unassigned_surface_damage_; +}; + } // namespace viz #endif // COMPONENTS_VIZ_SERVICE_DISPLAY_OVERLAY_CANDIDATE_H_ diff --git a/chromium/components/viz/service/display/overlay_dc_unittest.cc b/chromium/components/viz/service/display/overlay_dc_unittest.cc index d49769e18ef..8f7fd805e74 100644 --- a/chromium/components/viz/service/display/overlay_dc_unittest.cc +++ b/chromium/components/viz/service/display/overlay_dc_unittest.cc @@ -4,6 +4,7 @@ #include <stddef.h> +#include <memory> #include <utility> #include <vector> @@ -22,14 +23,15 @@ #include "components/viz/common/quads/stream_video_draw_quad.h" #include "components/viz/common/quads/texture_draw_quad.h" #include "components/viz/common/quads/video_hole_draw_quad.h" +#include "components/viz/common/quads/yuv_video_draw_quad.h" #include "components/viz/common/resources/transferable_resource.h" #include "components/viz/service/display/dc_layer_overlay.h" -#include "components/viz/service/display/display_resource_provider_gl.h" -#include "components/viz/service/display/gl_renderer.h" +#include "components/viz/service/display/display_resource_provider_skia.h" #include "components/viz/service/display/output_surface.h" #include "components/viz/service/display/output_surface_client.h" #include "components/viz/service/display/output_surface_frame.h" #include "components/viz/service/display/overlay_processor_win.h" +#include "components/viz/test/fake_skia_output_surface.h" #include "components/viz/test/test_context_provider.h" #include "components/viz/test/test_gles2_interface.h" #include "gpu/config/gpu_finch_features.h" @@ -48,47 +50,21 @@ namespace { const gfx::Rect kOverlayRect(0, 0, 256, 256); const gfx::Rect kOverlayBottomRightRect(128, 128, 128, 128); -class OverlayOutputSurface : public OutputSurface { +class MockDCLayerOutputSurface : public FakeSkiaOutputSurface { public: - explicit OverlayOutputSurface( - scoped_refptr<TestContextProvider> context_provider) - : OutputSurface(std::move(context_provider)) { + static std::unique_ptr<MockDCLayerOutputSurface> Create() { + auto provider = TestContextProvider::Create(); + provider->BindToCurrentThread(); + return std::make_unique<MockDCLayerOutputSurface>(std::move(provider)); + } + + explicit MockDCLayerOutputSurface(scoped_refptr<ContextProvider> provider) + : FakeSkiaOutputSurface(std::move(provider)) { capabilities_.supports_dc_layers = true; } // OutputSurface implementation. - void BindToClient(OutputSurfaceClient* client) override {} - void EnsureBackbuffer() override {} - void DiscardBackbuffer() override {} - void BindFramebuffer() override { bind_framebuffer_count_ += 1; } - void SetDrawRectangle(const gfx::Rect& rect) override {} MOCK_METHOD1(SetEnableDCLayers, void(bool)); - void Reshape(const gfx::Size& size, - float device_scale_factor, - const gfx::ColorSpace& color_space, - gfx::BufferFormat format, - bool use_stencil) override {} - void SwapBuffers(OutputSurfaceFrame frame) override {} - uint32_t GetFramebufferCopyTextureFormat() override { - // TestContextProvider has no real framebuffer, just use RGB. - return GL_RGB; - } - bool HasExternalStencilTest() const override { return false; } - void ApplyExternalStencil() override {} - bool IsDisplayedAsOverlayPlane() const override { return false; } - unsigned GetOverlayTextureId() const override { return 10000; } - unsigned UpdateGpuFence() override { return 0; } - void SetUpdateVSyncParametersCallback( - UpdateVSyncParametersCallback callback) override {} - void SetDisplayTransformHint(gfx::OverlayTransform transform) override {} - gfx::OverlayTransform GetDisplayTransform() override { - return gfx::OVERLAY_TRANSFORM_NONE; - } - - unsigned bind_framebuffer_count() const { return bind_framebuffer_count_; } - - private: - unsigned bind_framebuffer_count_ = 0; }; class DCTestOverlayProcessor : public OverlayProcessorWin { @@ -223,13 +199,12 @@ SkM44 GetIdentityColorMatrix() { class DCLayerOverlayTest : public testing::Test { protected: void SetUp() override { - provider_ = TestContextProvider::Create(); - provider_->BindToCurrentThread(); - output_surface_ = std::make_unique<OverlayOutputSurface>(provider_); - output_surface_->BindToClient(&client_); + output_surface_ = MockDCLayerOutputSurface::Create(); + output_surface_->BindToClient(&output_surface_client_); - resource_provider_ = - std::make_unique<DisplayResourceProviderGL>(provider_.get()); + resource_provider_ = std::make_unique<DisplayResourceProviderSkia>(); + lock_set_for_external_use_.emplace(resource_provider_.get(), + output_surface_.get()); child_provider_ = TestContextProvider::Create(); child_provider_->BindToCurrentThread(); @@ -247,15 +222,16 @@ class DCLayerOverlayTest : public testing::Test { child_resource_provider_->ShutdownAndReleaseAllResources(); child_resource_provider_ = nullptr; child_provider_ = nullptr; + lock_set_for_external_use_.reset(); resource_provider_ = nullptr; output_surface_ = nullptr; - provider_ = nullptr; } - scoped_refptr<TestContextProvider> provider_; - std::unique_ptr<OverlayOutputSurface> output_surface_; - cc::FakeOutputSurfaceClient client_; - std::unique_ptr<DisplayResourceProviderGL> resource_provider_; + std::unique_ptr<MockDCLayerOutputSurface> output_surface_; + cc::FakeOutputSurfaceClient output_surface_client_; + std::unique_ptr<DisplayResourceProviderSkia> resource_provider_; + absl::optional<DisplayResourceProviderSkia::LockSetForExternalUse> + lock_set_for_external_use_; scoped_refptr<TestContextProvider> child_provider_; std::unique_ptr<ClientResourceProvider> child_resource_provider_; std::unique_ptr<OverlayProcessorWin> overlay_processor_; @@ -310,7 +286,6 @@ TEST_F(DCLayerOverlayTest, Occluded) { &damage_rect_, &content_bounds_); EXPECT_EQ(2U, dc_layer_list.size()); - EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); EXPECT_EQ(-1, dc_layer_list.front().z_order); EXPECT_EQ(-2, dc_layer_list.back().z_order); // Entire underlay rect must be redrawn. @@ -361,7 +336,6 @@ TEST_F(DCLayerOverlayTest, Occluded) { &damage_rect_, &content_bounds_); EXPECT_EQ(2U, dc_layer_list.size()); - EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); EXPECT_EQ(-1, dc_layer_list.front().z_order); EXPECT_EQ(-2, dc_layer_list.back().z_order); @@ -412,7 +386,6 @@ TEST_F(DCLayerOverlayTest, DamageRectWithoutVideoDamage) { std::move(surface_damage_rect_list), nullptr, &dc_layer_list, &damage_rect_, &content_bounds_); EXPECT_EQ(1U, dc_layer_list.size()); - EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); EXPECT_EQ(-1, dc_layer_list.back().z_order); // All rects must be redrawn at the first frame. EXPECT_EQ(gfx::Rect(0, 0, 230, 230), damage_rect_); @@ -456,7 +429,6 @@ TEST_F(DCLayerOverlayTest, DamageRectWithoutVideoDamage) { std::move(surface_damage_rect_list), nullptr, &dc_layer_list, &damage_rect_, &content_bounds_); EXPECT_EQ(1U, dc_layer_list.size()); - EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); EXPECT_EQ(-1, dc_layer_list.back().z_order); // Only the non-overlay damaged rect need to be drawn by the gl compositor EXPECT_EQ(gfx::Rect(210, 210, 20, 20), damage_rect_); @@ -486,7 +458,6 @@ TEST_F(DCLayerOverlayTest, DamageRect) { std::move(surface_damage_rect_list), nullptr, &dc_layer_list, &damage_rect_, &content_bounds_); EXPECT_EQ(1U, dc_layer_list.size()); - EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); EXPECT_EQ(1, dc_layer_list.back().z_order); // Damage rect should be unchanged on initial frame because of resize, but // should be empty on the second frame because everything was put in a @@ -613,7 +584,6 @@ TEST_F(DCLayerOverlayTest, UnderlayDamageRectWithQuadOnTopUnchanged) { std::move(surface_damage_rect_list), nullptr, &dc_layer_list, &damage_rect_, &content_bounds_); EXPECT_EQ(1U, dc_layer_list.size()); - EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); EXPECT_EQ(-1, dc_layer_list.back().z_order); // Damage rect should be unchanged on initial frame, but should be reduced // to the size of quad on top, and empty on the third frame. @@ -675,7 +645,7 @@ TEST_F(DCLayerOverlayTest, RoundedCorners) { // rounded corner mask filter for the replaced solid quad. EXPECT_EQ(replaced_sqs->blend_mode, SkBlendMode::kDstOut); EXPECT_EQ(SolidColorDrawQuad::MaterialCast(replaced_quad)->color, - SK_ColorBLACK); + SkColors::kBlack); EXPECT_TRUE(replaced_sqs->mask_filter_info.HasRoundedCorners()); // The whole frame is damaged. @@ -733,7 +703,7 @@ TEST_F(DCLayerOverlayTest, RoundedCorners) { // rounded corner mask filter for the replaced solid quad. EXPECT_EQ(replaced_sqs->blend_mode, SkBlendMode::kDstOut); EXPECT_EQ(SolidColorDrawQuad::MaterialCast(replaced_quad)->color, - SK_ColorBLACK); + SkColors::kBlack); EXPECT_TRUE(replaced_sqs->mask_filter_info.HasRoundedCorners()); // Only the UI is damaged. @@ -791,7 +761,7 @@ TEST_F(DCLayerOverlayTest, RoundedCorners) { // solid quad. EXPECT_EQ(replaced_sqs->blend_mode, SkBlendMode::kDstOut); EXPECT_EQ(SolidColorDrawQuad::MaterialCast(replaced_quad)->color, - SK_ColorBLACK); + SkColors::kBlack); EXPECT_TRUE(replaced_sqs->mask_filter_info.HasRoundedCorners()); // Zero root damage rect. @@ -839,7 +809,6 @@ TEST_F(DCLayerOverlayTest, MultipleYUVOverlay) { // Skip overlays. EXPECT_EQ(0U, dc_layer_list.size()); - EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); EXPECT_EQ(gfx::Rect(0, 0, 220, 220), damage_rect_); // Check whether all 3 quads including two YUV quads are still in the render @@ -887,7 +856,6 @@ TEST_F(DCLayerOverlayTest, SetEnableDCLayers) { &damage_rect_, &content_bounds_); EXPECT_EQ(1U, dc_layer_list.size()); - EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); EXPECT_EQ(1, dc_layer_list.back().z_order); EXPECT_EQ(damage_rect_, expected_damage); @@ -932,7 +900,6 @@ TEST_F(DCLayerOverlayTest, SetEnableDCLayers) { &damage_rect_, &content_bounds_); EXPECT_EQ(0u, dc_layer_list.size()); - EXPECT_EQ(0u, output_surface_->bind_framebuffer_count()); EXPECT_EQ(damage_rect_, expected_damage); Mock::VerifyAndClearExpectations(output_surface_.get()); diff --git a/chromium/components/viz/service/display/overlay_processor_delegated.cc b/chromium/components/viz/service/display/overlay_processor_delegated.cc index ca130dab48b..b124668c2fa 100644 --- a/chromium/components/viz/service/display/overlay_processor_delegated.cc +++ b/chromium/components/viz/service/display/overlay_processor_delegated.cc @@ -100,9 +100,11 @@ OverlayProcessorDelegated::OverlayProcessorDelegated( OverlayProcessorDelegated::~OverlayProcessorDelegated() = default; +DBG_FLAG_FBOOL("delegated.enable.quad_split", quad_split) + bool OverlayProcessorDelegated::DisableSplittingQuads() const { - // If there is quads to split these will happen delegee side. - return true; + // This determines if we will split quads on delegation or on delegee side. + return !quad_split(); } constexpr size_t kTooManyQuads = 64; @@ -132,6 +134,11 @@ bool OverlayProcessorDelegated::AttemptWithStrategies( !render_pass_backdrop_filters.empty()) return false; + OverlayCandidateFactory candidate_factory = OverlayCandidateFactory( + render_pass, resource_provider, surface_damage_rect_list, + &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane), + is_delegated_context); + std::vector<QuadList::Iterator> candidate_quads; int num_quads_skipped = 0; for (auto it = quad_list->begin(); it != quad_list->end(); ++it) { @@ -144,10 +151,7 @@ bool OverlayProcessorDelegated::AttemptWithStrategies( gfx::Vector2dF(display_rect.origin().x(), display_rect.origin().y()), base::StringPrintf("m=%d rid=%d", static_cast<int>(it->material), it->resources.begin()->value())); - auto candidate_status = OverlayCandidate::FromDrawQuad( - resource_provider, surface_damage_rect_list, output_color_matrix, *it, - GetPrimaryPlaneDisplayRect(primary_plane), &candidate, - is_delegated_context); + auto candidate_status = candidate_factory.FromDrawQuad(*it, candidate); if (candidate_status == OverlayCandidate::CandidateStatus::kSuccess) { if (it->material == DrawQuad::Material::kSolidColor) { DBG_DRAW_RECT("delegated.overlay.color", candidate.display_rect); @@ -196,6 +200,8 @@ bool OverlayProcessorDelegated::AttemptWithStrategies( candidates->clear(); delegated_status_ = DelegationStatus::kCompositedCheckOverlayFail; DBG_DRAW_RECT("delegated.handled.failed", each.display_rect); + DBG_LOG("delegated.handled.failed", "Handled failed %s", + each.display_rect.ToString().c_str()); return false; } } diff --git a/chromium/components/viz/service/display/overlay_processor_interface.h b/chromium/components/viz/service/display/overlay_processor_interface.h index 22cb824f084..2d4c5ff010c 100644 --- a/chromium/components/viz/service/display/overlay_processor_interface.h +++ b/chromium/components/viz/service/display/overlay_processor_interface.h @@ -33,6 +33,10 @@ namespace cc { class DisplayResourceProvider; } +namespace gpu { +class SharedImageInterface; +} + namespace viz { struct DebugRendererSettings; class OutputSurface; @@ -95,9 +99,6 @@ class VIZ_SERVICE_EXPORT OverlayProcessorInterface { // Opacity of the overlay independent of buffer alpha. When rendered: // src-alpha = |opacity| * buffer-component-alpha. float opacity; - // TODO(weiliangc): Should be replaced by SharedImage mailbox. - // Gpu fence to wait for before overlay is ready for display. - unsigned gpu_fence_id; // Mailbox corresponding to the buffer backing the primary plane. gpu::Mailbox mailbox; // Hints for overlay prioritization. diff --git a/chromium/components/viz/service/display/overlay_processor_ozone.cc b/chromium/components/viz/service/display/overlay_processor_ozone.cc index ead3abb6834..3ae5364b310 100644 --- a/chromium/components/viz/service/display/overlay_processor_ozone.cc +++ b/chromium/components/viz/service/display/overlay_processor_ozone.cc @@ -61,6 +61,9 @@ void ConvertToOzoneOverlaySurface( ozone_candidate->requires_overlay = overlay_candidate.requires_overlay; ozone_candidate->priority_hint = overlay_candidate.priority_hint; ozone_candidate->rounded_corners = overlay_candidate.rounded_corners; + // That can be a solid color quad. + if (!overlay_candidate.is_solid_color) + ozone_candidate->background_color = overlay_candidate.color; } uint32_t MailboxToUInt32(const gpu::Mailbox& mailbox) { @@ -68,13 +71,6 @@ uint32_t MailboxToUInt32(const gpu::Mailbox& mailbox) { (mailbox.name[2] << 8) + mailbox.name[3]; } -void ReportSharedImageExists(bool exists) { - UMA_HISTOGRAM_BOOLEAN( - "Compositing.Display.OverlayProcessorOzone." - "SharedImageExists", - exists); -} - #if BUILDFLAG(IS_CHROMEOS_ASH) bool AllowColorSpaceCombination( const gfx::ColorSpace& source_color_space, @@ -132,7 +128,6 @@ OverlayProcessorOzone::OverlayProcessorOzone( NOTREACHED(); } } - MaybeObserveHardwareCapabilities(); } OverlayProcessorOzone::~OverlayProcessorOzone() = default; @@ -148,6 +143,8 @@ bool OverlayProcessorOzone::NeedsSurfaceDamageRectList() const { void OverlayProcessorOzone::CheckOverlaySupportImpl( const OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane, OverlayCandidateList* surfaces) { + MaybeObserveHardwareCapabilities(); + auto full_size = surfaces->size(); if (primary_plane) full_size += 1; @@ -256,27 +253,42 @@ void OverlayProcessorOzone::CheckOverlaySupportImpl( } void OverlayProcessorOzone::MaybeObserveHardwareCapabilities() { + if (tried_observing_hardware_capabilities_) { + return; + } + tried_observing_hardware_capabilities_ = true; + // HardwareCapabilities isn't necessary unless attempting multiple overlays. if (max_overlays_config_ <= 1) { return; } - overlay_candidates_->ObserveHardwareCapabilities( - base::BindRepeating(&OverlayProcessorOzone::ReceiveHardwareCapabilities, - weak_ptr_factory_.GetWeakPtr())); + if (overlay_candidates_) { + overlay_candidates_->ObserveHardwareCapabilities( + base::BindRepeating(&OverlayProcessorOzone::ReceiveHardwareCapabilities, + weak_ptr_factory_.GetWeakPtr())); + } } void OverlayProcessorOzone::ReceiveHardwareCapabilities( ui::HardwareCapabilities hardware_capabilities) { - // Subtract 1 because one of these overlay capable planes will be needed for - // the primary plane. - int max_overlays_supported = - hardware_capabilities.num_overlay_capable_planes - 1; - max_overlays_considered_ = - std::min(max_overlays_supported, max_overlays_config_); - - UMA_HISTOGRAM_COUNTS_100( - "Compositing.Display.OverlayProcessorOzone.MaxOverlaysSupported", - max_overlays_supported); + UMA_HISTOGRAM_BOOLEAN( + "Compositing.Display.OverlayProcessorOzone.HardwareCapabilitiesIsValid", + hardware_capabilities.is_valid); + if (hardware_capabilities.is_valid) { + // Subtract 1 because one of these overlay capable planes will be needed for + // the primary plane. + int max_overlays_supported = + hardware_capabilities.num_overlay_capable_planes - 1; + max_overlays_considered_ = + std::min(max_overlays_supported, max_overlays_config_); + + UMA_HISTOGRAM_COUNTS_100( + "Compositing.Display.OverlayProcessorOzone.MaxPlanesSupported", + hardware_capabilities.num_overlay_capable_planes); + } else { + // Default to attempting 1 overlay if we get an invalid response. + max_overlays_considered_ = 1; + } // Different hardware capabilities may mean a different result for a specific // combination of overlays, so clear this cache. @@ -300,11 +312,6 @@ bool OverlayProcessorOzone::SetNativePixmapForCandidate( bool is_primary) { DCHECK(shared_image_interface_); - UMA_HISTOGRAM_BOOLEAN( - "Compositing.Display.OverlayProcessorOzone." - "IsCandidateSharedImage", - mailbox.IsSharedImage()); - if (!mailbox.IsSharedImage()) return false; @@ -318,10 +325,8 @@ bool OverlayProcessorOzone::SetNativePixmapForCandidate( // candidate. We will try again next frame. DLOG(ERROR) << "Unable to find the NativePixmap corresponding to the " "overlay candidate"; - ReportSharedImageExists(false); return false; } - ReportSharedImageExists(true); if (is_primary && (candidate->buffer_size != native_pixmap->GetBufferSize() || candidate->format != native_pixmap->GetBufferFormat())) { diff --git a/chromium/components/viz/service/display/overlay_processor_ozone.h b/chromium/components/viz/service/display/overlay_processor_ozone.h index 5809388e329..b0ecacb1bb9 100644 --- a/chromium/components/viz/service/display/overlay_processor_ozone.h +++ b/chromium/components/viz/service/display/overlay_processor_ozone.h @@ -56,9 +56,11 @@ class VIZ_SERVICE_EXPORT OverlayProcessorOzone const gpu::Mailbox& mailbox, bool is_primary); + bool tried_observing_hardware_capabilities_ = false; std::unique_ptr<ui::OverlayCandidatesOzone> overlay_candidates_; const std::vector<OverlayStrategy> available_strategies_; gpu::SharedImageInterface* const shared_image_interface_; + base::WeakPtrFactory<OverlayProcessorOzone> weak_ptr_factory_{this}; }; } // namespace viz diff --git a/chromium/components/viz/service/display/overlay_processor_ozone_unittest.cc b/chromium/components/viz/service/display/overlay_processor_ozone_unittest.cc index b8b5d5d914b..d8c9251261a 100644 --- a/chromium/components/viz/service/display/overlay_processor_ozone_unittest.cc +++ b/chromium/components/viz/service/display/overlay_processor_ozone_unittest.cc @@ -261,22 +261,29 @@ class TestOverlayProcessorOzone : public OverlayProcessorOzone { }; TEST(OverlayProcessorOzoneTest, ObserveHardwareCapabilites) { + OverlayCandidateList candidates; // Enable 4 overlays const std::vector<base::test::ScopedFeatureList::FeatureAndParams> feature_and_params_list = {{features::kEnableOverlayPrioritization, {}}, {features::kUseMultipleOverlays, {{features::kMaxOverlaysParam, "4"}}}}; - base::test::ScopedFeatureList features; - features.InitWithFeaturesAndParameters(feature_and_params_list, {}); + base::test::ScopedFeatureList scoped_features; + scoped_features.InitWithFeaturesAndParameters(feature_and_params_list, {}); + // When overlay prioritization is explicitly disabled (Lacros) we should + // skip multiple overlays tests. + if (!features::IsOverlayPrioritizationEnabled()) { + GTEST_SKIP(); + } auto fake_candidates_unique = std::make_unique<FakeOverlayCandidatesOzone>(); auto* fake_candidates = fake_candidates_unique.get(); + TestOverlayProcessorOzone processor(std::move(fake_candidates_unique), {}, + nullptr); // No receive_callback yet. EXPECT_TRUE(fake_candidates->receive_callback().is_null()); - TestOverlayProcessorOzone processor(std::move(fake_candidates_unique), {}, - nullptr); + processor.CheckOverlaySupport(nullptr, &candidates); // Receive callback is set. EXPECT_FALSE(fake_candidates->receive_callback().is_null()); @@ -284,32 +291,44 @@ TEST(OverlayProcessorOzoneTest, ObserveHardwareCapabilites) { EXPECT_EQ(processor.MaxOverlaysConsidered(), 1); ui::HardwareCapabilities hc; + hc.is_valid = true; hc.num_overlay_capable_planes = 6; fake_candidates->receive_callback().Run(hc); // Uses max_overlays_config_ = 4. EXPECT_EQ(processor.MaxOverlaysConsidered(), 4); + hc.is_valid = true; hc.num_overlay_capable_planes = 4; fake_candidates->receive_callback().Run(hc); // Uses (num_overlay_capable_planes - 1) = 3. EXPECT_EQ(processor.MaxOverlaysConsidered(), 3); + + hc.is_valid = false; + hc.num_overlay_capable_planes = 0; + fake_candidates->receive_callback().Run(hc); + + // Defaults to 1 overlay when receiving an invalid response. + EXPECT_EQ(processor.MaxOverlaysConsidered(), 1); } TEST(OverlayProcessorOzoneTest, NoObserveHardwareCapabilites) { + OverlayCandidateList candidates; // Multiple overlays disabled. - base::test::ScopedFeatureList features; - features.InitAndDisableFeature(features::kUseMultipleOverlays); + base::test::ScopedFeatureList scoped_features; + scoped_features.InitAndDisableFeature(features::kUseMultipleOverlays); auto fake_candidates_unique = std::make_unique<FakeOverlayCandidatesOzone>(); auto* fake_candidates = fake_candidates_unique.get(); + OverlayProcessorOzone processor(std::move(fake_candidates_unique), {}, + nullptr); + // No receive_callback yet. EXPECT_TRUE(fake_candidates->receive_callback().is_null()); - TestOverlayProcessorOzone processor(std::move(fake_candidates_unique), {}, - nullptr); + processor.CheckOverlaySupport(nullptr, &candidates); // Receive callback is still unset because multiple overlays is disabled. EXPECT_TRUE(fake_candidates->receive_callback().is_null()); 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 e85914bee01..b42bc066367 100644 --- a/chromium/components/viz/service/display/overlay_processor_using_strategy.cc +++ b/chromium/components/viz/service/display/overlay_processor_using_strategy.cc @@ -47,7 +47,7 @@ namespace { // or why we aren't. enum class AttemptingMultipleOverlays { kYes = 0, - kNoTooFewMaxOverlaysConsidered = 1, + kNoFeatureDisabled = 1, kNoRequiredOverlay = 2, kNoUnsupportedStrategy = 3, kMaxValue = kNoUnsupportedStrategy, @@ -56,10 +56,10 @@ enum class AttemptingMultipleOverlays { constexpr char kShouldAttemptMultipleOverlaysHistogramName[] = "Compositing.Display.OverlayProcessorUsingStrategy." "ShouldAttemptMultipleOverlays"; -constexpr char kNumOverlaysAttemptedHistogramName[] = - "Compositing.Display.OverlayProcessorUsingStrategy.NumOverlaysAttempted"; constexpr char kNumOverlaysPromotedHistogramName[] = "Compositing.Display.OverlayProcessorUsingStrategy.NumOverlaysPromoted"; +constexpr char kNumOverlaysAttemptedHistogramName[] = + "Compositing.Display.OverlayProcessorUsingStrategy.NumOverlaysAttempted"; constexpr char kNumOverlaysFailedHistogramName[] = "Compositing.Display.OverlayProcessorUsingStrategy.NumOverlaysFailed"; @@ -161,6 +161,10 @@ void OverlayProcessorUsingStrategy::ProcessForOverlays( auto* render_pass = render_passes->back().get(); bool success = false; + UMA_HISTOGRAM_COUNTS_1000( + "Compositing.Display.OverlayProcessorUsingStrategy.NumQuadsConsidered", + render_pass->quad_list.size()); + DBG_DRAW_RECT("overlay.incoming.damage", (*damage_rect)); for (auto&& each : surface_damage_rect_list) { DBG_DRAW_RECT("overlay.surface.damage", each); @@ -185,6 +189,8 @@ void OverlayProcessorUsingStrategy::ProcessForOverlays( LogCheckOverlaySupportMetrics(); DCHECK(candidates->empty() || success); + UMA_HISTOGRAM_COUNTS_100(kNumOverlaysPromotedHistogramName, + candidates->size()); UpdateOverlayStatusMap(*candidates); UpdateDamageRect(surface_damage_rect_list, *damage_rect); @@ -413,7 +419,7 @@ void OverlayProcessorUsingStrategy::UpdateDamageRect( const auto& status = it.second; if (status.plane_z_order != 0) { RecordOverlayDamageRectHistograms(status.plane_z_order > 0, - status.damage_area_estimate != 0, + status.damage_area_estimate != 0.f, damage_rect.IsEmpty()); } } @@ -496,11 +502,11 @@ void OverlayProcessorUsingStrategy::SortProposedOverlayCandidatesPrioritized( // for low latency surfaces (inking like in the google keeps application). const bool force_update = it->candidate.overlay_damage_index != OverlayCandidate::kInvalidDamageIndex && - it->candidate.damage_area_estimate != 0; - track_data.AddRecord( - frame_sequence_number_, - static_cast<float>(it->candidate.damage_area_estimate) / display_area, - it->candidate.resource_id, tracker_config_, force_update); + it->candidate.damage_area_estimate != 0.f; + track_data.AddRecord(frame_sequence_number_, + it->candidate.damage_area_estimate / display_area, + it->candidate.resource_id, tracker_config_, + force_update); // Here a series of criteria are considered for wholesale rejection of a // candidate. The rational for rejection is usually power improvements but // this can indirectly reallocate limited overlay resources to another @@ -585,6 +591,11 @@ bool OverlayProcessorUsingStrategy::AttemptWithStrategiesPrioritized( num_proposed_pre_sort); SortProposedOverlayCandidatesPrioritized(&proposed_candidates); + if (proposed_candidates.size() == 0) { + LogStrategyEnumUMA(num_proposed_pre_sort != 0 + ? OverlayStrategy::kNoStrategyFailMin + : OverlayStrategy::kNoStrategyUsed); + } if (ShouldAttemptMultipleOverlays(proposed_candidates)) { auto* render_pass = render_pass_list->back().get(); @@ -665,11 +676,7 @@ bool OverlayProcessorUsingStrategy::AttemptWithStrategiesPrioritized( } RegisterOverlayRequirement(has_required_overlay); - if (proposed_candidates.size() == 0) { - LogStrategyEnumUMA(num_proposed_pre_sort != 0 - ? OverlayStrategy::kNoStrategyFailMin - : OverlayStrategy::kNoStrategyUsed); - } else { + if (proposed_candidates.size() != 0) { LogStrategyEnumUMA(OverlayStrategy::kNoStrategyAllFail); } OnOverlaySwitchUMA(ProposedCandidateKey()); @@ -678,10 +685,9 @@ bool OverlayProcessorUsingStrategy::AttemptWithStrategiesPrioritized( bool OverlayProcessorUsingStrategy::ShouldAttemptMultipleOverlays( const std::vector<OverlayProposedCandidate>& sorted_candidates) { - if (max_overlays_considered_ <= 1) { - UMA_HISTOGRAM_ENUMERATION( - kShouldAttemptMultipleOverlaysHistogramName, - AttemptingMultipleOverlays::kNoTooFewMaxOverlaysConsidered); + if (max_overlays_config_ <= 1) { + UMA_HISTOGRAM_ENUMERATION(kShouldAttemptMultipleOverlaysHistogramName, + AttemptingMultipleOverlays::kNoFeatureDisabled); return false; } @@ -718,6 +724,7 @@ bool OverlayProcessorUsingStrategy::AttemptMultipleOverlays( OverlayCandidateList& candidates) { if (sorted_candidates.empty()) { UMA_HISTOGRAM_COUNTS_100(kNumOverlaysAttemptedHistogramName, 0); + UMA_HISTOGRAM_COUNTS_100(kNumOverlaysFailedHistogramName, 0); return false; } @@ -796,12 +803,11 @@ bool OverlayProcessorUsingStrategy::AttemptMultipleOverlays( UMA_HISTOGRAM_COUNTS_100(kNumOverlaysAttemptedHistogramName, num_overlays_attempted); - UMA_HISTOGRAM_COUNTS_100(kNumOverlaysPromotedHistogramName, - num_overlays_promoted); UMA_HISTOGRAM_COUNTS_100(kNumOverlaysFailedHistogramName, num_overlays_attempted - num_overlays_promoted); if (candidates.empty()) { + LogStrategyEnumUMA(OverlayStrategy::kNoStrategyAllFail); return false; } @@ -825,6 +831,7 @@ bool OverlayProcessorUsingStrategy::AttemptMultipleOverlays( // Commit successful candidates. for (auto& test_candidate : test_candidates) { test_candidate.strategy->CommitCandidate(test_candidate, render_pass); + LogStrategyEnumUMA(test_candidate.strategy->GetUMAEnum()); } return true; 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 0a76a92ba9f..e355e7e624f 100644 --- a/chromium/components/viz/service/display/overlay_processor_using_strategy.h +++ b/chromium/components/viz/service/display/overlay_processor_using_strategy.h @@ -154,7 +154,7 @@ class VIZ_SERVICE_EXPORT OverlayProcessorUsingStrategy gfx::Rect overlay_rect; gfx::RectF damage_rect; uint32_t damage_index; - int damage_area_estimate; + float damage_area_estimate; bool has_mask_filter; int plane_z_order; bool is_underlay; diff --git a/chromium/components/viz/service/display/overlay_strategy_fullscreen.cc b/chromium/components/viz/service/display/overlay_strategy_fullscreen.cc index 9deafb078e0..8899c3e3b57 100644 --- a/chromium/components/viz/service/display/overlay_strategy_fullscreen.cc +++ b/chromium/components/viz/service/display/overlay_strategy_fullscreen.cc @@ -51,10 +51,11 @@ bool OverlayStrategyFullscreen::Attempt( return false; OverlayCandidate candidate; - if (OverlayCandidate::FromDrawQuad( - resource_provider, surface_damage_rect_list, output_color_matrix, - quad, GetPrimaryPlaneDisplayRect(primary_plane), - &candidate) != OverlayCandidate::CandidateStatus::kSuccess) { + OverlayCandidateFactory candidate_factory = OverlayCandidateFactory( + render_pass, resource_provider, surface_damage_rect_list, + &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane)); + if (candidate_factory.FromDrawQuad(quad, candidate) != + OverlayCandidate::CandidateStatus::kSuccess) { return false; } @@ -108,10 +109,11 @@ void OverlayStrategyFullscreen::ProposePrioritized( return; OverlayCandidate candidate; - if (OverlayCandidate::FromDrawQuad( - resource_provider, surface_damage_rect_list, output_color_matrix, - quad, GetPrimaryPlaneDisplayRect(primary_plane), - &candidate) != OverlayCandidate::CandidateStatus::kSuccess) { + OverlayCandidateFactory candidate_factory = OverlayCandidateFactory( + render_pass, resource_provider, surface_damage_rect_list, + &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane)); + if (candidate_factory.FromDrawQuad(quad, candidate) != + OverlayCandidate::CandidateStatus::kSuccess) { return; } diff --git a/chromium/components/viz/service/display/overlay_strategy_single_on_top.cc b/chromium/components/viz/service/display/overlay_strategy_single_on_top.cc index df36ded05b5..78612ba8c17 100644 --- a/chromium/components/viz/service/display/overlay_strategy_single_on_top.cc +++ b/chromium/components/viz/service/display/overlay_strategy_single_on_top.cc @@ -35,13 +35,15 @@ bool OverlayStrategySingleOnTop::Attempt( QuadList* quad_list = &render_pass->quad_list; // Build a list of candidates with the associated quad. OverlayCandidate best_candidate; + OverlayCandidateFactory candidate_factory = OverlayCandidateFactory( + render_pass, resource_provider, surface_damage_rect_list, + &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane)); + auto best_quad_it = quad_list->end(); for (auto it = quad_list->begin(); it != quad_list->end(); ++it) { OverlayCandidate candidate; - if (OverlayCandidate::FromDrawQuad( - resource_provider, surface_damage_rect_list, output_color_matrix, - *it, GetPrimaryPlaneDisplayRect(primary_plane), - &candidate) == OverlayCandidate::CandidateStatus::kSuccess && + if (candidate_factory.FromDrawQuad(*it, candidate) == + OverlayCandidate::CandidateStatus::kSuccess && !candidate.has_mask_filter && !OverlayCandidate::IsOccluded(candidate, quad_list->cbegin(), it)) { // If the candidate has been promoted previously and has not changed @@ -92,12 +94,14 @@ void OverlayStrategySingleOnTop::ProposePrioritized( auto* render_pass = render_pass_list->back().get(); QuadList* quad_list = &render_pass->quad_list; // Build a list of candidates with the associated quad. + OverlayCandidateFactory candidate_factory = OverlayCandidateFactory( + render_pass, resource_provider, surface_damage_rect_list, + &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane)); + for (auto it = quad_list->begin(); it != quad_list->end(); ++it) { OverlayCandidate candidate; - if (OverlayCandidate::FromDrawQuad( - resource_provider, surface_damage_rect_list, output_color_matrix, - *it, GetPrimaryPlaneDisplayRect(primary_plane), - &candidate) == OverlayCandidate::CandidateStatus::kSuccess && + if (candidate_factory.FromDrawQuad(*it, candidate) == + OverlayCandidate::CandidateStatus::kSuccess && !candidate.has_mask_filter && !OverlayCandidate::IsOccluded(candidate, quad_list->cbegin(), it)) { candidates->push_back({it, candidate, this}); diff --git a/chromium/components/viz/service/display/overlay_strategy_underlay.cc b/chromium/components/viz/service/display/overlay_strategy_underlay.cc index c7aa14df543..145eaffe805 100644 --- a/chromium/components/viz/service/display/overlay_strategy_underlay.cc +++ b/chromium/components/viz/service/display/overlay_strategy_underlay.cc @@ -36,13 +36,14 @@ bool OverlayStrategyUnderlay::Attempt( DCHECK(candidate_list->empty()); auto* render_pass = render_pass_list->back().get(); QuadList& quad_list = render_pass->quad_list; + auto candidate_factory = OverlayCandidateFactory( + render_pass, resource_provider, surface_damage_rect_list, + &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane)); for (auto it = quad_list.begin(); it != quad_list.end(); ++it) { OverlayCandidate candidate; - if (OverlayCandidate::FromDrawQuad( - resource_provider, surface_damage_rect_list, output_color_matrix, - *it, GetPrimaryPlaneDisplayRect(primary_plane), - &candidate) != OverlayCandidate::CandidateStatus::kSuccess || + if (candidate_factory.FromDrawQuad(*it, candidate) != + OverlayCandidate::CandidateStatus::kSuccess || (opaque_mode_ == OpaqueMode::RequireOpaqueCandidates && !candidate.is_opaque)) { continue; @@ -51,7 +52,7 @@ bool OverlayStrategyUnderlay::Attempt( // Filters read back the framebuffer to get the pixel values that need to // be filtered. This is a problem when there are hardware planes because // the planes are not composited until they are on the display controller. - if (OverlayCandidate::IsOccludedByFilteredQuad( + if (candidate_factory.IsOccludedByFilteredQuad( candidate, quad_list.begin(), it, render_pass_backdrop_filters)) { continue; } @@ -101,14 +102,14 @@ void OverlayStrategyUnderlay::ProposePrioritized( std::vector<gfx::Rect>* content_bounds) { auto* render_pass = render_pass_list->back().get(); QuadList& quad_list = render_pass->quad_list; + OverlayCandidateFactory candidate_factory = OverlayCandidateFactory( + render_pass, resource_provider, surface_damage_rect_list, + &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane)); for (auto it = quad_list.begin(); it != quad_list.end(); ++it) { OverlayCandidate candidate; - - if (OverlayCandidate::FromDrawQuad( - resource_provider, surface_damage_rect_list, output_color_matrix, - *it, GetPrimaryPlaneDisplayRect(primary_plane), - &candidate) != OverlayCandidate::CandidateStatus::kSuccess || + if (candidate_factory.FromDrawQuad(*it, candidate) != + OverlayCandidate::CandidateStatus::kSuccess || (opaque_mode_ == OpaqueMode::RequireOpaqueCandidates && !candidate.is_opaque)) { continue; @@ -120,13 +121,13 @@ void OverlayStrategyUnderlay::ProposePrioritized( // If we are requiring an overlay, then we should not block it due to this // condition. if (!candidate.requires_overlay && - OverlayCandidate::IsOccludedByFilteredQuad( + candidate_factory.IsOccludedByFilteredQuad( candidate, quad_list.begin(), it, render_pass_backdrop_filters)) { continue; } - candidate.damage_area_estimate = OverlayCandidate::EstimateVisibleDamage( - *it, surface_damage_rect_list, quad_list.begin(), it); + candidate.damage_area_estimate = candidate_factory.EstimateVisibleDamage( + *it, candidate, quad_list.begin(), it); candidates->push_back({it, candidate, this}); } 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 7746bf26d3d..db9fe19c0df 100644 --- a/chromium/components/viz/service/display/overlay_strategy_underlay_cast.cc +++ b/chromium/components/viz/service/display/overlay_strategy_underlay_cast.cc @@ -60,6 +60,10 @@ bool OverlayStrategyUnderlayCast::Attempt( QuadList& quad_list = render_pass->quad_list; bool found_underlay = false; gfx::Rect content_rect; + OverlayCandidateFactory candidate_factory = OverlayCandidateFactory( + render_pass, resource_provider, surface_damage_rect_list, + &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane)); + for (const auto* quad : base::Reversed(quad_list)) { if (OverlayCandidate::IsInvisibleQuad(quad)) continue; @@ -79,18 +83,15 @@ bool OverlayStrategyUnderlayCast::Attempt( // quad is supposed to be to replace it with a transparent quad to allow // the underlay to be visible. // VIDEO_HOLE implies it requires overlay. - is_underlay = - quad->material == DrawQuad::Material::kVideoHole && - OverlayCandidate::FromDrawQuad( - resource_provider, surface_damage_rect_list, output_color_matrix, - quad, GetPrimaryPlaneDisplayRect(primary_plane), - &candidate) == OverlayCandidate::CandidateStatus::kSuccess; + is_underlay = quad->material == DrawQuad::Material::kVideoHole && + candidate_factory.FromDrawQuad(quad, candidate) == + OverlayCandidate::CandidateStatus::kSuccess; found_underlay = is_underlay; } if (!found_underlay && quad->material == DrawQuad::Material::kSolidColor) { const SolidColorDrawQuad* solid = SolidColorDrawQuad::MaterialCast(quad); - if (solid->color == SK_ColorBLACK) + if (solid->color == SkColors::kBlack) continue; } @@ -110,10 +111,8 @@ bool OverlayStrategyUnderlayCast::Attempt( for (auto it = quad_list.begin(); it != quad_list.end(); ++it) { OverlayCandidate candidate; if (it->material != DrawQuad::Material::kVideoHole || - OverlayCandidate::FromDrawQuad( - resource_provider, surface_damage_rect_list, output_color_matrix, - *it, GetPrimaryPlaneDisplayRect(primary_plane), - &candidate) != OverlayCandidate::CandidateStatus::kSuccess) { + candidate_factory.FromDrawQuad(*it, candidate) != + OverlayCandidate::CandidateStatus::kSuccess) { continue; } @@ -145,6 +144,10 @@ void OverlayStrategyUnderlayCast::ProposePrioritized( QuadList& quad_list = render_pass->quad_list; OverlayCandidate candidate; auto overlay_iter = quad_list.end(); + OverlayCandidateFactory candidate_factory = OverlayCandidateFactory( + render_pass, resource_provider, surface_damage_rect_list, + &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane)); + // Original code did reverse iteration. // Here we do forward but find the last one. which should be the same thing. for (auto it = quad_list.begin(); it != quad_list.end(); ++it) { @@ -160,10 +163,8 @@ void OverlayStrategyUnderlayCast::ProposePrioritized( // the underlay to be visible. // VIDEO_HOLE implies it requires overlay. if (it->material == DrawQuad::Material::kVideoHole && - OverlayCandidate::FromDrawQuad( - resource_provider, surface_damage_rect_list, output_color_matrix, - *it, GetPrimaryPlaneDisplayRect(primary_plane), - &candidate) == OverlayCandidate::CandidateStatus::kSuccess) { + candidate_factory.FromDrawQuad(*it, candidate) == + OverlayCandidate::CandidateStatus::kSuccess) { overlay_iter = it; } } @@ -190,6 +191,10 @@ bool OverlayStrategyUnderlayCast::AttemptPrioritized( QuadList& quad_list = render_pass->quad_list; bool found_underlay = false; gfx::Rect content_rect; + OverlayCandidateFactory candidate_factory = OverlayCandidateFactory( + render_pass, resource_provider, surface_damage_rect_list, + &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane)); + for (const auto* quad : base::Reversed(quad_list)) { if (OverlayCandidate::IsInvisibleQuad(quad)) continue; @@ -209,18 +214,15 @@ bool OverlayStrategyUnderlayCast::AttemptPrioritized( // quad is supposed to be to replace it with a transparent quad to allow // the underlay to be visible. // VIDEO_HOLE implies it requires overlay. - is_underlay = - quad->material == DrawQuad::Material::kVideoHole && - OverlayCandidate::FromDrawQuad( - resource_provider, surface_damage_rect_list, output_color_matrix, - quad, GetPrimaryPlaneDisplayRect(primary_plane), - &candidate) == OverlayCandidate::CandidateStatus::kSuccess; + is_underlay = quad->material == DrawQuad::Material::kVideoHole && + candidate_factory.FromDrawQuad(quad, candidate) == + OverlayCandidate::CandidateStatus::kSuccess; found_underlay = is_underlay; } if (!found_underlay && quad->material == DrawQuad::Material::kSolidColor) { const SolidColorDrawQuad* solid = SolidColorDrawQuad::MaterialCast(quad); - if (solid->color == SK_ColorBLACK) + if (solid->color == SkColors::kBlack) continue; } @@ -240,10 +242,8 @@ bool OverlayStrategyUnderlayCast::AttemptPrioritized( for (auto it = quad_list.begin(); it != quad_list.end(); ++it) { OverlayCandidate candidate; if (it->material != DrawQuad::Material::kVideoHole || - OverlayCandidate::FromDrawQuad( - resource_provider, surface_damage_rect_list, output_color_matrix, - *it, GetPrimaryPlaneDisplayRect(primary_plane), - &candidate) != OverlayCandidate::CandidateStatus::kSuccess) { + candidate_factory.FromDrawQuad(*it, candidate) != + OverlayCandidate::CandidateStatus::kSuccess) { continue; } diff --git a/chromium/components/viz/service/display/overlay_unittest.cc b/chromium/components/viz/service/display/overlay_unittest.cc index e5a1102403b..954d384fa24 100644 --- a/chromium/components/viz/service/display/overlay_unittest.cc +++ b/chromium/components/viz/service/display/overlay_unittest.cc @@ -22,6 +22,7 @@ #include "cc/test/resource_provider_test_utils.h" #include "components/viz/client/client_resource_provider.h" #include "components/viz/common/features.h" +#include "components/viz/common/quads/aggregated_render_pass_draw_quad.h" #include "components/viz/common/quads/compositor_render_pass.h" #include "components/viz/common/quads/compositor_render_pass_draw_quad.h" #include "components/viz/common/quads/solid_color_draw_quad.h" @@ -31,8 +32,7 @@ #include "components/viz/common/resources/resource_id.h" #include "components/viz/common/resources/transferable_resource.h" #include "components/viz/service/display/ca_layer_overlay.h" -#include "components/viz/service/display/display_resource_provider_gl.h" -#include "components/viz/service/display/gl_renderer.h" +#include "components/viz/service/display/display_resource_provider_skia.h" #include "components/viz/service/display/output_surface.h" #include "components/viz/service/display/output_surface_client.h" #include "components/viz/service/display/output_surface_frame.h" @@ -44,6 +44,7 @@ #include "components/viz/service/display/overlay_strategy_single_on_top.h" #include "components/viz/service/display/overlay_strategy_underlay.h" #include "components/viz/service/display/overlay_strategy_underlay_cast.h" +#include "components/viz/test/fake_skia_output_surface.h" #include "components/viz/test/test_context_provider.h" #include "components/viz/test/test_gles2_interface.h" #include "gpu/config/gpu_finch_features.h" @@ -392,60 +393,6 @@ class FullThresholdUnderlayOverlayProcessor : public DefaultOverlayProcessor { } }; -class OverlayOutputSurface : public OutputSurface { - public: - explicit OverlayOutputSurface( - scoped_refptr<TestContextProvider> context_provider) - : OutputSurface(std::move(context_provider)) { - is_displayed_as_overlay_plane_ = true; - capabilities_.supports_viewporter = true; - } - - // OutputSurface implementation. - void BindToClient(OutputSurfaceClient* client) override {} - void EnsureBackbuffer() override {} - void DiscardBackbuffer() override {} - void BindFramebuffer() override { bind_framebuffer_count_ += 1; } - void Reshape(const gfx::Size& size, - float device_scale_factor, - const gfx::ColorSpace& color_space, - gfx::BufferFormat format, - bool use_stencil) override { - size_ = size; - } - void SwapBuffers(OutputSurfaceFrame frame) override {} - uint32_t GetFramebufferCopyTextureFormat() override { - // TestContextProvider has no real framebuffer, just use RGB. - return GL_RGB; - } - bool HasExternalStencilTest() const override { return false; } - void ApplyExternalStencil() override {} - bool IsDisplayedAsOverlayPlane() const override { - return is_displayed_as_overlay_plane_; - } - unsigned GetOverlayTextureId() const override { return 10000; } - unsigned UpdateGpuFence() override { return 0; } - void SetUpdateVSyncParametersCallback( - UpdateVSyncParametersCallback callback) override {} - void SetDisplayTransformHint(gfx::OverlayTransform transform) override {} - gfx::OverlayTransform GetDisplayTransform() override { - return gfx::OVERLAY_TRANSFORM_NONE; - } - - void set_is_displayed_as_overlay_plane(bool value) { - is_displayed_as_overlay_plane_ = value; - } - - unsigned bind_framebuffer_count() const { return bind_framebuffer_count_; } - void clear_bind_framebuffer_count() { bind_framebuffer_count_ = 0; } - gfx::Size size() const { return size_; } - - private: - gfx::Size size_; - bool is_displayed_as_overlay_plane_; - unsigned bind_framebuffer_count_ = 0; -}; - std::unique_ptr<AggregatedRenderPass> CreateRenderPass() { AggregatedRenderPassId render_pass_id{1}; gfx::Rect output_rect(0, 0, 256, 256); @@ -741,13 +688,12 @@ template <typename OverlayProcessorType> class OverlayTest : public testing::Test { protected: void SetUp() override { - provider_ = TestContextProvider::Create(); - provider_->BindToCurrentThread(); - output_surface_ = std::make_unique<OverlayOutputSurface>(provider_); - output_surface_->BindToClient(&client_); + output_surface_ = FakeSkiaOutputSurface::Create3d(); + output_surface_->BindToClient(&output_surface_client_); - resource_provider_ = - std::make_unique<DisplayResourceProviderGL>(provider_.get()); + resource_provider_ = std::make_unique<DisplayResourceProviderSkia>(); + lock_set_for_external_use_.emplace(resource_provider_.get(), + output_surface_.get()); child_provider_ = TestContextProvider::Create(); child_provider_->BindToCurrentThread(); @@ -761,9 +707,9 @@ class OverlayTest : public testing::Test { child_resource_provider_->ShutdownAndReleaseAllResources(); child_resource_provider_ = nullptr; child_provider_ = nullptr; + lock_set_for_external_use_.reset(); resource_provider_ = nullptr; output_surface_ = nullptr; - provider_ = nullptr; } void AddExpectedRectToOverlayProcessor(const gfx::RectF& rect) { @@ -774,10 +720,11 @@ class OverlayTest : public testing::Test { overlay_processor_->AddScalingSequence(scaling, uses_overlay); } - scoped_refptr<TestContextProvider> provider_; - std::unique_ptr<OverlayOutputSurface> output_surface_; - cc::FakeOutputSurfaceClient client_; - std::unique_ptr<DisplayResourceProvider> resource_provider_; + std::unique_ptr<SkiaOutputSurface> output_surface_; + cc::FakeOutputSurfaceClient output_surface_client_; + std::unique_ptr<DisplayResourceProviderSkia> resource_provider_; + absl::optional<DisplayResourceProviderSkia::LockSetForExternalUse> + lock_set_for_external_use_; scoped_refptr<TestContextProvider> child_provider_; std::unique_ptr<ClientResourceProvider> child_resource_provider_; std::unique_ptr<OverlayProcessorType> overlay_processor_; @@ -794,11 +741,21 @@ class UseMultipleOverlaysTest : public OverlayTest<OverlayProcessorType> { featureAndParamsList = {{features::kEnableOverlayPrioritization, {}}, {features::kUseMultipleOverlays, {{features::kMaxOverlaysParam, "4"}}}}; - features.InitWithFeaturesAndParameters(featureAndParamsList, {}); + scoped_features.InitWithFeaturesAndParameters(featureAndParamsList, {}); + } + + protected: + void SetUp() override { + OverlayTest<OverlayProcessorType>::SetUp(); + // When overlay prioritization is explicitly disabled (Lacros) we should + // skip multiple overlays tests. + if (!features::IsOverlayPrioritizationEnabled()) { + GTEST_SKIP(); + } } private: - base::test::ScopedFeatureList features; + base::test::ScopedFeatureList scoped_features; }; using FullscreenOverlayTest = OverlayTest<FullscreenOverlayProcessor>; @@ -817,15 +774,6 @@ using SizeSortedMultiOverlayTest = UseMultipleOverlaysTest<SizeSortedMultiOverlayProcessor>; TEST(OverlayTest, OverlaysProcessorHasStrategy) { - scoped_refptr<TestContextProvider> provider = TestContextProvider::Create(); - provider->BindToCurrentThread(); - OverlayOutputSurface output_surface(provider); - cc::FakeOutputSurfaceClient client; - output_surface.BindToClient(&client); - - auto resource_provider = - std::make_unique<DisplayResourceProviderGL>(provider.get()); - auto overlay_processor = std::make_unique<TestOverlayProcessor>(); EXPECT_GE(2U, overlay_processor->GetStrategyCount()); } @@ -1186,14 +1134,13 @@ TEST_F(SingleOverlayOnTopTest, CandidateIdCollision) { // Code to make sure the 'unique' tracking ids are actually identical. OverlayCandidate candidate_a; - SkM44 ident; - auto ret_a = OverlayCandidate::FromDrawQuad( - resource_provider_.get(), &surface_damage_rect_list, ident, quad_a, - gfx::RectF(pass->output_rect), &candidate_a); + auto color_mat = GetIdentityColorMatrix(); + auto candidate_factory = OverlayCandidateFactory( + pass.get(), resource_provider_.get(), &surface_damage_rect_list, + &color_mat, gfx::RectF(pass->output_rect)); + auto ret_a = candidate_factory.FromDrawQuad(quad_a, candidate_a); OverlayCandidate candidate_b; - auto ret_b = OverlayCandidate::FromDrawQuad( - resource_provider_.get(), &surface_damage_rect_list, ident, quad_b, - gfx::RectF(pass->output_rect), &candidate_b); + auto ret_b = candidate_factory.FromDrawQuad(quad_b, candidate_b); EXPECT_EQ(OverlayCandidate::CandidateStatus::kSuccess, ret_a); EXPECT_EQ(OverlayCandidate::CandidateStatus::kSuccess, ret_b); @@ -1203,10 +1150,9 @@ TEST_F(SingleOverlayOnTopTest, CandidateIdCollision) { pass_list.push_back(std::move(pass)); overlay_processor_->SetFrameSequenceNumber(1); overlay_processor_->ProcessForOverlays( - resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), - render_pass_filters, render_pass_backdrop_filters, - std::move(surface_damage_rect_list), nullptr, &candidate_list, - &damage_rect_, &content_bounds_); + resource_provider_.get(), &pass_list, color_mat, render_pass_filters, + render_pass_backdrop_filters, std::move(surface_damage_rect_list), + nullptr, &candidate_list, &damage_rect_, &content_bounds_); ASSERT_EQ(1U, candidate_list.size()); // Check that one quad is gone. @@ -1235,15 +1181,14 @@ TEST_F(SingleOverlayOnTopTest, CandidateTrackIdUniqueSurface) { kCandidateRect, SurfaceId(FrameSinkId(2, 2), LocalSurfaceId())); // Code to make sure the 'unique' tracking ids are actually different. OverlayCandidate candidate_a; - SkM44 ident; SurfaceDamageRectList surface_damage_rect_list; - auto ret_a = OverlayCandidate::FromDrawQuad( - resource_provider_.get(), &surface_damage_rect_list, ident, quad_a, - gfx::RectF(pass->output_rect), &candidate_a); + auto color_mat = GetIdentityColorMatrix(); + auto candidate_factory = OverlayCandidateFactory( + pass.get(), resource_provider_.get(), &surface_damage_rect_list, + &color_mat, gfx::RectF(pass->output_rect)); + auto ret_a = candidate_factory.FromDrawQuad(quad_a, candidate_a); OverlayCandidate candidate_b; - auto ret_b = OverlayCandidate::FromDrawQuad( - resource_provider_.get(), &surface_damage_rect_list, ident, quad_b, - gfx::RectF(pass->output_rect), &candidate_b); + auto ret_b = candidate_factory.FromDrawQuad(quad_b, candidate_b); EXPECT_EQ(OverlayCandidate::CandidateStatus::kSuccess, ret_a); EXPECT_EQ(OverlayCandidate::CandidateStatus::kSuccess, ret_b); @@ -1728,7 +1673,7 @@ TEST_F(SingleOverlayOnTopTest, RejectBackgroundColor) { TextureDrawQuad* quad = CreateFullscreenCandidateQuad( resource_provider_.get(), child_resource_provider_.get(), child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); - quad->background_color = SK_ColorRED; + quad->background_color = SkColors::kRed; OverlayCandidateList candidate_list; OverlayProcessorInterface::FilterOperationsMap render_pass_filters; @@ -1750,7 +1695,7 @@ TEST_F(SingleOverlayOnTopTest, AcceptBlackBackgroundColor) { TextureDrawQuad* quad = CreateFullscreenCandidateQuad( resource_provider_.get(), child_resource_provider_.get(), child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); - quad->background_color = SK_ColorBLACK; + quad->background_color = SkColors::kBlack; OverlayCandidateList candidate_list; OverlayProcessorInterface::FilterOperationsMap render_pass_filters; @@ -1772,7 +1717,7 @@ TEST_F(SingleOverlayOnTopTest, RejectBlackBackgroundColorWithBlending) { TextureDrawQuad* quad = CreateFullscreenCandidateQuad( resource_provider_.get(), child_resource_provider_.get(), child_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); - quad->background_color = SK_ColorBLACK; + quad->background_color = SkColors::kBlack; quad->needs_blending = true; OverlayCandidateList candidate_list; @@ -1922,9 +1867,9 @@ TEST_F(UnderlayTest, ReplacementQuad) { &damage_rect_, &content_bounds_); ASSERT_EQ(1U, pass_list.size()); ASSERT_EQ(1U, pass_list.front()->quad_list.size()); - EXPECT_EQ(SK_ColorTRANSPARENT, static_cast<SolidColorDrawQuad*>( - pass_list.front()->quad_list.front()) - ->color); + EXPECT_EQ(SkColors::kTransparent, static_cast<SolidColorDrawQuad*>( + pass_list.front()->quad_list.front()) + ->color); EXPECT_FALSE(pass_list.front()->quad_list.front()->ShouldDrawWithBlending()); EXPECT_FALSE(pass_list.front() ->quad_list.front() @@ -2764,7 +2709,7 @@ TEST_F(UnderlayTest, OverlayLayerUnderMainLayer) { auto* quad = static_cast<SolidColorDrawQuad*>(main_pass->quad_list.back()); EXPECT_EQ(quad->rect, quad->visible_rect); EXPECT_EQ(false, quad->needs_blending); - EXPECT_EQ(SK_ColorTRANSPARENT, quad->color); + EXPECT_EQ(SkColors::kTransparent, quad->color); } TEST_F(UnderlayTest, AllowOnTop) { @@ -2797,7 +2742,7 @@ TEST_F(UnderlayTest, AllowOnTop) { auto* quad = static_cast<SolidColorDrawQuad*>(main_pass->quad_list.front()); EXPECT_EQ(quad->rect, quad->visible_rect); EXPECT_EQ(false, quad->needs_blending); - EXPECT_EQ(SK_ColorTRANSPARENT, quad->color); + EXPECT_EQ(SkColors::kTransparent, quad->color); } // Pure overlays have a very specific optimization that does not produce damage @@ -2985,9 +2930,9 @@ TEST_F(TransitionOverlayTypeTest, MaskFilterBringsUnderlay) { ASSERT_EQ(1U, pass_list.size()); ASSERT_EQ(2U, pass_list.front()->quad_list.size()); - EXPECT_EQ(SK_ColorBLACK, static_cast<SolidColorDrawQuad*>( - pass_list.front()->quad_list.front()) - ->color); + EXPECT_EQ(SkColors::kBlack, static_cast<SolidColorDrawQuad*>( + pass_list.front()->quad_list.front()) + ->color); EXPECT_FALSE(pass_list.front() ->quad_list.front() ->shared_quad_state->are_contents_opaque); @@ -3750,9 +3695,9 @@ TEST_F(UnderlayCastTest, ReplacementQuad) { &damage_rect_, &content_bounds_); ASSERT_EQ(1U, pass_list.size()); ASSERT_EQ(1U, pass_list.front()->quad_list.size()); - EXPECT_EQ(SK_ColorTRANSPARENT, static_cast<SolidColorDrawQuad*>( - pass_list.front()->quad_list.front()) - ->color); + EXPECT_EQ(SkColors::kTransparent, static_cast<SolidColorDrawQuad*>( + pass_list.front()->quad_list.front()) + ->color); EXPECT_FALSE(pass_list.front()->quad_list.front()->ShouldDrawWithBlending()); EXPECT_FALSE(pass_list.front() ->quad_list.front() @@ -4009,9 +3954,9 @@ TEST_F(UnderlayCastTest, OverlayPromotionWithMaskFilter) { ASSERT_EQ(1U, pass_list.size()); ASSERT_EQ(1U, pass_list.front()->quad_list.size()); - EXPECT_EQ(SK_ColorBLACK, static_cast<SolidColorDrawQuad*>( - pass_list.front()->quad_list.front()) - ->color); + EXPECT_EQ(SkColors::kBlack, static_cast<SolidColorDrawQuad*>( + pass_list.front()->quad_list.front()) + ->color); EXPECT_FALSE(pass_list.front() ->quad_list.front() ->shared_quad_state->are_contents_opaque); @@ -4296,11 +4241,12 @@ TEST_F(SingleOverlayOnTopTest, IsOverlayRequiredBasic) { child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), kSmallCandidateRect); SurfaceDamageRectList surface_damage_rect_list; - SkM44 default_color = GetIdentityColorMatrix(); OverlayCandidate candidate; - OverlayCandidate::FromDrawQuad(resource_provider_.get(), - &surface_damage_rect_list, default_color, - new_quad, gfx::RectF(), &candidate); + auto color_mat = GetIdentityColorMatrix(); + auto candidate_factory = OverlayCandidateFactory( + pass.get(), resource_provider_.get(), &surface_damage_rect_list, + &color_mat, gfx::RectF(pass->output_rect)); + candidate_factory.FromDrawQuad(new_quad, candidate); // Verify that a default candidate is not a required overlay. EXPECT_FALSE(candidate.requires_overlay); @@ -4318,11 +4264,12 @@ TEST_F(SingleOverlayOnTopTest, IsOverlayRequiredHwProtectedVideo) { kSmallCandidateRect, gfx::ProtectedVideoType::kHardwareProtected, YUV_420_BIPLANAR); SurfaceDamageRectList surface_damage_rect_list; - SkM44 default_color = GetIdentityColorMatrix(); OverlayCandidate candidate; - OverlayCandidate::FromDrawQuad(resource_provider_.get(), - &surface_damage_rect_list, default_color, - new_quad, gfx::RectF(), &candidate); + auto color_mat = GetIdentityColorMatrix(); + auto candidate_factory = OverlayCandidateFactory( + pass.get(), resource_provider_.get(), &surface_damage_rect_list, + &color_mat, gfx::RectF(pass->output_rect)); + candidate_factory.FromDrawQuad(new_quad, candidate); // Verify that a HW protected video candidate requires overlay. EXPECT_TRUE(candidate.requires_overlay); @@ -4341,11 +4288,12 @@ TEST_F(SingleOverlayOnTopTest, RequiredOverlayClippingAndSubsampling) { YUV_420_BIPLANAR); pass->shared_quad_state_list.back()->clip_rect = kOverlayClipRect; SurfaceDamageRectList surface_damage_rect_list; - SkM44 default_color = GetIdentityColorMatrix(); OverlayCandidate candidate; - OverlayCandidate::FromDrawQuad(resource_provider_.get(), - &surface_damage_rect_list, default_color, - new_quad, gfx::RectF(), &candidate); + auto color_mat = GetIdentityColorMatrix(); + auto candidate_factory = OverlayCandidateFactory( + pass.get(), resource_provider_.get(), &surface_damage_rect_list, + &color_mat, gfx::RectF(pass->output_rect)); + candidate_factory.FromDrawQuad(new_quad, candidate); // Default uv rect is 0.1, 0.2, 1.0, 1.0 which in the 320x240 buffer // corresponds to 32, 48, 288x192. That maps to |kVideoCandidateRect| in the @@ -4374,13 +4322,14 @@ TEST_F(SingleOverlayOnTopTest, YUV_420_BIPLANAR); pass->shared_quad_state_list.back()->clip_rect = kOverlayClipRect; SurfaceDamageRectList surface_damage_rect_list; - SkM44 default_color = GetIdentityColorMatrix(); gfx::RectF primary_rect(0, 0, 100, 120); OverlayProcessorInterface::OutputSurfaceOverlayPlane primary_plane; OverlayCandidate candidate; - OverlayCandidate::FromDrawQuad(resource_provider_.get(), - &surface_damage_rect_list, default_color, - new_quad, primary_rect, &candidate); + auto color_mat = GetIdentityColorMatrix(); + auto candidate_factory = OverlayCandidateFactory( + pass.get(), resource_provider_.get(), &surface_damage_rect_list, + &color_mat, primary_rect); + candidate_factory.FromDrawQuad(new_quad, candidate); // Default uv rect is 0.1, 0.2, 1.0, 1.0 which in the 320x240 buffer // corresponds to 32, 48, 288x192. That maps to |kVideoCandidateRect| in the @@ -4449,7 +4398,7 @@ TEST_F(UnderlayTest, EstimateOccludedDamage) { kCandidateSmall * kCandidateSmall, kCandidateSmall * kCandidateSmall - kOccluderWidth * kOccluderWidth, kCandidateLarge * kCandidateLarge - kOccluderWidth * kOccluderWidth * 2, - kCandidateLarge * kCandidateLarge * 4 - + kOverlayRect.width() * kOverlayRect.height() - kOccluderWidth * kOccluderWidth * 2}; static_assert( @@ -4477,21 +4426,22 @@ TEST_F(UnderlayTest, EstimateOccludedDamage) { if (kCandidateUseSurfaceIndex[i]) { damaged_shared_quad_state->overlay_damage_index = surface_damage_rect_list.size(); - surface_damage_rect_list.emplace_back(kCandidateRects[i]); } else { damaged_shared_quad_state->overlay_damage_index.reset(); } + surface_damage_rect_list.emplace_back(kCandidateRects[i]); auto* quad_candidate = CreateCandidateQuadAt( resource_provider_.get(), child_resource_provider_.get(), child_provider_.get(), damaged_shared_quad_state, pass.get(), kCandidateRects[i]); - SkM44 default_color = GetIdentityColorMatrix(); OverlayCandidate candidate; - OverlayCandidate::FromDrawQuad(resource_provider_.get(), - &surface_damage_rect_list, default_color, - quad_candidate, gfx::RectF(), &candidate); + auto color_mat = GetIdentityColorMatrix(); + auto candidate_factory = OverlayCandidateFactory( + pass.get(), resource_provider_.get(), &surface_damage_rect_list, + &color_mat, gfx::RectF()); + candidate_factory.FromDrawQuad(quad_candidate, candidate); // Before the 'EstimateOccludedDamage' function is called the damage area // will just be whatever comes from the |surface_damage_rect_list|. @@ -4508,8 +4458,8 @@ TEST_F(UnderlayTest, EstimateOccludedDamage) { // Now we test the opaque occlusion provided by 'EstimateOccludedDamage' // function. - candidate.damage_area_estimate = OverlayCandidate::EstimateVisibleDamage( - quad_candidate, &surface_damage_rect_list, quad_list.begin(), + candidate.damage_area_estimate = candidate_factory.EstimateVisibleDamage( + quad_candidate, candidate, quad_list.begin(), std::next(quad_list.begin(), occluder_iter_count)); ASSERT_EQ(kExpectedDamages[i], candidate.damage_area_estimate); @@ -4710,6 +4660,41 @@ TEST_F(DelegatedTest, TestClipHandCrafted) { EXPECT_RECTF_NEAR(uv_rect, candidate_list[0].uv_rect, 0.01f); } +TEST_F(DelegatedTest, TestVisibleRectClip) { + auto pass = CreateRenderPass(); + const auto kSmallCandidateRect = gfx::Rect(0, 0, 100, 100); + const auto kTestClip = gfx::Rect(0, 50, 50, 50); + auto* tex_rect = CreateCandidateQuadAt( + resource_provider_.get(), child_resource_provider_.get(), + child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), + kSmallCandidateRect); + tex_rect->uv_bottom_right = gfx::PointF(1, 1); + tex_rect->uv_top_left = gfx::PointF(0, 0); + tex_rect->visible_rect = kTestClip; + // Check for potential candidates. + OverlayCandidateList candidate_list; + OverlayProcessorInterface::FilterOperationsMap render_pass_filters; + OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; + AggregatedRenderPassList pass_list; + // AggregatedRenderPass* main_pass = pass.get(); + SurfaceDamageRectList surface_damage_rect_list; + // Simplify by adding full root damage. + surface_damage_rect_list.push_back(pass->output_rect); + pass_list.push_back(std::move(pass)); + overlay_processor_->ProcessForOverlays( + resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), + render_pass_filters, render_pass_backdrop_filters, + std::move(surface_damage_rect_list), + overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list, + &damage_rect_, &content_bounds_); + + const auto uv_rect = gfx::RectF(0, 0.5f, 0.5f, 0.5f); + EXPECT_EQ(1U, candidate_list.size()); + EXPECT_RECTF_NEAR(gfx::RectF(kTestClip), candidate_list[0].display_rect, + 0.01f); + EXPECT_RECTF_NEAR(uv_rect, candidate_list[0].uv_rect, 0.01f); +} + TEST_F(DelegatedTest, TestClipComputed) { auto pass = CreateRenderPass(); const auto kSmallCandidateRect = gfx::Rect(5, 10, 128, 64); @@ -4821,7 +4806,8 @@ TEST_F(DelegatedTest, ScaledBufferDamage) { overlay_processor_->ProcessForOverlays( resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), render_pass_filters, render_pass_backdrop_filters, - std::move(surface_damage_rect_list), nullptr, &candidate_list, + std::move(surface_damage_rect_list), + overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list, &damage_rect_, &content_bounds_); // Expected damage is basically the intersection of the rect with the screen diff --git a/chromium/components/viz/service/display/program_binding.cc b/chromium/components/viz/service/display/program_binding.cc deleted file mode 100644 index 488151c3587..00000000000 --- a/chromium/components/viz/service/display/program_binding.cc +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2011 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/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" -#include "gpu/command_buffer/client/gles2_interface.h" -#include "ui/gfx/color_transform.h" - -using gpu::gles2::GLES2Interface; - -namespace viz { - -ProgramKey::ProgramKey() = default; - -ProgramKey::ProgramKey(const ProgramKey& other) = default; - -ProgramKey::~ProgramKey() = default; - -bool ProgramKey::operator==(const ProgramKey& other) const { - return type_ == other.type_ && precision_ == other.precision_ && - sampler_ == other.sampler_ && blend_mode_ == other.blend_mode_ && - aa_mode_ == other.aa_mode_ && is_opaque_ == other.is_opaque_ && - premultiplied_alpha_ == other.premultiplied_alpha_ && - has_background_color_ == other.has_background_color_ && - has_tex_clamp_rect_ == other.has_tex_clamp_rect_ && - mask_mode_ == other.mask_mode_ && - mask_for_background_ == other.mask_for_background_ && - has_color_matrix_ == other.has_color_matrix_ && - yuv_alpha_texture_mode_ == other.yuv_alpha_texture_mode_ && - uv_texture_mode_ == other.uv_texture_mode_ && - color_conversion_mode_ == other.color_conversion_mode_ && - color_transform_ == other.color_transform_ && - has_output_color_matrix_ == other.has_output_color_matrix_ && - has_rounded_corner_ == other.has_rounded_corner_; -} - -bool ProgramKey::operator!=(const ProgramKey& other) const { - return !(*this == other); -} - -// static -ProgramKey ProgramKey::DebugBorder() { - ProgramKey result; - result.type_ = PROGRAM_TYPE_DEBUG_BORDER; - return result; -} - -// static -ProgramKey ProgramKey::SolidColor(AAMode aa_mode, - bool tint_color, - bool rounded_corner) { - ProgramKey result; - result.type_ = PROGRAM_TYPE_SOLID_COLOR; - result.aa_mode_ = aa_mode; - result.has_tint_color_matrix_ = tint_color; - result.has_rounded_corner_ = rounded_corner; - return result; -} - -// static -ProgramKey ProgramKey::Tile(TexCoordPrecision precision, - SamplerType sampler, - AAMode aa_mode, - PremultipliedAlphaMode premultiplied_alpha, - bool is_opaque, - bool has_tex_clamp_rect, - bool tint_color, - bool rounded_corner) { - ProgramKey result; - result.type_ = PROGRAM_TYPE_TILE; - result.precision_ = precision; - result.sampler_ = sampler; - result.aa_mode_ = aa_mode; - result.is_opaque_ = is_opaque; - result.has_tex_clamp_rect_ = has_tex_clamp_rect; - result.has_tint_color_matrix_ = tint_color; - result.premultiplied_alpha_ = premultiplied_alpha; - result.has_rounded_corner_ = rounded_corner; - return result; -} - -// static -ProgramKey ProgramKey::Texture(TexCoordPrecision precision, - SamplerType sampler, - PremultipliedAlphaMode premultiplied_alpha, - bool has_background_color, - bool has_tex_clamp_rect, - bool tint_color, - bool rounded_corner) { - ProgramKey result; - result.type_ = PROGRAM_TYPE_TEXTURE; - result.precision_ = precision; - result.sampler_ = sampler; - result.premultiplied_alpha_ = premultiplied_alpha; - result.has_background_color_ = has_background_color; - result.has_tex_clamp_rect_ = has_tex_clamp_rect; - result.has_tint_color_matrix_ = tint_color; - result.has_rounded_corner_ = rounded_corner; - return result; -} - -// static -ProgramKey ProgramKey::RenderPass(TexCoordPrecision precision, - SamplerType sampler, - BlendMode blend_mode, - AAMode aa_mode, - MaskMode mask_mode, - bool mask_for_background, - bool has_color_matrix, - bool tint_color, - bool rounded_corner) { - ProgramKey result; - result.type_ = PROGRAM_TYPE_RENDER_PASS; - result.precision_ = precision; - result.sampler_ = sampler; - result.blend_mode_ = blend_mode; - result.aa_mode_ = aa_mode; - result.mask_mode_ = mask_mode; - result.mask_for_background_ = mask_for_background; - result.has_color_matrix_ = has_color_matrix; - result.has_tint_color_matrix_ = tint_color; - result.has_rounded_corner_ = rounded_corner; - return result; -} - -// static -ProgramKey ProgramKey::VideoStream(TexCoordPrecision precision, - bool rounded_corner) { - ProgramKey result; - result.type_ = PROGRAM_TYPE_VIDEO_STREAM; - result.precision_ = precision; - result.sampler_ = SAMPLER_TYPE_EXTERNAL_OES; - result.has_rounded_corner_ = rounded_corner; - return result; -} - -// static -ProgramKey ProgramKey::YUVVideo(TexCoordPrecision precision, - SamplerType sampler, - YUVAlphaTextureMode yuv_alpha_texture_mode, - UVTextureMode uv_texture_mode, - bool tint_color, - bool rounded_corner) { - ProgramKey result; - result.type_ = PROGRAM_TYPE_YUV_VIDEO; - result.precision_ = precision; - result.sampler_ = sampler; - result.yuv_alpha_texture_mode_ = yuv_alpha_texture_mode; - DCHECK(yuv_alpha_texture_mode == YUV_NO_ALPHA_TEXTURE || - yuv_alpha_texture_mode == YUV_HAS_ALPHA_TEXTURE); - result.uv_texture_mode_ = uv_texture_mode; - DCHECK(uv_texture_mode == UV_TEXTURE_MODE_UV || - uv_texture_mode == UV_TEXTURE_MODE_U_V); - result.has_tint_color_matrix_ = tint_color; - result.has_rounded_corner_ = rounded_corner; - return result; -} - -void ProgramKey::SetColorTransform(const gfx::ColorTransform* transform) { - color_transform_ = nullptr; - if (transform->IsIdentity()) { - color_conversion_mode_ = COLOR_CONVERSION_MODE_NONE; - } else { - color_conversion_mode_ = COLOR_CONVERSION_MODE_SHADER; - color_transform_ = transform; - } -} - -ProgramBindingBase::ProgramBindingBase() - : program_(0), - vertex_shader_id_(0), - fragment_shader_id_(0), - initialized_(false) {} - -ProgramBindingBase::~ProgramBindingBase() { - // If you hit these asserts, you initialized but forgot to call Cleanup(). - DCHECK(!program_); - DCHECK(!vertex_shader_id_); - DCHECK(!fragment_shader_id_); - DCHECK(!initialized_); -} - -bool ProgramBindingBase::Init(GLES2Interface* context, - const std::string& vertex_shader, - const std::string& fragment_shader) { - TRACE_EVENT0("viz", "ProgramBindingBase::init"); - vertex_shader_id_ = LoadShader(context, GL_VERTEX_SHADER, vertex_shader); - if (!vertex_shader_id_) - return false; - - fragment_shader_id_ = - LoadShader(context, GL_FRAGMENT_SHADER, fragment_shader); - if (!fragment_shader_id_) { - context->DeleteShader(vertex_shader_id_); - vertex_shader_id_ = 0; - return false; - } - - program_ = - CreateShaderProgram(context, vertex_shader_id_, fragment_shader_id_); - return !!program_; -} - -bool ProgramBindingBase::Link(GLES2Interface* context) { - context->LinkProgram(program_); - CleanupShaders(context); - if (!program_) - return false; -#ifndef NDEBUG - int linked = 0; - context->GetProgramiv(program_, GL_LINK_STATUS, &linked); - if (!linked) { - char buffer[1024] = ""; - context->GetProgramInfoLog(program_, sizeof(buffer), nullptr, buffer); - DLOG(ERROR) << "Error compiling shader: " << buffer; - return false; - } -#endif - return true; -} - -void ProgramBindingBase::Cleanup(GLES2Interface* context) { - initialized_ = false; - if (!program_) - return; - - DCHECK(context); - context->DeleteProgram(program_); - program_ = 0; - - CleanupShaders(context); -} - -unsigned ProgramBindingBase::LoadShader(GLES2Interface* context, - unsigned type, - const std::string& shader_source) { - unsigned shader = context->CreateShader(type); - if (!shader) - return 0u; - - const char* shader_source_str[] = {shader_source.data()}; - int shader_length[] = {static_cast<int>(shader_source.length())}; - context->ShaderSource(shader, 1, shader_source_str, shader_length); - context->CompileShader(shader); -#if EXPENSIVE_DCHECKS_ARE_ON() - int compiled = 0; - context->GetShaderiv(shader, GL_COMPILE_STATUS, &compiled); - if (!compiled) { - char buffer[1024] = ""; - context->GetShaderInfoLog(shader, sizeof(buffer), nullptr, buffer); - DLOG(ERROR) << "Error compiling shader: " << buffer - << "\n shader program: " << shader_source; - return 0u; - } -#endif // EXPENSIVE_DCHECKS_ARE_ON() - return shader; -} - -unsigned ProgramBindingBase::CreateShaderProgram(GLES2Interface* context, - unsigned vertex_shader, - unsigned fragment_shader) { - unsigned program_object = context->CreateProgram(); - if (!program_object) - return 0; - - context->AttachShader(program_object, vertex_shader); - context->AttachShader(program_object, fragment_shader); - - // Bind the common attrib locations. - context->BindAttribLocation( - program_object, GeometryBinding::PositionAttribLocation(), "a_position"); - context->BindAttribLocation( - program_object, GeometryBinding::TexCoordAttribLocation(), "a_texCoord"); - context->BindAttribLocation(program_object, - GeometryBinding::TriangleIndexAttribLocation(), - "a_index"); - - return program_object; -} - -void ProgramBindingBase::CleanupShaders(GLES2Interface* context) { - if (vertex_shader_id_) { - context->DeleteShader(vertex_shader_id_); - vertex_shader_id_ = 0; - } - if (fragment_shader_id_) { - context->DeleteShader(fragment_shader_id_); - fragment_shader_id_ = 0; - } -} - -bool ProgramBindingBase::IsContextLost(GLES2Interface* context) { - return context->GetGraphicsResetStatusKHR() != GL_NO_ERROR; -} - -} // namespace viz diff --git a/chromium/components/viz/service/display/program_binding.h b/chromium/components/viz/service/display/program_binding.h deleted file mode 100644 index b03673f9266..00000000000 --- a/chromium/components/viz/service/display/program_binding.h +++ /dev/null @@ -1,480 +0,0 @@ -// Copyright 2011 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_PROGRAM_BINDING_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_PROGRAM_BINDING_H_ - -#include <string> - -#include "base/check_op.h" -#include "base/memory/raw_ptr.h" -#include "build/build_config.h" -#include "components/viz/common/gpu/context_provider.h" -#include "components/viz/service/display/shader.h" -#include "components/viz/service/viz_service_export.h" - -namespace gfx { -class ColorTransform; -} - -namespace gpu { -namespace gles2 { -class GLES2Interface; -} -} // namespace gpu - -namespace viz { - -class VIZ_SERVICE_EXPORT ProgramBindingBase { - public: - ProgramBindingBase(); - - ProgramBindingBase(const ProgramBindingBase&) = delete; - ProgramBindingBase& operator=(const ProgramBindingBase&) = delete; - - ~ProgramBindingBase(); - - bool Init(gpu::gles2::GLES2Interface* context, - const std::string& vertex_shader, - const std::string& fragment_shader); - bool Link(gpu::gles2::GLES2Interface* context); - void Cleanup(gpu::gles2::GLES2Interface* context); - - unsigned program() const { return program_; } - bool initialized() const { return initialized_; } - - protected: - unsigned LoadShader(gpu::gles2::GLES2Interface* context, - unsigned type, - const std::string& shader_source); - unsigned CreateShaderProgram(gpu::gles2::GLES2Interface* context, - unsigned vertex_shader, - unsigned fragment_shader); - void CleanupShaders(gpu::gles2::GLES2Interface* context); - - bool IsContextLost(gpu::gles2::GLES2Interface* context); - - unsigned program_; - unsigned vertex_shader_id_; - unsigned fragment_shader_id_; - bool initialized_; -}; - -enum ProgramType { - PROGRAM_TYPE_DEBUG_BORDER, - PROGRAM_TYPE_SOLID_COLOR, - PROGRAM_TYPE_TILE, - PROGRAM_TYPE_TEXTURE, - PROGRAM_TYPE_RENDER_PASS, - PROGRAM_TYPE_VIDEO_STREAM, - PROGRAM_TYPE_YUV_VIDEO, -}; - -class VIZ_SERVICE_EXPORT ProgramKey { - public: - ProgramKey(); - ProgramKey(const ProgramKey& other); - ~ProgramKey(); - - static ProgramKey DebugBorder(); - static ProgramKey SolidColor(AAMode aa_mode, - bool tint_color, - bool rounded_corner); - static ProgramKey Tile(TexCoordPrecision precision, - SamplerType sampler, - AAMode aa_mode, - PremultipliedAlphaMode premultiplied_alpha, - bool is_opaque, - bool has_tex_clamp_rect, - bool tint_color, - bool rounded_corner); - static ProgramKey Texture(TexCoordPrecision precision, - SamplerType sampler, - PremultipliedAlphaMode premultiplied_alpha, - bool has_background_color, - bool has_tex_clamp_rect, - bool tint_color, - bool rounded_corner); - - // TODO(ccameron): Merge |mask_for_background| into MaskMode. - static ProgramKey RenderPass(TexCoordPrecision precision, - SamplerType sampler, - BlendMode blend_mode, - AAMode aa_mode, - MaskMode mask_mode, - bool mask_for_background, - bool has_color_matrix, - bool tint_color, - bool rounded_corner); - static ProgramKey VideoStream(TexCoordPrecision precision, - bool rounded_corner); - static ProgramKey YUVVideo(TexCoordPrecision precision, - SamplerType sampler, - YUVAlphaTextureMode yuv_alpha_texture_mode, - UVTextureMode uv_texture_mode, - bool tint_color, - bool rounded_corner); - - bool operator==(const ProgramKey& other) const; - bool operator!=(const ProgramKey& other) const; - - void SetColorTransform(const gfx::ColorTransform* transform); - - bool has_output_color_matrix() const { return has_output_color_matrix_; } - void set_has_output_color_matrix(bool value) { - has_output_color_matrix_ = value; - } - TexCoordPrecision tex_coord_precision() const { return precision_; } - - ProgramType type() const { return type_; } - - private: - friend struct ProgramKeyHash; - friend class Program; - - ProgramType type_ = PROGRAM_TYPE_DEBUG_BORDER; - TexCoordPrecision precision_ = TEX_COORD_PRECISION_NA; - SamplerType sampler_ = SAMPLER_TYPE_NA; - BlendMode blend_mode_ = BLEND_MODE_NONE; - AAMode aa_mode_ = NO_AA; - bool is_opaque_ = false; - - PremultipliedAlphaMode premultiplied_alpha_ = PREMULTIPLIED_ALPHA; - bool has_background_color_ = false; - - MaskMode mask_mode_ = NO_MASK; - bool mask_for_background_ = false; - bool has_color_matrix_ = false; - - YUVAlphaTextureMode yuv_alpha_texture_mode_ = YUV_NO_ALPHA_TEXTURE; - UVTextureMode uv_texture_mode_ = UV_TEXTURE_MODE_NA; - - ColorConversionMode color_conversion_mode_ = COLOR_CONVERSION_MODE_NONE; - raw_ptr<const gfx::ColorTransform> color_transform_ = nullptr; - - bool has_tex_clamp_rect_ = false; - - bool has_output_color_matrix_ = false; - bool has_tint_color_matrix_ = false; - bool has_rounded_corner_ = false; -}; - -struct ProgramKeyHash { - size_t operator()(const ProgramKey& key) const { - return (static_cast<size_t>(key.type_) << 0) ^ - (static_cast<size_t>(key.precision_) << 3) ^ - (static_cast<size_t>(key.sampler_) << 6) ^ - (static_cast<size_t>(key.blend_mode_) << 9) ^ - (static_cast<size_t>(key.aa_mode_) << 15) ^ - (static_cast<size_t>(key.is_opaque_) << 17) ^ - (static_cast<size_t>(key.premultiplied_alpha_) << 19) ^ - (static_cast<size_t>(key.has_background_color_) << 20) ^ - (static_cast<size_t>(key.mask_mode_) << 21) ^ - (static_cast<size_t>(key.mask_for_background_) << 22) ^ - (static_cast<size_t>(key.has_color_matrix_) << 23) ^ - (static_cast<size_t>(key.yuv_alpha_texture_mode_) << 24) ^ - (static_cast<size_t>(key.uv_texture_mode_) << 25) ^ - (static_cast<size_t>(key.color_conversion_mode_) << 26) ^ - (static_cast<size_t>(key.has_tex_clamp_rect_) << 28) ^ - (static_cast<size_t>(key.has_output_color_matrix_) << 29) ^ - (static_cast<size_t>(key.has_tint_color_matrix_) << 30) ^ - (static_cast<size_t>(key.has_rounded_corner_) << 31); - } -}; - -class VIZ_SERVICE_EXPORT Program : public ProgramBindingBase { - public: - Program() {} - - Program(const Program&) = delete; - Program& operator=(const Program&) = delete; - - void Initialize(ContextProvider* context_provider, const ProgramKey& key) { - // Set parameters that are common to all sub-classes. - vertex_shader_.aa_mode_ = key.aa_mode_; - fragment_shader_.aa_mode_ = key.aa_mode_; - fragment_shader_.blend_mode_ = key.blend_mode_; - fragment_shader_.tex_coord_precision_ = key.precision_; - fragment_shader_.sampler_type_ = key.sampler_; - fragment_shader_.premultiply_alpha_mode_ = key.premultiplied_alpha_; - fragment_shader_.mask_mode_ = key.mask_mode_; - fragment_shader_.mask_for_background_ = key.mask_for_background_; - fragment_shader_.color_conversion_mode_ = key.color_conversion_mode_; - fragment_shader_.color_transform_ = key.color_transform_; - fragment_shader_.has_output_color_matrix_ = key.has_output_color_matrix_; - fragment_shader_.has_tint_color_matrix_ = key.has_tint_color_matrix_; - fragment_shader_.has_rounded_corner_ = key.has_rounded_corner_; - - switch (key.type_) { - case PROGRAM_TYPE_DEBUG_BORDER: - InitializeDebugBorderProgram(); - break; - case PROGRAM_TYPE_SOLID_COLOR: - InitializeSolidColorProgram(key); - break; - case PROGRAM_TYPE_TILE: - InitializeTileProgram(key); - break; - case PROGRAM_TYPE_TEXTURE: - InitializeTextureProgram(key); - break; - case PROGRAM_TYPE_RENDER_PASS: - InitializeRenderPassProgram(key); - break; - case PROGRAM_TYPE_VIDEO_STREAM: - InitializeVideoStreamProgram(key); - break; - case PROGRAM_TYPE_YUV_VIDEO: - InitializeYUVVideo(key); - break; - } - InitializeInternal(context_provider); - } - - const VertexShader& vertex_shader() const { return vertex_shader_; } - const FragmentShader& fragment_shader() const { return fragment_shader_; } - - // Functions for querying uniform locations. - int vertex_tex_transform_location() const { - return vertex_shader_.vertex_tex_transform_location_; - } - int tex_matrix_location() const { - return vertex_shader_.tex_matrix_location_; - } - int ya_tex_scale_location() const { - return vertex_shader_.ya_tex_scale_location_; - } - int ya_tex_offset_location() const { - return vertex_shader_.ya_tex_offset_location_; - } - int uv_tex_scale_location() const { - return vertex_shader_.uv_tex_scale_location_; - } - int uv_tex_offset_location() const { - return vertex_shader_.uv_tex_offset_location_; - } - int matrix_location() const { return vertex_shader_.matrix_location_; } - int vertex_opacity_location() const { - return vertex_shader_.vertex_opacity_location_; - } - int viewport_location() const { return vertex_shader_.viewport_location_; } - int edge_location() const { return vertex_shader_.edge_location_; } - int quad_location() const { return vertex_shader_.quad_location_; } - - int sampler_location() const { return fragment_shader_.sampler_location_; } - int alpha_location() const { return fragment_shader_.alpha_location_; } - int color_location() const { return fragment_shader_.color_location_; } - int background_color_location() const { - return fragment_shader_.background_color_location_; - } - int fragment_tex_transform_location() const { - return fragment_shader_.fragment_tex_transform_location_; - } - int backdrop_location() const { return fragment_shader_.backdrop_location_; } - int backdrop_rect_location() const { - return fragment_shader_.backdrop_rect_location_; - } - int original_backdrop_location() const { - return fragment_shader_.original_backdrop_location_; - } - int mask_sampler_location() const { - return fragment_shader_.mask_sampler_location_; - } - int mask_tex_coord_scale_location() const { - return fragment_shader_.mask_tex_coord_scale_location_; - } - int mask_tex_coord_offset_location() const { - return fragment_shader_.mask_tex_coord_offset_location_; - } - int color_matrix_location() const { - return fragment_shader_.color_matrix_location_; - } - int color_offset_location() const { - return fragment_shader_.color_offset_location_; - } - int tex_clamp_rect_location() const { - return fragment_shader_.tex_clamp_rect_location_; - } - int y_texture_location() const { - return fragment_shader_.y_texture_location_; - } - int u_texture_location() const { - return fragment_shader_.u_texture_location_; - } - int v_texture_location() const { - return fragment_shader_.v_texture_location_; - } - int uv_texture_location() const { - return fragment_shader_.uv_texture_location_; - } - int a_texture_location() const { - return fragment_shader_.a_texture_location_; - } - int resource_multiplier_location() const { - return fragment_shader_.resource_multiplier_location_; - } - int resource_offset_location() const { - return fragment_shader_.resource_offset_location_; - } - int ya_clamp_rect_location() const { - return fragment_shader_.ya_clamp_rect_location_; - } - int uv_clamp_rect_location() const { - return fragment_shader_.uv_clamp_rect_location_; - } - int output_color_matrix_location() const { - return fragment_shader_.output_color_matrix_location_; - } - int tint_color_matrix_location() const { - return fragment_shader_.tint_color_matrix_location_; - } - int rounded_corner_rect_location() const { - return fragment_shader_.rounded_corner_rect_location_; - } - int rounded_corner_radius_location() const { - return fragment_shader_.rounded_corner_radius_location_; - } - - const gfx::ColorTransform* color_transform_for_testing() const { - return fragment_shader_.color_transform_; - } - - private: - void InitializeDebugBorderProgram() { - // Initialize fragment program. - fragment_shader_.input_color_type_ = INPUT_COLOR_SOURCE_UNIFORM; - fragment_shader_.frag_color_mode_ = FRAG_COLOR_MODE_DEFAULT; - } - - void InitializeSolidColorProgram(const ProgramKey& key) { - // Initialize vertex program. - vertex_shader_.position_source_ = POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM; -#if BUILDFLAG(IS_ANDROID) - if (key.aa_mode_ == NO_AA) - vertex_shader_.has_dummy_variables_ = true; -#endif - - // Initialize fragment program. - fragment_shader_.input_color_type_ = INPUT_COLOR_SOURCE_UNIFORM; - fragment_shader_.frag_color_mode_ = FRAG_COLOR_MODE_DEFAULT; - } - - void InitializeTileProgram(const ProgramKey& key) { - // Initialize vertex program. - vertex_shader_.position_source_ = POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM; - vertex_shader_.tex_coord_transform_ = TEX_COORD_TRANSFORM_VEC4; - vertex_shader_.tex_coord_source_ = TEX_COORD_SOURCE_ATTRIBUTE; - - // Initialize fragment program. - fragment_shader_.has_tex_clamp_rect_ = key.has_tex_clamp_rect_; - if (key.is_opaque_) { - DCHECK_EQ(key.aa_mode_, NO_AA); - fragment_shader_.frag_color_mode_ = FRAG_COLOR_MODE_OPAQUE; - } else { - // TODO(ccameron): This branch shouldn't be needed (this is always - // BLEND_MODE_NONE). - if (key.aa_mode_ == NO_AA) - fragment_shader_.frag_color_mode_ = FRAG_COLOR_MODE_APPLY_BLEND_MODE; - fragment_shader_.has_uniform_alpha_ = true; - } - // AA changes the texture coordinate mode (affecting both shaders). - if (key.aa_mode_ == USE_AA) { - vertex_shader_.tex_coord_source_ = TEX_COORD_SOURCE_POSITION; - vertex_shader_.aa_mode_ = USE_AA; - fragment_shader_.has_rgba_fragment_tex_transform_ = true; - // Tiles that have AA do their own clamping. - DCHECK(!fragment_shader_.has_tex_clamp_rect_); - } - } - - void InitializeTextureProgram(const ProgramKey& key) { - // Initialize vertex program. - vertex_shader_.tex_coord_source_ = TEX_COORD_SOURCE_ATTRIBUTE; - vertex_shader_.tex_coord_transform_ = TEX_COORD_TRANSFORM_VEC4; - vertex_shader_.has_vertex_opacity_ = true; - vertex_shader_.use_uniform_arrays_ = !key.has_tex_clamp_rect_; - - // Initialize fragment program. - fragment_shader_.has_varying_alpha_ = true; - fragment_shader_.has_background_color_ = key.has_background_color_; - fragment_shader_.has_tex_clamp_rect_ = key.has_tex_clamp_rect_; - } - - void InitializeRenderPassProgram(const ProgramKey& key) { - // Initialize vertex program. - if (key.aa_mode_ == NO_AA) { - vertex_shader_.tex_coord_source_ = TEX_COORD_SOURCE_ATTRIBUTE; - vertex_shader_.tex_coord_transform_ = TEX_COORD_TRANSFORM_VEC4; - vertex_shader_.has_vertex_opacity_ = true; - vertex_shader_.use_uniform_arrays_ = true; - } else { - vertex_shader_.position_source_ = - POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM; - vertex_shader_.tex_coord_source_ = TEX_COORD_SOURCE_POSITION; - vertex_shader_.tex_coord_transform_ = TEX_COORD_TRANSFORM_TRANSLATED_VEC4; - } - - // Initialize fragment program. - fragment_shader_.frag_color_mode_ = FRAG_COLOR_MODE_APPLY_BLEND_MODE; - fragment_shader_.has_uniform_alpha_ = true; - fragment_shader_.has_color_matrix_ = key.has_color_matrix_; - if (key.mask_mode_ == HAS_MASK) { - fragment_shader_.ignore_sampler_type_ = true; - } else { - DCHECK(!key.mask_for_background_); - } - } - - void InitializeVideoStreamProgram(const ProgramKey& key) { - vertex_shader_.tex_coord_source_ = TEX_COORD_SOURCE_ATTRIBUTE; - vertex_shader_.tex_coord_transform_ = TEX_COORD_TRANSFORM_MATRIX; - DCHECK_EQ(key.sampler_, SAMPLER_TYPE_EXTERNAL_OES); - } - - void InitializeYUVVideo(const ProgramKey& key) { - vertex_shader_.tex_coord_source_ = TEX_COORD_SOURCE_ATTRIBUTE; - vertex_shader_.is_ya_uv_ = true; - - fragment_shader_.input_color_type_ = INPUT_COLOR_SOURCE_YUV_TEXTURES; - fragment_shader_.has_uniform_alpha_ = true; - fragment_shader_.yuv_alpha_texture_mode_ = key.yuv_alpha_texture_mode_; - fragment_shader_.uv_texture_mode_ = key.uv_texture_mode_; - } - - void InitializeInternal(ContextProvider* context_provider) { - DCHECK(context_provider); - DCHECK(!initialized_); - - if (IsContextLost(context_provider->ContextGL())) - return; - - if (!ProgramBindingBase::Init(context_provider->ContextGL(), - vertex_shader_.GetShaderString(), - fragment_shader_.GetShaderString())) { - DCHECK(IsContextLost(context_provider->ContextGL())); - return; - } - - int base_uniform_index = 0; - vertex_shader_.Init(context_provider->ContextGL(), program_, - &base_uniform_index); - fragment_shader_.Init(context_provider->ContextGL(), program_, - &base_uniform_index); - - // Link after binding uniforms - if (!Link(context_provider->ContextGL())) { - DCHECK(IsContextLost(context_provider->ContextGL())); - return; - } - - initialized_ = true; - } - - VertexShader vertex_shader_; - FragmentShader fragment_shader_; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_PROGRAM_BINDING_H_ diff --git a/chromium/components/viz/service/display/renderer_perftest.cc b/chromium/components/viz/service/display/renderer_perftest.cc index 827da3a6da0..1ff1f6f4406 100644 --- a/chromium/components/viz/service/display/renderer_perftest.cc +++ b/chromium/components/viz/service/display/renderer_perftest.cc @@ -4,12 +4,11 @@ // This perf test measures the time from when the display compositor starts // drawing on the compositor thread to when a swap buffers occurs on the -// GPU main thread. It tests both GLRenderer and SkiaRenderer under -// simple work loads. +// GPU main thread. // // Example usage: // -// $ out/release/viz_perftests --gtest_filter="*RendererPerfTest*" \ +// $ out/release/viz_perftests --gtest_filter="RendererPerfTest*" \ // --use-gpu-in-tests --test-launcher-timeout=300000 \ // --perf-test-time-ms=240000 --disable_discard_framebuffer=1 \ // --use_virtualized_gl_contexts=1 @@ -26,24 +25,22 @@ #include "components/viz/client/client_resource_provider.h" #include "components/viz/common/display/renderer_settings.h" #include "components/viz/common/quads/texture_draw_quad.h" +#include "components/viz/common/quads/yuv_video_draw_quad.h" #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h" #include "components/viz/service/display/display.h" -#include "components/viz/service/display/gl_renderer.h" #include "components/viz/service/display/output_surface_client.h" #include "components/viz/service/display/overlay_processor_stub.h" #include "components/viz/service/display/skia_renderer.h" #include "components/viz/service/display/viz_perftest.h" -#include "components/viz/service/display_embedder/gl_output_surface_offscreen.h" -#include "components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h" #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h" #include "components/viz/service/display_embedder/skia_output_surface_dependency_impl.h" #include "components/viz/service/display_embedder/skia_output_surface_impl.h" -#include "components/viz/service/display_embedder/viz_process_context_provider.h" #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h" #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h" #include "components/viz/service/gl/gpu_service_impl.h" #include "components/viz/test/compositor_frame_helpers.h" #include "components/viz/test/test_gpu_service_holder.h" +#include "components/viz/test/test_in_process_context_provider.h" #include "gpu/command_buffer/client/shared_image_interface.h" #include "gpu/command_buffer/common/shared_image_usage.h" #include "testing/gtest/include/gtest/gtest.h" @@ -229,7 +226,6 @@ void CreateTestTileDrawQuad(ResourceId resource_id, } // namespace -template <typename RendererType> class RendererPerfTest : public VizPerfTest { public: RendererPerfTest() @@ -243,19 +239,15 @@ class RendererPerfTest : public VizPerfTest { RendererPerfTest(const RendererPerfTest&) = delete; RendererPerfTest& operator=(const RendererPerfTest&) = delete; - // Overloaded for concrete RendererType below. - std::unique_ptr<OutputSurface> CreateOutputSurface( + std::unique_ptr<SkiaOutputSurface> CreateOutputSurface( GpuServiceImpl* gpu_service, - DisplayCompositorMemoryAndTaskController* display_controller); + DisplayCompositorMemoryAndTaskController* display_controller) { + return SkiaOutputSurfaceImpl::Create(display_controller, renderer_settings_, + &debug_settings_); + } 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) - printf("Using SkiaRenderer\n"); - else - printf("Using GLRenderer\n"); #if BUILDFLAG(IS_ANDROID) renderer_settings_.color_space = gfx::ColorSpace::CreateSRGB(); @@ -264,42 +256,18 @@ class RendererPerfTest : public VizPerfTest { auto* gpu_service = TestGpuServiceHolder::GetInstance()->gpu_service(); - gpu_memory_buffer_manager_ = - std::make_unique<InProcessGpuMemoryBufferManager>( - gpu_service->gpu_memory_buffer_factory(), - gpu_service->sync_point_manager()); - gpu::ImageFactory* image_factory = gpu_service->gpu_image_factory(); - auto* gpu_channel_manager_delegate = - gpu_service->gpu_channel_manager()->delegate(); - auto child_task_scheduler = std::make_unique<gpu::GpuTaskSchedulerHelper>( - TestGpuServiceHolder::GetInstance()->task_executor()); - child_gpu_dependency_ = - std::make_unique<DisplayCompositorMemoryAndTaskController>( - TestGpuServiceHolder::GetInstance()->task_executor(), - image_factory); - child_context_provider_ = base::MakeRefCounted<VizProcessContextProvider>( - TestGpuServiceHolder::GetInstance()->task_executor(), - gpu::kNullSurfaceHandle, gpu_memory_buffer_manager_.get(), - image_factory, gpu_channel_manager_delegate, - child_gpu_dependency_.get(), renderer_settings_); + child_context_provider_ = + base::MakeRefCounted<TestInProcessContextProvider>( + TestContextType::kGLES2, /*support_locking=*/false); child_context_provider_->BindToCurrentThread(); child_resource_provider_ = std::make_unique<ClientResourceProvider>(); - std::unique_ptr<DisplayCompositorMemoryAndTaskController> - display_controller; - if (renderer_settings_.use_skia_renderer) { - auto skia_deps = std::make_unique<SkiaOutputSurfaceDependencyImpl>( - gpu_service, gpu::kNullSurfaceHandle); - display_controller = - std::make_unique<DisplayCompositorMemoryAndTaskController>( - std::move(skia_deps)); - } else { - auto* task_executor = - TestGpuServiceHolder::GetInstance()->task_executor(); - display_controller = - std::make_unique<DisplayCompositorMemoryAndTaskController>( - task_executor, image_factory); - } + auto skia_deps = std::make_unique<SkiaOutputSurfaceDependencyImpl>( + gpu_service, gpu::kNullSurfaceHandle); + auto display_controller = + std::make_unique<DisplayCompositorMemoryAndTaskController>( + std::move(skia_deps)); + auto output_surface = CreateOutputSurface(gpu_service, display_controller.get()); // WaitForSwapDisplayClient depends on this. @@ -320,8 +288,7 @@ class RendererPerfTest : public VizPerfTest { void TearDown() override { std::string story = - renderer_settings_.use_skia_renderer ? "SkiaRenderer_" : "GLRenderer_"; - story += ::testing::UnitTest::GetInstance()->current_test_info()->name(); + ::testing::UnitTest::GetInstance()->current_test_info()->name(); auto reporter = SetUpRendererReporter(story); reporter.AddResult(kMetricFps, timer_.LapsPerSecond()); @@ -358,8 +325,6 @@ class RendererPerfTest : public VizPerfTest { child_resource_provider_->ShutdownAndReleaseAllResources(); child_resource_provider_.reset(); child_context_provider_.reset(); - child_gpu_dependency_.reset(); - gpu_memory_buffer_manager_.reset(); display_.reset(); } @@ -661,69 +626,37 @@ class RendererPerfTest : public VizPerfTest { ServerSharedBitmapManager shared_bitmap_manager_; FrameSinkManagerImpl manager_; std::unique_ptr<CompositorFrameSinkSupport> support_; - std::unique_ptr<gpu::GpuMemoryBufferManager> gpu_memory_buffer_manager_; RendererSettings renderer_settings_; DebugRendererSettings debug_settings_; std::unique_ptr<Display> display_; - std::unique_ptr<DisplayCompositorMemoryAndTaskController> - child_gpu_dependency_; scoped_refptr<ContextProvider> child_context_provider_; std::unique_ptr<ClientResourceProvider> child_resource_provider_; std::vector<TransferableResource> resource_list_; std::unique_ptr<gl::DisableNullDrawGLBindings> enable_pixel_output_; }; -template <> -std::unique_ptr<OutputSurface> -RendererPerfTest<SkiaRenderer>::CreateOutputSurface( - GpuServiceImpl* gpu_service, - DisplayCompositorMemoryAndTaskController* display_controller) { - return SkiaOutputSurfaceImpl::Create( - display_controller, renderer_settings_, &debug_settings_); -} - -template <> -std::unique_ptr<OutputSurface> -RendererPerfTest<GLRenderer>::CreateOutputSurface( - GpuServiceImpl* gpu_service, - DisplayCompositorMemoryAndTaskController* display_controller) { - gpu::ImageFactory* image_factory = gpu_service->gpu_image_factory(); - auto* gpu_channel_manager_delegate = - gpu_service->gpu_channel_manager()->delegate(); - auto context_provider = base::MakeRefCounted<VizProcessContextProvider>( - TestGpuServiceHolder::GetInstance()->task_executor(), - gpu::kNullSurfaceHandle, gpu_memory_buffer_manager_.get(), image_factory, - gpu_channel_manager_delegate, display_controller, renderer_settings_); - context_provider->BindToCurrentThread(); - return std::make_unique<GLOutputSurfaceOffscreen>( - std::move(context_provider)); -} - -using RendererTypes = ::testing::Types<GLRenderer, SkiaRenderer>; -TYPED_TEST_SUITE(RendererPerfTest, RendererTypes); - -TYPED_TEST(RendererPerfTest, SingleTextureQuad) { +TEST_F(RendererPerfTest, SingleTextureQuad) { this->RunSingleTextureQuad(); } -TYPED_TEST(RendererPerfTest, TextureQuads5x5) { +TEST_F(RendererPerfTest, TextureQuads5x5) { this->RunTextureQuads5x5(); } -TYPED_TEST(RendererPerfTest, TextureQuads5x5SameTex) { +TEST_F(RendererPerfTest, TextureQuads5x5SameTex) { this->RunTextureQuads5x5SameTex(); } -TYPED_TEST(RendererPerfTest, RotatedTileQuadsShared) { +TEST_F(RendererPerfTest, RotatedTileQuadsShared) { this->RunRotatedTileQuadsShared(); } -TYPED_TEST(RendererPerfTest, RotatedTileQuads) { +TEST_F(RendererPerfTest, RotatedTileQuads) { this->RunRotatedTileQuads(); } #define TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(SITE, FRAME) \ - TYPED_TEST(RendererPerfTest, SITE) { \ + TEST_F(RendererPerfTest, SITE) { \ this->RunSingleRenderPassListFromJSON(/*tag=*/"top_real_world_desktop", \ /*site=*/#SITE, /*year=*/2018, \ /*frame_index=*/FRAME); \ diff --git a/chromium/components/viz/service/display/renderer_pixeltest.cc b/chromium/components/viz/service/display/renderer_pixeltest.cc index 15651bf9388..32312f8cd58 100644 --- a/chromium/components/viz/service/display/renderer_pixeltest.cc +++ b/chromium/components/viz/service/display/renderer_pixeltest.cc @@ -29,20 +29,22 @@ #include "cc/test/test_types.h" #include "components/viz/client/client_resource_provider.h" #include "components/viz/common/features.h" +#include "components/viz/common/quads/aggregated_render_pass_draw_quad.h" +#include "components/viz/common/quads/compositor_render_pass_draw_quad.h" #include "components/viz/common/quads/picture_draw_quad.h" +#include "components/viz/common/quads/solid_color_draw_quad.h" #include "components/viz/common/quads/texture_draw_quad.h" +#include "components/viz/common/quads/yuv_video_draw_quad.h" #include "components/viz/common/resources/bitmap_allocation.h" #include "components/viz/common/resources/resource_format_utils.h" #include "components/viz/common/switches.h" #include "components/viz/service/display/delegated_ink_point_pixel_test_helper.h" -#include "components/viz/service/display/gl_renderer.h" #include "components/viz/service/display/software_renderer.h" #include "components/viz/service/display/viz_pixel_test.h" #include "components/viz/test/buildflags.h" #include "components/viz/test/test_in_process_context_provider.h" #include "components/viz/test/test_shared_bitmap_manager.h" #include "components/viz/test/test_types.h" -#include "gpu/command_buffer/client/gles2_interface.h" #include "gpu/command_buffer/client/shared_image_interface.h" #include "gpu/command_buffer/common/shared_image_usage.h" #include "media/base/video_frame.h" @@ -58,8 +60,6 @@ #include "ui/gfx/geometry/mask_filter_info.h" #include "ui/gfx/test/icc_profiles.h" -using gpu::gles2::GLES2Interface; - namespace viz { namespace { @@ -905,27 +905,6 @@ INSTANTIATE_TEST_SUITE_P(, // GetGpuRendererTypesNoDawn() can return an empty list, e.g. on Fuchsia ARM64. GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GPURendererPixelTest); -// Provides an exact comparator for GLRenderer and fuzzy comparator for Skia -// based (eg. SoftwareRenderer and SkiaRenderer). -class FuzzyForSkiaOnlyPixelComparator : public cc::PixelComparator { - public: - explicit FuzzyForSkiaOnlyPixelComparator(RendererType type) { - if (type == RendererType::kGL) { - comparator_ = std::make_unique<cc::ExactPixelComparator>(false); - } else { - comparator_ = std::make_unique<cc::FuzzyPixelOffByOneComparator>(false); - } - } - - bool Compare(const SkBitmap& actual_bmp, - const SkBitmap& expected_bmp) const override { - return comparator_->Compare(actual_bmp, expected_bmp); - } - - private: - std::unique_ptr<cc::PixelComparator> comparator_; -}; - TEST_P(RendererPixelTest, SimpleGreenRect) { gfx::Rect rect(this->device_viewport_size_); @@ -1123,6 +1102,13 @@ TEST_P(RendererPixelTest, } TEST_P(RendererPixelTest, TextureDrawQuadVisibleRectInsetBottomRight) { +#if BUILDFLAG(IS_LINUX) && defined(THREAD_SANITIZER) + // Test is flaking with failed large allocations under TSAN when using + // SkiaRenderer with GL backend. See https://crbug.com/1320955. + if (renderer_type() == RendererType::kSkiaGL) + return; +#endif + gfx::Rect rect(this->device_viewport_size_); AggregatedRenderPassId id{1}; @@ -1781,25 +1767,16 @@ class VideoRendererPixelTestBase : public VizPixelTest { }; #if BUILDFLAG(ENABLE_GL_BACKEND_TESTS) -class VideoRendererPixelHiLoTest - : public VideoRendererPixelTestBase, - public testing::WithParamInterface<std::tuple<RendererType, bool>> { +class VideoRendererPixelHiLoTest : public VideoRendererPixelTestBase, + public testing::WithParamInterface<bool> { public: VideoRendererPixelHiLoTest() - : VideoRendererPixelTestBase(std::get<0>(GetParam())) {} + : VideoRendererPixelTestBase(RendererType::kSkiaGL) {} - bool IsHighbit() const { return std::get<1>(GetParam()); } + bool IsHighbit() const { return GetParam(); } }; -INSTANTIATE_TEST_SUITE_P(, - VideoRendererPixelHiLoTest, - testing::Combine(testing::Values( -#if BUILDFLAG(ENABLE_GL_RENDERER_TESTS) - RendererType::kGL, -#endif - RendererType::kSkiaGL), - testing::Bool()), - cc::PrintTupleToStringParamName()); +INSTANTIATE_TEST_SUITE_P(, VideoRendererPixelHiLoTest, testing::Bool()); TEST_P(VideoRendererPixelHiLoTest, SimpleYUVRect) { gfx::Rect rect(this->device_viewport_size_); @@ -2215,7 +2192,7 @@ TEST_P(RendererPixelTest, FastPassColorFilterAlpha) { // renderer so use a fuzzy comparator. EXPECT_TRUE(this->RunPixelTest( &pass_list, base::FilePath(FILE_PATH_LITERAL("blue_yellow_alpha.png")), - FuzzyForSkiaOnlyPixelComparator(renderer_type()))); + cc::FuzzyPixelOffByOneComparator(false))); } TEST_P(RendererPixelTest, FastPassSaturateFilter) { @@ -2275,7 +2252,7 @@ TEST_P(RendererPixelTest, FastPassSaturateFilter) { // renderer so use a fuzzy comparator. EXPECT_TRUE(this->RunPixelTest( &pass_list, base::FilePath(FILE_PATH_LITERAL("blue_yellow_alpha.png")), - FuzzyForSkiaOnlyPixelComparator(renderer_type()))); + cc::FuzzyPixelOffByOneComparator(false))); } TEST_P(RendererPixelTest, FastPassFilterChain) { @@ -2337,7 +2314,7 @@ TEST_P(RendererPixelTest, FastPassFilterChain) { EXPECT_TRUE(this->RunPixelTest( &pass_list, base::FilePath(FILE_PATH_LITERAL("blue_yellow_filter_chain.png")), - FuzzyForSkiaOnlyPixelComparator(renderer_type()))); + cc::FuzzyPixelOffByOneComparator(false))); } TEST_P(RendererPixelTest, FastPassColorFilterAlphaTranslation) { @@ -2420,7 +2397,7 @@ TEST_P(RendererPixelTest, FastPassColorFilterAlphaTranslation) { EXPECT_TRUE(this->RunPixelTest( &pass_list, base::FilePath(FILE_PATH_LITERAL("blue_yellow_alpha_translate.png")), - FuzzyForSkiaOnlyPixelComparator(renderer_type()))); + cc::FuzzyPixelOffByOneComparator(false))); } TEST_P(RendererPixelTest, EnlargedRenderPassTexture) { @@ -3083,11 +3060,6 @@ TEST_P(RendererPixelTestWithBackdropFilter, InvertFilter) { } TEST_P(RendererPixelTestWithBackdropFilter, InvertFilterWithMask) { - // TODO(crbug.com/989312): Delete this condition with GLRendere. The mask - // appears to be offset from the correct location but this isn't relevant. - if (is_gl_renderer()) - return; - this->backdrop_filters_.Append(cc::FilterOperation::CreateInvertFilter(1.f)); this->filter_pass_layer_rect_ = gfx::Rect(this->device_viewport_size_); this->filter_pass_layer_rect_.Inset(gfx::Insets::TLBR(14, 12, 18, 16)); @@ -3105,170 +3077,6 @@ TEST_P(RendererPixelTestWithBackdropFilter, InvertFilterWithMask) { cc::FuzzyPixelOffByOneComparator(false))); } -#if BUILDFLAG(ENABLE_GL_RENDERER_TESTS) -class GLRendererPixelTestWithBackdropFilter : public VizPixelTest { - public: - GLRendererPixelTestWithBackdropFilter() : VizPixelTest(RendererType::kGL) {} - - protected: - void SetUpRenderPassList() { - pass_list_.clear(); - gfx::Rect device_viewport_rect(this->device_viewport_size_); - - AggregatedRenderPassId root_id{1}; - auto root_pass = CreateTestRootRenderPass(root_id, device_viewport_rect); - root_pass->has_transparent_background = false; - - gfx::Transform identity_quad_to_target_transform; - - AggregatedRenderPassId filter_pass_id{2}; - gfx::Transform transform_to_root; - auto filter_pass = CreateTestRenderPass( - filter_pass_id, filter_pass_layer_rect_, transform_to_root); - filter_pass->backdrop_filters = this->backdrop_filters_; - filter_pass->backdrop_filter_bounds = this->backdrop_filter_bounds_; - - // A non-visible quad in the filtering render pass. - { - SharedQuadState* shared_state = CreateTestSharedQuadState( - identity_quad_to_target_transform, filter_pass_layer_rect_, - filter_pass.get(), gfx::MaskFilterInfo()); - auto* color_quad = - filter_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); - color_quad->SetNew(shared_state, filter_pass_layer_rect_, - filter_pass_layer_rect_, SK_ColorTRANSPARENT, false); - } - - { - SharedQuadState* shared_state = CreateTestSharedQuadState( - filter_pass_to_target_transform_, filter_pass_layer_rect_, - filter_pass.get(), gfx::MaskFilterInfo()); - auto* filter_pass_quad = - root_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); - filter_pass_quad->SetAll( - shared_state, filter_pass_layer_rect_, filter_pass_layer_rect_, - /*needs_blending=*/true, filter_pass_id, kInvalidResourceId, - gfx::RectF(), gfx::Size(), - gfx::Vector2dF(1.0f, 1.0f), // filters_scale - gfx::PointF(), // filters_origin - gfx::RectF(), // tex_coord_rect - false, // force_anti_aliasing_off - backdrop_filter_quality_, // backdrop_filter_quality - intersects_damage_under_); - } - - const int kGridWidth = device_viewport_rect.width() / 3; - const int kGridHeight = device_viewport_rect.height() / 3; - gfx::Rect left_rect = - gfx::Rect(kGridWidth / 2, kGridHeight, kGridWidth, kGridHeight); - - SharedQuadState* shared_state = CreateTestSharedQuadState( - identity_quad_to_target_transform, left_rect, root_pass.get(), - gfx::MaskFilterInfo()); - auto* color_quad = root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); - color_quad->SetNew(shared_state, left_rect, left_rect, SK_ColorGREEN, - false); - - gfx::Rect right_rect = - gfx::Rect(kGridWidth * 3 / 2, kGridHeight, kGridWidth, kGridHeight); - shared_state = CreateTestSharedQuadState( - identity_quad_to_target_transform, right_rect, root_pass.get(), - gfx::MaskFilterInfo()); - color_quad = root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); - color_quad->SetNew(shared_state, right_rect, right_rect, SK_ColorRED, - false); - - shared_state = CreateTestSharedQuadState( - identity_quad_to_target_transform, device_viewport_rect, - root_pass.get(), gfx::MaskFilterInfo()); - auto* background_quad = - root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); - background_quad->SetNew(shared_state, device_viewport_rect, - device_viewport_rect, SK_ColorWHITE, false); - - pass_list_.push_back(std::move(filter_pass)); - pass_list_.push_back(std::move(root_pass)); - } - - AggregatedRenderPassList pass_list_; - cc::FilterOperations backdrop_filters_; - absl::optional<gfx::RRectF> backdrop_filter_bounds_; - float backdrop_filter_quality_ = 1.0f; - bool intersects_damage_under_ = true; - gfx::Transform filter_pass_to_target_transform_; - gfx::Rect filter_pass_layer_rect_; -}; - -TEST_F(GLRendererPixelTestWithBackdropFilter, FilterQuality) { - this->backdrop_filters_.Append(cc::FilterOperation::CreateBlurFilter(2.0f)); - this->filter_pass_layer_rect_ = gfx::Rect(this->device_viewport_size_); - this->backdrop_filter_bounds_ = - gfx::RRectF(gfx::RectF(this->filter_pass_layer_rect_)); - this->backdrop_filter_quality_ = 1.0f; - this->SetUpRenderPassList(); - EXPECT_TRUE(this->RunPixelTest( - &this->pass_list_, - base::FilePath(FILE_PATH_LITERAL("gl_backdrop_filter_1.png")), - cc::FuzzyPixelOffByOneComparator(true))); - - if (this->context_provider()->ContextCapabilities().major_version < 3) - return; - this->backdrop_filter_quality_ = 0.33f; - this->SetUpRenderPassList(); - EXPECT_TRUE(this->RunPixelTest( - &this->pass_list_, - base::FilePath(FILE_PATH_LITERAL("gl_backdrop_filter_2.png")), - cc::FuzzyPixelOffByOneComparator(true))); -} - -TEST_F(GLRendererPixelTestWithBackdropFilter, CachedResultOfBackdropFilter) { - this->backdrop_filters_.Append(cc::FilterOperation::CreateBlurFilter(2.0f)); - this->filter_pass_layer_rect_ = gfx::Rect(this->device_viewport_size_); - this->backdrop_filter_bounds_ = - gfx::RRectF(gfx::RectF(this->filter_pass_layer_rect_)); - // Set the flag to use cached backdrop filtered texture. This makes the - // GLRenderer cache backdrop filtered result. - this->intersects_damage_under_ = false; - this->SetUpRenderPassList(); - - EXPECT_TRUE(this->RunPixelTest( - &this->pass_list_, - base::FilePath(FILE_PATH_LITERAL("gl_backdrop_filter_1.png")), - cc::FuzzyPixelOffByOneComparator(true))); - - // Same render pass list makes the GLRenderer to skip backdrop filter - // calculation and use cached texture. This should correctly produce the - // same output image. - this->SetUpRenderPassList(); - EXPECT_TRUE(this->RunPixelTest( - &this->pass_list_, - base::FilePath(FILE_PATH_LITERAL("gl_backdrop_filter_1.png")), - cc::FuzzyPixelOffByOneComparator(true))); - - // To prove the cached texture is used, change a quad on the root pass which - // is beneath the backdrop filter. The output image should still be the same - // as before. - this->SetUpRenderPassList(); - DrawQuad* background_quad = *pass_list_.back()->quad_list.rbegin(); - static_cast<SolidColorDrawQuad*>(background_quad)->color = SK_ColorYELLOW; - EXPECT_TRUE(this->RunPixelTest( - &this->pass_list_, - base::FilePath(FILE_PATH_LITERAL("gl_backdrop_filter_1.png")), - cc::FuzzyPixelOffByOneComparator(true))); - - // Set |intersects_damage_under_| to true to make GLRenderer re-run the - // backdrop filter calculation - this->intersects_damage_under_ = true; - this->SetUpRenderPassList(); - background_quad = *pass_list_.back()->quad_list.rbegin(); - static_cast<SolidColorDrawQuad*>(background_quad)->color = SK_ColorYELLOW; - EXPECT_TRUE(this->RunPixelTest( - &this->pass_list_, - base::FilePath(FILE_PATH_LITERAL("gl_backdrop_filter_3.png")), - cc::FuzzyPixelOffByOneComparator(true))); -} -#endif - // Software renderer does not support anti-aliased edges. TEST_P(GPURendererPixelTest, AntiAliasing) { gfx::Rect rect(this->device_viewport_size_); @@ -4590,21 +4398,10 @@ TEST_P(RendererPixelTest, RoundedCornerSimpleSolidDrawQuad) { AggregatedRenderPassList pass_list; pass_list.push_back(std::move(root_pass)); - if (is_gl_renderer()) { - // GL Renderer should have an exact match as that is the reference point. - EXPECT_TRUE(this->RunPixelTest( - &pass_list, - base::FilePath(FILE_PATH_LITERAL("rounded_corner_simple.png")), - cc::ExactPixelComparator(true))); - } else { - // Software/skia renderer uses skia rrect to create rounded corner clip. - // This results in a different corner path due to a different anti aliasing - // approach than the fragment shader in gl renderer. - EXPECT_TRUE(this->RunPixelTest( - &pass_list, - base::FilePath(FILE_PATH_LITERAL("rounded_corner_simple.png")), - cc::FuzzyPixelComparator(true, 0.55f, 0.f, 255.f, 255, 0))); - } + EXPECT_TRUE(this->RunPixelTest( + &pass_list, + base::FilePath(FILE_PATH_LITERAL("rounded_corner_simple.png")), + cc::FuzzyPixelComparator(true, 0.55f, 0.f, 255.f, 255, 0))); } TEST_P(GPURendererPixelTest, RoundedCornerSimpleTextureDrawQuad) { @@ -4659,21 +4456,10 @@ TEST_P(GPURendererPixelTest, RoundedCornerSimpleTextureDrawQuad) { AggregatedRenderPassList pass_list; pass_list.push_back(std::move(root_pass)); - if (is_gl_renderer()) { - // GL Renderer should have an exact match as that is the reference point. - EXPECT_TRUE(this->RunPixelTest( - &pass_list, - base::FilePath(FILE_PATH_LITERAL("rounded_corner_simple.png")), - cc::ExactPixelComparator(true))); - } else { - // SkiaRenderer uses skia rrect to create rounded corner clip. This results - // in a different corner path due to a different anti aliasing approach than - // the fragment shader in gl renderer. - EXPECT_TRUE(this->RunPixelTest( - &pass_list, - base::FilePath(FILE_PATH_LITERAL("rounded_corner_simple.png")), - cc::FuzzyPixelComparator(true, 0.6f, 0.f, 255.f, 255, 0))); - } + EXPECT_TRUE(this->RunPixelTest( + &pass_list, + base::FilePath(FILE_PATH_LITERAL("rounded_corner_simple.png")), + cc::FuzzyPixelComparator(true, 0.6f, 0.f, 255.f, 255, 0))); } TEST_P(RendererPixelTest, RoundedCornerOnRenderPass) { @@ -4774,21 +4560,13 @@ TEST_P(RendererPixelTest, RoundedCornerMultiRadii) { AggregatedRenderPassList pass_list; pass_list.push_back(std::move(root_pass)); - if (is_gl_renderer()) { - // GL Renderer should have an exact match as that is the reference point. - EXPECT_TRUE(this->RunPixelTest( - &pass_list, - base::FilePath(FILE_PATH_LITERAL("rounded_corner_multi_radii.png")), - cc::ExactPixelComparator(true))); - } else { - // Software/skia renderer uses skia rrect to create rounded corner clip. - // This results in a different corner path due to a different anti aliasing - // approach than the fragment shader in gl renderer. - EXPECT_TRUE(this->RunPixelTest( - &pass_list, - base::FilePath(FILE_PATH_LITERAL("rounded_corner_multi_radii.png")), - cc::FuzzyPixelComparator(true, 0.55f, 0.f, 255.f, 255, 0))); - } + // Software/skia renderer uses skia rrect to create rounded corner clip. + // This results in a different corner path due to a different anti aliasing + // approach than the fragment shader in gl renderer. + EXPECT_TRUE(this->RunPixelTest( + &pass_list, + base::FilePath(FILE_PATH_LITERAL("rounded_corner_multi_radii.png")), + cc::FuzzyPixelComparator(true, 0.55f, 0.f, 255.f, 255, 0))); } TEST_P(RendererPixelTest, RoundedCornerMultipleQads) { @@ -4867,21 +4645,11 @@ TEST_P(RendererPixelTest, RoundedCornerMultipleQads) { AggregatedRenderPassList pass_list; pass_list.push_back(std::move(root_pass)); - // GL Renderer should have an exact match as that is the reference point. - // Software/skia renderer use skia rrect to create rounded corner clip. - // This results in a different corner path due to a different anti aliasing - // approach than the fragment shader in gl renderer. - std::unique_ptr<cc::PixelComparator> comparator; - comparator.reset( - is_gl_renderer() - ? static_cast<cc::PixelComparator*>( - new cc::ExactPixelComparator(true)) - : static_cast<cc::PixelComparator*>( - new cc::FuzzyPixelComparator(true, 0.55f, 0.f, 255.f, 255, 0))); + cc::FuzzyPixelComparator comparator(true, 0.55f, 0.f, 255.f, 255, 0); EXPECT_TRUE(this->RunPixelTest( &pass_list, base::FilePath(FILE_PATH_LITERAL("rounded_corner_multi_quad.png")), - *comparator)); + comparator)); } class RendererPixelTestWithOverdrawFeedback : public VizPixelTestWithParam { @@ -4913,19 +4681,12 @@ TEST_P(RendererPixelTestWithOverdrawFeedback, TranslucentRectangles) { AggregatedRenderPassList pass_list; pass_list.push_back(std::move(pass)); - if (is_gl_renderer()) { - EXPECT_TRUE(this->RunPixelTest( - &pass_list, - base::FilePath(FILE_PATH_LITERAL("translucent_rectangles.png")), - cc::ExactPixelComparator(true))); - } else { - // TODO(xing.xu): investigate why overdraw feedback has small difference - // (http://crbug.com/909971) - EXPECT_TRUE(this->RunPixelTest( - &pass_list, - base::FilePath(FILE_PATH_LITERAL("skia_translucent_rectangles.png")), - cc::FuzzyPixelComparator(false, 2.f, 0.f, 256.f, 256, 0.f))); - } + // TODO(xing.xu): investigate why overdraw feedback has small difference + // (http://crbug.com/909971) + EXPECT_TRUE(this->RunPixelTest( + &pass_list, + base::FilePath(FILE_PATH_LITERAL("translucent_rectangles.png")), + cc::FuzzyPixelComparator(false, 2.f, 0.f, 256.f, 256, 0.f))); } INSTANTIATE_TEST_SUITE_P(, @@ -4962,28 +4723,13 @@ class ColorTransformPixelTest } this->display_color_spaces_ = gfx::DisplayColorSpaces(this->dst_color_space_); - float sdr_max_luminance_nits = - this->display_color_spaces_.GetSDRMaxLuminanceNits(); - if (src_color_space_.GetSDRWhiteLevel(&sdr_max_luminance_nits)) { - this->display_color_spaces_.SetSDRMaxLuminanceNits( - sdr_max_luminance_nits); - } this->premultiplied_alpha_ = std::get<3>(GetParam()); } void Basic() { - // Skip piecewise transfer functions because SkColorSpace (needed for - // CopyOutputResult::AsSkBitmap) doesn't support them.. - if ((src_color_space_.GetTransferID() == TransferID::PIECEWISE_HDR || - dst_color_space_.GetTransferID() == TransferID::PIECEWISE_HDR)) { - LOG(ERROR) << "Skipping piecewise HDR function"; - return; - } - if (src_color_space_.GetTransferID() == TransferID::PQ && !dst_color_space_.IsHDR()) { - LOG(ERROR) << "Skipping tonemapped output"; - return; + GTEST_SKIP() << "Skipping tonemapped output"; } gfx::Rect rect(this->device_viewport_size_); @@ -5018,8 +4764,7 @@ class ColorTransformPixelTest } gfx::ColorTransform::Options options; - options.sdr_max_luminance_nits = - display_color_spaces_.GetSDRMaxLuminanceNits(); + options.sdr_max_luminance_nits = gfx::ColorSpace::kDefaultSDRWhiteLevel; std::unique_ptr<gfx::ColorTransform> transform = gfx::ColorTransform::NewColorTransform(this->src_color_space_, this->dst_color_space_, options); @@ -5086,9 +4831,19 @@ class ColorTransformPixelTest AggregatedRenderPassList pass_list; pass_list.push_back(std::move(pass)); - // Allow a difference of 2 bytes in comparison for shader-based transforms, - // and 4 bytes for LUT-based transforms (determined empirically). - cc::FuzzyPixelComparator comparator(false, 100.f, 0.f, 2.f, 2, 0); + // Allow a difference of 2 bytes in comparison for most cases. + float avg_abs_error_limit = 2.0f; + int max_abs_error_limit = 2; +#if BUILDFLAG(IS_FUCHSIA) + if (src_color_space_.GetTransferID() == TransferID::PQ) { + // Fuchsia+SwiftShader/Vulkan has higher error on some pixels with HDR + // color spaces. See https://crbug.com/1312141. + max_abs_error_limit = 5; + } +#endif + + cc::FuzzyPixelComparator comparator(false, 100.f, 0.f, avg_abs_error_limit, + max_abs_error_limit, 0); EXPECT_TRUE( this->RunPixelTest(&pass_list, &expected_output_colors, comparator)) << " src:" << src_color_space_ << ", dst:" << dst_color_space_; @@ -5099,13 +4854,14 @@ class ColorTransformPixelTest bool premultiplied_alpha_ = false; }; -// crbug.com/1312043 Disable the test due to flaky. -#if (BUILDFLAG(IS_LINUX) && defined(THREAD_SANITIZER)) || BUILDFLAG(IS_FUCHSIA) -#define MAYBE_Basic DISABLED_Basic -#else -#define MAYBE_Basic Basic +TEST_P(ColorTransformPixelTest, Basic) { +#if BUILDFLAG(IS_LINUX) && defined(THREAD_SANITIZER) + // Test is flaking with failed large allocations under TSAN when using + // SkiaRenderer with GL backend. See https://crbug.com/1320955. + if (renderer_type() == RendererType::kSkiaGL) + return; #endif -TEST_P(ColorTransformPixelTest, MAYBE_Basic) { + Basic(); } @@ -5121,10 +4877,7 @@ gfx::ColorSpace src_color_spaces[] = { gfx::ColorSpace(PrimaryID::BT709, TransferID::SMPTEST428_1), gfx::ColorSpace(PrimaryID::BT709, TransferID::SRGB_HDR), gfx::ColorSpace(PrimaryID::BT709, TransferID::LINEAR_HDR), - // Piecewise HDR transfer functions skipped with SkiaRenderer. - gfx::ColorSpace::CreatePiecewiseHDR(PrimaryID::BT709, 0.5, 1.5), - gfx::ColorSpace::CreateHDR10(50.f), - gfx::ColorSpace::CreateHDR10(250.f), + gfx::ColorSpace::CreateHDR10(), }; gfx::ColorSpace dst_color_spaces[] = { @@ -5138,8 +4891,6 @@ gfx::ColorSpace dst_color_spaces[] = { gfx::ColorSpace(PrimaryID::BT709, TransferID::SRGB), gfx::ColorSpace(PrimaryID::BT709, TransferID::SRGB_HDR), gfx::ColorSpace(PrimaryID::BT709, TransferID::LINEAR_HDR), - // Piecewise HDR transfer functions are skipped with SkiaRenderer. - gfx::ColorSpace::CreatePiecewiseHDR(PrimaryID::BT709, 0.25, 2.5), }; gfx::ColorSpace intermediate_color_spaces[] = { diff --git a/chromium/components/viz/service/display/resource_fence.h b/chromium/components/viz/service/display/resource_fence.h index dc4153ea490..59e1101247e 100644 --- a/chromium/components/viz/service/display/resource_fence.h +++ b/chromium/components/viz/service/display/resource_fence.h @@ -7,6 +7,8 @@ #include "base/memory/ref_counted.h" +#include "ui/gfx/gpu_fence_handle.h" + namespace viz { // An abstract interface used to ensure reading from resources passed between @@ -16,8 +18,16 @@ class ResourceFence : public base::RefCountedThreadSafe<ResourceFence> { ResourceFence(const ResourceFence&) = delete; ResourceFence& operator=(const ResourceFence&) = delete; + // Notifies the fence is needed. virtual void Set() = 0; + // Tells if the fence is ready. virtual bool HasPassed() = 0; + // A release fence which availability depends on the type of resource fence + // (managed by DisplayResourceProvider and + // TransferableResource::synchronization_type). The client must ensure that + // HasPassed is true before trying to access the release fence handle. + // Otherwise, it's not guaranteed that the fence handle is valid. + virtual gfx::GpuFenceHandle GetGpuFenceHandle() = 0; protected: friend class base::RefCountedThreadSafe<ResourceFence>; diff --git a/chromium/components/viz/service/display/scoped_gpu_memory_buffer_texture.cc b/chromium/components/viz/service/display/scoped_gpu_memory_buffer_texture.cc deleted file mode 100644 index e7968ed7377..00000000000 --- a/chromium/components/viz/service/display/scoped_gpu_memory_buffer_texture.cc +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2017 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/scoped_gpu_memory_buffer_texture.h" - -#include "base/check.h" -#include "components/viz/common/gpu/context_provider.h" -#include "components/viz/common/resources/resource_format.h" -#include "components/viz/common/resources/resource_format_utils.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "gpu/command_buffer/client/gles2_interface.h" -#include "gpu/command_buffer/common/gpu_memory_buffer_support.h" - -namespace viz { - -ScopedGpuMemoryBufferTexture::ScopedGpuMemoryBufferTexture( - ContextProvider* context_provider, - const gfx::Size& size, - const gfx::ColorSpace& color_space) - : context_provider_(context_provider), - size_(size), - color_space_(color_space) { - DCHECK(context_provider_); - - const auto& caps = context_provider->ContextCapabilities(); - // This capability is needed to use TexStorage2DImageCHROMIUM, and should be - // known to be enabled before using an object of this type. - DCHECK(caps.texture_storage_image); - - gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); - gl->GenTextures(1, &gl_id_); - - gfx::BufferUsage usage = gfx::BufferUsage::SCANOUT; - ResourceFormat format = RGBA_8888; - gfx::BufferFormat buffer_format = BufferFormat(format); - - target_ = gpu::GetBufferTextureTarget(usage, buffer_format, caps); - - gl->BindTexture(target_, gl_id_); - gl->TexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl->TexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl->TexParameteri(target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl->TexParameteri(target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - gl->TexStorage2DImageCHROMIUM( - target_, TextureStorageFormat(format, caps.angle_rgbx_internal_format), - GL_SCANOUT_CHROMIUM, size_.width(), size_.height()); - if (color_space_.IsValid()) { - gl->SetColorSpaceMetadataCHROMIUM(gl_id_, color_space_.AsGLColorSpace()); - } - gl->BindTexture(target_, 0); -} - -ScopedGpuMemoryBufferTexture::ScopedGpuMemoryBufferTexture() = default; - -ScopedGpuMemoryBufferTexture::~ScopedGpuMemoryBufferTexture() { - Free(); -} - -ScopedGpuMemoryBufferTexture::ScopedGpuMemoryBufferTexture( - ScopedGpuMemoryBufferTexture&& other) - : context_provider_(other.context_provider_), - gl_id_(other.gl_id_), - target_(other.target_), - size_(other.size_), - color_space_(other.color_space_) { - other.gl_id_ = 0; -} - -ScopedGpuMemoryBufferTexture& ScopedGpuMemoryBufferTexture::operator=( - ScopedGpuMemoryBufferTexture&& other) { - DCHECK(!context_provider_ || !other.context_provider_ || - context_provider_ == other.context_provider_); - if (this != &other) { - Free(); - context_provider_ = other.context_provider_; - gl_id_ = other.gl_id_; - target_ = other.target_; - size_ = other.size_; - color_space_ = other.color_space_; - - other.gl_id_ = 0; - } - return *this; -} - -void ScopedGpuMemoryBufferTexture::Free() { - if (!gl_id_) - return; - gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); - gl->DeleteTextures(1, &gl_id_); - gl_id_ = 0; -} - -} // namespace viz diff --git a/chromium/components/viz/service/display/scoped_gpu_memory_buffer_texture.h b/chromium/components/viz/service/display/scoped_gpu_memory_buffer_texture.h deleted file mode 100644 index 1711224bcb1..00000000000 --- a/chromium/components/viz/service/display/scoped_gpu_memory_buffer_texture.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2017 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_SCOPED_GPU_MEMORY_BUFFER_TEXTURE_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_SCOPED_GPU_MEMORY_BUFFER_TEXTURE_H_ - -#include "base/memory/raw_ptr.h" -#include "components/viz/service/viz_service_export.h" -#include "ui/gfx/color_space.h" -#include "ui/gfx/geometry/size.h" - -namespace viz { -class ContextProvider; - -// ScopedGpuMemoryBufferTexture is a GL texture backed by a GL image and a -// GpuMemoryBuffer, so that it can be used as an overlay. -class VIZ_SERVICE_EXPORT ScopedGpuMemoryBufferTexture { - public: - explicit ScopedGpuMemoryBufferTexture(ContextProvider* context_provider, - const gfx::Size& size, - const gfx::ColorSpace& color_space); - - ScopedGpuMemoryBufferTexture(); - ~ScopedGpuMemoryBufferTexture(); - - ScopedGpuMemoryBufferTexture(ScopedGpuMemoryBufferTexture&& other); - ScopedGpuMemoryBufferTexture& operator=(ScopedGpuMemoryBufferTexture&& other); - - uint32_t id() const { return gl_id_; } - uint32_t target() const { return target_; } - const gfx::Size& size() const { return size_; } - const gfx::ColorSpace& color_space() const { return color_space_; } - - private: - void Free(); - - // The ContextProvider used to free the texture when this object is destroyed, - // so it must outlive this object. - raw_ptr<ContextProvider> context_provider_ = nullptr; - uint32_t gl_id_ = 0; - uint32_t target_ = 0; - gfx::Size size_; - gfx::ColorSpace color_space_; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_SCOPED_GPU_MEMORY_BUFFER_TEXTURE_H_ diff --git a/chromium/components/viz/service/display/scoped_render_pass_texture.cc b/chromium/components/viz/service/display/scoped_render_pass_texture.cc deleted file mode 100644 index 9a668018e08..00000000000 --- a/chromium/components/viz/service/display/scoped_render_pass_texture.cc +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2017 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/scoped_render_pass_texture.h" - -#include <algorithm> - -#include "base/bits.h" -#include "base/check.h" -#include "components/viz/common/gpu/context_provider.h" -#include "components/viz/common/resources/resource_format_utils.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "gpu/command_buffer/client/gles2_interface.h" - -namespace viz { - -ScopedRenderPassTexture::ScopedRenderPassTexture() = default; - -ScopedRenderPassTexture::ScopedRenderPassTexture( - ContextProvider* context_provider, - const gfx::Size& size, - ResourceFormat format, - const gfx::ColorSpace& color_space, - bool mipmap) - : context_provider_(context_provider), - size_(size), - mipmap_(mipmap), - color_space_(color_space) { - DCHECK(context_provider_); - gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); - const gpu::Capabilities& caps = context_provider_->ContextCapabilities(); - gl->GenTextures(1, &gl_id_); - - gl->BindTexture(GL_TEXTURE_2D, gl_id_); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - // This texture will be bound as a framebuffer, so optimize for that. - if (caps.texture_usage) { - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_USAGE_ANGLE, - GL_FRAMEBUFFER_ATTACHMENT_ANGLE); - } - - if (caps.texture_storage) { - GLint levels = 1; - if (caps.texture_npot && mipmap_) - levels += base::bits::Log2Floor(std::max(size_.width(), size_.height())); - - gl->TexStorage2DEXT( - GL_TEXTURE_2D, levels, - TextureStorageFormat(format, context_provider_->ContextCapabilities() - .angle_rgbx_internal_format), - size_.width(), size_.height()); - } else { - DCHECK(GLSupportsFormat(format)); - gl->TexImage2D(GL_TEXTURE_2D, 0, GLInternalFormat(format), size_.width(), - size_.height(), 0, GLDataFormat(format), GLDataType(format), - nullptr); - } -} - -ScopedRenderPassTexture::~ScopedRenderPassTexture() { - Free(); -} - -ScopedRenderPassTexture::ScopedRenderPassTexture( - ScopedRenderPassTexture&& other) { - context_provider_ = other.context_provider_; - size_ = other.size_; - mipmap_ = other.mipmap_; - color_space_ = other.color_space_; - gl_id_ = other.gl_id_; - mipmap_state_ = other.mipmap_state_; - - // When being moved, other will no longer hold this gl_id_. - other.gl_id_ = 0; -} - -ScopedRenderPassTexture& ScopedRenderPassTexture::operator=( - ScopedRenderPassTexture&& other) { - if (this != &other) { - Free(); - context_provider_ = other.context_provider_; - size_ = other.size_; - mipmap_ = other.mipmap_; - color_space_ = other.color_space_; - gl_id_ = other.gl_id_; - mipmap_state_ = other.mipmap_state_; - - // When being moved, other will no longer hold this gl_id_. - other.gl_id_ = 0; - } - return *this; -} - -void ScopedRenderPassTexture::Free() { - if (!gl_id_) - return; - gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); - gl->DeleteTextures(1, &gl_id_); - gl_id_ = 0; -} - -void ScopedRenderPassTexture::BindForSampling() { - gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); - gl->BindTexture(GL_TEXTURE_2D, gl_id_); - switch (mipmap_state_) { - case INVALID: - break; - case GENERATE: - // TODO(crbug.com/803286): npot texture always return false on ubuntu - // desktop. The npot texture check is probably failing on desktop GL. - DCHECK(context_provider_->ContextCapabilities().texture_npot); - gl->GenerateMipmap(GL_TEXTURE_2D); - mipmap_state_ = VALID; - [[fallthrough]]; - case VALID: - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, - GL_LINEAR_MIPMAP_LINEAR); - break; - } -} - -} // namespace viz diff --git a/chromium/components/viz/service/display/scoped_render_pass_texture.h b/chromium/components/viz/service/display/scoped_render_pass_texture.h deleted file mode 100644 index da29d49b70b..00000000000 --- a/chromium/components/viz/service/display/scoped_render_pass_texture.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2017 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_SCOPED_RENDER_PASS_TEXTURE_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_SCOPED_RENDER_PASS_TEXTURE_H_ - -#include "base/memory/raw_ptr.h" -#include "components/viz/common/resources/resource_format.h" -#include "components/viz/service/viz_service_export.h" -#include "third_party/khronos/GLES2/gl2.h" -#include "ui/gfx/color_space.h" -#include "ui/gfx/geometry/size.h" - -namespace viz { -class ContextProvider; - -// ScopedRenderPassTexture is resource used inside the same GL context and will -// not being sent into another process. So no need to create fence and mailbox -// for these resources. -class VIZ_SERVICE_EXPORT ScopedRenderPassTexture { - public: - ScopedRenderPassTexture(); - ScopedRenderPassTexture(ContextProvider* context_provider, - const gfx::Size& size, - ResourceFormat format, - const gfx::ColorSpace& color_space, - bool mipmap); - ~ScopedRenderPassTexture(); - - ScopedRenderPassTexture(ScopedRenderPassTexture&& other); - ScopedRenderPassTexture& operator=(ScopedRenderPassTexture&& other); - void BindForSampling(); - - GLuint id() const { return gl_id_; } - const gfx::Size& size() const { return size_; } - bool mipmap() const { return mipmap_; } - const gfx::ColorSpace& color_space() const { return color_space_; } - void set_generate_mipmap() { mipmap_state_ = GENERATE; } - - private: - void Free(); - - raw_ptr<ContextProvider> context_provider_ = nullptr; - // The GL texture id. - GLuint gl_id_ = 0; - // Size of the resource in pixels. - gfx::Size size_; - // When true, and immutable textures are used, this specifies to - // generate mipmaps at powers of 2. - bool mipmap_ = false; - // TODO(xing.xu): Remove this and set the color space when we draw the - // CompositorRenderPassDrawQuad. - gfx::ColorSpace color_space_; - enum MipmapState { INVALID, GENERATE, VALID }; - MipmapState mipmap_state_ = INVALID; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_SCOPED_RENDER_PASS_TEXTURE_H_ diff --git a/chromium/components/viz/service/display/shader.cc b/chromium/components/viz/service/display/shader.cc deleted file mode 100644 index ac9e11564f7..00000000000 --- a/chromium/components/viz/service/display/shader.cc +++ /dev/null @@ -1,1168 +0,0 @@ -// Copyright 2011 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/shader.h" - -#include <stddef.h> - -#include <algorithm> -#include <utility> -#include <vector> - -#include "base/check_op.h" -#include "base/notreached.h" -#include "base/strings/char_traits.h" -#include "base/strings/strcat.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "components/viz/service/display/static_geometry_binding.h" -#include "gpu/command_buffer/client/gles2_interface.h" -#include "ui/gfx/color_transform.h" -#include "ui/gfx/geometry/point.h" -#include "ui/gfx/geometry/size.h" - -constexpr base::StringPiece StripLambda(base::StringPiece shader) { - // Must contain at least "[]() {}". - DCHECK_EQ(shader.substr(0, 6), "[]() {"); - DCHECK_EQ(shader.back(), '}'); - shader.remove_prefix(6); - shader.remove_suffix(1); - return shader; -} - -// Shaders are passed in with lambda syntax, which tricks clang-format into -// handling them correctly. StripLambda removes this. -#define SHADER0(Src) StripLambda(#Src) - -#define HDR(x) \ - do { \ - header += x "\n"; \ - } while (0) -#define SRC(x) \ - do { \ - source += " " x "\n"; \ - } while (0) - -using gpu::gles2::GLES2Interface; - -namespace viz { - -namespace { - -static void GetProgramUniformLocations(GLES2Interface* context, - unsigned program, - size_t count, - const char** uniforms, - int* locations, - int* base_uniform_index) { - for (size_t i = 0; i < count; i++) { - locations[i] = (*base_uniform_index)++; - context->BindUniformLocationCHROMIUM(program, locations[i], uniforms[i]); - } -} - -static void SetFragmentTexCoordPrecision(TexCoordPrecision requested_precision, - std::string* shader_string) { - const char* prefix = ""; - switch (requested_precision) { - case TEX_COORD_PRECISION_HIGH: - DCHECK_NE(shader_string->find("TexCoordPrecision"), std::string::npos); - prefix = - "#ifdef GL_FRAGMENT_PRECISION_HIGH\n" - " #define TexCoordPrecision highp\n" - "#else\n" - " #define TexCoordPrecision mediump\n" - "#endif\n"; - break; - case TEX_COORD_PRECISION_MEDIUM: - DCHECK_NE(shader_string->find("TexCoordPrecision"), std::string::npos); - prefix = "#define TexCoordPrecision mediump\n"; - break; - case TEX_COORD_PRECISION_NA: - DCHECK_EQ(shader_string->find("TexCoordPrecision"), std::string::npos); - DCHECK_EQ(shader_string->find("texture2D"), std::string::npos); - DCHECK_EQ(shader_string->find("texture2DRect"), std::string::npos); - break; - default: - NOTREACHED(); - break; - } - const char* lut_prefix = "#define LutLookup texture2D\n"; - shader_string->insert(0, prefix); - shader_string->insert(0, lut_prefix); -} - -TexCoordPrecision TexCoordPrecisionRequired(GLES2Interface* context, - int* highp_threshold_cache, - int highp_threshold_min, - int x, - int y) { - if (*highp_threshold_cache == 0) { - // Initialize range and precision with minimum spec values for when - // GetShaderPrecisionFormat is a test stub. - // TODO(brianderson): Implement better stubs of GetShaderPrecisionFormat - // everywhere. - GLint range[2] = {14, 14}; - GLint precision = 10; - context->GetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_MEDIUM_FLOAT, - range, &precision); - *highp_threshold_cache = 1 << precision; - } - - int highp_threshold = std::max(*highp_threshold_cache, highp_threshold_min); - if (x > highp_threshold || y > highp_threshold) - return TEX_COORD_PRECISION_HIGH; - return TEX_COORD_PRECISION_MEDIUM; -} - -void SetFragmentSamplerType(SamplerType requested_type, - std::string* shader_string) { - const char* prefix = nullptr; - switch (requested_type) { - case SAMPLER_TYPE_2D: - DCHECK_NE(shader_string->find("SamplerType"), std::string::npos); - DCHECK_NE(shader_string->find("TextureLookup"), std::string::npos); - prefix = - "#define SamplerType sampler2D\n" - "#define TextureLookup texture2D\n"; - break; - case SAMPLER_TYPE_2D_RECT: - DCHECK_NE(shader_string->find("SamplerType"), std::string::npos); - DCHECK_NE(shader_string->find("TextureLookup"), std::string::npos); - prefix = - "#extension GL_ARB_texture_rectangle : require\n" - "#define SamplerType sampler2DRect\n" - "#define TextureLookup texture2DRect\n"; - break; - case SAMPLER_TYPE_EXTERNAL_OES: - DCHECK_NE(shader_string->find("SamplerType"), std::string::npos); - DCHECK_NE(shader_string->find("TextureLookup"), std::string::npos); - prefix = - "#extension GL_OES_EGL_image_external : enable\n" - "#extension GL_NV_EGL_stream_consumer_external : enable\n" - "#define SamplerType samplerExternalOES\n" - "#define TextureLookup texture2D\n"; - break; - case SAMPLER_TYPE_NA: - DCHECK_EQ(shader_string->find("SamplerType"), std::string::npos); - DCHECK_EQ(shader_string->find("TextureLookup"), std::string::npos); - return; - default: - NOTREACHED(); - return; - } - shader_string->insert(0, prefix); -} - -} // namespace - -TexCoordPrecision TexCoordPrecisionRequired(GLES2Interface* context, - int* highp_threshold_cache, - int highp_threshold_min, - const gfx::Point& max_coordinate) { - return TexCoordPrecisionRequired(context, highp_threshold_cache, - highp_threshold_min, max_coordinate.x(), - max_coordinate.y()); -} - -TexCoordPrecision TexCoordPrecisionRequired(GLES2Interface* context, - int* highp_threshold_cache, - int highp_threshold_min, - const gfx::Size& max_size) { - return TexCoordPrecisionRequired(context, highp_threshold_cache, - highp_threshold_min, max_size.width(), - max_size.height()); -} - -VertexShader::VertexShader() {} - -void VertexShader::Init(GLES2Interface* context, - unsigned program, - int* base_uniform_index) { - std::vector<const char*> uniforms; - std::vector<int> locations; - - switch (tex_coord_transform_) { - case TEX_COORD_TRANSFORM_NONE: - break; - case TEX_COORD_TRANSFORM_VEC4: - case TEX_COORD_TRANSFORM_TRANSLATED_VEC4: - uniforms.push_back("vertexTexTransform"); - break; - case TEX_COORD_TRANSFORM_MATRIX: - uniforms.push_back("texMatrix"); - break; - } - if (is_ya_uv_) { - uniforms.push_back("yaTexScale"); - uniforms.push_back("yaTexOffset"); - uniforms.push_back("uvTexScale"); - uniforms.push_back("uvTexOffset"); - } - uniforms.push_back("matrix"); - if (has_vertex_opacity_) - uniforms.push_back("opacity"); - if (aa_mode_ == USE_AA) { - uniforms.push_back("viewport"); - uniforms.push_back("edge"); - } - if (position_source_ == POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM) - uniforms.push_back("quad"); - - locations.resize(uniforms.size()); - - GetProgramUniformLocations(context, program, uniforms.size(), uniforms.data(), - locations.data(), base_uniform_index); - - size_t index = 0; - switch (tex_coord_transform_) { - case TEX_COORD_TRANSFORM_NONE: - break; - case TEX_COORD_TRANSFORM_VEC4: - case TEX_COORD_TRANSFORM_TRANSLATED_VEC4: - vertex_tex_transform_location_ = locations[index++]; - break; - case TEX_COORD_TRANSFORM_MATRIX: - tex_matrix_location_ = locations[index++]; - break; - } - if (is_ya_uv_) { - ya_tex_scale_location_ = locations[index++]; - ya_tex_offset_location_ = locations[index++]; - uv_tex_scale_location_ = locations[index++]; - uv_tex_offset_location_ = locations[index++]; - } - matrix_location_ = locations[index++]; - if (has_vertex_opacity_) - vertex_opacity_location_ = locations[index++]; - if (aa_mode_ == USE_AA) { - viewport_location_ = locations[index++]; - edge_location_ = locations[index++]; - } - if (position_source_ == POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM) - quad_location_ = locations[index++]; -} - -std::string VertexShader::GetShaderString() const { - // We unconditionally use highp in the vertex shader since - // we are unlikely to be vertex shader bound when drawing large quads. - // Also, some vertex shaders mutate the texture coordinate in such a - // way that the effective precision might be lower than expected. - std::string header = "#define TexCoordPrecision highp\n"; - std::string source = "void main() {\n"; - - // Define the size of quads for attribute indexed uniform arrays. - if (use_uniform_arrays_) { - header += base::StringPrintf("#define NUM_QUADS %d\n", - StaticGeometryBinding::NUM_QUADS); - } - - // Read the index variables. - if (use_uniform_arrays_ || has_vertex_opacity_ || - position_source_ == POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM) { - HDR("attribute float a_index;"); - SRC("// Compute indices for uniform arrays."); - SRC("int vertex_index = int(a_index);"); - if (use_uniform_arrays_) - SRC("int quad_index = int(a_index * 0.25);"); - SRC(""); - } - - // Read the position and compute gl_Position. - HDR("attribute TexCoordPrecision vec4 a_position;"); - SRC("// Compute the position."); - switch (position_source_) { - case POSITION_SOURCE_ATTRIBUTE: - SRC("vec4 pos = a_position;"); - break; - case POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM: - HDR("uniform TexCoordPrecision vec2 quad[4];"); - SRC("vec4 pos = vec4(quad[vertex_index], a_position.z, a_position.w);"); - break; - } - if (use_uniform_arrays_) { - HDR("uniform mat4 matrix[NUM_QUADS];"); - SRC("gl_Position = matrix[quad_index] * pos;"); - } else { - HDR("uniform mat4 matrix;"); - SRC("gl_Position = matrix * pos;"); - } - - // Compute the anti-aliasing edge distances. - if (aa_mode_ == USE_AA) { - HDR("uniform TexCoordPrecision vec3 edge[8];"); - HDR("uniform vec4 viewport;"); - HDR("varying TexCoordPrecision vec4 edge_dist[2]; // 8 edge distances."); - SRC("// Compute anti-aliasing properties.\n"); - SRC("vec2 ndc_pos = 0.5 * (1.0 + gl_Position.xy / gl_Position.w);"); - SRC("vec3 screen_pos = vec3(viewport.xy + viewport.zw * ndc_pos, 1.0);"); - SRC("edge_dist[0] = vec4(dot(edge[0], screen_pos),"); - SRC(" dot(edge[1], screen_pos),"); - SRC(" dot(edge[2], screen_pos),"); - SRC(" dot(edge[3], screen_pos)) * gl_Position.w;"); - SRC("edge_dist[1] = vec4(dot(edge[4], screen_pos),"); - SRC(" dot(edge[5], screen_pos),"); - SRC(" dot(edge[6], screen_pos),"); - SRC(" dot(edge[7], screen_pos)) * gl_Position.w;"); - } - - // Read, transform, and write texture coordinates. - if (tex_coord_source_ != TEX_COORD_SOURCE_NONE) { - if (is_ya_uv_) { - HDR("varying TexCoordPrecision vec2 v_uvTexCoord;"); - HDR("varying TexCoordPrecision vec2 v_yaTexCoord;"); - } else { - HDR("varying TexCoordPrecision vec2 v_texCoord;"); - } - - SRC("// Compute texture coordinates."); - // Read coordinates. - switch (tex_coord_source_) { - case TEX_COORD_SOURCE_NONE: - break; - case TEX_COORD_SOURCE_POSITION: - SRC("vec2 texCoord = pos.xy;"); - break; - case TEX_COORD_SOURCE_ATTRIBUTE: - HDR("attribute TexCoordPrecision vec2 a_texCoord;"); - SRC("vec2 texCoord = a_texCoord;"); - break; - } - // Transform coordinates (except YUV). - switch (tex_coord_transform_) { - case TEX_COORD_TRANSFORM_NONE: - break; - case TEX_COORD_TRANSFORM_TRANSLATED_VEC4: - SRC("texCoord = texCoord + vec2(0.5);"); - [[fallthrough]]; - case TEX_COORD_TRANSFORM_VEC4: - if (use_uniform_arrays_) { - HDR("uniform TexCoordPrecision vec4 vertexTexTransform[NUM_QUADS];"); - SRC("TexCoordPrecision vec4 texTrans ="); - SRC(" vertexTexTransform[quad_index];"); - SRC("texCoord = texCoord * texTrans.zw + texTrans.xy;"); - } else { - HDR("uniform TexCoordPrecision vec4 vertexTexTransform;"); - SRC("texCoord = texCoord * vertexTexTransform.zw +"); - SRC(" vertexTexTransform.xy;"); - } - break; - case TEX_COORD_TRANSFORM_MATRIX: - HDR("uniform TexCoordPrecision mat4 texMatrix;"); - SRC("texCoord = (texMatrix * vec4(texCoord.xy, 0.0, 1.0)).xy;"); - break; - } - // Write the output texture coordinates. - if (is_ya_uv_) { - HDR("uniform TexCoordPrecision vec2 uvTexOffset;"); - HDR("uniform TexCoordPrecision vec2 uvTexScale;"); - HDR("uniform TexCoordPrecision vec2 yaTexOffset;"); - HDR("uniform TexCoordPrecision vec2 yaTexScale;"); - SRC("v_yaTexCoord = texCoord * yaTexScale + yaTexOffset;"); - SRC("v_uvTexCoord = texCoord * uvTexScale + uvTexOffset;"); - } else { - SRC("v_texCoord = texCoord;"); - } - } - - // Write varying vertex opacity. - if (has_vertex_opacity_) { - HDR("varying float v_alpha;"); - if (use_uniform_arrays_) { - HDR("uniform float opacity[NUM_QUADS * 4];"); - } else { - HDR("uniform float opacity[4];"); - } - SRC("v_alpha = opacity[vertex_index];"); - } - - // Add cargo-culted dummy variables for Android. - if (has_dummy_variables_) { - HDR("uniform TexCoordPrecision vec2 dummy_uniform;"); - HDR("varying TexCoordPrecision vec2 dummy_varying;"); - SRC("dummy_varying = dummy_uniform;"); - } - - source += "}\n"; - return header + source; -} - -FragmentShader::FragmentShader() {} - -std::string FragmentShader::GetShaderString() const { - TexCoordPrecision precision = tex_coord_precision_; - // The AA shader values will use TexCoordPrecision. - if (aa_mode_ == USE_AA && precision == TEX_COORD_PRECISION_NA) - precision = TEX_COORD_PRECISION_MEDIUM; - std::string shader = GetShaderSource(); - SetBlendModeFunctions(&shader); - SetRoundedCornerFunctions(&shader); - SetFragmentSamplerType(sampler_type_, &shader); - SetFragmentTexCoordPrecision(precision, &shader); - return shader; -} - -void FragmentShader::Init(GLES2Interface* context, - unsigned program, - int* base_uniform_index) { - std::vector<const char*> uniforms; - std::vector<int> locations; - if (has_blend_mode()) { - uniforms.push_back("s_backdropTexture"); - uniforms.push_back("s_originalBackdropTexture"); - uniforms.push_back("backdropRect"); - } - if (mask_mode_ != NO_MASK) { - uniforms.push_back("s_mask"); - uniforms.push_back("maskTexCoordScale"); - uniforms.push_back("maskTexCoordOffset"); - } - if (has_color_matrix_) { - uniforms.push_back("colorMatrix"); - uniforms.push_back("colorOffset"); - } - if (has_uniform_alpha_) - uniforms.push_back("alpha"); - if (has_background_color_) - uniforms.push_back("background_color"); - if (has_tex_clamp_rect_) - uniforms.push_back("tex_clamp_rect"); - switch (input_color_type_) { - case INPUT_COLOR_SOURCE_RGBA_TEXTURE: - uniforms.push_back("s_texture"); - if (has_rgba_fragment_tex_transform_) - uniforms.push_back("fragmentTexTransform"); - break; - case INPUT_COLOR_SOURCE_YUV_TEXTURES: - uniforms.push_back("y_texture"); - if (uv_texture_mode_ == UV_TEXTURE_MODE_UV) - uniforms.push_back("uv_texture"); - if (uv_texture_mode_ == UV_TEXTURE_MODE_U_V) { - uniforms.push_back("u_texture"); - uniforms.push_back("v_texture"); - } - if (yuv_alpha_texture_mode_ == YUV_HAS_ALPHA_TEXTURE) - uniforms.push_back("a_texture"); - uniforms.push_back("ya_clamp_rect"); - uniforms.push_back("uv_clamp_rect"); - uniforms.push_back("resource_multiplier"); - uniforms.push_back("resource_offset"); - break; - case INPUT_COLOR_SOURCE_UNIFORM: - uniforms.push_back("color"); - break; - } - if (has_output_color_matrix_) - uniforms.emplace_back("output_color_matrix"); - - if (has_tint_color_matrix_) - uniforms.emplace_back("tint_color_matrix"); - - if (has_rounded_corner_) { - uniforms.emplace_back("roundedCornerRect"); - uniforms.emplace_back("roundedCornerRadius"); - } - - locations.resize(uniforms.size()); - - GetProgramUniformLocations(context, program, uniforms.size(), uniforms.data(), - locations.data(), base_uniform_index); - - size_t index = 0; - if (has_blend_mode()) { - backdrop_location_ = locations[index++]; - original_backdrop_location_ = locations[index++]; - backdrop_rect_location_ = locations[index++]; - } - if (mask_mode_ != NO_MASK) { - mask_sampler_location_ = locations[index++]; - mask_tex_coord_scale_location_ = locations[index++]; - mask_tex_coord_offset_location_ = locations[index++]; - } - if (has_color_matrix_) { - color_matrix_location_ = locations[index++]; - color_offset_location_ = locations[index++]; - } - if (has_uniform_alpha_) - alpha_location_ = locations[index++]; - if (has_background_color_) - background_color_location_ = locations[index++]; - if (has_tex_clamp_rect_) - tex_clamp_rect_location_ = locations[index++]; - switch (input_color_type_) { - case INPUT_COLOR_SOURCE_RGBA_TEXTURE: - sampler_location_ = locations[index++]; - if (has_rgba_fragment_tex_transform_) - fragment_tex_transform_location_ = locations[index++]; - break; - case INPUT_COLOR_SOURCE_YUV_TEXTURES: - y_texture_location_ = locations[index++]; - if (uv_texture_mode_ == UV_TEXTURE_MODE_UV) - uv_texture_location_ = locations[index++]; - if (uv_texture_mode_ == UV_TEXTURE_MODE_U_V) { - u_texture_location_ = locations[index++]; - v_texture_location_ = locations[index++]; - } - if (yuv_alpha_texture_mode_ == YUV_HAS_ALPHA_TEXTURE) - a_texture_location_ = locations[index++]; - ya_clamp_rect_location_ = locations[index++]; - uv_clamp_rect_location_ = locations[index++]; - resource_multiplier_location_ = locations[index++]; - resource_offset_location_ = locations[index++]; - break; - case INPUT_COLOR_SOURCE_UNIFORM: - color_location_ = locations[index++]; - break; - } - - if (has_output_color_matrix_) - output_color_matrix_location_ = locations[index++]; - - if (has_tint_color_matrix_) - tint_color_matrix_location_ = locations[index++]; - - if (has_rounded_corner_) { - rounded_corner_rect_location_ = locations[index++]; - rounded_corner_radius_location_ = locations[index++]; - } - - DCHECK_EQ(index, locations.size()); -} - -void FragmentShader::SetRoundedCornerFunctions( - std::string* shader_string) const { - if (!has_rounded_corner_) - return; - - static constexpr base::StringPiece kUniforms = SHADER0([]() { - uniform vec4 roundedCornerRect; - uniform vec4 roundedCornerRadius; - }); - - static constexpr base::StringPiece kFunctionRcUtility = SHADER0([]() { - // Returns a vector of size 4. Each component of a vector is set to 1 or 0 - // representing whether |rcCoord| is a part of the respective corner or - // not. - // The component ordering is: - // [Top left, Top right, Bottom right, Bottom left] - vec4 IsCorner(vec2 rcCoord) { - // Top left corner - if (rcCoord.x < roundedCornerRadius.x && - rcCoord.y < roundedCornerRadius.x) { - return vec4(1.0, 0.0, 0.0, 0.0); - } - - // Top right corner - if (rcCoord.x > roundedCornerRect.z - roundedCornerRadius.y && - rcCoord.y < roundedCornerRadius.y) { - return vec4(0.0, 1.0, 0.0, 0.0); - } - - // Bottom right corner - if (rcCoord.x > roundedCornerRect.z - roundedCornerRadius.z && - rcCoord.y > roundedCornerRect.w - roundedCornerRadius.z) { - return vec4(0.0, 0.0, 1.0, 0.0); - } - - // Bottom left corner - if (rcCoord.x < roundedCornerRadius.w && - rcCoord.y > roundedCornerRect.w - roundedCornerRadius.w) { - return vec4(0.0, 0.0, 0.0, 1.0); - } - return vec4(0.0, 0.0, 0.0, 0.0); - } - - // Returns the center of the rounded corner. |corner| holds the info on - // which corner the center is requested for. - vec2 GetCenter(vec4 corner, float radius) { - if (corner.x == 1.0) { - // Top left corner - return vec2(radius, radius); - } else if (corner.y == 1.0) { - // Top right corner - return vec2(roundedCornerRect.z - radius, radius); - } else if (corner.z == 1.0) { - // Bottom right corner - return vec2(roundedCornerRect.z - radius, roundedCornerRect.w - radius); - } else { - // Bottom left corner - return vec2(radius, roundedCornerRect.w - radius); - } - } - }); - - static constexpr base::StringPiece kFunctionApplyRoundedCorner = - SHADER0([]() { - vec4 ApplyRoundedCorner(vec4 src) { - vec2 rcCoord = gl_FragCoord.xy - roundedCornerRect.xy; - - vec4 isCorner = IsCorner(rcCoord); - - // Get the radius to use based on the corner this fragment lies in. - float r = dot(isCorner, roundedCornerRadius); - - // If the radius is 0, then there is no rounded corner here. We can do - // an early return. - if (r == 0.0) - return src; - - // Vector to the corner's center this fragment is in. - // Due to precision errors on android, this variable requires a highp. - // See https://crbug.com/1009322 - RoundedCornerPrecision vec2 cornerCenter = GetCenter(isCorner, r); - - // Vector from the center of the corner to the current fragment center - vec2 cxy = rcCoord - cornerCenter; - - // Compute the distance of the fragment's center from the corner's - // center. - float fragDst = length(cxy); - - float alpha = smoothstep(r - 1.0, r + 1.0, fragDst); - return vec4(0.0) * alpha + src * (1.0 - alpha); - } - }); - - std::string shader; - shader.reserve(shader_string->size() + 2048); - shader += "precision mediump float;"; - shader += - "\n#ifdef GL_FRAGMENT_PRECISION_HIGH\n" - " #define RoundedCornerPrecision highp\n" - "#else\n" - " #define RoundedCornerPrecision mediump\n" - "#endif\n"; - base::StrAppend(&shader, {kUniforms, kFunctionRcUtility, - kFunctionApplyRoundedCorner, *shader_string}); - *shader_string = std::move(shader); -} - -void FragmentShader::SetBlendModeFunctions(std::string* shader_string) const { - if (!has_blend_mode()) { - return; - } - - static constexpr base::StringPiece kUniforms = SHADER0([]() { - uniform sampler2D s_backdropTexture; - uniform sampler2D s_originalBackdropTexture; - uniform TexCoordPrecision vec4 backdropRect; - }); - - base::StringPiece function_apply_blend_mode; - if (mask_for_background_) { - static constexpr base::StringPiece kFunctionApplyBlendMode = SHADER0([]() { - vec4 ApplyBlendMode(vec4 src, float mask) { - TexCoordPrecision vec2 bgTexCoord = gl_FragCoord.xy - backdropRect.xy; - bgTexCoord *= backdropRect.zw; - vec4 backdrop = texture2D(s_backdropTexture, bgTexCoord); - vec4 original_backdrop = - texture2D(s_originalBackdropTexture, bgTexCoord); - vec4 dst = mix(original_backdrop, backdrop, mask); - return Blend(src, dst); - } - }); - function_apply_blend_mode = kFunctionApplyBlendMode; - } else { - static constexpr base::StringPiece kFunctionApplyBlendMode = SHADER0([]() { - vec4 ApplyBlendMode(vec4 src) { - TexCoordPrecision vec2 bgTexCoord = gl_FragCoord.xy - backdropRect.xy; - bgTexCoord *= backdropRect.zw; - vec4 dst = texture2D(s_backdropTexture, bgTexCoord); - return Blend(src, dst); - } - }); - function_apply_blend_mode = kFunctionApplyBlendMode; - } - - std::string shader; - shader.reserve(shader_string->size() + 1024); - shader += "precision mediump float;"; - AppendHelperFunctions(&shader); - AppendBlendFunction(&shader); - base::StrAppend(&shader, - {kUniforms, function_apply_blend_mode, *shader_string}); - *shader_string = std::move(shader); -} - -void FragmentShader::AppendHelperFunctions(std::string* buffer) const { - static constexpr base::StringPiece kFunctionHardLight = SHADER0([]() { - vec3 hardLight(vec4 src, vec4 dst) { - vec3 result; - result.r = - (2.0 * src.r <= src.a) - ? (2.0 * src.r * dst.r) - : (src.a * dst.a - 2.0 * (dst.a - dst.r) * (src.a - src.r)); - result.g = - (2.0 * src.g <= src.a) - ? (2.0 * src.g * dst.g) - : (src.a * dst.a - 2.0 * (dst.a - dst.g) * (src.a - src.g)); - result.b = - (2.0 * src.b <= src.a) - ? (2.0 * src.b * dst.b) - : (src.a * dst.a - 2.0 * (dst.a - dst.b) * (src.a - src.b)); - result.rgb += src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a); - return result; - } - }); - - static constexpr base::StringPiece kFunctionColorDodgeComponent = - SHADER0([]() { - float getColorDodgeComponent(float srcc, float srca, float dstc, - float dsta) { - if (0.0 == dstc) - return srcc * (1.0 - dsta); - float d = srca - srcc; - if (0.0 == d) - return srca * dsta + srcc * (1.0 - dsta) + dstc * (1.0 - srca); - d = min(dsta, dstc * srca / d); - return d * srca + srcc * (1.0 - dsta) + dstc * (1.0 - srca); - } - }); - - static constexpr base::StringPiece kFunctionColorBurnComponent = - SHADER0([]() { - float getColorBurnComponent(float srcc, float srca, float dstc, - float dsta) { - if (dsta == dstc) - return srca * dsta + srcc * (1.0 - dsta) + dstc * (1.0 - srca); - if (0.0 == srcc) - return dstc * (1.0 - srca); - float d = max(0.0, dsta - (dsta - dstc) * srca / srcc); - return srca * d + srcc * (1.0 - dsta) + dstc * (1.0 - srca); - } - }); - - static constexpr base::StringPiece kFunctionSoftLightComponentPosDstAlpha = - SHADER0([]() { - float getSoftLightComponent(float srcc, float srca, float dstc, - float dsta) { - if (2.0 * srcc <= srca) { - return (dstc * dstc * (srca - 2.0 * srcc)) / dsta + - (1.0 - dsta) * srcc + dstc * (-srca + 2.0 * srcc + 1.0); - } else if (4.0 * dstc <= dsta) { - float DSqd = dstc * dstc; - float DCub = DSqd * dstc; - float DaSqd = dsta * dsta; - float DaCub = DaSqd * dsta; - return (-DaCub * srcc + - DaSqd * (srcc - dstc * (3.0 * srca - 6.0 * srcc - 1.0)) + - 12.0 * dsta * DSqd * (srca - 2.0 * srcc) - - 16.0 * DCub * (srca - 2.0 * srcc)) / - DaSqd; - } else { - return -sqrt(dsta * dstc) * (srca - 2.0 * srcc) - dsta * srcc + - dstc * (srca - 2.0 * srcc + 1.0) + srcc; - } - } - }); - - static constexpr base::StringPiece kFunctionLum = SHADER0([]() { - float luminance(vec3 color) { return dot(vec3(0.3, 0.59, 0.11), color); } - - vec3 set_luminance(vec3 hueSat, float alpha, vec3 lumColor) { - float diff = luminance(lumColor - hueSat); - vec3 outColor = hueSat + diff; - float outLum = luminance(outColor); - float minComp = min(min(outColor.r, outColor.g), outColor.b); - float maxComp = max(max(outColor.r, outColor.g), outColor.b); - if (minComp < 0.0 && outLum != minComp) { - outColor = - outLum + ((outColor - vec3(outLum, outLum, outLum)) * outLum) / - (outLum - minComp); - } - if (maxComp > alpha && maxComp != outLum) { - outColor = outLum + ((outColor - vec3(outLum, outLum, outLum)) * - (alpha - outLum)) / - (maxComp - outLum); - } - return outColor; - } - }); - - static constexpr base::StringPiece kFunctionSat = SHADER0([]() { - float saturation(vec3 color) { - return max(max(color.r, color.g), color.b) - - min(min(color.r, color.g), color.b); - } - - vec3 set_saturation_helper(float minComp, float midComp, float maxComp, - float sat) { - if (minComp < maxComp) { - vec3 result; - result.r = 0.0; - result.g = sat * (midComp - minComp) / (maxComp - minComp); - result.b = sat; - return result; - } else { - return vec3(0, 0, 0); - } - } - - vec3 set_saturation(vec3 hueLumColor, vec3 satColor) { - float sat = saturation(satColor); - if (hueLumColor.r <= hueLumColor.g) { - if (hueLumColor.g <= hueLumColor.b) { - hueLumColor.rgb = set_saturation_helper(hueLumColor.r, hueLumColor.g, - hueLumColor.b, sat); - } else if (hueLumColor.r <= hueLumColor.b) { - hueLumColor.rbg = set_saturation_helper(hueLumColor.r, hueLumColor.b, - hueLumColor.g, sat); - } else { - hueLumColor.brg = set_saturation_helper(hueLumColor.b, hueLumColor.r, - hueLumColor.g, sat); - } - } else if (hueLumColor.r <= hueLumColor.b) { - hueLumColor.grb = set_saturation_helper(hueLumColor.g, hueLumColor.r, - hueLumColor.b, sat); - } else if (hueLumColor.g <= hueLumColor.b) { - hueLumColor.gbr = set_saturation_helper(hueLumColor.g, hueLumColor.b, - hueLumColor.r, sat); - } else { - hueLumColor.bgr = set_saturation_helper(hueLumColor.b, hueLumColor.g, - hueLumColor.r, sat); - } - return hueLumColor; - } - }); - - switch (blend_mode_) { - case BLEND_MODE_OVERLAY: - case BLEND_MODE_HARD_LIGHT: - buffer->append(kFunctionHardLight.data(), kFunctionHardLight.size()); - return; - case BLEND_MODE_COLOR_DODGE: - buffer->append(kFunctionColorDodgeComponent.data(), - kFunctionColorDodgeComponent.size()); - return; - case BLEND_MODE_COLOR_BURN: - buffer->append(kFunctionColorBurnComponent.data(), - kFunctionColorBurnComponent.size()); - return; - case BLEND_MODE_SOFT_LIGHT: - buffer->append(kFunctionSoftLightComponentPosDstAlpha.data(), - kFunctionSoftLightComponentPosDstAlpha.size()); - return; - case BLEND_MODE_HUE: - case BLEND_MODE_SATURATION: - base::StrAppend(buffer, {kFunctionLum, kFunctionSat}); - return; - case BLEND_MODE_COLOR: - case BLEND_MODE_LUMINOSITY: - buffer->append(kFunctionLum.data(), kFunctionLum.size()); - return; - default: - return; - } -} - -void FragmentShader::AppendBlendFunction(std::string* buffer) const { - *buffer += - "vec4 Blend(vec4 src, vec4 dst) {" - " vec4 result;"; - base::StrAppend( - buffer, {GetBlendFunctionBodyForAlpha(), GetBlendFunctionBodyForRGB()}); - *buffer += - " return result;" - "}"; -} - -base::StringPiece FragmentShader::GetBlendFunctionBodyForAlpha() const { - if (blend_mode_ == BLEND_MODE_DESTINATION_IN) - return "result.a = src.a * dst.a;"; - else - return "result.a = src.a + (1.0 - src.a) * dst.a;"; -} - -base::StringPiece FragmentShader::GetBlendFunctionBodyForRGB() const { - switch (blend_mode_) { - case BLEND_MODE_NORMAL: - return "result.rgb = src.rgb + dst.rgb * (1.0 - src.a);"; - case BLEND_MODE_DESTINATION_IN: - return "result.rgb = dst.rgb * src.a;"; - case BLEND_MODE_SCREEN: - return "result.rgb = src.rgb + (1.0 - src.rgb) * dst.rgb;"; - case BLEND_MODE_LIGHTEN: - return "result.rgb = max((1.0 - src.a) * dst.rgb + src.rgb," - " (1.0 - dst.a) * src.rgb + dst.rgb);"; - case BLEND_MODE_OVERLAY: - return "result.rgb = hardLight(dst, src);"; - case BLEND_MODE_DARKEN: - return "result.rgb = min((1.0 - src.a) * dst.rgb + src.rgb," - " (1.0 - dst.a) * src.rgb + dst.rgb);"; - case BLEND_MODE_COLOR_DODGE: - return "result.r = getColorDodgeComponent(src.r, src.a, dst.r, dst.a);" - "result.g = getColorDodgeComponent(src.g, src.a, dst.g, dst.a);" - "result.b = getColorDodgeComponent(src.b, src.a, dst.b, dst.a);"; - case BLEND_MODE_COLOR_BURN: - return "result.r = getColorBurnComponent(src.r, src.a, dst.r, dst.a);" - "result.g = getColorBurnComponent(src.g, src.a, dst.g, dst.a);" - "result.b = getColorBurnComponent(src.b, src.a, dst.b, dst.a);"; - case BLEND_MODE_HARD_LIGHT: - return "result.rgb = hardLight(src, dst);"; - case BLEND_MODE_SOFT_LIGHT: - return "if (0.0 == dst.a) {" - " result.rgb = src.rgb;" - "} else {" - " result.r = getSoftLightComponent(src.r, src.a, dst.r, dst.a);" - " result.g = getSoftLightComponent(src.g, src.a, dst.g, dst.a);" - " result.b = getSoftLightComponent(src.b, src.a, dst.b, dst.a);" - "}"; - case BLEND_MODE_DIFFERENCE: - return "result.rgb = src.rgb + dst.rgb -" - " 2.0 * min(src.rgb * dst.a, dst.rgb * src.a);"; - case BLEND_MODE_EXCLUSION: - return "result.rgb = dst.rgb + src.rgb - 2.0 * dst.rgb * src.rgb;"; - case BLEND_MODE_MULTIPLY: - return "result.rgb = (1.0 - src.a) * dst.rgb +" - " (1.0 - dst.a) * src.rgb + src.rgb * dst.rgb;"; - case BLEND_MODE_HUE: - return "vec4 dstSrcAlpha = dst * src.a;" - "result.rgb =" - " set_luminance(set_saturation(src.rgb * dst.a," - " dstSrcAlpha.rgb)," - " dstSrcAlpha.a," - " dstSrcAlpha.rgb);" - "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;"; - case BLEND_MODE_SATURATION: - return "vec4 dstSrcAlpha = dst * src.a;" - "result.rgb = set_luminance(set_saturation(dstSrcAlpha.rgb," - " src.rgb * dst.a)," - " dstSrcAlpha.a," - " dstSrcAlpha.rgb);" - "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;"; - case BLEND_MODE_COLOR: - return "vec4 srcDstAlpha = src * dst.a;" - "result.rgb = set_luminance(srcDstAlpha.rgb," - " srcDstAlpha.a," - " dst.rgb * src.a);" - "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;"; - case BLEND_MODE_LUMINOSITY: - return "vec4 srcDstAlpha = src * dst.a;" - "result.rgb = set_luminance(dst.rgb * src.a," - " srcDstAlpha.a," - " srcDstAlpha.rgb);" - "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;"; - case BLEND_MODE_NONE: - NOTREACHED(); - } - return "result = vec4(1.0, 0.0, 0.0, 1.0);"; -} - -std::string FragmentShader::GetShaderSource() const { - std::string header = "precision mediump float;\n"; - std::string source = "void main() {\n"; - - // Read the input into vec4 texColor. - switch (input_color_type_) { - case INPUT_COLOR_SOURCE_RGBA_TEXTURE: - if (ignore_sampler_type_) - HDR("uniform sampler2D s_texture;"); - else - HDR("uniform SamplerType s_texture;"); - HDR("varying TexCoordPrecision vec2 v_texCoord;"); - if (has_rgba_fragment_tex_transform_) { - HDR("uniform TexCoordPrecision vec4 fragmentTexTransform;"); - SRC("// Transformed texture lookup"); - SRC("TexCoordPrecision vec2 texCoord ="); - SRC(" clamp(v_texCoord, 0.0, 1.0) * fragmentTexTransform.zw +"); - SRC(" fragmentTexTransform.xy;"); - SRC("vec4 texColor = TextureLookup(s_texture, texCoord);"); - DCHECK(!ignore_sampler_type_); - DCHECK(!has_tex_clamp_rect_); - } else { - SRC("// Texture lookup"); - if (ignore_sampler_type_) { - SRC("vec4 texColor = texture2D(s_texture, v_texCoord);"); - DCHECK(!has_tex_clamp_rect_); - } else { - SRC("TexCoordPrecision vec2 texCoord = v_texCoord;"); - if (has_tex_clamp_rect_) { - HDR("uniform vec4 tex_clamp_rect;"); - SRC("texCoord = max(tex_clamp_rect.xy,"); - SRC(" min(tex_clamp_rect.zw, texCoord));"); - } - SRC("vec4 texColor = TextureLookup(s_texture, texCoord);"); - } - } - break; - case INPUT_COLOR_SOURCE_YUV_TEXTURES: - DCHECK(!has_tex_clamp_rect_); - // Compute the clamped texture coordinates for the YA and UV textures. - HDR("uniform SamplerType y_texture;"); - SRC("// YUV texture lookup and conversion to RGB."); - SRC("vec2 ya_clamped ="); - SRC(" max(ya_clamp_rect.xy, min(ya_clamp_rect.zw, v_yaTexCoord));"); - SRC("vec2 uv_clamped ="); - SRC(" max(uv_clamp_rect.xy, min(uv_clamp_rect.zw, v_uvTexCoord));"); - // Read the Y and UV or U and V textures into |yuv|. - SRC("vec4 texColor;"); - SRC("texColor.w = 1.0;"); - SRC("texColor.x = TextureLookup(y_texture, ya_clamped).x;"); - if (uv_texture_mode_ == UV_TEXTURE_MODE_UV) { - HDR("uniform SamplerType uv_texture;"); - SRC("texColor.yz = TextureLookup(uv_texture, uv_clamped).xy;"); - } - if (uv_texture_mode_ == UV_TEXTURE_MODE_U_V) { - HDR("uniform SamplerType u_texture;"); - HDR("uniform SamplerType v_texture;"); - SRC("texColor.y = TextureLookup(u_texture, uv_clamped).x;"); - SRC("texColor.z = TextureLookup(v_texture, uv_clamped).x;"); - } - if (yuv_alpha_texture_mode_ == YUV_HAS_ALPHA_TEXTURE) - HDR("uniform SamplerType a_texture;"); - HDR("uniform vec4 ya_clamp_rect;"); - HDR("uniform vec4 uv_clamp_rect;"); - HDR("uniform float resource_multiplier;"); - HDR("uniform float resource_offset;"); - HDR("varying TexCoordPrecision vec2 v_yaTexCoord;"); - HDR("varying TexCoordPrecision vec2 v_uvTexCoord;"); - SRC("texColor.xyz -= vec3(resource_offset);"); - SRC("texColor.xyz *= resource_multiplier;"); - break; - case INPUT_COLOR_SOURCE_UNIFORM: - DCHECK(!ignore_sampler_type_); - DCHECK(!has_rgba_fragment_tex_transform_); - DCHECK(!has_tex_clamp_rect_); - HDR("uniform vec4 color;"); - SRC("// Uniform color"); - SRC("vec4 texColor = color;"); - break; - } - - // Apply color conversion. - switch (color_conversion_mode_) { - case COLOR_CONVERSION_MODE_SHADER: - header += color_transform_->GetShaderSource(); - // Un-premultiply by alpha. - if (premultiply_alpha_mode_ != NON_PREMULTIPLIED_ALPHA) { - SRC("// un-premultiply alpha"); - SRC("if (texColor.a > 0.0) texColor.rgb /= texColor.a;"); - } - SRC("texColor.rgb = DoColorConversion(texColor.xyz);"); - SRC("texColor.rgb *= texColor.a;"); - break; - case COLOR_CONVERSION_MODE_NONE: - // Premultiply by alpha. - if (premultiply_alpha_mode_ == NON_PREMULTIPLIED_ALPHA) { - SRC("// Premultiply alpha"); - SRC("texColor.rgb *= texColor.a;"); - } - break; - } - - // Apply the color matrix to texColor. - if (has_color_matrix_) { - HDR("uniform mat4 colorMatrix;"); - HDR("uniform vec4 colorOffset;"); - SRC("// Apply color matrix"); - SRC("float nonZeroAlpha = max(texColor.a, 0.00001);"); - SRC("texColor = vec4(texColor.rgb / nonZeroAlpha, nonZeroAlpha);"); - SRC("texColor = colorMatrix * texColor + colorOffset;"); - SRC("texColor.rgb *= texColor.a;"); - SRC("texColor = clamp(texColor, 0.0, 1.0);"); - } - - // Read the mask texture. - if (mask_mode_ != NO_MASK) { - HDR("uniform SamplerType s_mask;"); - HDR("uniform vec2 maskTexCoordScale;"); - HDR("uniform vec2 maskTexCoordOffset;"); - SRC("// Read the mask"); - SRC("TexCoordPrecision vec2 maskTexCoord ="); - SRC(" vec2(maskTexCoordOffset.x + v_texCoord.x * maskTexCoordScale.x,"); - SRC(" maskTexCoordOffset.y + v_texCoord.y * maskTexCoordScale.y);"); - SRC("vec4 maskColor = TextureLookup(s_mask, maskTexCoord);"); - } - - // Compute AA. - if (aa_mode_ == USE_AA) { - HDR("varying TexCoordPrecision vec4 edge_dist[2]; // 8 edge distances."); - SRC("// Compute AA"); - SRC("vec4 d4 = min(edge_dist[0], edge_dist[1]);"); - SRC("vec2 d2 = min(d4.xz, d4.yw);"); - SRC("float aa = clamp(gl_FragCoord.w * min(d2.x, d2.y), 0.0, 1.0);"); - } - - // Apply background texture. - if (has_background_color_) { - HDR("uniform vec4 background_color;"); - SRC("// Apply uniform background color blending"); - SRC("texColor += background_color * (1.0 - texColor.a);"); - } - - // Finally apply the output color matrix to texColor. - if (has_output_color_matrix_) { - HDR("uniform mat4 output_color_matrix;"); - SRC("// Apply the output color matrix"); - SRC("texColor = output_color_matrix * texColor;"); - } - - // Tint the final color. Used for debugging composited content. - if (has_tint_color_matrix_) { - HDR("uniform mat4 tint_color_matrix;"); - SRC("// Apply the tint color matrix"); - SRC("texColor = tint_color_matrix * texColor;"); - } - - // Include header text for alpha. - if (has_uniform_alpha_) { - HDR("uniform float alpha;"); - } - if (has_varying_alpha_) { - HDR("varying float v_alpha;"); - } - - // Apply uniform alpha, aa, varying alpha, and the mask. - if (has_varying_alpha_ || aa_mode_ == USE_AA || has_uniform_alpha_ || - mask_mode_ != NO_MASK) { - SRC("// Apply alpha from uniform, varying, aa, and mask."); - std::string line = " texColor = texColor"; - if (has_varying_alpha_) - line += " * v_alpha"; - if (has_uniform_alpha_) - line += " * alpha"; - if (aa_mode_ == USE_AA) - line += " * aa"; - if (mask_mode_ != NO_MASK) - line += " * maskColor.a"; - if (yuv_alpha_texture_mode_ == YUV_HAS_ALPHA_TEXTURE) - line += " * TextureLookup(a_texture, ya_clamped).x"; - line += ";\n"; - source += line; - } - - // Write the fragment color. - SRC("// Write the fragment color"); - switch (frag_color_mode_) { - case FRAG_COLOR_MODE_DEFAULT: - DCHECK_EQ(blend_mode_, BLEND_MODE_NONE); - SRC("gl_FragColor = texColor;"); - break; - case FRAG_COLOR_MODE_OPAQUE: - DCHECK_EQ(blend_mode_, BLEND_MODE_NONE); - SRC("gl_FragColor = vec4(texColor.rgb, 1.0);"); - break; - case FRAG_COLOR_MODE_APPLY_BLEND_MODE: - if (!has_blend_mode()) { - SRC("gl_FragColor = texColor;"); - } else if (mask_mode_ != NO_MASK) { - if (mask_for_background_) - SRC("gl_FragColor = ApplyBlendMode(texColor, maskColor.w);"); - else - SRC("gl_FragColor = ApplyBlendMode(texColor);"); - } else { - SRC("gl_FragColor = ApplyBlendMode(texColor);"); - } - break; - } - - if (has_rounded_corner_) - SRC("gl_FragColor = ApplyRoundedCorner(gl_FragColor);"); - - source += "}\n"; - - return header + source; -} - -} // namespace viz diff --git a/chromium/components/viz/service/display/shader.h b/chromium/components/viz/service/display/shader.h deleted file mode 100644 index 41d6a000d66..00000000000 --- a/chromium/components/viz/service/display/shader.h +++ /dev/null @@ -1,324 +0,0 @@ -// Copyright 2011 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_SHADER_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_SHADER_H_ - -#include <string> - -#include "base/memory/raw_ptr.h" -#include "base/strings/string_piece.h" -#include "components/viz/service/viz_service_export.h" - -namespace gfx { -class ColorTransform; -class Point; -class Size; -} // namespace gfx - -namespace gpu { -namespace gles2 { -class GLES2Interface; -} -} // namespace gpu - -namespace viz { - -enum TexCoordPrecision { - TEX_COORD_PRECISION_NA = 0, - TEX_COORD_PRECISION_MEDIUM = 1, - TEX_COORD_PRECISION_HIGH = 2, -}; - -// Texture coordinate sources for the vertex shader. -enum TexCoordSource { - // Vertex shader does not populate a texture coordinate. - TEX_COORD_SOURCE_NONE, - // Texture coordinate is set to the untransformed position. - TEX_COORD_SOURCE_POSITION, - // Texture coordinate has its own attribute. - TEX_COORD_SOURCE_ATTRIBUTE, -}; - -// Texture coordinate transformation modes for the vertex shader. -enum TexCoordTransform { - // Texture coordinates are not transformed. - TEX_COORD_TRANSFORM_NONE, - // Texture coordinates are transformed by a uniform vec4, scaling by zw and - // then translating by xy. - TEX_COORD_TRANSFORM_VEC4, - // Same as the above, but add vec2(0.5) to the texture coordinate first. - TEX_COORD_TRANSFORM_TRANSLATED_VEC4, - // Texture coordiantes are transformed by a uniform mat4. - TEX_COORD_TRANSFORM_MATRIX, -}; - -// Position source for the vertex shader. -enum PositionSource { - // The position is read directly from the position attribute. - POSITION_SOURCE_ATTRIBUTE, - // The position is read by attribute index into a uniform array for xy, and - // getting zw from the attribute. - POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM, -}; - -enum AAMode { - NO_AA = 0, - USE_AA = 1, -}; - -enum PremultipliedAlphaMode { - PREMULTIPLIED_ALPHA = 0, - NON_PREMULTIPLIED_ALPHA = 1, -}; - -enum SamplerType { - SAMPLER_TYPE_NA = 0, - SAMPLER_TYPE_2D = 1, - SAMPLER_TYPE_2D_RECT = 2, - SAMPLER_TYPE_EXTERNAL_OES = 3, -}; - -enum BlendMode { - BLEND_MODE_NONE, - BLEND_MODE_NORMAL, - BLEND_MODE_DESTINATION_IN, - BLEND_MODE_SCREEN, - BLEND_MODE_OVERLAY, - BLEND_MODE_DARKEN, - BLEND_MODE_LIGHTEN, - BLEND_MODE_COLOR_DODGE, - BLEND_MODE_COLOR_BURN, - BLEND_MODE_HARD_LIGHT, - BLEND_MODE_SOFT_LIGHT, - BLEND_MODE_DIFFERENCE, - BLEND_MODE_EXCLUSION, - BLEND_MODE_MULTIPLY, - BLEND_MODE_HUE, - BLEND_MODE_SATURATION, - BLEND_MODE_COLOR, - BLEND_MODE_LUMINOSITY, - LAST_BLEND_MODE = BLEND_MODE_LUMINOSITY -}; - -enum InputColorSource { - // This includes RGB and RGBA textures. - INPUT_COLOR_SOURCE_RGBA_TEXTURE, - // This includes Y and either UV or U-and-V textures. - INPUT_COLOR_SOURCE_YUV_TEXTURES, - // A solid color specified as a uniform value. - INPUT_COLOR_SOURCE_UNIFORM, -}; - -enum UVTextureMode { - // Shader does not use YUV textures. - UV_TEXTURE_MODE_NA, - // UV plane is a single texture. - UV_TEXTURE_MODE_UV, - // U and V planes have separate textures. - UV_TEXTURE_MODE_U_V, -}; - -enum YUVAlphaTextureMode { - YUV_ALPHA_TEXTURE_MODE_NA, - YUV_NO_ALPHA_TEXTURE, - YUV_HAS_ALPHA_TEXTURE, -}; - -enum ColorConversionMode { - // No color conversion is performed. - COLOR_CONVERSION_MODE_NONE, - // Conversion is done analytically in the shader. - COLOR_CONVERSION_MODE_SHADER, -}; - -// TODO(ccameron): Merge this with BlendMode. -enum FragColorMode { - FRAG_COLOR_MODE_DEFAULT, - FRAG_COLOR_MODE_OPAQUE, - FRAG_COLOR_MODE_APPLY_BLEND_MODE, -}; - -enum MaskMode { - NO_MASK = 0, - HAS_MASK = 1, -}; - -// Note: The highp_threshold_cache must be provided by the caller to make -// the caching multi-thread/context safe in an easy low-overhead manner. -// The caller must make sure to clear highp_threshold_cache to 0, so it can be -// reinitialized, if a new or different context is used. -VIZ_SERVICE_EXPORT TexCoordPrecision -TexCoordPrecisionRequired(gpu::gles2::GLES2Interface* context, - int* highp_threshold_cache, - int highp_threshold_min, - const gfx::Point& max_coordinate); - -VIZ_SERVICE_EXPORT TexCoordPrecision -TexCoordPrecisionRequired(gpu::gles2::GLES2Interface* context, - int* highp_threshold_cache, - int highp_threshold_min, - const gfx::Size& max_size); - -class VIZ_SERVICE_EXPORT VertexShader { - public: - VertexShader(); - void Init(gpu::gles2::GLES2Interface* context, - unsigned program, - int* base_uniform_index); - std::string GetShaderString() const; - - protected: - friend class Program; - - // Use arrays of uniforms for matrix, texTransform, and opacity. - bool use_uniform_arrays_ = false; - - PositionSource position_source_ = POSITION_SOURCE_ATTRIBUTE; - TexCoordSource tex_coord_source_ = TEX_COORD_SOURCE_NONE; - TexCoordTransform tex_coord_transform_ = TEX_COORD_TRANSFORM_NONE; - - // Used only with TEX_COORD_TRANSFORM_VEC4. - int vertex_tex_transform_location_ = -1; - - // Used only with TEX_COORD_TRANSFORM_MATRIX. - int tex_matrix_location_ = -1; - - // Uniforms for YUV textures. - bool is_ya_uv_ = false; - int ya_tex_scale_location_ = -1; - int ya_tex_offset_location_ = -1; - int uv_tex_scale_location_ = -1; - int uv_tex_offset_location_ = -1; - - // Matrix to transform the position. - int matrix_location_ = -1; - - // Used only with POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM. - int quad_location_ = -1; - - // Extra dummy variables to work around bugs on Android. - // TODO(ccameron): This is likley unneeded cargo-culting. - // http://crbug.com/240602 - bool has_dummy_variables_ = false; - - bool has_vertex_opacity_ = false; - int vertex_opacity_location_ = -1; - - AAMode aa_mode_ = NO_AA; - int viewport_location_ = -1; - int edge_location_ = -1; -}; - -class VIZ_SERVICE_EXPORT FragmentShader { - public: - FragmentShader(const FragmentShader&) = delete; - FragmentShader& operator=(const FragmentShader&) = delete; - - virtual void Init(gpu::gles2::GLES2Interface* context, - unsigned program, - int* base_uniform_index); - std::string GetShaderString() const; - - protected: - FragmentShader(); - virtual std::string GetShaderSource() const; - bool has_blend_mode() const { return blend_mode_ != BLEND_MODE_NONE; } - - void SetBlendModeFunctions(std::string* shader_string) const; - void SetRoundedCornerFunctions(std::string* shader_string) const; - - // Settings that are modified by sub-classes. - AAMode aa_mode_ = NO_AA; - bool has_varying_alpha_ = false; - PremultipliedAlphaMode premultiply_alpha_mode_ = PREMULTIPLIED_ALPHA; - FragColorMode frag_color_mode_ = FRAG_COLOR_MODE_DEFAULT; - InputColorSource input_color_type_ = INPUT_COLOR_SOURCE_RGBA_TEXTURE; - - // Used only if |blend_mode_| is not BLEND_MODE_NONE. - int backdrop_location_ = -1; - int original_backdrop_location_ = -1; - int backdrop_rect_location_ = -1; - - // Used only if |input_color_type_| is INPUT_COLOR_SOURCE_RGBA_TEXTURE. - bool has_rgba_fragment_tex_transform_ = false; - int sampler_location_ = -1; - int fragment_tex_transform_location_ = -1; - - // Always use sampler2D and texture2D for the RGBA texture, regardless of the - // specified SamplerType. - // TODO(ccameron): Change GLRenderer to always specify the correct - // SamplerType. - bool ignore_sampler_type_ = false; - - // Used only if |input_color_type_| is INPUT_COLOR_SOURCE_UNIFORM. - int color_location_ = -1; - - MaskMode mask_mode_ = NO_MASK; - int mask_sampler_location_ = -1; - int mask_tex_coord_scale_location_ = -1; - int mask_tex_coord_offset_location_ = -1; - - bool has_color_matrix_ = false; - int color_matrix_location_ = -1; - int color_offset_location_ = -1; - - bool has_uniform_alpha_ = false; - int alpha_location_ = -1; - - bool has_background_color_ = false; - int background_color_location_ = -1; - - bool has_tex_clamp_rect_ = false; - int tex_clamp_rect_location_ = -1; - - TexCoordPrecision tex_coord_precision_ = TEX_COORD_PRECISION_NA; - SamplerType sampler_type_ = SAMPLER_TYPE_NA; - - BlendMode blend_mode_ = BLEND_MODE_NONE; - bool mask_for_background_ = false; - - // YUV-only parameters. - YUVAlphaTextureMode yuv_alpha_texture_mode_ = YUV_ALPHA_TEXTURE_MODE_NA; - UVTextureMode uv_texture_mode_ = UV_TEXTURE_MODE_UV; - - ColorConversionMode color_conversion_mode_ = COLOR_CONVERSION_MODE_NONE; - raw_ptr<const gfx::ColorTransform> color_transform_ = nullptr; - - bool has_output_color_matrix_ = false; - int output_color_matrix_location_ = -1; - - bool has_tint_color_matrix_ = false; - int tint_color_matrix_location_ = -1; - - // YUV uniform locations. - int y_texture_location_ = -1; - int u_texture_location_ = -1; - int v_texture_location_ = -1; - int uv_texture_location_ = -1; - int a_texture_location_ = -1; - int ya_clamp_rect_location_ = -1; - int uv_clamp_rect_location_ = -1; - - // Rounded corner locations - bool has_rounded_corner_ = false; - int rounded_corner_rect_location_ = -1; - int rounded_corner_radius_location_ = -1; - - // The resource offset and multiplier to adjust for bit depth. - int resource_multiplier_location_ = -1; - int resource_offset_location_ = -1; - - private: - friend class Program; - - void AppendHelperFunctions(std::string* buffer) const; - void AppendBlendFunction(std::string* buffer) const; - base::StringPiece GetBlendFunctionBodyForAlpha() const; - base::StringPiece GetBlendFunctionBodyForRGB() const; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_SHADER_H_ diff --git a/chromium/components/viz/service/display/shader_unittest.cc b/chromium/components/viz/service/display/shader_unittest.cc deleted file mode 100644 index 6990cbf14de..00000000000 --- a/chromium/components/viz/service/display/shader_unittest.cc +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2013 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/shader.h" - -#include "components/viz/test/test_context_provider.h" -#include "components/viz/test/test_gles2_interface.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/geometry/point.h" -#include "ui/gfx/geometry/size.h" - -namespace viz { - -TEST(ShaderTest, HighpThresholds) { - // The test gl always uses a mediump precision of 10 bits which - // corresponds to a native highp threshold of 2^10 = 1024 - scoped_refptr<TestContextProvider> provider = TestContextProvider::Create(); - provider->BindToCurrentThread(); - gpu::gles2::GLES2Interface* test_gl = provider->ContextGL(); - - int threshold_cache = 0; - int threshold_min; - gfx::Point closePoint(512, 512); - gfx::Size smallSize(512, 512); - gfx::Point farPoint(2560, 2560); - gfx::Size bigSize(2560, 2560); - - threshold_min = 0; - EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM, - TexCoordPrecisionRequired(test_gl, &threshold_cache, threshold_min, - closePoint)); - EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM, - TexCoordPrecisionRequired(test_gl, &threshold_cache, threshold_min, - smallSize)); - EXPECT_EQ(TEX_COORD_PRECISION_HIGH, - TexCoordPrecisionRequired(test_gl, &threshold_cache, threshold_min, - farPoint)); - EXPECT_EQ(TEX_COORD_PRECISION_HIGH, - TexCoordPrecisionRequired(test_gl, &threshold_cache, threshold_min, - bigSize)); - - threshold_min = 3000; - EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM, - TexCoordPrecisionRequired(test_gl, &threshold_cache, threshold_min, - closePoint)); - EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM, - TexCoordPrecisionRequired(test_gl, &threshold_cache, threshold_min, - smallSize)); - EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM, - TexCoordPrecisionRequired(test_gl, &threshold_cache, threshold_min, - farPoint)); - EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM, - TexCoordPrecisionRequired(test_gl, &threshold_cache, threshold_min, - bigSize)); -} - -} // namespace viz diff --git a/chromium/components/viz/service/display/skia_output_surface.h b/chromium/components/viz/service/display/skia_output_surface.h index 635a9e0a6b3..a6557e31d0c 100644 --- a/chromium/components/viz/service/display/skia_output_surface.h +++ b/chromium/components/viz/service/display/skia_output_surface.h @@ -17,6 +17,7 @@ #include "components/viz/service/display/overlay_processor_interface.h" #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/core/SkYUVAInfo.h" +#include "ui/gfx/gpu_fence_handle.h" #if BUILDFLAG(IS_WIN) #include "components/viz/service/display/dc_layer_overlay.h" @@ -29,14 +30,14 @@ class SkCanvas; class SkImage; -#if BUILDFLAG(IS_APPLE) -class SkDeferredDisplayList; -#endif - namespace gfx { class ColorSpace; } // namespace gfx +namespace gpu { +class SharedImageInterface; +} + namespace viz { class OverlayCandidate; @@ -121,6 +122,7 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurface : public OutputSurface, ResourceFormat format, bool mipmap, sk_sp<SkColorSpace> color_space, + bool is_overlay, const gpu::Mailbox& mailbox) = 0; // Finish painting the current frame or current render pass, depends on which @@ -128,8 +130,13 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurface : public OutputSurface, // play the DDL back on GPU thread on a cached SkSurface. // Optionally the caller may specify |on_finished| callback to be called after // the GPU has finished processing all submitted commands. The callback may be - // called on a different thread. - virtual void EndPaint(base::OnceClosure on_finished) = 0; + // called on a different thread. The caller may also specify + // |return_release_fence_cb| callback to be called after all commands are + // submitted. The callback will return the release fence which will be + // signaled once the submitted commands are processed. + virtual void EndPaint(base::OnceClosure on_finished, + base::OnceCallback<void(gfx::GpuFenceHandle)> + return_release_fence_cb) = 0; // Make a promise SkImage from a render pass id. The render pass has been // painted with BeginPaintRenderPass and FinishPaintRenderPass. The format @@ -162,8 +169,7 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurface : public OutputSurface, // the GPU has finished processing all submitted commands. The callback may be // called on a different thread. virtual void ScheduleOverlays(OverlayList overlays, - std::vector<gpu::SyncToken> sync_tokens, - base::OnceClosure on_finished) = 0; + std::vector<gpu::SyncToken> sync_tokens) = 0; // Add context lost observer. virtual void AddContextLostObserver(ContextLostObserver* observer) = 0; @@ -194,15 +200,6 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurface : public OutputSurface, // 0 < n <= capabilities_.number_of_buffers. // Return true if new buffers are allocated. virtual bool EnsureMinNumberOfBuffers(int n) = 0; - -#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) - virtual SkCanvas* BeginPaintRenderPassOverlay( - const gfx::Size& size, - ResourceFormat format, - bool mipmap, - sk_sp<SkColorSpace> color_space) = 0; - virtual sk_sp<SkDeferredDisplayList> EndPaintRenderPassOverlay() = 0; -#endif }; } // namespace viz diff --git a/chromium/components/viz/service/display/skia_readback_pixeltest.cc b/chromium/components/viz/service/display/skia_readback_pixeltest.cc index c3bffea471f..db740b2c878 100644 --- a/chromium/components/viz/service/display/skia_readback_pixeltest.cc +++ b/chromium/components/viz/service/display/skia_readback_pixeltest.cc @@ -17,9 +17,11 @@ #include "cc/test/pixel_test.h" #include "cc/test/pixel_test_utils.h" #include "cc/test/resource_provider_test_utils.h" +#include "components/viz/common/frame_sinks/blit_request.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" +#include "components/viz/common/gpu/context_provider.h" #include "components/viz/service/display_embedder/skia_output_surface_impl.h" #include "components/viz/service/gl/gpu_service_impl.h" #include "components/viz/test/gl_scaler_test_util.h" @@ -572,7 +574,8 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SkiaReadbackPixelTestNV12); class SkiaReadbackPixelTestNV12WithBlit : public SkiaReadbackPixelTest, - public testing::WithParamInterface<bool> { + public testing::WithParamInterface< + std::tuple<bool, LetterboxingBehavior, bool>> { public: CopyOutputResult::Destination RequestDestination() const { return CopyOutputResult::Destination::kNativeTextures; @@ -583,8 +586,17 @@ class SkiaReadbackPixelTestNV12WithBlit } void SetUp() override { - SkiaReadbackPixelTest::SetUpReadbackPixeltest(GetParam()); + SkiaReadbackPixelTest::SetUpReadbackPixeltest(std::get<0>(GetParam())); + } + + LetterboxingBehavior GetLetterboxingBehavior() const { + return std::get<1>(GetParam()); } + + // Test parameter that will return `true` if we'll claim that the textures we + // create come from GpuMemoryBuffer, `false` otherwise. This exercises a + // different code path in SkiaRenderer. + bool populates_gpu_memory_buffer() const { return std::get<2>(GetParam()); } }; // Test that SkiaRenderer readback works correctly. This test will use the @@ -625,8 +637,7 @@ TEST_P(SkiaReadbackPixelTestNV12WithBlit, ExecutesCopyRequestWithBlit) { << " The test case expects the blit region's origin to be even for NV12 " "blit requests"; - const SkColor rgba_red = SkColorSetARGB(0xff, 0xff, 0, 0); - const SkColor yuv_red = GLScalerTestUtil::ConvertRGBAColorToYUV(rgba_red); + const SkColor yuv_red = GLScalerTestUtil::ConvertRGBAColorToYUV(SK_ColorRED); const std::vector<uint8_t> luma_pattern = { static_cast<uint8_t>(SkColorGetR(yuv_red))}; @@ -669,8 +680,9 @@ TEST_P(SkiaReadbackPixelTestNV12WithBlit, ExecutesCopyRequestWithBlit) { request.set_result_selection(result_selection); - request.set_blit_request( - BlitRequest(destination_subregion.origin(), mailboxes)); + request.set_blit_request(BlitRequest( + destination_subregion.origin(), GetLetterboxingBehavior(), + mailboxes, populates_gpu_memory_buffer())); })); // Check that a result was produced and is of the expected rect/size. @@ -716,7 +728,18 @@ TEST_P(SkiaReadbackPixelTestNV12WithBlit, ExecutesCopyRequestWithBlit) { // The textures that we passed in to BlitRequest contained NV12 plane data for // an all-red image, let's re-create such a bitmap: SkBitmap expected = GLScalerTestUtil::AllocateRGBABitmap(source_size); - expected.eraseColor(rgba_red); + + if (GetLetterboxingBehavior() == LetterboxingBehavior::kLetterbox) { + // We have requested the results to be letterboxed, so everything that + // CopyOutputRequest is not populating w/ render pass contents should be + // black: + expected.eraseColor(SK_ColorBLACK); + } else { + // We have requested the results to not be letterboxed, so everything that + // CopyOutputRequest is not populating w/ render pass will have original + // contents (red in our case): + expected.eraseColor(SK_ColorRED); + } // Blit request should "stitch" the pixels from the source image into a // sub-region of caller-provided texture - let's write our expected pixels @@ -732,10 +755,15 @@ TEST_P(SkiaReadbackPixelTestNV12WithBlit, ExecutesCopyRequestWithBlit) { } #if !BUILDFLAG(IS_ANDROID) || !defined(ARCH_CPU_X86_FAMILY) -INSTANTIATE_TEST_SUITE_P(, - SkiaReadbackPixelTestNV12WithBlit, - // Result scaling: Scale by half? - testing::Values(true, false)); +INSTANTIATE_TEST_SUITE_P( + , + SkiaReadbackPixelTestNV12WithBlit, + testing::Combine( + testing::Bool(), // Result scaling: Scale by half? + testing::Values(LetterboxingBehavior::kDoNotLetterbox, + LetterboxingBehavior::kLetterbox), + testing::Bool() // Should behave as if COR is populating a GMB? + )); #else // Don't instantiate the NV12 tests when run on Android emulator, they won't // work since the SkiaRenderer currently does not support CopyOutputRequests diff --git a/chromium/components/viz/service/display/skia_renderer.cc b/chromium/components/viz/service/display/skia_renderer.cc index 5a6eb5feb46..1b0a7116b73 100644 --- a/chromium/components/viz/service/display/skia_renderer.cc +++ b/chromium/components/viz/service/display/skia_renderer.cc @@ -45,7 +45,6 @@ #include "components/viz/service/display/renderer_utils.h" #include "components/viz/service/display/resource_fence.h" #include "components/viz/service/display/skia_output_surface.h" -#include "gpu/command_buffer/client/gles2_interface.h" #include "gpu/command_buffer/client/shared_image_interface.h" #include "gpu/command_buffer/common/shared_image_usage.h" #include "gpu/command_buffer/common/sync_token.h" @@ -575,8 +574,7 @@ class SkiaRenderer::ScopedSkImageBuilder { bool maybe_concurrent_reads, SkAlphaType alpha_type = kPremul_SkAlphaType, GrSurfaceOrigin origin = kTopLeft_GrSurfaceOrigin, - const absl::optional<gfx::ColorSpace>& - override_colorspace = absl::nullopt, + sk_sp<SkColorSpace> override_color_space = nullptr, bool raw_draw_if_possible = false); ScopedSkImageBuilder(const ScopedSkImageBuilder&) = delete; @@ -586,12 +584,12 @@ class SkiaRenderer::ScopedSkImageBuilder { const SkImage* sk_image() const { return sk_image_; } const cc::PaintOpBuffer* paint_op_buffer() const { return paint_op_buffer_; } - const absl::optional<SkColor>& clear_color() const { return clear_color_; } + const absl::optional<SkColor4f>& clear_color() const { return clear_color_; } private: raw_ptr<const SkImage> sk_image_ = nullptr; raw_ptr<const cc::PaintOpBuffer> paint_op_buffer_ = nullptr; - absl::optional<SkColor> clear_color_; + absl::optional<SkColor4f> clear_color_; }; SkiaRenderer::ScopedSkImageBuilder::ScopedSkImageBuilder( @@ -600,7 +598,7 @@ SkiaRenderer::ScopedSkImageBuilder::ScopedSkImageBuilder( bool maybe_concurrent_reads, SkAlphaType alpha_type, GrSurfaceOrigin origin, - const absl::optional<gfx::ColorSpace>& override_color_space, + sk_sp<SkColorSpace> override_color_space, bool raw_draw_if_possible) { if (!resource_id) return; @@ -702,22 +700,30 @@ class SkiaRenderer::ScopedYUVSkImageBuilder { sk_sp<SkImage> sk_image_; }; -class SkiaRenderer::FrameResourceFence : public ResourceFence { +// A read lock based fence that is signaled after gpu commands are completed +// meaning the resource has been read. +class SkiaRenderer::FrameResourceGpuCommandsCompletedFence + : public ResourceFence { public: - FrameResourceFence() = default; - - FrameResourceFence(const FrameResourceFence&) = delete; - FrameResourceFence& operator=(const FrameResourceFence&) = delete; + FrameResourceGpuCommandsCompletedFence() = default; + FrameResourceGpuCommandsCompletedFence( + const FrameResourceGpuCommandsCompletedFence&) = delete; + FrameResourceGpuCommandsCompletedFence& operator=( + const FrameResourceGpuCommandsCompletedFence&) = delete; // ResourceFence implementation. void Set() override { set_ = true; } bool HasPassed() override { return event_.IsSignaled(); } + gfx::GpuFenceHandle GetGpuFenceHandle() override { + NOTREACHED(); + return gfx::GpuFenceHandle(); + } bool WasSet() { return set_; } void Signal() { event_.Signal(); } private: - ~FrameResourceFence() override = default; + ~FrameResourceGpuCommandsCompletedFence() override = default; // Accessed only from compositor thread. bool set_ = false; @@ -725,6 +731,41 @@ class SkiaRenderer::FrameResourceFence : public ResourceFence { base::WaitableEvent event_; }; +// FrameResourceFence that gets a ReleaseFence which is later set to returned +// resources. +class SkiaRenderer::FrameResourceReleaseFence : public ResourceFence { + public: + FrameResourceReleaseFence() = default; + FrameResourceReleaseFence(const FrameResourceReleaseFence&) = delete; + FrameResourceReleaseFence& operator=(const FrameResourceReleaseFence&) = + delete; + + // ResourceFence implementation: + void Set() override { set_ = true; } + // If the fence handle has been set, |this| has passed aka the callback has + // been called. + bool HasPassed() override { return release_fence_.has_value(); } + gfx::GpuFenceHandle GetGpuFenceHandle() override { + return HasPassed() ? release_fence_.value().Clone() : gfx::GpuFenceHandle(); + } + + bool WasSet() { return set_; } + void SetReleaseFenceCallback(gfx::GpuFenceHandle release_fence) { + release_fence_ = std::move(release_fence); + } + + private: + ~FrameResourceReleaseFence() override = default; + + // Accessed only from compositor thread. + bool set_ = false; + + // This is made optional so that the value is set after + // SetReleaseFenceCallback is called. Otherwise, there is no way to know if + // the fence has been set and a null handle is a "valid" handle. + absl::optional<gfx::GpuFenceHandle> release_fence_; +}; + SkiaRenderer::SkiaRenderer(const RendererSettings* settings, const DebugRendererSettings* debug_settings, OutputSurface* output_surface, @@ -741,9 +782,17 @@ SkiaRenderer::SkiaRenderer(const RendererSettings* settings, DCHECK(skia_output_surface_); lock_set_for_external_use_.emplace(resource_provider, skia_output_surface_); - current_frame_resource_fence_ = base::MakeRefCounted<FrameResourceFence>(); - this->resource_provider()->SetReadLockFence( - current_frame_resource_fence_.get()); + // There can be different synchronization types requested for different + // resources. Some of them may require SyncToken, others - ReadLockFence, and + // others may need ReleaseFence. SyncTokens are set when the output surface + // is flushed and external resources are released. However, other resources + // require additional setup, which helps to handle that. + current_gpu_commands_completed_fence_ = + base::MakeRefCounted<FrameResourceGpuCommandsCompletedFence>(); + current_release_fence_ = base::MakeRefCounted<FrameResourceReleaseFence>(); + this->resource_provider()->SetGpuCommandsCompletedFence( + current_gpu_commands_completed_fence_.get()); + this->resource_provider()->SetReleaseFence(current_release_fence_.get()); #if OS_ANDROID use_real_color_space_for_stream_video_ = @@ -760,7 +809,8 @@ bool SkiaRenderer::CanPartialSwap() { void SkiaRenderer::BeginDrawingFrame() { TRACE_EVENT0("viz", "SkiaRenderer::BeginDrawingFrame"); - DCHECK(!current_frame_resource_fence_->WasSet()); + DCHECK(!current_gpu_commands_completed_fence_->WasSet()); + DCHECK(!current_release_fence_->WasSet()); } void SkiaRenderer::FinishDrawingFrame() { @@ -808,6 +858,16 @@ void SkiaRenderer::SwapBuffers(SwapFrameData swap_frame_data) { swap_buffer_rect_ = gfx::Rect(); FlushOutputSurface(); + +#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) + // Delete render pass overlay backings from the previous frame that will not + // be used again. + for (auto& backing : available_render_pass_overlay_backings_) { + skia_output_surface_->GetSharedImageInterface()->DestroySharedImage( + gpu::SyncToken(), backing.mailbox); + } + available_render_pass_overlay_backings_.clear(); +#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE) } void SkiaRenderer::SwapBuffersSkipped() { @@ -828,13 +888,13 @@ void SkiaRenderer::SwapBuffersComplete(gfx::GpuFenceHandle release_fence) { auto read_fence_lock_iter = committed_overlay_locks_.end(); if (!release_fence.is_null()) { - // Set release fences for returning for last frame overlay resources. + // Set release fences to return overlay resources for last frame. for (auto& lock : committed_overlay_locks_) { lock.SetReleaseFence(release_fence.Clone()); } - // Find all locks that have a read-lock fence associated with them. - // If we have a release fence, it's not safe to release them here. - // Release them later in BuffersPresented. + // Find all locks that have a read-lock fence associated with them and move + // them to the back of locks. If we have a release fence, it's not safe to + // release them here. Release them later in BuffersPresented(). read_fence_lock_iter = std::partition( committed_overlay_locks_.begin(), committed_overlay_locks_.end(), [](auto& lock) { return !lock.HasReadLockFence(); }); @@ -846,7 +906,7 @@ void SkiaRenderer::SwapBuffersComplete(gfx::GpuFenceHandle release_fence) { // Right now, only macOS and Ozone need to return mailboxes of released // overlays, so we should not release |committed_overlay_locks_| here. The - // resources in it will be released by DidReceiveReleasedOverlays() later. + // resources in it will be released in DidReceiveReleasedOverlays() later. #if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) for (auto lock_iter = committed_overlay_locks_.begin(); lock_iter != read_fence_lock_iter; ++lock_iter) { @@ -854,6 +914,8 @@ void SkiaRenderer::SwapBuffersComplete(gfx::GpuFenceHandle release_fence) { } #endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE) + // Current pending locks should have been committed by the next time + // SwapBuffers() is completed. committed_overlay_locks_.clear(); std::swap(committed_overlay_locks_, pending_overlay_locks_.front()); pending_overlay_locks_.pop_front(); @@ -869,19 +931,38 @@ void SkiaRenderer::DidReceiveReleasedOverlays( // This method is only called on macOS and Ozone right now. #if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) for (const auto& mailbox : released_overlays) { - auto it = awaiting_release_overlay_locks_.find(mailbox); - if (it == awaiting_release_overlay_locks_.end()) { - // TODO(crbug.com/1299794): Re-enable this DCHECK on Ozone. + // If this mailbox is for render pass overlay, mark the released render pass + // overlay backing as available to be re-used. + auto backing_iter = + std::find_if(in_flight_render_pass_overlay_backings_.begin(), + in_flight_render_pass_overlay_backings_.end(), + [&mailbox](const RenderPassBacking& backing) { + return backing.mailbox == mailbox; + }); + if (backing_iter != in_flight_render_pass_overlay_backings_.end()) { + available_render_pass_overlay_backings_.push_back( + std::move(*backing_iter)); + in_flight_render_pass_overlay_backings_.erase(backing_iter); + } + + auto iter = std::find_if(awaiting_release_overlay_locks_.begin(), + awaiting_release_overlay_locks_.end(), + [&mailbox](const OverlayLock& lock) { + return lock.mailbox() == mailbox; + }); + if (iter == awaiting_release_overlay_locks_.end()) { +// TODO(crbug.com/1299794): Re-enable this DCHECK on Ozone. #if !defined(USE_OZONE) + // The released overlay should always be found as awaiting to be released. DLOG(FATAL) << "Got an unexpected mailbox"; #endif // !defined(USE_OZONE) continue; } - awaiting_release_overlay_locks_.erase(it); + awaiting_release_overlay_locks_.erase(iter); } #else NOTREACHED(); -#endif // !(BUILDFLAG(IS_APPLE) || defined (USE_OZONE)) +#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE) } bool SkiaRenderer::FlippedFramebuffer() const { @@ -899,8 +980,6 @@ void SkiaRenderer::EnsureScissorTestDisabled() { } void SkiaRenderer::BindFramebufferToOutputSurface() { - DCHECK(!output_surface_->HasExternalStencilTest()); - root_canvas_ = skia_output_surface_->BeginPaintCurrentFrame(); current_canvas_ = root_canvas_; current_surface_ = root_surface_.get(); @@ -915,7 +994,8 @@ void SkiaRenderer::BindFramebufferToTexture( RenderPassBacking& backing = iter->second; current_canvas_ = skia_output_surface_->BeginPaintRenderPass( render_pass_id, backing.size, backing.format, backing.generate_mipmap, - backing.color_space.ToSkColorSpace(), backing.mailbox); + RenderPassBackingSkColorSpace(backing), /*is_overlay=*/false, + backing.mailbox); } void SkiaRenderer::SetScissorTestRect(const gfx::Rect& scissor_rect) { @@ -941,9 +1021,13 @@ void SkiaRenderer::ClearFramebuffer() { if (current_frame()->current_render_pass->has_transparent_background) { ClearCanvas(SkColorSetARGB(0, 0, 0, 0)); } else { -#if DCHECK_IS_ON() +#if DCHECK_IS_ON() && !BUILDFLAG(IS_LINUX) // On DEBUG builds, opaque render passes are cleared to blue // to easily see regions that were not drawn on the screen. + // ClearCavas() call causes slight pixel difference, so linux-ref and + // linux-blink-ref bots cannot share the same baseline for webtest. + // So remove this ClearCanvas() call for dcheck on build for now. + // TODO(crbug.com/1330278): add it back. ClearCanvas(SkColorSetARGB(255, 0, 0, 255)); #endif } @@ -1889,10 +1973,11 @@ void SkiaRenderer::DrawSingleImage(const SkImage* image, constraint); } -void SkiaRenderer::DrawPaintOpBuffer(const cc::PaintOpBuffer* buffer, - const absl::optional<SkColor>& clear_color, - const TileDrawQuad* quad, - const DrawQuadParams* params) { +void SkiaRenderer::DrawPaintOpBuffer( + const cc::PaintOpBuffer* buffer, + const absl::optional<SkColor4f>& clear_color, + const TileDrawQuad* quad, + const DrawQuadParams* params) { TRACE_EVENT0("viz", "SkiaRenderer::DrawPaintOpBuffer"); if (!batched_quads_.empty()) FlushBatchedQuads(); @@ -2024,7 +2109,7 @@ void SkiaRenderer::DrawPictureQuad(const PictureDrawQuad* quad, void SkiaRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad, const DrawRPDQParams* rpdq_params, DrawQuadParams* params) { - DrawColoredQuad(SkColor4f::FromColor(quad->color), rpdq_params, params); + DrawColoredQuad(quad->color, rpdq_params, params); } void SkiaRenderer::DrawStreamVideoQuad(const StreamVideoDrawQuad* quad, @@ -2033,13 +2118,12 @@ void SkiaRenderer::DrawStreamVideoQuad(const StreamVideoDrawQuad* quad, TRACE_EVENT0("viz", "SkiaRenderer::DrawStreamVideoQuad"); DCHECK(!MustFlushBatchedQuads(quad, rpdq_params, *params)); - absl::optional<gfx::ColorSpace> override_color_space; + sk_sp<SkColorSpace> override_color_space; // Force SRGB color space if we don't want real color space from media // decoder. - if (!use_real_color_space_for_stream_video_) { - override_color_space = gfx::ColorSpace::CreateSRGB(); - } + if (!use_real_color_space_for_stream_video_) + override_color_space = SkColorSpace::MakeSRGB(); ScopedSkImageBuilder builder(this, quad->resource_id(), /*maybe_concurrent_reads=*/true, @@ -2079,17 +2163,17 @@ void SkiaRenderer::DrawTextureQuad(const TextureDrawQuad* quad, const bool needs_color_conversion_filter = quad->is_video_frame && src_color_space.IsHDR(); - absl::optional<gfx::ColorSpace> override_color_space; + sk_sp<SkColorSpace> override_color_space; if (needs_color_conversion_filter) - override_color_space = CurrentRenderPassColorSpace(); + override_color_space = CurrentRenderPassSkColorSpace(); - // TODO(b/221643955): Some Chrome OS tests rely on the old GLRenderer - // behavior of skipping color space conversions if the quad's color space is - // invalid. Once these tests are migrated, we can remove the override here - // and revert to Skia's default behavior of assuming sRGB on invalid. + // TODO(b/221643955): Some Chrome OS tests rely on the old GLRenderer + // behavior of skipping color space conversions if the quad's color space is + // invalid. Once these tests are migrated, we can remove the override here + // and revert to Skia's default behavior of assuming sRGB on invalid. #if BUILDFLAG(IS_CHROMEOS_ASH) if (!src_color_space.IsValid()) - override_color_space = CurrentRenderPassColorSpace(); + override_color_space = CurrentRenderPassSkColorSpace(); #endif // BUILDFLAG(IS_CHROMEOS_ASH) ScopedSkImageBuilder builder( @@ -2118,7 +2202,7 @@ void SkiaRenderer::DrawTextureQuad(const TextureDrawQuad* quad, // 2. The vertex opacities are not all 1s. // 3. The quad contains video which might need special white level adjustment. const bool blend_background = - quad->background_color != SK_ColorTRANSPARENT && !image->isOpaque(); + quad->background_color != SkColors::kTransparent && !image->isOpaque(); const bool vertex_alpha = quad->vertex_opacity[0] < 1.f || quad->vertex_opacity[1] < 1.f || quad->vertex_opacity[2] < 1.f || quad->vertex_opacity[3] < 1.f; @@ -2204,8 +2288,9 @@ void SkiaRenderer::DrawTextureQuad(const TextureDrawQuad* quad, if (blend_background) { // Add a color filter that does DstOver blending between texture and the // background color. Then, modulate by quad's opacity *after* blending. - sk_sp<SkColorFilter> cf = - SkColorFilters::Blend(quad->background_color, SkBlendMode::kDstOver); + // TODO(crbug/1308932) remove toSkColor and make all SkColor4f + sk_sp<SkColorFilter> cf = SkColorFilters::Blend( + quad->background_color.toSkColor(), SkBlendMode::kDstOver); if (quad_alpha < 1.f) { cf = MakeOpacityFilter(quad_alpha, std::move(cf)); quad_alpha = 1.f; @@ -2220,7 +2305,7 @@ void SkiaRenderer::DrawTextureQuad(const TextureDrawQuad* quad, // Skia won't perform color conversion. const gfx::ColorSpace dst_color_space = CurrentRenderPassColorSpace(); DCHECK(SkColorSpace::Equals(image->colorSpace(), - dst_color_space.ToSkColorSpace().get())); + CurrentRenderPassSkColorSpace().get())); sk_sp<SkColorFilter> color_filter = GetColorSpaceConversionFilter(src_color_space, dst_color_space); paint.setColorFilter(color_filter->makeComposed(paint.refColorFilter())); @@ -2254,7 +2339,7 @@ void SkiaRenderer::DrawTileDrawQuad(const TileDrawQuad* quad, this, quad->resource_id(), /*maybe_concurrent_reads=*/false, quad->is_premultiplied ? kPremul_SkAlphaType : kUnpremul_SkAlphaType, /*origin=*/kTopLeft_GrSurfaceOrigin, - /*override_colorspace=*/absl::nullopt, raw_draw_if_possible); + /*override_color_space=*/nullptr, raw_draw_if_possible); params->vis_tex_coords = cc::MathUtil::ScaleRectProportional( quad->tex_coord_rect, gfx::RectF(quad->rect), params->visible_rect); @@ -2322,8 +2407,7 @@ void SkiaRenderer::DrawYUVVideoQuad(const YUVVideoDrawQuad* quad, // color space we lie and say the SkImage destination color space is always // the same as the rest of the frame. Otherwise the two color space // adjustments combined will produce the wrong result. - const gfx::ColorSpace& frame_color_space = CurrentRenderPassColorSpace(); - gfx::ColorSpace dst_color_space = frame_color_space; + gfx::ColorSpace dst_color_space = CurrentRenderPassColorSpace(); #if BUILDFLAG(IS_WIN) // Force sRGB output on Windows for overlay candidate video quads to match @@ -2344,12 +2428,11 @@ void SkiaRenderer::DrawYUVVideoQuad(const YUVVideoDrawQuad* quad, #endif DCHECK(resource_provider()); - // Pass in |frame_color_space| here instead of |dst_color_space| so the color - // space transform going from SkImage to SkSurface is identity. The - // SkColorFilter already handles color space conversion so this avoids + // Pass in |CurrentRenderPassSkColorSpace()| here instead of |dst_color_space| + // so the color space transform going from SkImage to SkSurface is identity. + // The SkColorFilter already handles color space conversion so this avoids // applying the conversion twice. - ScopedYUVSkImageBuilder builder(this, quad, - frame_color_space.ToSkColorSpace()); + ScopedYUVSkImageBuilder builder(this, quad, CurrentRenderPassSkColorSpace()); const SkImage* image = builder.sk_image(); if (!image) return; @@ -2384,9 +2467,14 @@ void SkiaRenderer::DrawUnsupportedQuad(const DrawQuad* quad, } void SkiaRenderer::ScheduleOverlays() { - DCHECK(!current_frame_resource_fence_->WasSet()); + DCHECK(!current_gpu_commands_completed_fence_->WasSet()); + DCHECK(!current_release_fence_->WasSet()); + // Always add an empty set of locks to be used in either SwapBuffersSkipped() + // or SwapBuffersComplete(). pending_overlay_locks_.emplace_back(); + [[maybe_unused]] auto& locks = pending_overlay_locks_.back(); + if (current_frame()->overlay_list.empty()) return; @@ -2401,7 +2489,6 @@ void SkiaRenderer::ScheduleOverlays() { // switched over to OverlayProcessor. // TODO(weiliangc): Remove this when CrOS and Android SurfaceControl switch // to OverlayProcessor as well. - auto& locks = pending_overlay_locks_.back(); for (auto& overlay : current_frame()->overlay_list) { // Resources will be unlocked after the next SwapBuffers() is completed. locks.emplace_back(resource_provider(), overlay.resource_id); @@ -2416,7 +2503,6 @@ void SkiaRenderer::ScheduleOverlays() { DCHECK(!overlay.mailbox.IsZero()); } #elif BUILDFLAG(IS_WIN) - auto& locks = pending_overlay_locks_.back(); for (auto& dc_layer_overlay : current_frame()->overlay_list) { for (size_t i = 0; i < DCLayerOverlay::kNumResources; ++i) { ResourceId resource_id = dc_layer_overlay.resources[i]; @@ -2437,10 +2523,10 @@ void SkiaRenderer::ScheduleOverlays() { DCHECK(!dc_layer_overlay.mailbox[0].IsZero()); } #elif BUILDFLAG(IS_APPLE) - auto& locks = pending_overlay_locks_.back(); for (CALayerOverlay& ca_layer_overlay : current_frame()->overlay_list) { if (ca_layer_overlay.rpdq) { PrepareRenderPassOverlay(&ca_layer_overlay); + locks.emplace_back(ca_layer_overlay.mailbox); continue; } // Some overlays are for solid-color layers. @@ -2465,15 +2551,14 @@ void SkiaRenderer::ScheduleOverlays() { } #elif defined(USE_OZONE) // Only Wayland uses this code path. - auto& locks = pending_overlay_locks_.back(); for (auto& overlay : current_frame()->overlay_list) { if (overlay.rpdq) { PrepareRenderPassOverlay(&overlay); - // The output will be attached via mailbox when overlays are scheduled. + locks.emplace_back(overlay.mailbox); continue; } // Solid Color quads do not have associated resource buffers. - if (overlay.solid_color.has_value()) + if (overlay.is_solid_color) continue; // Resources will be unlocked after the next SwapBuffers() is completed. @@ -2495,17 +2580,11 @@ void SkiaRenderer::ScheduleOverlays() { NOTREACHED(); #endif // BUILDFLAG(IS_ANDROID) - base::OnceClosure on_finished_callback; - if (current_frame_resource_fence_->WasSet()) { - on_finished_callback = base::BindOnce( - &FrameResourceFence::Signal, std::move(current_frame_resource_fence_)); - current_frame_resource_fence_ = base::MakeRefCounted<FrameResourceFence>(); - resource_provider()->SetReadLockFence(current_frame_resource_fence_.get()); - } + DCHECK(!current_gpu_commands_completed_fence_->WasSet()); + DCHECK(!current_release_fence_->WasSet()); skia_output_surface_->ScheduleOverlays( - std::move(current_frame()->overlay_list), std::move(sync_tokens), - std::move(on_finished_callback)); + std::move(current_frame()->overlay_list), std::move(sync_tokens)); } sk_sp<SkColorFilter> SkiaRenderer::GetColorSpaceConversionFilter( @@ -2513,13 +2592,8 @@ sk_sp<SkColorFilter> SkiaRenderer::GetColorSpaceConversionFilter( const gfx::ColorSpace& dst, float resource_offset, float resource_multiplier) { - // If the input color space is HDR, and it did not specify a white level, - // override it with the frame's white level. - gfx::ColorSpace adjusted_src = src.GetWithSDRWhiteLevel( - current_frame()->display_color_spaces.GetSDRMaxLuminanceNits()); - return color_filter_cache_.Get( - adjusted_src, dst, resource_offset, resource_multiplier, + src, dst, resource_offset, resource_multiplier, current_frame()->display_color_spaces.GetSDRMaxLuminanceNits(), current_frame()->display_color_spaces.GetHDRMaxLuminanceRelative()); } @@ -2768,7 +2842,7 @@ void SkiaRenderer::DrawRenderPassQuad(const AggregatedRenderPassDrawQuad* quad, sk_sp<SkImage> content_image = skia_output_surface_->MakePromiseSkImageFromRenderPass( quad->render_pass_id, backing.size, backing.format, - backing.generate_mipmap, backing.color_space.ToSkColorSpace(), + backing.generate_mipmap, RenderPassBackingSkColorSpace(backing), backing.mailbox); DLOG_IF(ERROR, !content_image) << "MakePromiseSkImageFromRenderPass() failed for render pass"; @@ -2852,16 +2926,7 @@ void SkiaRenderer::FinishDrawingQuadList() { if (is_root_render_pass && UsingSkiaForDelegatedInk()) DrawDelegatedInkTrail(); - base::OnceClosure on_finished_callback; - // Signal |current_frame_resource_fence_| when the root render pass is - // finished. - if (current_frame_resource_fence_->WasSet()) { - on_finished_callback = base::BindOnce( - &FrameResourceFence::Signal, std::move(current_frame_resource_fence_)); - current_frame_resource_fence_ = base::MakeRefCounted<FrameResourceFence>(); - resource_provider()->SetReadLockFence(current_frame_resource_fence_.get()); - } - skia_output_surface_->EndPaint(std::move(on_finished_callback)); + EndPaint(/*failed=*/false); // Defer flushing drawing task for root render pass, to avoid extra // MakeCurrent() call. It is expensive on GL. @@ -2927,9 +2992,17 @@ void SkiaRenderer::AllocateRenderPassResourceIfNeeded( // TODO(penghuang): check supported format correctly. gpu::Capabilities caps; caps.texture_format_bgra8888 = true; + +#if BUILDFLAG(IS_CHROMEOS_LACROS) + // TODO(crbug.com/1317015): add support RGBA_F16 in LaCrOS. + auto format = color_space.IsHDR() + ? RGBA_1010102 + : PlatformColor::BestSupportedTextureFormat(caps); +#else auto format = color_space.IsHDR() ? RGBA_F16 : PlatformColor::BestSupportedTextureFormat(caps); +#endif uint32_t usage = gpu::SHARED_IMAGE_USAGE_DISPLAY; if (requirements.generate_mipmap) usage |= gpu::SHARED_IMAGE_USAGE_MIPMAP; @@ -3058,7 +3131,7 @@ void SkiaRenderer::PrepareRenderPassOverlay( ResourceFormat buffer_format{}; gfx::ColorSpace color_space; - RenderPassBacking* backing = nullptr; + RenderPassBacking* src_quad_backing = nullptr; auto bypass = render_pass_bypass_quads_.find(quad->render_pass_id); BypassMode bypass_mode = BypassMode::kSkip; // When Render Pass has a single quad inside we would draw that directly. @@ -3077,9 +3150,9 @@ void SkiaRenderer::PrepareRenderPassOverlay( DCHECK(render_pass_backings_.end() != it); // This function is called after AllocateRenderPassResourceIfNeeded, so // there should be backing ready. - backing = &it->second; - buffer_format = backing->format; - color_space = backing->color_space; + src_quad_backing = &it->second; + buffer_format = src_quad_backing->format; + color_space = src_quad_backing->color_space; } // Adjust the overlay |buffer_size| to reduce memory fragmentation. It also @@ -3090,16 +3163,50 @@ void SkiaRenderer::PrepareRenderPassOverlay( // TODO(petermcneeley) : Support buffer rounding by dynamically changing // texture uvs. constexpr int kBufferMultiple = 1; -#endif +#endif // BUILDFLAG(IS_APPLE) gfx::Size buffer_size( cc::MathUtil::CheckedRoundUp(filter_bounds.width(), kBufferMultiple), cc::MathUtil::CheckedRoundUp(filter_bounds.height(), kBufferMultiple)); - current_canvas_ = skia_output_surface_->BeginPaintRenderPassOverlay( - buffer_size, buffer_format, /*mipmap=*/false, - color_space.ToSkColorSpace()); + auto it = std::find_if(available_render_pass_overlay_backings_.begin(), + available_render_pass_overlay_backings_.end(), + [&buffer_format, &buffer_size, + &color_space](const RenderPassBacking& backing) { + return backing.format == buffer_format && + backing.size == buffer_size && + backing.color_space == color_space; + }); + + if (it == available_render_pass_overlay_backings_.end()) { + // Allocate the image for render pass overlay if there is no existing + // available one. + constexpr auto kOverlayUsage = gpu::SHARED_IMAGE_USAGE_SCANOUT | + gpu::SHARED_IMAGE_USAGE_DISPLAY | + gpu::SHARED_IMAGE_USAGE_RASTER; + auto mailbox = + skia_output_surface_->GetSharedImageInterface()->CreateSharedImage( + buffer_format, buffer_size, color_space, kTopLeft_GrSurfaceOrigin, + kPremul_SkAlphaType, kOverlayUsage, gpu::kNullSurfaceHandle); + in_flight_render_pass_overlay_backings_.push_back( + RenderPassBacking{buffer_size, /*generate_mipmap=*/false, color_space, + buffer_format, mailbox}); + overlay->mailbox = std::move(mailbox); + } else { + overlay->mailbox = std::move(it->mailbox); + in_flight_render_pass_overlay_backings_.push_back(std::move(*it)); + available_render_pass_overlay_backings_.erase(it); + } + const RenderPassBacking& dst_overlay_backing = + in_flight_render_pass_overlay_backings_.back(); + + current_canvas_ = skia_output_surface_->BeginPaintRenderPass( + quad->render_pass_id, dst_overlay_backing.size, + dst_overlay_backing.format, /*mipmap=*/false, + RenderPassBackingSkColorSpace(dst_overlay_backing), /*is_overlay=*/true, + overlay->mailbox); if (!current_canvas_) { - DLOG(ERROR) << "BeginPaintRenderPassOverlay() failed."; + DLOG(ERROR) + << "BeginPaintRenderPass() in PrepareRenderPassOverlay() failed."; return; } @@ -3126,19 +3233,20 @@ void SkiaRenderer::PrepareRenderPassOverlay( NOTREACHED(); } } else { - DCHECK(backing); + DCHECK(src_quad_backing); auto content_image = skia_output_surface_->MakePromiseSkImageFromRenderPass( - quad->render_pass_id, backing->size, backing->format, - backing->generate_mipmap, backing->color_space.ToSkColorSpace(), - backing->mailbox); + quad->render_pass_id, src_quad_backing->size, src_quad_backing->format, + src_quad_backing->generate_mipmap, + RenderPassBackingSkColorSpace(*src_quad_backing), + src_quad_backing->mailbox); if (!content_image) { - DLOG(ERROR) - << "MakePromiseSkImageFromRenderPass() failed for render pass"; - skia_output_surface_->EndPaintRenderPassOverlay(); + DLOG(ERROR) << "MakePromiseSkImageFromRenderPass() in " + "PrepareRenderPassOverlay() failed."; + EndPaint(/*failed=*/true); return; } - if (backing->generate_mipmap) + if (src_quad_backing->generate_mipmap) params.sampling = SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear); @@ -3153,19 +3261,47 @@ void SkiaRenderer::PrepareRenderPassOverlay( } current_canvas_ = nullptr; - auto ddl = skia_output_surface_->EndPaintRenderPassOverlay(); - DCHECK(ddl); - // Put overlay related information in CALayerOverlay, - // so SkiaOutputSurfaceImplOnGpu can use the DDL to create overlay buffer and - // play the DDL back to it accordingly. - overlay->ddl = std::move(ddl); + EndPaint(/*failed=*/false); // Adjust |bounds_rect| to contain the whole buffer and at the right location. overlay->bounds_rect.set_origin(gfx::PointF(filter_bounds.origin())); overlay->bounds_rect.set_size(gfx::SizeF(buffer_size)); } -#endif +#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE) + +void SkiaRenderer::EndPaint(bool failed) { + base::OnceClosure on_finished_callback; + base::OnceCallback<void(gfx::GpuFenceHandle)> on_return_release_fence_cb; + // If SkiaRenderer has not failed, prepare callbacks and pass them to + // SkiaOutputSurface. + if (!failed) { + // Signal |current_frame_resource_fence_| when the root render pass is + // finished. + if (current_gpu_commands_completed_fence_->WasSet()) { + on_finished_callback = + base::BindOnce(&FrameResourceGpuCommandsCompletedFence::Signal, + std::move(current_gpu_commands_completed_fence_)); + current_gpu_commands_completed_fence_ = + base::MakeRefCounted<FrameResourceGpuCommandsCompletedFence>(); + resource_provider()->SetGpuCommandsCompletedFence( + current_gpu_commands_completed_fence_.get()); + } + + // Return a release fence to the |current_release_fence_| + // when the root render pass is finished. + if (current_release_fence_->WasSet()) { + on_return_release_fence_cb = + base::BindOnce(&FrameResourceReleaseFence::SetReleaseFenceCallback, + std::move(current_release_fence_)); + current_release_fence_ = + base::MakeRefCounted<FrameResourceReleaseFence>(); + resource_provider()->SetReleaseFence(current_release_fence_.get()); + } + } + skia_output_surface_->EndPaint(std::move(on_finished_callback), + std::move(on_return_release_fence_cb)); +} bool SkiaRenderer::IsRenderPassResourceAllocated( const AggregatedRenderPassId& render_pass_id) const { @@ -3223,23 +3359,42 @@ bool SkiaRenderer::UsingSkiaForDelegatedInk() const { return delegated_ink_handler_ && delegated_ink_handler_->GetInkRenderer(); } +SkiaRenderer::OverlayLock::OverlayLock( + DisplayResourceProvider* resource_provider, + ResourceId resource_id) { + resource_lock.emplace(resource_provider, resource_id); +} + +SkiaRenderer::OverlayLock::~OverlayLock() = default; + +SkiaRenderer::OverlayLock::OverlayLock(SkiaRenderer::OverlayLock&& other) { + resource_lock = std::move(other.resource_lock); + #if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) -bool SkiaRenderer::ScopedReadLockComparator::operator()( - const DisplayResourceProviderSkia::ScopedReadLockSharedImage& lhs, - const DisplayResourceProviderSkia::ScopedReadLockSharedImage& rhs) const { - return lhs.mailbox() < rhs.mailbox(); + render_pass_lock = std::move(other.render_pass_lock); +#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE) } -bool SkiaRenderer::ScopedReadLockComparator::operator()( - const DisplayResourceProviderSkia::ScopedReadLockSharedImage& lhs, - const gpu::Mailbox& rhs) const { - return lhs.mailbox() < rhs; +SkiaRenderer::OverlayLock& SkiaRenderer::OverlayLock::OverlayLock::operator=( + SkiaRenderer::OverlayLock&& other) { + resource_lock = std::move(other.resource_lock); + +#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) + render_pass_lock = std::move(other.render_pass_lock); +#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE) + + return *this; } -bool SkiaRenderer::ScopedReadLockComparator::operator()( - const gpu::Mailbox& lhs, - const DisplayResourceProviderSkia::ScopedReadLockSharedImage& rhs) const { - return lhs < rhs.mailbox(); +#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) +SkiaRenderer::OverlayLock::OverlayLock(gpu::Mailbox mailbox) { + render_pass_lock.emplace(mailbox); +} + +bool SkiaRenderer::OverlayLockComparator::operator()( + const OverlayLock& lhs, + const OverlayLock& rhs) const { + return lhs.mailbox() < rhs.mailbox(); } #endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE) diff --git a/chromium/components/viz/service/display/skia_renderer.h b/chromium/components/viz/service/display/skia_renderer.h index d8912a61296..7922aee3b61 100644 --- a/chromium/components/viz/service/display/skia_renderer.h +++ b/chromium/components/viz/service/display/skia_renderer.h @@ -7,6 +7,7 @@ #include <memory> #include <tuple> +#include <utility> #include <vector> #include "base/containers/flat_map.h" @@ -16,7 +17,6 @@ #include "cc/cc_export.h" #include "components/viz/service/display/direct_renderer.h" #include "components/viz/service/display/display_resource_provider_skia.h" -#include "components/viz/service/display/sync_query_collection.h" #include "components/viz/service/viz_service_export.h" #include "third_party/skia/include/core/SkCanvas.h" #include "ui/gfx/color_conversion_sk_filter_cache.h" @@ -195,7 +195,7 @@ class VIZ_SERVICE_EXPORT SkiaRenderer : public DirectRenderer { DrawQuadParams* params); void DrawPaintOpBuffer(const cc::PaintOpBuffer* buffer, - const absl::optional<SkColor>& clear_color, + const absl::optional<SkColor4f>& clear_color, const TileDrawQuad* quad, const DrawQuadParams* params); @@ -264,6 +264,11 @@ class VIZ_SERVICE_EXPORT SkiaRenderer : public DirectRenderer { OverlayProcessorInterface::PlatformOverlayCandidate* overlay); #endif + // Sets up callbacks for frame resource fences and passes them to + // SkiaOutputSurface by calling EndPaint on that. If |failed|, + // SkiaOutputSurface::EndPaint will be called with null callbacks. + void EndPaint(bool failed); + DisplayResourceProviderSkia* resource_provider() { return static_cast<DisplayResourceProviderSkia*>(resource_provider_); } @@ -278,14 +283,22 @@ class VIZ_SERVICE_EXPORT SkiaRenderer : public DirectRenderer { }; base::flat_map<AggregatedRenderPassId, RenderPassBacking> render_pass_backings_; + sk_sp<SkColorSpace> RenderPassBackingSkColorSpace( + const RenderPassBacking& backing) { + return backing.color_space.ToSkColorSpace(CurrentFrameSDRWhiteLevel()); + } // Interface used for drawing. Common among different draw modes. sk_sp<SkSurface> root_surface_; raw_ptr<SkCanvas> root_canvas_ = nullptr; raw_ptr<SkCanvas> current_canvas_ = nullptr; raw_ptr<SkSurface> current_surface_ = nullptr; - class FrameResourceFence; - scoped_refptr<FrameResourceFence> current_frame_resource_fence_; + + class FrameResourceGpuCommandsCompletedFence; + scoped_refptr<FrameResourceGpuCommandsCompletedFence> + current_gpu_commands_completed_fence_; + class FrameResourceReleaseFence; + scoped_refptr<FrameResourceReleaseFence> current_release_fence_; bool disable_picture_quad_image_filtering_ = false; bool is_scissor_enabled_ = false; @@ -326,42 +339,88 @@ class VIZ_SERVICE_EXPORT SkiaRenderer : public DirectRenderer { absl::optional<DisplayResourceProviderSkia::LockSetForExternalUse> lock_set_for_external_use_; - // Locks for overlays are pending for swapbuffers. - base::circular_deque< - std::vector<DisplayResourceProviderSkia::ScopedReadLockSharedImage>> - pending_overlay_locks_; + struct OverlayLock { + OverlayLock(DisplayResourceProvider* resource_provider, + ResourceId resource_id); + +#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) + explicit OverlayLock(gpu::Mailbox mailbox); +#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE) + + ~OverlayLock(); + + OverlayLock(OverlayLock&& other); + OverlayLock& operator=(OverlayLock&& other); + + OverlayLock(const OverlayLock&) = delete; + OverlayLock& operator=(const OverlayLock&) = delete; + + const gpu::Mailbox& mailbox() const { +#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) + if (render_pass_lock.has_value()) { + return *render_pass_lock; + } +#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE) + + DCHECK(resource_lock.has_value()); + return resource_lock->mailbox(); + } + + const gpu::SyncToken& sync_token() const { + DCHECK(resource_lock.has_value()); + return resource_lock->sync_token(); + } + + void SetReleaseFence(gfx::GpuFenceHandle release_fence) { + if (resource_lock.has_value()) { + resource_lock->SetReleaseFence(std::move(release_fence)); + } + } + + bool HasReadLockFence() { + if (resource_lock.has_value()) { + return resource_lock->HasReadLockFence(); + } + return false; + } + + // Either resource_lock is set for non render pass overlays (i.e. videos), + // or render_pass_lock is set for render pass overlays. + absl::optional<DisplayResourceProviderSkia::ScopedReadLockSharedImage> + resource_lock; + +#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) + absl::optional<gpu::Mailbox> render_pass_lock; +#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE) + }; + + // Locks for overlays that are pending for SwapBuffers(). + base::circular_deque<std::vector<OverlayLock>> pending_overlay_locks_; - // Locks for overlays have been committed. |pending_overlay_locks_| will - // be moved to |committed_overlay_locks_| after SwapBuffers() completed. - std::vector<DisplayResourceProviderSkia::ScopedReadLockSharedImage> - committed_overlay_locks_; + // Locks for overlays that have been committed. |pending_overlay_locks_| will + // be moved to |committed_overlay_locks_| after SwapBuffers() is completed. + std::vector<OverlayLock> committed_overlay_locks_; // Locks for overlays that have release fences and read lock fences. - base::circular_deque< - std::vector<DisplayResourceProviderSkia::ScopedReadLockSharedImage>> + base::circular_deque<std::vector<OverlayLock>> read_lock_release_fence_overlay_locks_; #if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) - class ScopedReadLockComparator { + class OverlayLockComparator { public: using is_transparent = void; - bool operator()( - const DisplayResourceProviderSkia::ScopedReadLockSharedImage& lhs, - const DisplayResourceProviderSkia::ScopedReadLockSharedImage& rhs) - const; - bool operator()( - const DisplayResourceProviderSkia::ScopedReadLockSharedImage& lhs, - const gpu::Mailbox& rhs) const; - bool operator()( - const gpu::Mailbox& lhs, - const DisplayResourceProviderSkia::ScopedReadLockSharedImage& rhs) - const; + bool operator()(const OverlayLock& lhs, const OverlayLock& rhs) const; }; - // a set for locks of overlays which are waiting for releasing. - // The set is using lock.mailbox() as the unique key. - base::flat_set<DisplayResourceProviderSkia::ScopedReadLockSharedImage, - ScopedReadLockComparator> + + // A set for locks of overlays which are waiting to be released, using + // mailbox() as the unique key. + base::flat_set<OverlayLock, OverlayLockComparator> awaiting_release_overlay_locks_; + + // Tracks render pass overlay backings that are currently in use and available + // for re-using via mailboxes. RenderPassBacking.generate_mipmap is not used. + std::vector<RenderPassBacking> in_flight_render_pass_overlay_backings_; + std::vector<RenderPassBacking> available_render_pass_overlay_backings_; #endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE) gfx::ColorConversionSkFilterCache color_filter_cache_; diff --git a/chromium/components/viz/service/display/software_renderer.cc b/chromium/components/viz/service/display/software_renderer.cc index 945245b1299..7ee2ca60256 100644 --- a/chromium/components/viz/service/display/software_renderer.cc +++ b/chromium/components/viz/service/display/software_renderer.cc @@ -134,7 +134,6 @@ void SoftwareRenderer::EnsureScissorTestDisabled() { } void SoftwareRenderer::BindFramebufferToOutputSurface() { - DCHECK(!output_surface_->HasExternalStencilTest()); DCHECK(!root_canvas_); current_framebuffer_canvas_.reset(); @@ -367,21 +366,20 @@ void SoftwareRenderer::DoDrawQuad(const DrawQuad* quad, } void SoftwareRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad) { - // We need to apply the matrix manually to have pixel-sized stroke width. - SkPoint vertices[4]; - gfx::RectFToSkRect(QuadVertexRect()).toQuad(vertices); - SkPoint transformed_vertices[4]; - current_canvas_->getTotalMatrix().mapPoints(transformed_vertices, vertices, - 4); + SkMatrix m = current_canvas_->getTotalMatrix(); current_canvas_->resetMatrix(); + SkPath path; + path.addRect(gfx::RectFToSkRect(QuadVertexRect())); + path.transform(m); + current_paint_.setColor(quad->color); - current_paint_.setAlpha(quad->shared_quad_state->opacity * - SkColorGetA(quad->color)); + current_paint_.setAlphaf(quad->shared_quad_state->opacity * quad->color.fA); current_paint_.setStyle(SkPaint::kStroke_Style); + current_paint_.setStrokeJoin(SkPaint::kMiter_Join); current_paint_.setStrokeWidth(quad->width); - current_canvas_->drawPoints(SkCanvas::kPolygon_PointMode, 4, - transformed_vertices, current_paint_); + + current_canvas_->drawPath(path, current_paint_); } void SoftwareRenderer::DrawPictureQuad(const PictureDrawQuad* quad) { @@ -429,8 +427,7 @@ void SoftwareRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad) { gfx::RectF visible_quad_vertex_rect = cc::MathUtil::ScaleRectProportional( QuadVertexRect(), gfx::RectF(quad->rect), gfx::RectF(quad->visible_rect)); current_paint_.setColor(quad->color); - current_paint_.setAlpha(quad->shared_quad_state->opacity * - SkColorGetA(quad->color)); + current_paint_.setAlphaf(quad->shared_quad_state->opacity * quad->color.fA); current_canvas_->drawRect(gfx::RectFToSkRect(visible_quad_vertex_rect), current_paint_); } @@ -462,7 +459,7 @@ void SoftwareRenderer::DrawTextureQuad(const TextureDrawQuad* quad) { current_canvas_->scale(1, -1); bool blend_background = - quad->background_color != SK_ColorTRANSPARENT && !image->isOpaque(); + quad->background_color != SkColors::kTransparent && !image->isOpaque(); bool needs_layer = blend_background && (current_paint_.getAlpha() != 0xFF); if (needs_layer) { current_canvas_->saveLayerAlpha(&quad_rect, current_paint_.getAlpha()); @@ -592,9 +589,9 @@ void SoftwareRenderer::DrawRenderPassQuad( void SoftwareRenderer::DrawUnsupportedQuad(const DrawQuad* quad) { #ifdef NDEBUG - current_paint_.setColor(SK_ColorWHITE); + current_paint_.setColor(SkColors::kWhite); #else - current_paint_.setColor(SK_ColorMAGENTA); + current_paint_.setColor(SkColors::kMagenta); #endif current_paint_.setAlpha(quad->shared_quad_state->opacity * 255); current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()), @@ -604,8 +601,7 @@ void SoftwareRenderer::DrawUnsupportedQuad(const DrawQuad* quad) { void SoftwareRenderer::CopyDrawnRenderPass( const copy_output::RenderPassGeometry& geometry, std::unique_ptr<CopyOutputRequest> request) { - sk_sp<SkColorSpace> color_space = - CurrentRenderPassColorSpace().ToSkColorSpace(); + sk_sp<SkColorSpace> color_space = CurrentRenderPassSkColorSpace(); DCHECK(color_space); SkBitmap bitmap; diff --git a/chromium/components/viz/service/display/software_renderer_unittest.cc b/chromium/components/viz/service/display/software_renderer_unittest.cc index f706e41e9f1..521d833e6b9 100644 --- a/chromium/components/viz/service/display/software_renderer_unittest.cc +++ b/chromium/components/viz/service/display/software_renderer_unittest.cc @@ -25,6 +25,7 @@ #include "components/viz/common/quads/compositor_frame_metadata.h" #include "components/viz/common/quads/compositor_render_pass.h" #include "components/viz/common/quads/compositor_render_pass_draw_quad.h" +#include "components/viz/common/quads/debug_border_draw_quad.h" #include "components/viz/common/quads/solid_color_draw_quad.h" #include "components/viz/common/quads/tile_draw_quad.h" #include "components/viz/common/resources/bitmap_allocation.h" @@ -46,8 +47,8 @@ class SoftwareRendererTest : public testing::Test { public: void InitializeRenderer( std::unique_ptr<SoftwareOutputDevice> software_output_device) { - output_surface_ = - FakeOutputSurface::CreateSoftware(std::move(software_output_device)); + output_surface_ = std::make_unique<FakeSoftwareOutputSurface>( + std::move(software_output_device)); output_surface_->BindToClient(&output_surface_client_); shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>(); @@ -134,7 +135,7 @@ class SoftwareRendererTest : public testing::Test { RendererSettings settings_; DebugRendererSettings debug_settings_; cc::FakeOutputSurfaceClient output_surface_client_; - std::unique_ptr<FakeOutputSurface> output_surface_; + std::unique_ptr<FakeSoftwareOutputSurface> output_surface_; std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_; std::unique_ptr<DisplayResourceProviderSoftware> resource_provider_; std::unique_ptr<ClientResourceProvider> child_resource_provider_; @@ -187,6 +188,78 @@ TEST_F(SoftwareRendererTest, SolidColorQuad) { output->getColor(inner_size.width() - 1, inner_size.height() - 1)); } +TEST_F(SoftwareRendererTest, DebugBorderDrawQuad) { + gfx::Size rect_size(10, 10); + gfx::Size full_size(100, 100); + gfx::Rect screen_rect(full_size); + gfx::Rect rect_1(rect_size); + gfx::Rect rect_2(gfx::Point(1, 1), rect_size); + gfx::Rect rect_3(gfx::Point(2, 2), rect_size); + gfx::Rect rect_4(gfx::Point(3, 3), rect_size); + + InitializeRenderer(std::make_unique<SoftwareOutputDevice>()); + + AggregatedRenderPassId root_render_pass_id{1}; + auto root_render_pass = std::make_unique<AggregatedRenderPass>(); + root_render_pass->SetNew(root_render_pass_id, screen_rect, screen_rect, + gfx::Transform()); + SharedQuadState* shared_quad_state = + root_render_pass->CreateAndAppendSharedQuadState(); + shared_quad_state->SetAll(gfx::Transform(), screen_rect, screen_rect, + gfx::MaskFilterInfo(), absl::nullopt, true, 1.0, + SkBlendMode::kSrcOver, 0); + + auto* quad_1 = + root_render_pass->CreateAndAppendDrawQuad<DebugBorderDrawQuad>(); + quad_1->SetNew(shared_quad_state, rect_1, rect_1, SK_ColorCYAN, false); + auto* quad_2 = + root_render_pass->CreateAndAppendDrawQuad<DebugBorderDrawQuad>(); + quad_2->SetNew(shared_quad_state, rect_2, rect_2, SK_ColorMAGENTA, false); + + auto* quad_3 = + root_render_pass->CreateAndAppendDrawQuad<DebugBorderDrawQuad>(); + quad_3->SetNew(shared_quad_state, rect_3, rect_3, SK_ColorYELLOW, false); + + // Test one non-opaque color + SkColor semi_transparent_white = SkColorSetARGB(127, 255, 255, 255); + auto* quad_4 = + root_render_pass->CreateAndAppendDrawQuad<DebugBorderDrawQuad>(); + quad_4->SetNew(shared_quad_state, rect_4, rect_4, semi_transparent_white, + false); + + AggregatedRenderPassList list; + list.push_back(std::move(root_render_pass)); + + float device_scale_factor = 1.f; + std::unique_ptr<SkBitmap> output = + DrawAndCopyOutput(&list, device_scale_factor, full_size); + EXPECT_EQ(screen_rect.width(), output->info().width()); + EXPECT_EQ(screen_rect.height(), output->info().height()); + + // Top left corners + EXPECT_EQ(SK_ColorCYAN, output->getColor(0, 0)); + EXPECT_EQ(SK_ColorMAGENTA, output->getColor(1, 1)); + EXPECT_EQ(SK_ColorYELLOW, output->getColor(2, 2)); + // The corners end up being more opaque due to the miter, go one to the right + EXPECT_EQ(semi_transparent_white, output->getColor(3, 4)); + + // Un-drawn pixels as the quads are just outlines + EXPECT_EQ(SK_ColorTRANSPARENT, output->getColor(4, 4)); + EXPECT_EQ(SK_ColorTRANSPARENT, + output->getColor(rect_size.width() - 2, rect_size.height() - 2)); + + // The bottom rightmost pixel of these quads are not filled because of the + // SkPaint::kMiter_Join StrokeJoin, go one pixel to the left + EXPECT_EQ(SK_ColorCYAN, + output->getColor(rect_size.width() - 1, rect_size.height())); + EXPECT_EQ(SK_ColorMAGENTA, + output->getColor(rect_size.width(), rect_size.height() + 1)); + EXPECT_EQ(SK_ColorYELLOW, + output->getColor(rect_size.width() + 1, rect_size.height() + 2)); + EXPECT_EQ(semi_transparent_white, + output->getColor(rect_size.width() + 2, rect_size.height() + 3)); +} + TEST_F(SoftwareRendererTest, TileQuad) { gfx::Size outer_size(100, 100); gfx::Size inner_size(98, 98); diff --git a/chromium/components/viz/service/display/static_geometry_binding.cc b/chromium/components/viz/service/display/static_geometry_binding.cc deleted file mode 100644 index 1dc0e9b717a..00000000000 --- a/chromium/components/viz/service/display/static_geometry_binding.cc +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2015 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/static_geometry_binding.h" - -#include <stddef.h> -#include <stdint.h> - -#include "gpu/command_buffer/client/gles2_interface.h" -#include "ui/gfx/geometry/rect_f.h" - -namespace viz { - -StaticGeometryBinding::StaticGeometryBinding(gpu::gles2::GLES2Interface* gl, - const gfx::RectF& quad_vertex_rect) - : gl_(gl), quad_vertices_vbo_(0), quad_elements_vbo_(0) { - GeometryBindingQuad quads[NUM_QUADS]; - GeometryBindingQuadIndex quad_indices[NUM_QUADS]; - - static_assert(sizeof(GeometryBindingQuad) == 24 * sizeof(float), - "struct Quad should be densely packed"); - static_assert(sizeof(GeometryBindingQuadIndex) == 6 * sizeof(uint16_t), - "struct QuadIndex should be densely packed"); - - for (size_t i = 0; i < NUM_QUADS; i++) { - GeometryBindingVertex v0 = { - {quad_vertex_rect.x(), quad_vertex_rect.bottom(), 0.0f}, - {0.0f, 1.0f}, - i * 4.0f + 0.0f}; - GeometryBindingVertex v1 = { - {quad_vertex_rect.x(), quad_vertex_rect.y(), 0.0f}, - {0.0f, 0.0f}, - i * 4.0f + 1.0f}; - GeometryBindingVertex v2 = { - {quad_vertex_rect.right(), quad_vertex_rect.y(), 0.0f}, - {1.0f, 0.0f}, - i * 4.0f + 2.0f}; - GeometryBindingVertex v3 = { - {quad_vertex_rect.right(), quad_vertex_rect.bottom(), 0.0f}, - {1.0f, 1.0f}, - i * 4.0f + 3.0f}; - GeometryBindingQuad x(v0, v1, v2, v3); - quads[i] = x; - GeometryBindingQuadIndex y( - static_cast<uint16_t>(0 + 4 * i), static_cast<uint16_t>(1 + 4 * i), - static_cast<uint16_t>(2 + 4 * i), static_cast<uint16_t>(3 + 4 * i), - static_cast<uint16_t>(0 + 4 * i), static_cast<uint16_t>(2 + 4 * i)); - quad_indices[i] = y; - } - - gl_->GenBuffers(1, &quad_vertices_vbo_); - gl_->GenBuffers(1, &quad_elements_vbo_); - - gl_->BindBuffer(GL_ARRAY_BUFFER, quad_vertices_vbo_); - gl_->BufferData(GL_ARRAY_BUFFER, sizeof(GeometryBindingQuad) * NUM_QUADS, - quads, GL_STATIC_DRAW); - - gl_->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_elements_vbo_); - gl_->BufferData(GL_ELEMENT_ARRAY_BUFFER, - sizeof(GeometryBindingQuadIndex) * NUM_QUADS, &quad_indices, - GL_STATIC_DRAW); -} - -StaticGeometryBinding::~StaticGeometryBinding() { - gl_->DeleteBuffers(1, &quad_vertices_vbo_); - gl_->DeleteBuffers(1, &quad_elements_vbo_); -} - -void StaticGeometryBinding::PrepareForDraw() { - SetupGLContext(gl_, quad_elements_vbo_, quad_vertices_vbo_); -} - -} // namespace viz diff --git a/chromium/components/viz/service/display/static_geometry_binding.h b/chromium/components/viz/service/display/static_geometry_binding.h deleted file mode 100644 index 2a09459426c..00000000000 --- a/chromium/components/viz/service/display/static_geometry_binding.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2015 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_STATIC_GEOMETRY_BINDING_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_STATIC_GEOMETRY_BINDING_H_ - -#include "base/memory/raw_ptr.h" -#include "components/viz/service/display/geometry_binding.h" -#include "components/viz/service/viz_service_export.h" - -using gpu::gles2::GLES2Interface; - -namespace viz { - -class VIZ_SERVICE_EXPORT StaticGeometryBinding { - public: - StaticGeometryBinding(gpu::gles2::GLES2Interface* gl, - const gfx::RectF& quad_vertex_rect); - - StaticGeometryBinding(const StaticGeometryBinding&) = delete; - StaticGeometryBinding& operator=(const StaticGeometryBinding&) = delete; - - ~StaticGeometryBinding(); - - void PrepareForDraw(); - - enum { - NUM_QUADS = 9, - }; - - private: - raw_ptr<gpu::gles2::GLES2Interface> gl_; - - GLuint quad_vertices_vbo_; - GLuint quad_elements_vbo_; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_STATIC_GEOMETRY_BINDING_H_ diff --git a/chromium/components/viz/service/display/surface_aggregator.cc b/chromium/components/viz/service/display/surface_aggregator.cc index 8d8eec8d786..24d580b44ba 100644 --- a/chromium/components/viz/service/display/surface_aggregator.cc +++ b/chromium/components/viz/service/display/surface_aggregator.cc @@ -302,16 +302,19 @@ SurfaceAggregator::SurfaceAggregator( aggregate_only_damaged_(aggregate_only_damaged), needs_surface_damage_rect_list_(needs_surface_damage_rect_list), de_jelly_enabled_(DeJellyEnabled()), - clip_prewalk_damage_(features::IsClipPrewalkDamageEnabled()), extra_pass_for_readback_option_(extra_pass_option) { DCHECK(manager_); DCHECK(provider_); + manager_->AddObserver(this); } SurfaceAggregator::~SurfaceAggregator() { - // Notify client of all surfaces being removed. + manager_->RemoveObserver(this); + contained_surfaces_.clear(); contained_frame_sinks_.clear(); + + // Notify client of all surfaces being removed. ProcessAddedAndRemovedSurfaces(); } @@ -534,10 +537,21 @@ bool SurfaceAggregator::CanPotentiallyMergePass( sqs->de_jelly_delta_y == 0; } +void SurfaceAggregator::OnSurfaceDestroyed(const SurfaceId& surface_id) { + DCHECK(!is_inside_aggregate_); + + auto iter = resolved_frames_.find(surface_id); + if (iter != resolved_frames_.end()) { + TRACE_EVENT0("viz", "SurfaceAggregator::SurfaceDestroyed"); + ReleaseResources(surface_id); + resolved_frames_.erase(iter); + } +} + const ResolvedFrameData* SurfaceAggregator::GetLatestFrameData( const SurfaceId& surface_id) { - auto* surface = manager_->GetSurfaceForId(surface_id); - return GetResolvedFrame(surface, /*inside_aggregation=*/false); + DCHECK(!is_inside_aggregate_); + return GetResolvedFrame(surface_id); } ResolvedFrameData* SurfaceAggregator::GetResolvedFrame( @@ -546,59 +560,51 @@ ResolvedFrameData* SurfaceAggregator::GetResolvedFrame( // this aggregation, then find ResolvedFrameData for that surface. auto iter = resolved_surface_ranges_.find(range); if (iter == resolved_surface_ranges_.end()) { - iter = resolved_surface_ranges_ - .emplace(range, manager_->GetLatestInFlightSurface(range)) - .first; + auto* surface = manager_->GetLatestInFlightSurface(range); + SurfaceId surface_id = surface ? surface->surface_id() : SurfaceId(); + iter = resolved_surface_ranges_.emplace(range, surface_id).first; } - return GetResolvedFrame(iter->second, /*inside_aggregation=*/true); -} + if (!iter->second.is_valid()) { + // There is no surface for `range`. + return nullptr; + } -ResolvedFrameData* SurfaceAggregator::GetResolvedFrame( - const SurfaceId& surface_id) { - return GetResolvedFrame(manager_->GetSurfaceForId(surface_id), - /*inside_aggregation=*/true); + return GetResolvedFrame(iter->second); } ResolvedFrameData* SurfaceAggregator::GetResolvedFrame( - Surface* surface, - bool inside_aggregation) { - if (!surface || !surface->HasActiveFrame()) { - // If there is no resolved surface or the surface has no active frame there - // is no resolved frame data to return. - return nullptr; - } + const SurfaceId& surface_id) { + DCHECK(surface_id.is_valid()); - auto iter = resolved_frames_.find(surface); + auto iter = resolved_frames_.find(surface_id); if (iter == resolved_frames_.end()) { - iter = - resolved_frames_ - .emplace(std::piecewise_construct, std::forward_as_tuple(surface), - std::forward_as_tuple(surface->surface_id(), surface)) - .first; + auto* surface = manager_->GetSurfaceForId(surface_id); + if (!surface || !surface->HasActiveFrame()) { + // If there is no resolved surface or the surface has no active frame + // there is no resolved frame data to return. + return nullptr; + } + + iter = resolved_frames_ + .emplace(std::piecewise_construct, + std::forward_as_tuple(surface_id), + std::forward_as_tuple(surface->surface_id(), surface)) + .first; } ResolvedFrameData& resolved_frame = iter->second; - DCHECK_EQ(resolved_frame.surface(), surface); - - // Verify that a new surface wasn't created at the same address as a deleted - // surface with a different SurfaceId. - // TODO(kylechar): Invalidate cached resolved frame data when - // SurfaceObserver signals the surface has been destroyed instead. - if (resolved_frame.surface_id() != surface->surface_id()) { - resolved_frames_.erase(iter); - return GetResolvedFrame(surface, inside_aggregation); - } + Surface* surface = resolved_frame.surface(); // Mark the frame as used this aggregation so it persists. - bool first_use = inside_aggregation ? resolved_frame.MarkAsUsed() : true; + bool first_use = is_inside_aggregate_ ? resolved_frame.MarkAsUsed() : true; if (first_use) { // If there is a new CompositorFrame for `surface` compute resolved frame // data for the new resolved CompositorFrame. if (resolved_frame.frame_index() != surface->GetActiveFrameIndex() || surface->HasSurfaceAnimationDamage()) { - DCHECK(inside_aggregation); + DCHECK(is_inside_aggregate_); base::ElapsedTimer timer; ProcessResolvedFrame(resolved_frame); stats_->declare_resources_time += timer.Elapsed(); @@ -969,15 +975,17 @@ void SurfaceAggregator::EmitDefaultBackgroundColorQuad( // No matching surface was found so create a SolidColorDrawQuad with the // SurfaceDrawQuad default background color. - SkColor background_color = surface_quad->default_background_color; + SkColor4f background_color = surface_quad->default_background_color; auto* shared_quad_state = CopySharedQuadState(surface_quad->shared_quad_state, target_transform, clip_rect, mask_filter_info, dest_pass); auto* solid_color_quad = dest_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); + // TODO(crbug/1308932) remove toSkColor and make all SkColor4f solid_color_quad->SetNew(shared_quad_state, surface_quad->rect, - surface_quad->visible_rect, background_color, false); + surface_quad->visible_rect, + background_color.toSkColor(), false); } void SurfaceAggregator::EmitGutterQuadsIfNecessary( @@ -1262,6 +1270,9 @@ void SurfaceAggregator::CopyQuadsToPass( const DrawQuad* quad_with_overlay_damage_index = nullptr; if (needs_surface_damage_rect_list_ && resolved_pass.aggregation().will_draw) { + // TODO(crbug.com/1323002): If there is one specific quad for this pass's + // damage we should move the allocation of the damage index below to be + // consistent with quad ordering. quad_with_overlay_damage_index = FindQuadWithOverlayDamage(source_pass, dest_pass, target_transform, surface, &overlay_damage_index); @@ -1727,13 +1738,11 @@ gfx::Rect SurfaceAggregator::PrewalkRenderPass( } } - if (clip_prewalk_damage_) { - // Clip the quad damage to the quad visible before converting back to - // render pass coordinate space. Expanded damage outside the quad rect for - // filters are added to |damage_rect| directly so this only clips damage - // from drawing the quad itself. - quad_damage_rect.Intersect(quad->visible_rect); - } + // Clip the quad damage to the quad visible before converting back to + // render pass coordinate space. Expanded damage outside the quad rect for + // filters are added to |damage_rect| directly so this only clips damage + // from drawing the quad itself. + quad_damage_rect.Intersect(quad->visible_rect); if (!quad_damage_rect.IsEmpty()) { // Convert the quad damage rect into its target space and clip it if @@ -1758,15 +1767,13 @@ gfx::Rect SurfaceAggregator::PrewalkRenderPass( damage_rect.Union(render_pass.output_rect); } - if (clip_prewalk_damage_) { - // The added damage from quads in the render pass is transformed back - // into the render pass coordinate space without clipping, so it can - // extend beyond the edge of the current render pass. Coordinates outside - // the output_rect are invalid in this render passes coordinate space but - // they may be valid coordinates in the embedder coordinate space, causing - // unnecessary damage expansion. - damage_rect.Intersect(render_pass.output_rect); - } + // The added damage from quads in the render pass is transformed back into + // the render pass coordinate space without clipping, so it can extend + // beyond the edge of the current render pass. Coordinates outside the + // output_rect are invalid in this render passes coordinate space but they + // may be valid coordinates in the embedder coordinate space, causing + // unnecessary damage expansion. + damage_rect.Intersect(render_pass.output_rect); } return damage_rect; @@ -1982,23 +1989,26 @@ AggregatedFrame SurfaceAggregator::Aggregate( const gfx::Rect& target_damage, int64_t display_trace_id) { DCHECK(!expected_display_time.is_null()); - - root_surface_id_ = surface_id; - Surface* surface = manager_->GetSurfaceForId(surface_id); - DCHECK(surface); DCHECK(contained_surfaces_.empty()); - CheckFrameSinksChanged(surface); + DCHECK(!is_inside_aggregate_); + is_inside_aggregate_ = true; + + root_surface_id_ = surface_id; // Start recording new stats for this aggregation. stats_.emplace(); base::ElapsedTimer prewalk_timer; - ResolvedFrameData* resolved_frame = - GetResolvedFrame(surface, /*inside_aggregation=*/true); + ResolvedFrameData* resolved_frame = GetResolvedFrame(surface_id); - if (!resolved_frame || !resolved_frame->is_valid()) + if (!resolved_frame || !resolved_frame->is_valid()) { + ResetAfterAggregate(); return {}; + } + + Surface* surface = resolved_frame->surface(); + CheckFrameSinksChanged(surface); display_trace_id_ = display_trace_id; expected_display_time_ = expected_display_time; @@ -2162,6 +2172,9 @@ void SurfaceAggregator::RecordStatHistograms() { } void SurfaceAggregator::ResetAfterAggregate() { + DCHECK(is_inside_aggregate_); + + is_inside_aggregate_ = false; dest_pass_list_ = nullptr; surface_damage_rect_list_ = nullptr; current_zero_damage_rect_is_not_recorded_ = false; diff --git a/chromium/components/viz/service/display/surface_aggregator.h b/chromium/components/viz/service/display/surface_aggregator.h index 338dc871b87..03482fd6129 100644 --- a/chromium/components/viz/service/display/surface_aggregator.h +++ b/chromium/components/viz/service/display/surface_aggregator.h @@ -23,6 +23,7 @@ #include "components/viz/common/surfaces/surface_range.h" #include "components/viz/service/display/aggregated_frame.h" #include "components/viz/service/display/resolved_frame_data.h" +#include "components/viz/service/surfaces/surface_observer.h" #include "components/viz/service/viz_service_export.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/gfx/delegated_ink_metadata.h" @@ -37,7 +38,7 @@ class SurfaceManager; struct MaskFilterInfoExt; -class VIZ_SERVICE_EXPORT SurfaceAggregator { +class VIZ_SERVICE_EXPORT SurfaceAggregator : public SurfaceObserver { public: using SurfaceIndexMap = base::flat_map<SurfaceId, uint64_t>; using FrameSinkIdMap = base::flat_map<FrameSinkId, LocalSurfaceId>; @@ -73,7 +74,7 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator { SurfaceAggregator(const SurfaceAggregator&) = delete; SurfaceAggregator& operator=(const SurfaceAggregator&) = delete; - ~SurfaceAggregator(); + ~SurfaceAggregator() override; // These constants are used for all time related metrics recorded in // SurfaceAggregator. @@ -96,7 +97,6 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator { // Aggregate() to make sure no CompositorFrame did arrive between the calls. const ResolvedFrameData* GetLatestFrameData(const SurfaceId& surface_id); - void ReleaseResources(const SurfaceId& surface_id); const SurfaceIndexMap& previous_contained_surfaces() const { return previous_contained_surfaces_; } @@ -135,13 +135,16 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator { base::TimeDelta declare_resources_time; }; + // SurfaceObserver implementation. + void OnSurfaceDestroyed(const SurfaceId& surface_id) override; + + void ReleaseResources(const SurfaceId& surface_id); + // Get resolved frame data for the resolved surfaces active frame. Returns // null if there is no matching surface or the surface doesn't have an active // CompositorFrame. ResolvedFrameData* GetResolvedFrame(const SurfaceRange& range); ResolvedFrameData* GetResolvedFrame(const SurfaceId& surface_id); - ResolvedFrameData* GetResolvedFrame(Surface* surface, - bool inside_aggregation); // - |source_pass| is the render pass that contains |surface_quad|. // - |target_transform| is the transform from the coordinate space of @@ -386,10 +389,11 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator { // Whether de-jelly may be active. const bool de_jelly_enabled_; - const bool clip_prewalk_damage_; - const ExtraPassForReadbackOption extra_pass_for_readback_option_; + // Will be true for duration of Aggregate() function. + bool is_inside_aggregate_ = false; + bool output_is_secure_ = false; // Whether |CopyOutputRequests| should be moved over to the aggregated frame. @@ -410,6 +414,9 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator { // The id for the optional render pass used to apply the display transform. AggregatedRenderPassId display_transform_render_pass_id_; + // Persistent storage for ResolvedFrameData. + std::map<SurfaceId, ResolvedFrameData> resolved_frames_; + base::flat_map<SurfaceId, int> surface_id_to_resource_child_id_; // The following state is only valid for the duration of one Aggregate call @@ -439,8 +446,8 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator { base::TimeTicks expected_display_time_; int64_t display_trace_id_ = -1; - // Map from SurfaceRange to Surface for current aggregation. - base::flat_map<SurfaceRange, Surface*> resolved_surface_ranges_; + // Map from SurfaceRange to SurfaceId for current aggregation. + base::flat_map<SurfaceRange, SurfaceId> resolved_surface_ranges_; // The root damage rect of the currently-aggregating frame. gfx::Rect root_damage_rect_; @@ -503,9 +510,6 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator { // by FindQuadWithOverlayDamage(). bool current_zero_damage_rect_is_not_recorded_ = false; - // Persistent storage for ResolvedFrameData. - std::map<Surface*, ResolvedFrameData> resolved_frames_; - // Used to generate new unique render pass ids in the aggregated namespace. AggregatedRenderPassId::Generator render_pass_id_generator_; }; diff --git a/chromium/components/viz/service/display/surface_aggregator_pixeltest.cc b/chromium/components/viz/service/display/surface_aggregator_pixeltest.cc index 07e18febf5c..aa9f8cdac09 100644 --- a/chromium/components/viz/service/display/surface_aggregator_pixeltest.cc +++ b/chromium/components/viz/service/display/surface_aggregator_pixeltest.cc @@ -339,10 +339,6 @@ TEST_P(SurfaceAggregatorPixelTest, DrawAggregatedFrameWithSurfaceTransforms) { // Draw a simple frame with a delegated ink trail on top of it, then confirm // that it is erased by the next aggregation. TEST_P(SurfaceAggregatorPixelTest, DrawAndEraseDelegatedInkTrail) { - // DelegatedInkTrail isn't supported on non-Skia renderers. - if (renderer_type() == RendererType::kGL) - return; - DelegatedInkPointPixelTestHelper delegated_ink_helper(renderer_.get()); // Create and send metadata and points to the renderer that will be drawn. diff --git a/chromium/components/viz/service/display/surface_aggregator_unittest.cc b/chromium/components/viz/service/display/surface_aggregator_unittest.cc index 4e454627318..481853d3eda 100644 --- a/chromium/components/viz/service/display/surface_aggregator_unittest.cc +++ b/chromium/components/viz/service/display/surface_aggregator_unittest.cc @@ -341,7 +341,8 @@ class SurfaceAggregatorTest : public testing::Test, public DisplayTimeSource { const auto* solid_color_quad = SolidColorDrawQuad::MaterialCast(quad); - EXPECT_EQ(expected_quad.color, solid_color_quad->color); + EXPECT_EQ(SkColor4f::FromColor(expected_quad.color), + solid_color_quad->color); EXPECT_EQ(expected_quad.rect, solid_color_quad->rect); break; } @@ -6000,13 +6001,13 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, ColorSpaceTestWin) { gfx::BufferFormat::RGBA_8888); display_color_spaces.SetOutputColorSpaceAndBufferFormat( gfx::ContentColorUsage::kWideColorGamut, true /* needs_alpha */, - gfx::ColorSpace::CreateSCRGBLinear(), gfx::BufferFormat::RGBA_8888); + gfx::ColorSpace::CreateSRGBLinear(), gfx::BufferFormat::RGBA_8888); display_color_spaces.SetOutputColorSpaceAndBufferFormat( gfx::ContentColorUsage::kHDR, false /* needs_alpha */, gfx::ColorSpace::CreateHDR10(), gfx::BufferFormat::BGRA_1010102); display_color_spaces.SetOutputColorSpaceAndBufferFormat( gfx::ContentColorUsage::kHDR, true /* needs_alpha */, - gfx::ColorSpace::CreateSCRGBLinear(), gfx::BufferFormat::RGBA_F16); + gfx::ColorSpace::CreateSRGBLinear(), gfx::BufferFormat::RGBA_F16); std::vector<Pass> passes = { Pass(quads[0], CompositorRenderPassId{2}, kSurfaceSize), @@ -6085,7 +6086,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, ColorSpaceTestWin) { gfx::BufferFormat::BGRA_1010102); display_color_spaces.SetOutputColorSpaceAndBufferFormat( gfx::ContentColorUsage::kHDR, true /* needs_alpha */, - gfx::ColorSpace::CreateSCRGBLinear(), gfx::BufferFormat::RGBA_F16); + gfx::ColorSpace::CreateSRGBLinear(), gfx::BufferFormat::RGBA_F16); // Opaque content renders to the appropriate space directly. passes[1].has_transparent_background = false; diff --git a/chromium/components/viz/service/display/sync_query_collection.cc b/chromium/components/viz/service/display/sync_query_collection.cc deleted file mode 100644 index 51a85c415da..00000000000 --- a/chromium/components/viz/service/display/sync_query_collection.cc +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2018 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/sync_query_collection.h" - -#include <utility> - -#include "base/logging.h" -#include "base/memory/raw_ptr.h" -#include "base/memory/weak_ptr.h" -#include "cc/base/container_util.h" -#include "components/viz/service/display/resource_fence.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "gpu/command_buffer/client/gles2_interface.h" - -namespace viz { -namespace { -// Block or crash if the number of pending sync queries reach this high as -// something is seriously wrong on the service side if this happens. -const size_t kMaxPendingSyncQueries = 16; -} // anonymous namespace - -class SyncQuery { - public: - explicit SyncQuery(gpu::gles2::GLES2Interface* gl) - : gl_(gl), query_id_(0u), is_pending_(false) { - gl_->GenQueriesEXT(1, &query_id_); - } - - SyncQuery(const SyncQuery&) = delete; - SyncQuery& operator=(const SyncQuery&) = delete; - - virtual ~SyncQuery() { gl_->DeleteQueriesEXT(1, &query_id_); } - - scoped_refptr<ResourceFence> Begin() { - DCHECK(!IsPending()); - // Invalidate weak pointer held by old fence. - weak_ptr_factory_.InvalidateWeakPtrs(); - // Note: In case the set of drawing commands issued before End() do not - // depend on the query, defer BeginQueryEXT call until Set() is called and - // query is required. - return base::MakeRefCounted<Fence>(weak_ptr_factory_.GetWeakPtr()); - } - - void Set() { - if (is_pending_) - return; - - // Note: BeginQueryEXT on GL_COMMANDS_COMPLETED_CHROMIUM is effectively a - // noop relative to GL, so it doesn't matter where it happens but we still - // make sure to issue this command when Set() is called (prior to issuing - // any drawing commands that depend on query), in case some future extension - // can take advantage of this. - gl_->BeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, query_id_); - is_pending_ = true; - } - - void End() { - if (!is_pending_) - return; - - gl_->EndQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM); - } - - bool IsPending() { - if (!is_pending_) - return false; - - unsigned result_available = 1; - gl_->GetQueryObjectuivEXT(query_id_, GL_QUERY_RESULT_AVAILABLE_EXT, - &result_available); - is_pending_ = !result_available; - return is_pending_; - } - - void Wait() { - if (!is_pending_) - return; - - unsigned result = 0; - gl_->GetQueryObjectuivEXT(query_id_, GL_QUERY_RESULT_EXT, &result); - is_pending_ = false; - } - - private: - class Fence : public ResourceFence { - public: - explicit Fence(base::WeakPtr<SyncQuery> query) : query_(query) {} - - Fence(const Fence&) = delete; - Fence& operator=(const Fence&) = delete; - - // ResourceFence implementation. - void Set() override { - DCHECK(query_); - query_->Set(); - } - bool HasPassed() override { return !query_ || !query_->IsPending(); } - - private: - ~Fence() override {} - - base::WeakPtr<SyncQuery> query_; - }; - - raw_ptr<gpu::gles2::GLES2Interface> gl_; - unsigned query_id_; - bool is_pending_; - base::WeakPtrFactory<SyncQuery> weak_ptr_factory_{this}; -}; - -SyncQueryCollection::SyncQueryCollection(gpu::gles2::GLES2Interface* gl) - : gl_(gl) {} - -SyncQueryCollection::~SyncQueryCollection() = default; -SyncQueryCollection::SyncQueryCollection(SyncQueryCollection&&) = default; -SyncQueryCollection& SyncQueryCollection::operator=(SyncQueryCollection&&) = - default; - -scoped_refptr<ResourceFence> SyncQueryCollection::StartNewFrame() { - // Block until oldest sync query has passed if the number of pending queries - // ever reach kMaxPendingSyncQueries. - if (pending_sync_queries_.size() >= kMaxPendingSyncQueries) { - LOG(ERROR) << "Reached limit of pending sync queries."; - - pending_sync_queries_.front()->Wait(); - DCHECK(!pending_sync_queries_.front()->IsPending()); - } - - while (!pending_sync_queries_.empty()) { - if (pending_sync_queries_.front()->IsPending()) - break; - - available_sync_queries_.push_back(cc::PopFront(&pending_sync_queries_)); - } - - current_sync_query_ = available_sync_queries_.empty() - ? std::make_unique<SyncQuery>(gl_) - : cc::PopFront(&available_sync_queries_); - - return current_sync_query_->Begin(); -} - -void SyncQueryCollection::EndCurrentFrame() { - DCHECK(current_sync_query_); - current_sync_query_->End(); - pending_sync_queries_.push_back(std::move(current_sync_query_)); -} - -} // namespace viz diff --git a/chromium/components/viz/service/display/sync_query_collection.h b/chromium/components/viz/service/display/sync_query_collection.h deleted file mode 100644 index fd09cbba6d4..00000000000 --- a/chromium/components/viz/service/display/sync_query_collection.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2018 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_SYNC_QUERY_COLLECTION_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_SYNC_QUERY_COLLECTION_H_ - -#include <memory> - -#include "base/containers/circular_deque.h" -#include "base/memory/raw_ptr.h" -#include "base/memory/ref_counted.h" - -namespace gpu { -namespace gles2 { -class GLES2Interface; -} -} // namespace gpu - -namespace viz { -class SyncQuery; -class ResourceFence; - -class SyncQueryCollection { - public: - explicit SyncQueryCollection(gpu::gles2::GLES2Interface* gl); - SyncQueryCollection(SyncQueryCollection&&); - SyncQueryCollection& operator=(SyncQueryCollection&&); - ~SyncQueryCollection(); - scoped_refptr<ResourceFence> StartNewFrame(); - void EndCurrentFrame(); - - private: - base::circular_deque<std::unique_ptr<SyncQuery>> pending_sync_queries_; - base::circular_deque<std::unique_ptr<SyncQuery>> available_sync_queries_; - std::unique_ptr<SyncQuery> current_sync_query_; - raw_ptr<gpu::gles2::GLES2Interface> gl_; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_SYNC_QUERY_COLLECTION_H_ diff --git a/chromium/components/viz/service/display/texture_deleter.cc b/chromium/components/viz/service/display/texture_deleter.cc deleted file mode 100644 index 76e2cf0b0f8..00000000000 --- a/chromium/components/viz/service/display/texture_deleter.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2013 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/texture_deleter.h" - -#include <stddef.h> -#include <utility> - -#include "base/bind.h" -#include "base/location.h" -#include "base/memory/weak_ptr.h" -#include "base/task/bind_post_task.h" -#include "base/task/single_thread_task_runner.h" -#include "components/viz/common/gpu/context_provider.h" -#include "gpu/command_buffer/client/shared_image_interface.h" -#include "gpu/command_buffer/common/mailbox.h" -#include "gpu/command_buffer/common/sync_token.h" - -namespace viz { - -static void DeleteTextureOnImplThread( - const scoped_refptr<ContextProvider>& context_provider, - const gpu::Mailbox& mailbox, - const gpu::SyncToken& sync_token, - bool is_lost) { - context_provider->SharedImageInterface()->DestroySharedImage(sync_token, - mailbox); -} - -TextureDeleter::TextureDeleter( - scoped_refptr<base::SingleThreadTaskRunner> task_runner) - : impl_task_runner_(std::move(task_runner)) {} - -TextureDeleter::~TextureDeleter() { - for (auto& callback : impl_callbacks_) - std::move(*callback).Run(gpu::SyncToken(), /*is_lost=*/true); -} - -ReleaseCallback TextureDeleter::GetReleaseCallback( - scoped_refptr<ContextProvider> context_provider, - const gpu::Mailbox& mailbox) { - // This callback owns the |context_provider|. It must be destroyed on the impl - // thread. Upon destruction of this class, the callback must immediately be - // destroyed. - auto impl_callback = std::make_unique<ReleaseCallback>(base::BindOnce( - &DeleteTextureOnImplThread, std::move(context_provider), mailbox)); - - impl_callbacks_.push_back(std::move(impl_callback)); - - // The raw pointer to the impl-side callback is valid as long as this - // class is alive. So we guard it with a WeakPtr. - ReleaseCallback run_impl_callback = base::BindOnce( - &TextureDeleter::RunDeleteTextureOnImplThread, - weak_ptr_factory_.GetWeakPtr(), impl_callbacks_.back().get()); - - // Provide a callback for the main thread that posts back to the impl - // thread. - ReleaseCallback main_callback; - if (impl_task_runner_) { - main_callback = - base::BindPostTask(impl_task_runner_, std::move(run_impl_callback)); - } else { - main_callback = std::move(run_impl_callback); - } - - return main_callback; -} - -void TextureDeleter::RunDeleteTextureOnImplThread( - ReleaseCallback* impl_callback, - const gpu::SyncToken& sync_token, - bool is_lost) { - for (size_t i = 0; i < impl_callbacks_.size(); ++i) { - if (impl_callbacks_[i].get() == impl_callback) { - // Run the callback, then destroy it here on the impl thread. - std::move(*impl_callbacks_[i]).Run(sync_token, is_lost); - impl_callbacks_.erase(impl_callbacks_.begin() + i); - return; - } - } - - NOTREACHED() << "The Callback returned by GetDeleteCallback() was called " - << "more than once."; -} - -} // namespace viz diff --git a/chromium/components/viz/service/display/texture_deleter.h b/chromium/components/viz/service/display/texture_deleter.h deleted file mode 100644 index eadcd3dbcc2..00000000000 --- a/chromium/components/viz/service/display/texture_deleter.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2013 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_TEXTURE_DELETER_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_TEXTURE_DELETER_H_ - -#include <memory> -#include <vector> - -#include "base/memory/weak_ptr.h" -#include "components/viz/common/resources/release_callback.h" -#include "components/viz/service/viz_service_export.h" - -namespace base { -class SingleThreadTaskRunner; -} - -namespace gpu { -struct Mailbox; -struct SyncToken; -} - -namespace viz { -class ContextProvider; - -class VIZ_SERVICE_EXPORT TextureDeleter { - public: - // task_runner corresponds with the thread the delete task should be posted - // to. If null, the delete will happen on the calling thread. - explicit TextureDeleter( - scoped_refptr<base::SingleThreadTaskRunner> task_runner); - - TextureDeleter(const TextureDeleter&) = delete; - TextureDeleter& operator=(const TextureDeleter&) = delete; - - ~TextureDeleter(); - - // Returns a Callback that can be used as the ReleaseCallback for a - // |texture_id|. The ReleaseCallback can be passed to other threads and will - // destroy the texture, once it is run, on the impl thread. If the - // TextureDeleter is destroyed due to the compositor shutting down, then the - // ReleaseCallback will become a no-op and the texture will be deleted - // immediately on the impl thread, along with dropping the reference to the - // ContextProvider. - ReleaseCallback GetReleaseCallback( - scoped_refptr<ContextProvider> context_provider, - const gpu::Mailbox& mailbox); - - private: - // Runs the |impl_callback| to delete the texture and removes the callback - // from the |impl_callbacks_| list. - void RunDeleteTextureOnImplThread(ReleaseCallback* impl_callback, - const gpu::SyncToken& sync_token, - bool is_lost); - - scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner_; - std::vector<std::unique_ptr<ReleaseCallback>> impl_callbacks_; - base::WeakPtrFactory<TextureDeleter> weak_ptr_factory_{this}; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_TEXTURE_DELETER_H_ diff --git a/chromium/components/viz/service/display/texture_deleter_unittest.cc b/chromium/components/viz/service/display/texture_deleter_unittest.cc deleted file mode 100644 index e1df0821e0c..00000000000 --- a/chromium/components/viz/service/display/texture_deleter_unittest.cc +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2013 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/texture_deleter.h" - -#include <utility> - -#include "base/task/single_thread_task_runner.h" -#include "base/threading/thread_task_runner_handle.h" -#include "components/viz/common/resources/release_callback.h" -#include "components/viz/test/test_context_provider.h" -#include "gpu/command_buffer/client/shared_image_interface.h" -#include "gpu/command_buffer/common/shared_image_usage.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/color_space.h" - -namespace viz { -namespace { - -TEST(TextureDeleterTest, Destroy) { - auto deleter = - std::make_unique<TextureDeleter>(base::ThreadTaskRunnerHandle::Get()); - - scoped_refptr<TestContextProvider> context_provider = - TestContextProvider::Create(); - context_provider->BindToCurrentThread(); - - auto* sii = context_provider->SharedImageInterface(); - - gpu::Mailbox mailbox = sii->CreateSharedImage( - ResourceFormat::RGBA_8888, gfx::Size(1, 1), gfx::ColorSpace(), - kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, - gpu::SHARED_IMAGE_USAGE_GLES2, gpu::kNullSurfaceHandle); - - EXPECT_TRUE(context_provider->HasOneRef()); - EXPECT_EQ(1u, sii->shared_image_count()); - - ReleaseCallback cb = deleter->GetReleaseCallback(context_provider, mailbox); - EXPECT_FALSE(context_provider->HasOneRef()); - EXPECT_EQ(1u, sii->shared_image_count()); - - // When the deleter is destroyed, it immediately drops its ref on the - // ContextProvider, and deletes the shared image. - deleter = nullptr; - EXPECT_TRUE(context_provider->HasOneRef()); - EXPECT_EQ(0u, sii->shared_image_count()); - - // Run the scoped release callback before destroying it, but it won't do - // anything. - std::move(cb).Run(gpu::SyncToken(), false); -} - -TEST(TextureDeleterTest, NullTaskRunner) { - auto deleter = std::make_unique<TextureDeleter>(nullptr); - - scoped_refptr<TestContextProvider> context_provider = - TestContextProvider::Create(); - context_provider->BindToCurrentThread(); - - auto* sii = context_provider->SharedImageInterface(); - - gpu::Mailbox mailbox = sii->CreateSharedImage( - ResourceFormat::RGBA_8888, gfx::Size(1, 1), gfx::ColorSpace(), - kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, - gpu::SHARED_IMAGE_USAGE_GLES2, gpu::kNullSurfaceHandle); - - EXPECT_TRUE(context_provider->HasOneRef()); - EXPECT_EQ(1u, sii->shared_image_count()); - - ReleaseCallback cb = deleter->GetReleaseCallback(context_provider, mailbox); - EXPECT_FALSE(context_provider->HasOneRef()); - EXPECT_EQ(1u, sii->shared_image_count()); - - std::move(cb).Run(gpu::SyncToken(), false); - - // With no task runner the callback will immediately drops its ref on the - // ContextProvider and delete the shared image. - EXPECT_TRUE(context_provider->HasOneRef()); - EXPECT_EQ(0u, sii->shared_image_count()); -} - -} // namespace -} // namespace viz diff --git a/chromium/components/viz/service/display/viz_pixel_test.cc b/chromium/components/viz/service/display/viz_pixel_test.cc index c68d0e6448d..c3076fa3b23 100644 --- a/chromium/components/viz/service/display/viz_pixel_test.cc +++ b/chromium/components/viz/service/display/viz_pixel_test.cc @@ -30,9 +30,6 @@ void VizPixelTest::SetUp() { case RendererType::kSoftware: SetUpSoftwareRenderer(); break; - case RendererType::kGL: - SetUpGLRenderer(GetSurfaceOrigin()); - break; case RendererType::kSkiaGL: case RendererType::kSkiaVk: case RendererType::kSkiaDawn: diff --git a/chromium/components/viz/service/display/viz_pixel_test.h b/chromium/components/viz/service/display/viz_pixel_test.h index ee041e7fe54..5a3ca05d8b6 100644 --- a/chromium/components/viz/service/display/viz_pixel_test.h +++ b/chromium/components/viz/service/display/viz_pixel_test.h @@ -33,8 +33,6 @@ class VizPixelTest : public cc::PixelTest { switch (renderer_type_) { case RendererType::kSoftware: return "software"; - case RendererType::kGL: - return "gl"; case RendererType::kSkiaGL: case RendererType::kSkiaVk: return "skia"; @@ -47,8 +45,6 @@ class VizPixelTest : public cc::PixelTest { return renderer_type_ == RendererType::kSoftware; } - bool is_gl_renderer() const { return renderer_type_ == RendererType::kGL; } - protected: static GraphicsBackend RenderTypeToBackend(RendererType renderer_type); diff --git a/chromium/components/viz/service/display_embedder/DEPS b/chromium/components/viz/service/display_embedder/DEPS index 4988c8b24d6..c8b25c28439 100644 --- a/chromium/components/viz/service/display_embedder/DEPS +++ b/chromium/components/viz/service/display_embedder/DEPS @@ -19,7 +19,9 @@ include_rules = [ "+components/viz/service/display/skia_output_surface.h", "+components/viz/service/display/software_output_device.h", "+components/viz/service/gl/gpu_service_impl.h", - "+gpu/command_buffer/client", + "+gpu/command_buffer/client/gpu_memory_buffer_manager.h", + "+gpu/command_buffer/client/shared_image_interface.h", + "+gpu/command_buffer/client/shared_memory_limits.h", "+gpu/command_buffer/common", "+gpu/command_buffer/service", "+gpu/config", @@ -45,14 +47,12 @@ include_rules = [ "+ui/ozone/public", # TODO(danakj): Double check the layering for these dependencies. - "+components/viz/service/display/gl_renderer_copier.h", "+components/viz/service/display/overlay_processor.h", "+components/viz/service/display/overlay_processor_interface.h", "+components/viz/service/display/overlay_strategy_fullscreen.h", "+components/viz/service/display/overlay_strategy_single_on_top.h", "+components/viz/service/display/overlay_strategy_underlay_cast.h", "+components/viz/service/display/overlay_strategy_underlay.h", - "+components/viz/service/display/texture_deleter.h", ] specific_include_rules = { diff --git a/chromium/components/viz/service/display_embedder/compositor_gpu_thread.cc b/chromium/components/viz/service/display_embedder/compositor_gpu_thread.cc index 6e5914b1614..02db4e3c66c 100644 --- a/chromium/components/viz/service/display_embedder/compositor_gpu_thread.cc +++ b/chromium/components/viz/service/display_embedder/compositor_gpu_thread.cc @@ -47,7 +47,8 @@ std::unique_ptr<CompositorGpuThread> CompositorGpuThread::Create( // currently always enables this extension, we are adding DCHECK() to ensure // that instead of enabling/disabling DrDc based on the extension. if (gl::GetGLImplementation() == gl::kGLImplementationEGLANGLE) - DCHECK(gl::GLSurfaceEGL::IsANGLEContextVirtualizationSupported()); + DCHECK(gl::GLSurfaceEGL::GetGLDisplayEGL() + ->IsANGLEContextVirtualizationSupported()); #endif scoped_refptr<VulkanContextProvider> vulkan_context_provider; @@ -113,8 +114,12 @@ CompositorGpuThread::GetSharedContextState() { const bool use_passthrough_decoder = gpu::gles2::PassthroughCommandDecoderSupported() && gpu_preferences.use_passthrough_cmd_decoder; + gpu::ContextCreationAttribs attribs_helper; + attribs_helper.context_type = features::UseGles2ForOopR() + ? gpu::CONTEXT_TYPE_OPENGLES2 + : gpu::CONTEXT_TYPE_OPENGLES3; gl::GLContextAttribs attribs = gpu::gles2::GenerateGLContextAttribs( - gpu::ContextCreationAttribs(), use_passthrough_decoder); + attribs_helper, use_passthrough_decoder); attribs.angle_context_virtualization_group_number = gl::AngleContextVirtualizationGroup::kDrDc; @@ -186,6 +191,17 @@ bool CompositorGpuThread::Initialize() { return init_succeded_; } +void CompositorGpuThread::HandleMemoryPressure( + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { + DCHECK(task_runner()->BelongsToCurrentThread()); + + // Context should be current for cache/memory cleanup. + if (shared_context_state_ && + shared_context_state_->MakeCurrent(nullptr, /*needs_gl=*/true)) { + shared_context_state_->PurgeMemory(memory_pressure_level); + } +} + void CompositorGpuThread::Init() { const auto& gpu_preferences = gpu_channel_manager_->gpu_preferences(); if (enable_watchdog_) { @@ -196,10 +212,20 @@ void CompositorGpuThread::Init() { if (!watchdog_thread_) return; watchdog_thread_->OnInitComplete(); + + // Making sure to create the |memory_pressure_listener_| on + // CompositorGpuThread since this callback will be called on the thread it was + // created on. + memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>( + FROM_HERE, base::BindRepeating(&CompositorGpuThread::HandleMemoryPressure, + base::Unretained(this))), init_succeded_ = true; } void CompositorGpuThread::CleanUp() { + // Destroying |memory_pressure_listener_| here to ensure its destroyed on the + // same thread on which it was created on. + memory_pressure_listener_.reset(); if (watchdog_thread_) watchdog_thread_->OnGpuProcessTearDown(); diff --git a/chromium/components/viz/service/display_embedder/compositor_gpu_thread.h b/chromium/components/viz/service/display_embedder/compositor_gpu_thread.h index 7ca40835ddf..f23c4e05233 100644 --- a/chromium/components/viz/service/display_embedder/compositor_gpu_thread.h +++ b/chromium/components/viz/service/display_embedder/compositor_gpu_thread.h @@ -7,6 +7,7 @@ #include <memory> +#include "base/memory/memory_pressure_listener.h" #include "base/memory/raw_ptr.h" #include "base/threading/thread.h" #include "components/viz/service/viz_service_export.h" @@ -67,6 +68,9 @@ class VIZ_SERVICE_EXPORT CompositorGpuThread bool Initialize(); + void HandleMemoryPressure( + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level); + raw_ptr<gpu::GpuChannelManager> gpu_channel_manager_; const bool enable_watchdog_; bool init_succeded_ = false; @@ -80,6 +84,14 @@ class VIZ_SERVICE_EXPORT CompositorGpuThread std::unique_ptr<gpu::GpuWatchdogThread> watchdog_thread_; scoped_refptr<gpu::SharedContextState> shared_context_state_; + // To start listening memory pressure signals from the platform, we create a + // new instance of MemoryPressureListener, passing a callback to a + // function that takes a MemoryPressureLevel parameter.To stop listening, + // simply delete the listener object. The implementation guarantees + // that the callback will always be called on the thread that created + // the listener. + std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_; + base::WeakPtrFactory<CompositorGpuThread> weak_ptr_factory_; }; diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface.cc b/chromium/components/viz/service/display_embedder/gl_output_surface.cc deleted file mode 100644 index 182bfc70aa9..00000000000 --- a/chromium/components/viz/service/display_embedder/gl_output_surface.cc +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright 2014 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/gl_output_surface.h" - -#include <utility> -#include <vector> - -#include "base/bind.h" -#include "base/threading/thread_task_runner_handle.h" -#include "cc/base/math_util.h" -#include "components/viz/common/frame_sinks/begin_frame_source.h" -#include "components/viz/common/gpu/context_provider.h" -#include "components/viz/service/display/output_surface_client.h" -#include "components/viz/service/display/output_surface_frame.h" -#include "components/viz/service/display/renderer_utils.h" -#include "gpu/command_buffer/client/context_support.h" -#include "gpu/command_buffer/client/gles2_interface.h" -#include "gpu/command_buffer/common/swap_buffers_complete_params.h" -#include "gpu/command_buffer/common/swap_buffers_flags.h" -#include "gpu/config/gpu_feature_info.h" -#include "ui/gfx/buffer_format_util.h" -#include "ui/gfx/overlay_transform_utils.h" - -namespace viz { - -GLOutputSurface::GLOutputSurface( - scoped_refptr<VizProcessContextProvider> context_provider, - gpu::SurfaceHandle surface_handle) - : OutputSurface(context_provider), - viz_context_provider_(context_provider), - surface_handle_(surface_handle), - use_gpu_fence_( - context_provider->ContextCapabilities().chromium_gpu_fence && - context_provider->ContextCapabilities() - .use_gpu_fences_for_overlay_planes) { - const auto& context_capabilities = context_provider->ContextCapabilities(); - capabilities_.output_surface_origin = context_capabilities.surface_origin; - capabilities_.supports_stencil = context_capabilities.num_stencil_bits > 0; - // Since one of the buffers is used by the surface for presentation, there can - // be at most |num_surface_buffers - 1| pending buffers that the compositor - // can use. - capabilities_.pending_swap_params.max_pending_swaps = - context_capabilities.num_surface_buffers - 1; - capabilities_.supports_gpu_vsync = context_capabilities.gpu_vsync; - capabilities_.supports_dc_layers = context_capabilities.dc_layers; - capabilities_.supports_surfaceless = context_capabilities.surfaceless; - capabilities_.android_surface_control_feature_enabled = - context_provider->GetGpuFeatureInfo() - .status_values[gpu::GPU_FEATURE_TYPE_ANDROID_SURFACE_CONTROL] == - gpu::kGpuFeatureStatusEnabled; - capabilities_.max_render_target_size = context_capabilities.max_texture_size; -} - -GLOutputSurface::~GLOutputSurface() { - viz_context_provider_->SetUpdateVSyncParametersCallback( - UpdateVSyncParametersCallback()); - viz_context_provider_->SetGpuVSyncCallback(GpuVSyncCallback()); - if (gpu_fence_id_ > 0) - context_provider()->ContextGL()->DestroyGpuFenceCHROMIUM(gpu_fence_id_); -} - -void GLOutputSurface::BindToClient(OutputSurfaceClient* client) { - DCHECK(client); - DCHECK(!client_); - client_ = client; -} - -void GLOutputSurface::EnsureBackbuffer() {} - -void GLOutputSurface::DiscardBackbuffer() { - context_provider()->ContextGL()->DiscardBackbufferCHROMIUM(); -} - -void GLOutputSurface::BindFramebuffer() { - context_provider()->ContextGL()->BindFramebuffer(GL_FRAMEBUFFER, 0); -} - -void GLOutputSurface::SetDrawRectangle(const gfx::Rect& rect) { - DCHECK(capabilities_.supports_dc_layers); - - if (set_draw_rectangle_for_frame_) - return; - DCHECK(gfx::Rect(size_).Contains(rect)); - DCHECK(has_set_draw_rectangle_since_last_resize_ || - (gfx::Rect(size_) == rect)); - set_draw_rectangle_for_frame_ = true; - has_set_draw_rectangle_since_last_resize_ = true; - context_provider()->ContextGL()->SetDrawRectangleCHROMIUM( - rect.x(), rect.y(), rect.width(), rect.height()); -} - -void GLOutputSurface::SetEnableDCLayers(bool enable) { - DCHECK(capabilities_.supports_dc_layers); - context_provider()->ContextGL()->SetEnableDCLayersCHROMIUM(enable); -} - -void GLOutputSurface::Reshape(const gfx::Size& size, - float device_scale_factor, - const gfx::ColorSpace& color_space, - gfx::BufferFormat format, - bool use_stencil) { - size_ = size; - has_set_draw_rectangle_since_last_resize_ = false; - set_draw_rectangle_for_frame_ = false; - context_provider()->ContextGL()->ResizeCHROMIUM( - size.width(), size.height(), device_scale_factor, - color_space.AsGLColorSpace(), gfx::AlphaBitsForBufferFormat(format)); -} - -void GLOutputSurface::SwapBuffers(OutputSurfaceFrame frame) { - DCHECK(context_provider_); - - uint32_t flags = 0; - if (wants_vsync_parameter_updates_) - flags |= gpu::SwapBuffersFlags::kVSyncParams; - - // The |swap_size| here should always be in the UI's logical screen space - // since it is forwarded to the client code which is unaware of the display - // transform optimization. - gfx::Size swap_size = ApplyDisplayInverse(gfx::Rect(size_)).size(); - auto swap_callback = base::BindOnce( - &GLOutputSurface::OnGpuSwapBuffersCompleted, - weak_ptr_factory_.GetWeakPtr(), std::move(frame.latency_info), - frame.top_controls_visible_height_changed, swap_size); - gpu::ContextSupport::PresentationCallback presentation_callback; - presentation_callback = base::BindOnce(&GLOutputSurface::OnPresentation, - weak_ptr_factory_.GetWeakPtr()); - - set_draw_rectangle_for_frame_ = false; - if (frame.sub_buffer_rect) { - HandlePartialSwap(*frame.sub_buffer_rect, flags, std::move(swap_callback), - std::move(presentation_callback)); - } else if (!frame.content_bounds.empty()) { - context_provider_->ContextSupport()->SwapWithBounds( - frame.content_bounds, flags, std::move(swap_callback), - std::move(presentation_callback)); - } else { - context_provider_->ContextSupport()->Swap(flags, std::move(swap_callback), - std::move(presentation_callback)); - } -} - -uint32_t GLOutputSurface::GetFramebufferCopyTextureFormat() { - auto* gl = static_cast<VizProcessContextProvider*>(context_provider()); - return gl->GetCopyTextureInternalFormat(); -} - -bool GLOutputSurface::IsDisplayedAsOverlayPlane() const { - return false; -} - -unsigned GLOutputSurface::GetOverlayTextureId() const { - return 0; -} - -bool GLOutputSurface::HasExternalStencilTest() const { - return false; -} - -void GLOutputSurface::ApplyExternalStencil() {} - -void GLOutputSurface::DidReceiveSwapBuffersAck( - const gfx::SwapResponse& response, - gfx::GpuFenceHandle release_fence) { - client_->DidReceiveSwapBuffersAck(response.timings, std::move(release_fence)); -} - -void GLOutputSurface::HandlePartialSwap( - const gfx::Rect& sub_buffer_rect, - uint32_t flags, - gpu::ContextSupport::SwapCompletedCallback swap_callback, - gpu::ContextSupport::PresentationCallback presentation_callback) { - context_provider_->ContextSupport()->PartialSwapBuffers( - sub_buffer_rect, flags, std::move(swap_callback), - std::move(presentation_callback)); -} - -void GLOutputSurface::OnGpuSwapBuffersCompleted( - std::vector<ui::LatencyInfo> latency_info, - bool top_controls_visible_height_changed, - const gfx::Size& pixel_size, - const gpu::SwapBuffersCompleteParams& params, - gfx::GpuFenceHandle release_fence) { - if (!params.texture_in_use_responses.empty()) - client_->DidReceiveTextureInUseResponses(params.texture_in_use_responses); - if (!params.ca_layer_params.is_empty) - client_->DidReceiveCALayerParams(params.ca_layer_params); - DidReceiveSwapBuffersAck(params.swap_response, std::move(release_fence)); - - UpdateLatencyInfoOnSwap(params.swap_response, &latency_info); - latency_tracker_.OnGpuSwapBuffersCompleted( - std::move(latency_info), top_controls_visible_height_changed); - - if (needs_swap_size_notifications_) - client_->DidSwapWithSize(pixel_size); -} - -void GLOutputSurface::OnPresentation( - const gfx::PresentationFeedback& feedback) { - client_->DidReceivePresentationFeedback(feedback); -} - -unsigned GLOutputSurface::UpdateGpuFence() { - if (!use_gpu_fence_) - return 0; - - if (gpu_fence_id_ > 0) - context_provider()->ContextGL()->DestroyGpuFenceCHROMIUM(gpu_fence_id_); - - gpu_fence_id_ = context_provider()->ContextGL()->CreateGpuFenceCHROMIUM(); - - return gpu_fence_id_; -} - -void GLOutputSurface::SetNeedsSwapSizeNotifications( - bool needs_swap_size_notifications) { - needs_swap_size_notifications_ = needs_swap_size_notifications; -} - -void GLOutputSurface::SetUpdateVSyncParametersCallback( - UpdateVSyncParametersCallback callback) { - wants_vsync_parameter_updates_ = !callback.is_null(); - viz_context_provider_->SetUpdateVSyncParametersCallback(std::move(callback)); -} - -void GLOutputSurface::SetGpuVSyncCallback(GpuVSyncCallback callback) { - DCHECK(capabilities_.supports_gpu_vsync); - viz_context_provider_->SetGpuVSyncCallback(std::move(callback)); -} - -void GLOutputSurface::SetGpuVSyncEnabled(bool enabled) { - DCHECK(capabilities_.supports_gpu_vsync); - viz_context_provider_->SetGpuVSyncEnabled(enabled); -} - -gfx::OverlayTransform GLOutputSurface::GetDisplayTransform() { - return gfx::OVERLAY_TRANSFORM_NONE; -} - -gfx::Rect GLOutputSurface::ApplyDisplayInverse(const gfx::Rect& input) { - gfx::Transform display_inverse = gfx::OverlayTransformToTransform( - gfx::InvertOverlayTransform(GetDisplayTransform()), gfx::SizeF(size_)); - return cc::MathUtil::MapEnclosedRectWith2dAxisAlignedTransform( - display_inverse, input); -} - -base::ScopedClosureRunner GLOutputSurface::GetCacheBackBufferCb() { - return viz_context_provider_->GetCacheBackBufferCb(); -} - -gpu::SurfaceHandle GLOutputSurface::GetSurfaceHandle() const { - return surface_handle_; -} - -void GLOutputSurface::SetFrameRate(float frame_rate) { - viz_context_provider_->ContextSupport()->SetFrameRate(frame_rate); -} - -void GLOutputSurface::SetNeedsMeasureNextDrawLatency() { - viz_context_provider_->SetNeedsMeasureNextDrawLatency(); -} - -} // namespace viz diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface.h b/chromium/components/viz/service/display_embedder/gl_output_surface.h deleted file mode 100644 index c611894a647..00000000000 --- a/chromium/components/viz/service/display_embedder/gl_output_surface.h +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2014 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_GL_OUTPUT_SURFACE_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_H_ - -#include <memory> -#include <vector> - -#include "base/callback_helpers.h" -#include "base/memory/raw_ptr.h" -#include "components/viz/common/display/update_vsync_parameters_callback.h" -#include "components/viz/service/display/output_surface.h" -#include "components/viz/service/display_embedder/viz_process_context_provider.h" -#include "gpu/command_buffer/client/context_support.h" -#include "ui/latency/latency_tracker.h" - -namespace viz { - -// An OutputSurface implementation that directly draws and -// swaps to an actual GL surface. -class GLOutputSurface : public OutputSurface { - public: - GLOutputSurface(scoped_refptr<VizProcessContextProvider> context_provider, - gpu::SurfaceHandle surface_handle); - ~GLOutputSurface() override; - - // OutputSurface implementation - void BindToClient(OutputSurfaceClient* client) override; - void EnsureBackbuffer() override; - void DiscardBackbuffer() override; - void BindFramebuffer() override; - void SetDrawRectangle(const gfx::Rect& draw_rectangle) override; - void SetEnableDCLayers(bool enabled) override; - void Reshape(const gfx::Size& size, - float device_scale_factor, - const gfx::ColorSpace& color_space, - gfx::BufferFormat format, - bool use_stencil) override; - void SwapBuffers(OutputSurfaceFrame frame) override; - uint32_t GetFramebufferCopyTextureFormat() override; - bool IsDisplayedAsOverlayPlane() const override; - unsigned GetOverlayTextureId() const override; - bool HasExternalStencilTest() const override; - void ApplyExternalStencil() override; - unsigned UpdateGpuFence() override; - void SetNeedsSwapSizeNotifications( - bool needs_swap_size_notifications) override; - void SetUpdateVSyncParametersCallback( - UpdateVSyncParametersCallback callback) override; - void SetGpuVSyncCallback(GpuVSyncCallback callback) override; - void SetGpuVSyncEnabled(bool enabled) override; - void SetDisplayTransformHint(gfx::OverlayTransform transform) override {} - gfx::OverlayTransform GetDisplayTransform() override; - base::ScopedClosureRunner GetCacheBackBufferCb() override; - - gpu::SurfaceHandle GetSurfaceHandle() const override; - void SetFrameRate(float frame_rate) override; - void SetNeedsMeasureNextDrawLatency() override; - - protected: - OutputSurfaceClient* client() const { return client_; } - ui::LatencyTracker* latency_tracker() { return &latency_tracker_; } - bool needs_swap_size_notifications() { - return needs_swap_size_notifications_; - } - - // Called when a swap completion is signaled from ImageTransportSurface. - virtual void DidReceiveSwapBuffersAck(const gfx::SwapResponse& response, - gfx::GpuFenceHandle release_fence); - - // Called in SwapBuffers() when a swap is determined to be partial. Subclasses - // might override this method because different platforms handle partial swaps - // differently. - virtual void HandlePartialSwap( - const gfx::Rect& sub_buffer_rect, - uint32_t flags, - gpu::ContextSupport::SwapCompletedCallback swap_callback, - gpu::ContextSupport::PresentationCallback presentation_callback); - - private: - // Called when a swap completion is signaled from ImageTransportSurface. - void OnGpuSwapBuffersCompleted(std::vector<ui::LatencyInfo> latency_info, - bool top_controls_visible_height_changed, - const gfx::Size& pixel_size, - const gpu::SwapBuffersCompleteParams& params, - gfx::GpuFenceHandle release_fence); - void OnPresentation(const gfx::PresentationFeedback& feedback); - void OnGpuVSync(base::TimeTicks vsync_time, base::TimeDelta vsync_interval); - gfx::Rect ApplyDisplayInverse(const gfx::Rect& input); - - scoped_refptr<VizProcessContextProvider> viz_context_provider_; - raw_ptr<OutputSurfaceClient> client_ = nullptr; - bool wants_vsync_parameter_updates_ = false; - ui::LatencyTracker latency_tracker_; - - const gpu::SurfaceHandle surface_handle_; - - bool set_draw_rectangle_for_frame_ = false; - // True if the draw rectangle has been set at all since the last resize. - bool has_set_draw_rectangle_since_last_resize_ = false; - gfx::Size size_; - bool use_gpu_fence_; - unsigned gpu_fence_id_ = 0; - // Whether to send OutputSurfaceClient::DidSwapWithSize notifications. - bool needs_swap_size_notifications_ = false; - - base::WeakPtrFactory<GLOutputSurface> weak_ptr_factory_{this}; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_H_ diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_android.cc b/chromium/components/viz/service/display_embedder/gl_output_surface_android.cc deleted file mode 100644 index 741facb6742..00000000000 --- a/chromium/components/viz/service/display_embedder/gl_output_surface_android.cc +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2018 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/gl_output_surface_android.h" - -namespace viz { - -GLOutputSurfaceAndroid::GLOutputSurfaceAndroid( - scoped_refptr<VizProcessContextProvider> context_provider, - gpu::SurfaceHandle surface_handle) - : GLOutputSurface(context_provider, surface_handle) {} - -GLOutputSurfaceAndroid::~GLOutputSurfaceAndroid() = default; - -void GLOutputSurfaceAndroid::HandlePartialSwap( - const gfx::Rect& sub_buffer_rect, - uint32_t flags, - gpu::ContextSupport::SwapCompletedCallback swap_callback, - gpu::ContextSupport::PresentationCallback presentation_callback) { - DCHECK(sub_buffer_rect.IsEmpty()); - context_provider_->ContextSupport()->CommitOverlayPlanes( - flags, std::move(swap_callback), std::move(presentation_callback)); -} - -} // namespace viz diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_android.h b/chromium/components/viz/service/display_embedder/gl_output_surface_android.h deleted file mode 100644 index 8ad4a4326a9..00000000000 --- a/chromium/components/viz/service/display_embedder/gl_output_surface_android.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2018 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_GL_OUTPUT_SURFACE_ANDROID_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_ANDROID_H_ - -#include "components/viz/service/display_embedder/gl_output_surface.h" - -namespace viz { -class GLOutputSurfaceAndroid : public GLOutputSurface { - public: - GLOutputSurfaceAndroid( - scoped_refptr<VizProcessContextProvider> context_provider, - gpu::SurfaceHandle surface_handle); - - GLOutputSurfaceAndroid(const GLOutputSurfaceAndroid&) = delete; - GLOutputSurfaceAndroid& operator=(const GLOutputSurfaceAndroid&) = delete; - - ~GLOutputSurfaceAndroid() override; - - // GLOutputSurface implementation: - void HandlePartialSwap( - const gfx::Rect& sub_buffer_rect, - uint32_t flags, - gpu::ContextSupport::SwapCompletedCallback swap_callback, - gpu::ContextSupport::PresentationCallback presentation_callback) override; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_ANDROID_H_ 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 deleted file mode 100644 index 6641808ac3e..00000000000 --- a/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright 2016 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/gl_output_surface_buffer_queue.h" - -#include <utility> - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/logging.h" -#include "build/build_config.h" -#include "build/chromeos_buildflags.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" -#include "components/viz/service/display/output_surface_client.h" -#include "components/viz/service/display/output_surface_frame.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "gpu/command_buffer/client/context_support.h" -#include "gpu/command_buffer/client/gles2_interface.h" -#include "gpu/command_buffer/common/gpu_memory_buffer_support.h" -#include "gpu/command_buffer/common/sync_token.h" -#include "ui/gl/buffer_format_utils.h" -#include "ui/gl/gl_enums.h" -#include "ui/gl/gl_fence.h" - -namespace viz { - -GLOutputSurfaceBufferQueue::GLOutputSurfaceBufferQueue( - scoped_refptr<VizProcessContextProvider> context_provider, - gpu::SurfaceHandle surface_handle, - std::unique_ptr<BufferQueue> buffer_queue) - : GLOutputSurface(context_provider, surface_handle), - buffer_queue_(std::move(buffer_queue)) { - capabilities_.only_invalidates_damage_rect = false; - capabilities_.uses_default_gl_framebuffer = false; - capabilities_.output_surface_origin = gfx::SurfaceOrigin::kTopLeft; - // Set |max_pending_swaps| to 2 for buffer_queue, which aligns scheduling - // more closely with the previous surfaced behavior. - // With a surface, swap buffer ack used to return early, before actually - // presenting the back buffer, enabling the browser compositor to run ahead. - // BufferQueue implementation acks at the time of actual buffer swap, which - // shifts the start of the new frame forward relative to the old - // implementation. - capabilities_.pending_swap_params.max_pending_swaps = 2; - // GetCurrentFramebufferDamage will return an upper bound of the part of the - // buffer that needs to be recomposited. -#if BUILDFLAG(IS_APPLE) - 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_| - // allocates at most one additional buffer. - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - if (command_line->HasSwitch(switches::kDoubleBufferCompositing)) { - capabilities_.pending_swap_params.max_pending_swaps = 1; - buffer_queue_->SetMaxBuffers(2); - } - - // It is safe to pass a raw pointer to *this because |buffer_queue_| is fully - // owned and it doesn't use the SyncTokenProvider after it's destroyed. - DCHECK(buffer_queue_); - buffer_queue_->SetSyncTokenProvider(this); - context_provider_->ContextGL()->GenFramebuffers(1, &fbo_); -} - -GLOutputSurfaceBufferQueue::~GLOutputSurfaceBufferQueue() { - auto* gl = context_provider_->ContextGL(); - DCHECK_NE(0u, fbo_); - gl->DeleteFramebuffers(1, &fbo_); - if (stencil_buffer_) - gl->DeleteRenderbuffers(1, &stencil_buffer_); - for (const auto& buffer_texture : buffer_queue_textures_) - gl->DeleteTextures(1u, &buffer_texture.second); - buffer_queue_textures_.clear(); - current_texture_ = 0u; - last_bound_texture_ = 0u; - last_bound_mailbox_.SetZero(); - - // Freeing the BufferQueue here ensures that *this is fully alive in case the - // BufferQueue needs the SyncTokenProvider functionality. - buffer_queue_.reset(); - fbo_ = 0u; - stencil_buffer_ = 0u; -} - -void GLOutputSurfaceBufferQueue::BindFramebuffer() { - auto* gl = context_provider_->ContextGL(); - gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_); - - // If we have a |current_texture_|, it means we haven't swapped the buffer, so - // we're just wanting to rebind the GL framebuffer. - if (current_texture_) - return; - - DCHECK(buffer_queue_); - gpu::SyncToken creation_sync_token; - gfx::GpuFenceHandle release_fence; - const gpu::Mailbox current_buffer = - buffer_queue_->GetCurrentBuffer(&creation_sync_token, &release_fence); - if (current_buffer.IsZero()) - return; - gl->WaitSyncTokenCHROMIUM(creation_sync_token.GetConstData()); - if (!release_fence.is_null()) { - auto fence = gfx::GpuFence(std::move(release_fence)); - if (gl::GLFence::IsGpuFenceSupported()) { - auto id = gl->CreateClientGpuFenceCHROMIUM(fence.AsClientGpuFence()); - gl->WaitGpuFenceCHROMIUM(id); - gl->DestroyGpuFenceCHROMIUM(id); - } else { - fence.Wait(); - } - } - unsigned& buffer_texture = buffer_queue_textures_[current_buffer]; - if (!buffer_texture) { - buffer_texture = - gl->CreateAndTexStorage2DSharedImageCHROMIUM(current_buffer.name); - } - current_texture_ = buffer_texture; - gl->BeginSharedImageAccessDirectCHROMIUM( - current_texture_, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM); - gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - texture_target_, current_texture_, 0); - last_bound_texture_ = current_texture_; - last_bound_mailbox_ = current_buffer; - -#if DCHECK_IS_ON() && BUILDFLAG(IS_CHROMEOS_ASH) - const GLenum result = gl->CheckFramebufferStatus(GL_FRAMEBUFFER); - if (result != GL_FRAMEBUFFER_COMPLETE) - DLOG(ERROR) << " Incomplete fb: " << gl::GLEnums::GetStringError(result); -#endif - - // Reshape() must be called to go from using a stencil buffer to not using it. - DCHECK(use_stencil_ || !stencil_buffer_); - if (use_stencil_ && !stencil_buffer_) { - gl->GenRenderbuffers(1, &stencil_buffer_); - CHECK_NE(stencil_buffer_, 0u); - gl->BindRenderbuffer(GL_RENDERBUFFER, stencil_buffer_); - gl->RenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, - reshape_size_.width(), reshape_size_.height()); - gl->BindRenderbuffer(GL_RENDERBUFFER, 0); - gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, stencil_buffer_); - } -} - -// We call this on every frame that a value changes, but changing the size once -// we've allocated backing NativePixmapBufferQueue instances will cause a DCHECK -// because Chrome never Reshape(s) after the first one from (0,0). NB: this -// implies that screen size changes need to be plumbed differently. In -// particular, we must create the native window in the size that the hardware -// reports. -void GLOutputSurfaceBufferQueue::Reshape(const gfx::Size& size, - float device_scale_factor, - const gfx::ColorSpace& color_space, - gfx::BufferFormat format, - bool use_stencil) { - reshape_size_ = size; - use_stencil_ = use_stencil; - GLOutputSurface::Reshape(size, device_scale_factor, color_space, format, - use_stencil); - DCHECK(buffer_queue_); - const bool may_have_freed_buffers = - buffer_queue_->Reshape(size, color_space, format); - if (may_have_freed_buffers || (stencil_buffer_ && !use_stencil)) { - auto* gl = context_provider_->ContextGL(); - gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_); - if (stencil_buffer_) { - gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, 0); - gl->DeleteRenderbuffers(1, &stencil_buffer_); - stencil_buffer_ = 0u; - } - - // Note that |texture_target_| is initially set to 0, and so if it has not - // been set to a valid value, then no buffers have been allocated. - if (texture_target_ && may_have_freed_buffers) { - gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - texture_target_, 0, 0); - for (const auto& buffer_texture : buffer_queue_textures_) - gl->DeleteTextures(1u, &buffer_texture.second); - buffer_queue_textures_.clear(); - current_texture_ = 0u; - last_bound_texture_ = 0u; - last_bound_mailbox_.SetZero(); - } - } - - texture_target_ = - gpu::GetBufferTextureTarget(gfx::BufferUsage::SCANOUT, format, - context_provider_->ContextCapabilities()); -} - -void GLOutputSurfaceBufferQueue::SwapBuffers(OutputSurfaceFrame frame) { - DCHECK(buffer_queue_); - - // TODO(rjkroege): What if swap happens again before DidReceiveSwapBuffersAck - // then it would see the wrong size? - DCHECK(reshape_size_ == frame.size); - swap_size_ = reshape_size_; - - gfx::Rect damage_rect = - frame.sub_buffer_rect ? *frame.sub_buffer_rect : gfx::Rect(swap_size_); - - // If the client is currently drawing, we first end access to the - // corresponding shared image. Then, we can swap the buffers. That way, we - // know that whatever GL commands GLOutputSurface::SwapBuffers() emits can - // access the shared image. - auto* gl = context_provider_->ContextGL(); - if (current_texture_) { - gl->EndSharedImageAccessDirectCHROMIUM(current_texture_); - gl->BindFramebuffer(GL_FRAMEBUFFER, 0u); - current_texture_ = 0u; - } - buffer_queue_->SwapBuffers(damage_rect); - GLOutputSurface::SwapBuffers(std::move(frame)); -} - -gfx::Rect GLOutputSurfaceBufferQueue::GetCurrentFramebufferDamage() const { - return buffer_queue_->CurrentBufferDamage(); -} - -uint32_t GLOutputSurfaceBufferQueue::GetFramebufferCopyTextureFormat() { - return base::strict_cast<GLenum>( - gl::BufferFormatToGLInternalFormat(buffer_queue_->buffer_format())); -} - -bool GLOutputSurfaceBufferQueue::IsDisplayedAsOverlayPlane() const { - return true; -} - -unsigned GLOutputSurfaceBufferQueue::GetOverlayTextureId() const { - DCHECK(last_bound_texture_); - return last_bound_texture_; -} - -gpu::Mailbox GLOutputSurfaceBufferQueue::GetOverlayMailbox() const { - return last_bound_mailbox_; -} - -void GLOutputSurfaceBufferQueue::DidReceiveSwapBuffersAck( - const gfx::SwapResponse& response, - gfx::GpuFenceHandle release_fence) { - bool force_swap = false; - if (response.result == gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS) { - // Even through the swap failed, this is a fixable error so we can pretend - // it succeeded to the rest of the system. - buffer_queue_->FreeAllSurfaces(); - - // TODO(andrescj): centralize the logic that deletes the stencil buffer and - // the textures since we do this in multiple places. - auto* gl = context_provider_->ContextGL(); - gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_); - if (stencil_buffer_) { - gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, 0); - gl->DeleteRenderbuffers(1, &stencil_buffer_); - stencil_buffer_ = 0u; - } - - // Reshape() must have been called before we got here, so |texture_target_| - // should contain a valid value. - DCHECK(texture_target_); - gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - texture_target_, 0, 0); - for (const auto& buffer_texture : buffer_queue_textures_) - gl->DeleteTextures(1u, &buffer_texture.second); - buffer_queue_textures_.clear(); - current_texture_ = 0u; - last_bound_texture_ = 0u; - last_bound_mailbox_.SetZero(); - - force_swap = true; - } - - buffer_queue_->PageFlipComplete(release_fence.Clone()); - client()->DidReceiveSwapBuffersAck(response.timings, - std::move(release_fence)); - - if (force_swap) - client()->SetNeedsRedrawRect(gfx::Rect(swap_size_)); -} - -gpu::SyncToken GLOutputSurfaceBufferQueue::GenSyncToken() { - // This should only be called as long as the BufferQueue is alive. We cannot - // use |buffer_queue_| to detect this because in the dtor, |buffer_queue_| - // becomes nullptr before BufferQueue's dtor is called, so GenSyncToken() - // would be called after |buffer_queue_| is nullptr when in fact, the - // BufferQueue is still alive. Hence, we use |fbo_| to detect that the - // BufferQueue is still alive. - DCHECK(fbo_); - gpu::SyncToken sync_token; - context_provider_->ContextGL()->GenUnverifiedSyncTokenCHROMIUM( - sync_token.GetData()); - return sync_token; -} - -void GLOutputSurfaceBufferQueue::SetDisplayTransformHint( - gfx::OverlayTransform transform) { - display_transform_ = transform; - - if (context_provider_) - context_provider_->ContextSupport()->SetDisplayTransform(transform); -} - -gfx::OverlayTransform GLOutputSurfaceBufferQueue::GetDisplayTransform() { - return display_transform_; -} - -} // namespace viz diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue.h b/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue.h deleted file mode 100644 index 73bcccc0e45..00000000000 --- a/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue.h +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2016 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_GL_OUTPUT_SURFACE_BUFFER_QUEUE_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_BUFFER_QUEUE_H_ - -#include <stdint.h> - -#include <memory> - -#include "base/containers/flat_map.h" -#include "base/gtest_prod_util.h" -#include "base/memory/weak_ptr.h" -#include "components/viz/common/gpu/context_provider.h" -#include "components/viz/service/display/output_surface.h" -#include "components/viz/service/display_embedder/buffer_queue.h" -#include "components/viz/service/display_embedder/gl_output_surface.h" -#include "components/viz/service/display_embedder/viz_process_context_provider.h" -#include "components/viz/service/viz_service_export.h" -#include "gpu/command_buffer/common/mailbox.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/geometry/size.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/gfx/swap_result.h" -#include "ui/gl/gl_surface.h" - -namespace viz { - -// An OutputSurface implementation that directly draws and swap to a GL -// "buffer_queue" surface (aka one backed by a buffer managed explicitly). -class VIZ_SERVICE_EXPORT GLOutputSurfaceBufferQueue - : public GLOutputSurface, - public BufferQueue::SyncTokenProvider { - public: - GLOutputSurfaceBufferQueue( - scoped_refptr<VizProcessContextProvider> context_provider, - gpu::SurfaceHandle surface_handle, - std::unique_ptr<BufferQueue> buffer_queue); - - GLOutputSurfaceBufferQueue(const GLOutputSurfaceBufferQueue&) = delete; - GLOutputSurfaceBufferQueue& operator=(const GLOutputSurfaceBufferQueue&) = - delete; - - ~GLOutputSurfaceBufferQueue() override; - - // BufferQueue::SyncTokenProvider implementation. - gpu::SyncToken GenSyncToken() override; - - protected: - // OutputSurface implementation. - void SetDisplayTransformHint(gfx::OverlayTransform transform) override; - gfx::OverlayTransform GetDisplayTransform() override; - void Reshape(const gfx::Size& size, - float device_scale_factor, - const gfx::ColorSpace& color_space, - gfx::BufferFormat format, - bool use_stencil) override; - - private: - FRIEND_TEST_ALL_PREFIXES(GLOutputSurfaceBufferQueueTest, HandleSwapNAK); - - // OutputSurface implementation. - void BindFramebuffer() override; - void SwapBuffers(OutputSurfaceFrame frame) override; - gfx::Rect GetCurrentFramebufferDamage() const override; - uint32_t GetFramebufferCopyTextureFormat() override; - bool IsDisplayedAsOverlayPlane() const override; - unsigned GetOverlayTextureId() const override; - gpu::Mailbox GetOverlayMailbox() const override; - - // GLOutputSurface: - void DidReceiveSwapBuffersAck(const gfx::SwapResponse& response, - gfx::GpuFenceHandle release_fence) override; - - std::unique_ptr<BufferQueue> buffer_queue_; - - // |buffer_queue_textures_| caches the textures generated by consuming the - // SharedImage mailboxes from the |buffer_queue_| so that we don't have to - // generate a new texture every time a shared image is re-used. - base::flat_map<gpu::Mailbox, unsigned> buffer_queue_textures_; - - // |current_texture_| is the texture currently being drawn to. It's one of - // |buffer_queue_textures_| or 0 if the client is not currently drawing (i.e., - // we're not currently in between a BindFramebuffer()/SwapBuffers() pair). - // |last_bound_texture_| is the texture that was last bound to |fbo_|. It's - // also one of |buffer_queue_textures_| or 0 if no texture has been bound to - // |fbo_| or all the buffers in the buffer queue have been freed. - // |last_bound_mailbox_| is the mailbox corresponding to - // |last_bound_texture_|. - // - // TODO(andrescj): use an RAII pattern to scope access to |current_texture_| - // because it requires Begin/EndSharedImageAccessDirectCHROMIUM(). - unsigned current_texture_ = 0u; - unsigned last_bound_texture_ = 0u; - gpu::Mailbox last_bound_mailbox_; - unsigned texture_target_ = 0u; - - unsigned fbo_ = 0u; - - bool use_stencil_ = false; - unsigned stencil_buffer_ = 0u; - - gfx::OverlayTransform display_transform_ = gfx::OVERLAY_TRANSFORM_NONE; - gfx::Size reshape_size_; - gfx::Size swap_size_; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_BUFFER_QUEUE_H_ diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue_unittest.cc b/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue_unittest.cc deleted file mode 100644 index 3876eadb8fd..00000000000 --- a/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue_unittest.cc +++ /dev/null @@ -1,359 +0,0 @@ -// Copyright 2019 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/gl_output_surface_buffer_queue.h" - -#include <utility> -#include <vector> - -#include "base/memory/raw_ptr.h" -#include "components/viz/service/display/output_surface_client.h" -#include "components/viz/service/display/output_surface_frame.h" -#include "components/viz/service/display_embedder/buffer_queue.h" -#include "components/viz/test/test_context_provider.h" -#include "components/viz/test/test_context_support.h" -#include "components/viz/test/test_gles2_interface.h" -#include "gpu/command_buffer/common/command_buffer_id.h" -#include "gpu/command_buffer/common/constants.h" -#include "gpu/command_buffer/common/mailbox.h" -#include "gpu/command_buffer/common/sync_token.h" -#include "gpu/ipc/common/surface_handle.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/buffer_types.h" -#include "ui/gfx/swap_result.h" - -using testing::_; -using testing::DoAll; -using testing::Eq; -using testing::InSequence; -using testing::Mock; -using testing::Ne; -using testing::NotNull; -using testing::Pointee; -using testing::Return; -using testing::SetArgPointee; -using testing::StrictMock; - -namespace viz { -namespace { - -class TestVizProcessContextProvider : public VizProcessContextProvider { - public: - TestVizProcessContextProvider(std::unique_ptr<TestContextSupport> support, - std::unique_ptr<TestGLES2Interface> gl) - : support_(std::move(support)), context_gl_(std::move(gl)) {} - TestVizProcessContextProvider(const TestVizProcessContextProvider&) = delete; - TestVizProcessContextProvider& operator=( - const TestVizProcessContextProvider&) = delete; - - // ContextProvider implementation. - gpu::gles2::GLES2Interface* ContextGL() override { return context_gl_.get(); } - gpu::ContextSupport* ContextSupport() override { return support_.get(); } - const gpu::Capabilities& ContextCapabilities() const override { - return gpu_capabilities_; - } - - const gpu::GpuFeatureInfo& GetGpuFeatureInfo() const override { - return gpu_feature_info_; - } - - void SetUpdateVSyncParametersCallback( - UpdateVSyncParametersCallback callback) override {} - void SetGpuVSyncCallback(GpuVSyncCallback callback) override {} - void SetGpuVSyncEnabled(bool enabled) override {} - bool UseRGB565PixelFormat() const override { return false; } - uint32_t GetCopyTextureInternalFormat() override { return 0u; } - base::ScopedClosureRunner GetCacheBackBufferCb() override { - return base::ScopedClosureRunner(base::DoNothing()); - } - - protected: - ~TestVizProcessContextProvider() override = default; - - private: - std::unique_ptr<TestContextSupport> support_; - std::unique_ptr<TestGLES2Interface> context_gl_; - gpu::Capabilities gpu_capabilities_; - gpu::GpuFeatureInfo gpu_feature_info_; -}; - -class MockGLES2Interface : public TestGLES2Interface { - public: - MockGLES2Interface() = default; - ~MockGLES2Interface() override = default; - - MOCK_METHOD2(DeleteTextures, void(GLsizei, const GLuint*)); - MOCK_METHOD2(BindFramebuffer, void(GLenum, GLuint)); - MOCK_METHOD2(GenRenderbuffers, void(GLsizei, GLuint*)); - MOCK_METHOD2(BindRenderbuffer, void(GLenum, GLuint)); - MOCK_METHOD2(DeleteRenderbuffers, void(GLsizei n, const GLuint*)); - MOCK_METHOD1(CreateAndTexStorage2DSharedImageCHROMIUM, GLuint(const GLbyte*)); - MOCK_METHOD1(WaitSyncTokenCHROMIUM, void(const GLbyte*)); - MOCK_METHOD2(BeginSharedImageAccessDirectCHROMIUM, void(GLuint, GLenum)); - MOCK_METHOD1(EndSharedImageAccessDirectCHROMIUM, void(GLuint)); -}; - -class MockBufferQueue : public BufferQueue { - public: - MockBufferQueue() : BufferQueue(/*sii_=*/nullptr, gpu::kNullSurfaceHandle) {} - ~MockBufferQueue() override = default; - - MOCK_METHOD2(GetCurrentBuffer, - gpu::Mailbox(gpu::SyncToken*, gfx::GpuFenceHandle*)); - MOCK_CONST_METHOD0(CurrentBufferDamage, gfx::Rect()); - MOCK_METHOD1(SwapBuffers, void(const gfx::Rect&)); - MOCK_METHOD1(PageFlipComplete, void(gfx::GpuFenceHandle)); - MOCK_METHOD0(FreeAllSurfaces, void()); - MOCK_METHOD3(Reshape, - bool(const gfx::Size&, - const gfx::ColorSpace&, - gfx::BufferFormat)); - - MOCK_METHOD0(DoSetSyncTokenProvider, void()); - void SetSyncTokenProvider(SyncTokenProvider* sync_token_provider) override { - BufferQueue::SetSyncTokenProvider(sync_token_provider); - DoSetSyncTokenProvider(); - } -}; - -} // namespace - -class GLOutputSurfaceBufferQueueTest : public ::testing::Test, - public OutputSurfaceClient { - public: - GLOutputSurfaceBufferQueueTest() = default; - ~GLOutputSurfaceBufferQueueTest() override = default; - - void SetUp() override { - auto buffer_queue = std::make_unique<StrictMock<MockBufferQueue>>(); - buffer_queue_ = buffer_queue.get(); - - auto gles2_interface = std::make_unique<StrictMock<MockGLES2Interface>>(); - gles2_interface_ = gles2_interface.get(); - - EXPECT_CALL(*buffer_queue_, DoSetSyncTokenProvider()); - surface_ = std::make_unique<GLOutputSurfaceBufferQueue>( - base::MakeRefCounted<TestVizProcessContextProvider>( - std::make_unique<TestContextSupport>(), std::move(gles2_interface)), - gpu::kNullSurfaceHandle, std::move(buffer_queue)); - surface_->BindToClient(this); - - Mock::VerifyAndClearExpectations(gles2_interface_); - Mock::VerifyAndClearExpectations(buffer_queue_); - } - - // OutputSurfaceClient implementation. - void DidReceiveSwapBuffersAck(const gfx::SwapTimings& timings, - gfx::GpuFenceHandle release_fence) override {} - void SetNeedsRedrawRect(const gfx::Rect& damage_rect) override {} - void DidReceiveTextureInUseResponses( - const gpu::TextureInUseResponses& responses) override {} - void DidReceiveCALayerParams( - const gfx::CALayerParams& ca_layer_params) override {} - void DidSwapWithSize(const gfx::Size& pixel_size) override {} - void DidReceivePresentationFeedback( - const gfx::PresentationFeedback& feedback) override {} - void DidReceiveReleasedOverlays( - const std::vector<gpu::Mailbox>& released_overlays) override {} - - protected: - std::unique_ptr<OutputSurface> surface_; - raw_ptr<StrictMock<MockGLES2Interface>> gles2_interface_; - raw_ptr<StrictMock<MockBufferQueue>> buffer_queue_; -}; - -MATCHER_P(SyncTokenEqualTo, expected_sync_token, "") { - auto* actual_sync_token = reinterpret_cast<const gpu::SyncToken*>(arg); - return expected_sync_token == *actual_sync_token; -} - -MATCHER_P(SharedImageEqualTo, expected_shared_image, "") { - gpu::Mailbox actual_shared_image; - actual_shared_image.SetName(arg); - return expected_shared_image == actual_shared_image; -} - -// Make sure that the surface uses the buffer queue and the GL context correctly -// when we request it to bind the framebuffer twice and then swap the buffer. -TEST_F(GLOutputSurfaceBufferQueueTest, BindFramebufferAndSwap) { - const gpu::SyncToken fake_sync_token( - gpu::CommandBufferNamespace::GPU_IO, - gpu::CommandBufferId::FromUnsafeValue(567u), - /*release_count=*/5u); - const gpu::Mailbox fake_shared_image = gpu::Mailbox::GenerateForSharedImage(); - constexpr GLuint kFakeTexture = 123u; - { - InSequence dummy_sequence; - - // The first call to |surface_|->BindFramebuffer() should result in binding - // the GL framebuffer, requesting a new buffer, waiting on the corresponding - // sync token, and beginning read/write access to the shared image. - EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Ne(0u))); - EXPECT_CALL(*buffer_queue_, GetCurrentBuffer(NotNull(), NotNull())) - .WillOnce(DoAll(SetArgPointee<0>(fake_sync_token), - Return(fake_shared_image))); - EXPECT_CALL(*gles2_interface_, - WaitSyncTokenCHROMIUM(SyncTokenEqualTo(fake_sync_token))); - EXPECT_CALL(*gles2_interface_, CreateAndTexStorage2DSharedImageCHROMIUM( - SharedImageEqualTo(fake_shared_image))) - .WillOnce(Return(kFakeTexture)); - EXPECT_CALL( - *gles2_interface_, - BeginSharedImageAccessDirectCHROMIUM( - kFakeTexture, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM)); - - // The second call to |surface_|->BindFramebuffer() should only result in - // binding the GL framebuffer because the underlying buffer hasn't been - // swapped. - EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Ne(0u))); - - // Calling |surface_|->SwapBuffers() should result in ending read/write - // access to the underlying buffer and unbinding the GL framebuffer. - EXPECT_CALL(*gles2_interface_, - EndSharedImageAccessDirectCHROMIUM(kFakeTexture)); - EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Eq(0u))); - EXPECT_CALL(*buffer_queue_, SwapBuffers(_)); - - // Destroying |surface_| should result in the deletion of the texture - // obtained from consuming the shared image. - EXPECT_CALL(*gles2_interface_, - DeleteTextures(1u, Pointee(Eq(kFakeTexture)))); - } - - surface_->BindFramebuffer(); - surface_->BindFramebuffer(); - surface_->SwapBuffers(OutputSurfaceFrame()); -} - -TEST_F(GLOutputSurfaceBufferQueueTest, EmptySwap) { - const gpu::SyncToken fake_sync_token( - gpu::CommandBufferNamespace::GPU_IO, - gpu::CommandBufferId::FromUnsafeValue(567u), - /*release_count=*/5u); - const gpu::Mailbox fake_shared_image = gpu::Mailbox::GenerateForSharedImage(); - constexpr GLuint kFakeTexture = 123u; - { - InSequence dummy_sequence; - - // The call to |surface_|->BindFramebuffer() should result in binding the GL - // framebuffer, requesting a new buffer, waiting on the corresponding sync - // token, and beginning read/write access to the shared image. - EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Ne(0u))); - EXPECT_CALL(*buffer_queue_, GetCurrentBuffer(NotNull(), NotNull())) - .WillOnce(DoAll(SetArgPointee<0>(fake_sync_token), - Return(fake_shared_image))); - EXPECT_CALL(*gles2_interface_, - WaitSyncTokenCHROMIUM(SyncTokenEqualTo(fake_sync_token))); - EXPECT_CALL(*gles2_interface_, CreateAndTexStorage2DSharedImageCHROMIUM( - SharedImageEqualTo(fake_shared_image))) - .WillOnce(Return(kFakeTexture)); - EXPECT_CALL( - *gles2_interface_, - BeginSharedImageAccessDirectCHROMIUM( - kFakeTexture, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM)); - - // The first call to |surface_|->SwapBuffers() should result in ending - // read/write access to the underlying buffer and unbinding the GL - // framebuffer. - EXPECT_CALL(*gles2_interface_, - EndSharedImageAccessDirectCHROMIUM(kFakeTexture)); - EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Eq(0u))); - EXPECT_CALL(*buffer_queue_, SwapBuffers(_)); - - // The two empty swaps should only result in telling the buffer queue to - // swap the buffers. - EXPECT_CALL(*buffer_queue_, SwapBuffers(_)).Times(2); - - // Destroying |surface_| should result in the deletion of the texture - // obtained from consuming the shared image. - EXPECT_CALL(*gles2_interface_, - DeleteTextures(1u, Pointee(Eq(kFakeTexture)))); - } - surface_->BindFramebuffer(); - unsigned texture_for_first_buffer = surface_->GetOverlayTextureId(); - EXPECT_GT(texture_for_first_buffer, 0u); - surface_->SwapBuffers(OutputSurfaceFrame()); - - // Now do two empty swaps (which don't call BindFramebuffer()). - EXPECT_EQ(texture_for_first_buffer, surface_->GetOverlayTextureId()); - surface_->SwapBuffers(OutputSurfaceFrame()); - EXPECT_EQ(texture_for_first_buffer, surface_->GetOverlayTextureId()); - surface_->SwapBuffers(OutputSurfaceFrame()); -} - -// Make sure that receiving a swap NAK doesn't cause us to leak resources. -TEST_F(GLOutputSurfaceBufferQueueTest, HandleSwapNAK) { - const gpu::SyncToken fake_sync_token( - gpu::CommandBufferNamespace::GPU_IO, - gpu::CommandBufferId::FromUnsafeValue(567u), - /*release_count=*/5u); - constexpr gfx::Size kBufferSize(100, 100); - const gpu::Mailbox fake_shared_image = gpu::Mailbox::GenerateForSharedImage(); - constexpr GLuint kFakeTexture = 123u; - constexpr GLuint kFakeStencilBuffer = 456u; - { - InSequence dummy_sequence; - - EXPECT_CALL(*buffer_queue_, Reshape(_, _, _)).WillOnce(Return(true)); - EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Ne(0u))); - - // The call to |surface_|->BindFramebuffer() should result in binding the GL - // framebuffer, requesting a new buffer, waiting on the corresponding sync - // token, beginning read/write access to the shared image, and creating a - // stencil buffer. - EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Ne(0u))); - EXPECT_CALL(*buffer_queue_, GetCurrentBuffer(NotNull(), NotNull())) - .WillOnce(DoAll(SetArgPointee<0>(fake_sync_token), - Return(fake_shared_image))); - - EXPECT_CALL(*gles2_interface_, - WaitSyncTokenCHROMIUM(SyncTokenEqualTo(fake_sync_token))); - EXPECT_CALL(*gles2_interface_, CreateAndTexStorage2DSharedImageCHROMIUM( - SharedImageEqualTo(fake_shared_image))) - .WillOnce(Return(kFakeTexture)); - EXPECT_CALL( - *gles2_interface_, - BeginSharedImageAccessDirectCHROMIUM( - kFakeTexture, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM)); - EXPECT_CALL(*gles2_interface_, GenRenderbuffers(1u, NotNull())) - .WillOnce(SetArgPointee<1>(kFakeStencilBuffer)); - EXPECT_CALL(*gles2_interface_, - BindRenderbuffer(GL_RENDERBUFFER, kFakeStencilBuffer)); - EXPECT_CALL(*gles2_interface_, BindRenderbuffer(GL_RENDERBUFFER, 0u)); - - // Calling |surface_|->SwapBuffers() should result in ending read/write - // access to the underlying buffer and unbinding the GL framebuffer. - EXPECT_CALL(*gles2_interface_, - EndSharedImageAccessDirectCHROMIUM(kFakeTexture)); - EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Eq(0u))); - EXPECT_CALL(*buffer_queue_, SwapBuffers(_)); - - // Receiving a swap NAK should result in the deletion of the texture - // obtained from consuming the shared image. It should also result in the - // deletion of the stencil buffer. - EXPECT_CALL(*buffer_queue_, FreeAllSurfaces()); - EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Ne(0u))); - EXPECT_CALL(*gles2_interface_, - DeleteRenderbuffers(1u, Pointee(Eq(kFakeStencilBuffer)))); - EXPECT_CALL(*gles2_interface_, - DeleteTextures(1u, Pointee(Eq(kFakeTexture)))); - EXPECT_CALL(*buffer_queue_, PageFlipComplete(_)); - } - - surface_->Reshape(kBufferSize, /*device_scale_factor=*/1.0, - gfx::ColorSpace::CreateSRGB(), gfx::BufferFormat::BGRA_8888, - /*use_stencil=*/true); - surface_->BindFramebuffer(); - OutputSurfaceFrame frame; - frame.size = kBufferSize; - surface_->SwapBuffers(std::move(frame)); - gfx::SwapResponse swap_response{}; - swap_response.result = gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS; - (static_cast<GLOutputSurfaceBufferQueue*>(surface_.get())) - ->DidReceiveSwapBuffersAck(swap_response, - /*release_fence=*/gfx::GpuFenceHandle()); -} - -} // namespace viz diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_chromeos.cc b/chromium/components/viz/service/display_embedder/gl_output_surface_chromeos.cc deleted file mode 100644 index d3cc8558658..00000000000 --- a/chromium/components/viz/service/display_embedder/gl_output_surface_chromeos.cc +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019 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/gl_output_surface_chromeos.h" - -namespace viz { - -GLOutputSurfaceChromeOS::GLOutputSurfaceChromeOS( - scoped_refptr<VizProcessContextProvider> context_provider, - gpu::SurfaceHandle surface_handle) - : GLOutputSurface(context_provider, surface_handle) {} - -GLOutputSurfaceChromeOS::~GLOutputSurfaceChromeOS() = default; - -void GLOutputSurfaceChromeOS::SetDisplayTransformHint( - gfx::OverlayTransform transform) { - display_transform_ = transform; -} - -gfx::OverlayTransform GLOutputSurfaceChromeOS::GetDisplayTransform() { - return display_transform_; -} - -} // namespace viz diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_chromeos.h b/chromium/components/viz/service/display_embedder/gl_output_surface_chromeos.h deleted file mode 100644 index 63ad613a96d..00000000000 --- a/chromium/components/viz/service/display_embedder/gl_output_surface_chromeos.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019 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_GL_OUTPUT_SURFACE_CHROMEOS_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_CHROMEOS_H_ - -#include "components/viz/service/display_embedder/gl_output_surface.h" - -namespace viz { - -class GLOutputSurfaceChromeOS : public GLOutputSurface { - public: - GLOutputSurfaceChromeOS( - scoped_refptr<VizProcessContextProvider> context_provider, - gpu::SurfaceHandle surface_handle); - - GLOutputSurfaceChromeOS(const GLOutputSurfaceChromeOS&) = delete; - GLOutputSurfaceChromeOS& operator=(const GLOutputSurfaceChromeOS&) = delete; - - ~GLOutputSurfaceChromeOS() override; - - // GLOutputSurface: - void SetDisplayTransformHint(gfx::OverlayTransform transform) override; - gfx::OverlayTransform GetDisplayTransform() override; - - private: - gfx::OverlayTransform display_transform_ = gfx::OVERLAY_TRANSFORM_NONE; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_CHROMEOS_H_ diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_offscreen.cc b/chromium/components/viz/service/display_embedder/gl_output_surface_offscreen.cc deleted file mode 100644 index 9e7190ad7b0..00000000000 --- a/chromium/components/viz/service/display_embedder/gl_output_surface_offscreen.cc +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2018 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/gl_output_surface_offscreen.h" - -#include <stdint.h> - -#include <algorithm> -#include <utility> -#include <vector> - -#include "base/bind.h" -#include "components/viz/common/resources/resource_format_utils.h" -#include "components/viz/service/display/output_surface_client.h" -#include "components/viz/service/display/output_surface_frame.h" -#include "gpu/command_buffer/client/context_support.h" -#include "gpu/command_buffer/client/gles2_interface.h" -#include "gpu/command_buffer/client/shared_image_interface.h" -#include "gpu/command_buffer/common/shared_image_usage.h" -#include "third_party/khronos/GLES2/gl2.h" -#include "third_party/khronos/GLES2/gl2ext.h" -#include "ui/gfx/swap_result.h" -#include "ui/gl/gl_utils.h" - -namespace viz { -namespace { - -constexpr ResourceFormat kFboTextureFormat = RGBA_8888; - -} // namespace - -GLOutputSurfaceOffscreen::GLOutputSurfaceOffscreen( - scoped_refptr<VizProcessContextProvider> context_provider) - : GLOutputSurface(context_provider, gpu::kNullSurfaceHandle) {} - -GLOutputSurfaceOffscreen::~GLOutputSurfaceOffscreen() { - DiscardBackbuffer(); -} - -void GLOutputSurfaceOffscreen::EnsureBackbuffer() { - if (size_.IsEmpty()) - return; - - if (!texture_id_) { - gpu::SharedImageInterface* sii = context_provider_->SharedImageInterface(); - gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); - - const int max_texture_size = - context_provider_->ContextCapabilities().max_texture_size; - gfx::Size texture_size(std::min(size_.width(), max_texture_size), - std::min(size_.height(), max_texture_size)); - - const uint32_t flags = gpu::SHARED_IMAGE_USAGE_GLES2 | - gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT | - gpu::SHARED_IMAGE_USAGE_DISPLAY; - - mailbox_ = sii->CreateSharedImage( - kFboTextureFormat, texture_size, color_space_, kTopLeft_GrSurfaceOrigin, - kPremul_SkAlphaType, flags, gpu::kNullSurfaceHandle); - - // Ensure mailbox is valid before using it. - gl->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData()); - - texture_id_ = gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox_.name); - - gl->GenFramebuffers(1, &fbo_); - gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_); - gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, texture_id_, 0); - } -} - -void GLOutputSurfaceOffscreen::DiscardBackbuffer() { - if (fbo_) { - gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); - gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_); - gl->DeleteFramebuffers(1, &fbo_); - fbo_ = 0; - } - - if (texture_id_) { - gpu::SharedImageInterface* sii = context_provider_->SharedImageInterface(); - sii->DestroySharedImage(gpu::SyncToken(), mailbox_); - mailbox_.SetZero(); - texture_id_ = 0; - } -} - -void GLOutputSurfaceOffscreen::BindFramebuffer() { - if (!texture_id_) { - EnsureBackbuffer(); - } else { - gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); - gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_); - } -} - -void GLOutputSurfaceOffscreen::Reshape(const gfx::Size& size, - float scale_factor, - const gfx::ColorSpace& color_space, - gfx::BufferFormat format, - bool stencil) { - size_ = size; - color_space_ = color_space; - DiscardBackbuffer(); - EnsureBackbuffer(); -} - -void GLOutputSurfaceOffscreen::SwapBuffers(OutputSurfaceFrame frame) { - DCHECK_EQ(frame.size, size_); - - gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); - - gpu::SyncToken sync_token; - gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); - context_provider_->ContextSupport()->SignalSyncToken( - sync_token, - base::BindOnce(&GLOutputSurfaceOffscreen::OnSwapBuffersComplete, - weak_ptr_factory_.GetWeakPtr(), - std::move(frame.latency_info))); -} - -void GLOutputSurfaceOffscreen::OnSwapBuffersComplete( - std::vector<ui::LatencyInfo> latency_info) { - latency_tracker()->OnGpuSwapBuffersCompleted(std::move(latency_info)); - // Swap timings are not available since for offscreen there is no Swap, just a - // SignalSyncToken. We use base::TimeTicks::Now() as an overestimate. - auto now = base::TimeTicks::Now(); - client()->DidReceiveSwapBuffersAck({.swap_start = now}, - /*release_fence=*/gfx::GpuFenceHandle()); - client()->DidReceivePresentationFeedback( - gfx::PresentationFeedback(now, base::Milliseconds(16), /*flags=*/0)); - - if (needs_swap_size_notifications()) - client()->DidSwapWithSize(size_); -} - -} // namespace viz diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_offscreen.h b/chromium/components/viz/service/display_embedder/gl_output_surface_offscreen.h deleted file mode 100644 index e769f1254ed..00000000000 --- a/chromium/components/viz/service/display_embedder/gl_output_surface_offscreen.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2018 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_GL_OUTPUT_SURFACE_OFFSCREEN_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_OFFSCREEN_H_ - -#include <memory> - -#include "components/viz/common/frame_sinks/begin_frame_source.h" -#include "components/viz/service/display_embedder/gl_output_surface.h" -#include "components/viz/service/display_embedder/viz_process_context_provider.h" -#include "components/viz/service/viz_service_export.h" -#include "gpu/command_buffer/common/mailbox.h" -#include "ui/gfx/color_space.h" - -namespace viz { - -// An OutputSurface implementation that draws and swaps to an offscreen GL -// framebuffer. -class VIZ_SERVICE_EXPORT GLOutputSurfaceOffscreen : public GLOutputSurface { - public: - explicit GLOutputSurfaceOffscreen( - scoped_refptr<VizProcessContextProvider> context_provider); - - GLOutputSurfaceOffscreen(const GLOutputSurfaceOffscreen&) = delete; - GLOutputSurfaceOffscreen& operator=(const GLOutputSurfaceOffscreen&) = delete; - - ~GLOutputSurfaceOffscreen() override; - - // OutputSurface implementation. - void EnsureBackbuffer() override; - void DiscardBackbuffer() override; - void BindFramebuffer() override; - void Reshape(const gfx::Size& size, - float scale_factor, - const gfx::ColorSpace& color_space, - gfx::BufferFormat format, - bool stencil) override; - void SwapBuffers(OutputSurfaceFrame frame) override; - - private: - void OnSwapBuffersComplete(std::vector<ui::LatencyInfo> latency_info); - - gpu::Mailbox mailbox_; - - uint32_t fbo_ = 0; - uint32_t texture_id_ = 0; - gfx::Size size_; - gfx::ColorSpace color_space_; - - base::WeakPtrFactory<GLOutputSurfaceOffscreen> weak_ptr_factory_{this}; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_OFFSCREEN_H_ diff --git a/chromium/components/viz/service/display_embedder/image_context_impl.h b/chromium/components/viz/service/display_embedder/image_context_impl.h index 00a51c95254..79e45adbc33 100644 --- a/chromium/components/viz/service/display_embedder/image_context_impl.h +++ b/chromium/components/viz/service/display_embedder/image_context_impl.h @@ -73,9 +73,9 @@ class ImageContextImpl final : public ExternalUseClient::ImageContext { SkPromiseImageTexture* promise_image_texture() const { return promise_image_texture_; } - GrBackendSurfaceMutableState* end_access_state() const { + std::unique_ptr<GrBackendSurfaceMutableState> TakeAccessEndState() const { return representation_scoped_read_access_ - ? representation_scoped_read_access_->end_state() + ? representation_scoped_read_access_->TakeEndState() : nullptr; } diff --git a/chromium/components/viz/service/display_embedder/output_presenter.cc b/chromium/components/viz/service/display_embedder/output_presenter.cc index 0209a8bac21..0c9aced7778 100644 --- a/chromium/components/viz/service/display_embedder/output_presenter.cc +++ b/chromium/components/viz/service/display_embedder/output_presenter.cc @@ -84,12 +84,13 @@ void OutputPresenter::Image::EndWriteSkia(bool force_flush) { // The Flush now takes place in finishPaintCurrentBuffer on the CPU side. // check if end_semaphores is not empty then flush here DCHECK(scoped_skia_write_access_); - if (!end_semaphores_.empty() || force_flush) { + auto end_state = scoped_skia_write_access_->TakeEndState(); + if (!end_semaphores_.empty() || end_state || force_flush) { GrFlushInfo flush_info = { .fNumSemaphores = end_semaphores_.size(), .fSignalSemaphores = end_semaphores_.data(), }; - scoped_skia_write_access_->surface()->flush(flush_info); + scoped_skia_write_access_->surface()->flush(flush_info, end_state.get()); auto* direct_context = scoped_skia_write_access_->surface() ->recordingContext() ->asDirectContext(); @@ -105,9 +106,8 @@ void OutputPresenter::Image::EndWriteSkia(bool force_flush) { void OutputPresenter::Image::PreGrContextSubmit() { DCHECK(scoped_skia_write_access_); - if (scoped_skia_write_access_->end_state()) { - scoped_skia_write_access_->surface()->flush( - {}, scoped_skia_write_access_->end_state()); + if (auto end_state = scoped_skia_write_access_->TakeEndState()) { + scoped_skia_write_access_->surface()->flush({}, end_state.get()); } } diff --git a/chromium/components/viz/service/display_embedder/output_presenter_gl.cc b/chromium/components/viz/service/display_embedder/output_presenter_gl.cc index 40790839cd5..5faa7dafe5d 100644 --- a/chromium/components/viz/service/display_embedder/output_presenter_gl.cc +++ b/chromium/components/viz/service/display_embedder/output_presenter_gl.cc @@ -224,7 +224,8 @@ std::unique_ptr<OutputPresenterGL> OutputPresenterGL::Create( return nullptr; // TODO(https://crbug.com/1012401): don't depend on GL. auto gl_surface = base::MakeRefCounted<gl::GLSurfaceEGLSurfaceControl>( - window, base::ThreadTaskRunnerHandle::Get()); + gl::GLSurfaceEGL::GetGLDisplayEGL(), window, + base::ThreadTaskRunnerHandle::Get()); if (!gl_surface->Initialize(gl::GLSurfaceFormat())) { LOG(ERROR) << "Failed to initialize GLSurfaceEGLSurfaceControl."; return nullptr; @@ -416,8 +417,14 @@ void OutputPresenterGL::ScheduleOneOverlay(const OverlayCandidate& overlay, ScopedOverlayAccess* access) { #if BUILDFLAG(IS_ANDROID) || defined(USE_OZONE) auto* gl_image = access ? access->gl_image() : nullptr; - if (gl_image || overlay.solid_color.has_value()) { - DCHECK(!overlay.gpu_fence_id); + if (gl_image || overlay.is_solid_color) { +#if DCHECK_IS_ON() + if (overlay.is_solid_color) { + LOG_IF(FATAL, !overlay.color.has_value()) + << "Solid color quads must have color set."; + } + CHECK(!overlay.gpu_fence_id); +#endif gl_surface_->ScheduleOverlayPlane( gl_image, access ? TakeGpuFence(access->TakeAcquireFences()) : nullptr, gfx::OverlayPlaneData( @@ -425,7 +432,7 @@ void OutputPresenterGL::ScheduleOneOverlay(const OverlayCandidate& overlay, overlay.uv_rect, !overlay.is_opaque, ToEnclosingRect(overlay.damage_rect), overlay.opacity, overlay.priority_hint, overlay.rounded_corners, overlay.color_space, - overlay.hdr_metadata, overlay.solid_color)); + overlay.hdr_metadata, overlay.color, overlay.is_solid_color)); } #else // BUILDFLAG(IS_ANDROID) || defined(USE_OZONE) NOTREACHED(); @@ -477,8 +484,14 @@ void OutputPresenterGL::ScheduleOverlays( // may have a protocol that asks Wayland compositor to create a solid color // buffer for a client. OverlayProcessorDelegated decides if a solid color // overlay is an overlay candidate and should be scheduled. - if (gl_image || overlay.solid_color.has_value()) { - DCHECK(!overlay.gpu_fence_id); + if (gl_image || overlay.is_solid_color) { +#if DCHECK_IS_ON() + if (overlay.is_solid_color) { + LOG_IF(FATAL, !overlay.color.has_value()) + << "Solid color quads must have color set."; + } + CHECK(!overlay.gpu_fence_id); +#endif gl_surface_->ScheduleOverlayPlane( gl_image, TakeGpuFenceForOverlay(dependency_, accesses[i], current_frame_fence), @@ -487,12 +500,10 @@ void OutputPresenterGL::ScheduleOverlays( overlay.uv_rect, !overlay.is_opaque, ToEnclosingRect(overlay.damage_rect), overlay.opacity, overlay.priority_hint, overlay.rounded_corners, - overlay.color_space, overlay.hdr_metadata, overlay.solid_color)); + overlay.color_space, overlay.hdr_metadata, overlay.color, + overlay.is_solid_color)); } #elif BUILDFLAG(IS_APPLE) - // For RenderPassDrawQuad the ddl is not nullptr, and the opacity is applied - // when the ddl is recorded, so the content already is with opacity applied. - float opacity = overlay.ddl ? 1.0 : overlay.shared_state->opacity; gl_surface_->ScheduleCALayer(ui::CARendererLayerParams( overlay.shared_state->is_clipped, gfx::ToEnclosingRect(overlay.shared_state->clip_rect), @@ -500,8 +511,8 @@ void OutputPresenterGL::ScheduleOverlays( 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, opacity, overlay.filter, - overlay.protected_video_type)); + overlay.background_color.toSkColor(), overlay.edge_aa_mask, + overlay.opacity, overlay.filter, overlay.protected_video_type)); #endif } #endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE) || defined(USE_OZONE) diff --git a/chromium/components/viz/service/display_embedder/output_surface_provider.h b/chromium/components/viz/service/display_embedder/output_surface_provider.h index 77d463e683d..2e9453c18f2 100644 --- a/chromium/components/viz/service/display_embedder/output_surface_provider.h +++ b/chromium/components/viz/service/display_embedder/output_surface_provider.h @@ -28,8 +28,7 @@ class OutputSurfaceProvider { // of this should feed into the CreateOutputSurface function. virtual std::unique_ptr<DisplayCompositorMemoryAndTaskController> CreateGpuDependency(bool gpu_compositing, - gpu::SurfaceHandle surface_handle, - const RendererSettings& renderer_settings) = 0; + gpu::SurfaceHandle surface_handle) = 0; // Creates a new OutputSurface for |surface_handle|. If creating an // OutputSurface fails this function will return null. 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 8a277c6337d..85857907957 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 @@ -20,37 +20,21 @@ #include "components/viz/common/display/renderer_settings.h" #include "components/viz/common/frame_sinks/begin_frame_source.h" #include "components/viz/service/display/display_compositor_memory_and_task_controller.h" -#include "components/viz/service/display_embedder/gl_output_surface.h" -#include "components/viz/service/display_embedder/gl_output_surface_buffer_queue.h" -#include "components/viz/service/display_embedder/gl_output_surface_offscreen.h" #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h" #include "components/viz/service/display_embedder/skia_output_surface_dependency_impl.h" #include "components/viz/service/display_embedder/skia_output_surface_impl.h" #include "components/viz/service/display_embedder/software_output_surface.h" -#include "components/viz/service/display_embedder/viz_process_context_provider.h" #include "components/viz/service/gl/gpu_service_impl.h" -#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h" #include "gpu/command_buffer/client/shared_memory_limits.h" -#include "gpu/command_buffer/service/image_factory.h" -#include "gpu/command_buffer/service/mailbox_manager_factory.h" #include "gpu/config/gpu_finch_features.h" -#include "gpu/ipc/command_buffer_task_executor.h" #include "gpu/ipc/common/surface_handle.h" #include "gpu/ipc/scheduler_sequence.h" -#include "gpu/ipc/service/gpu_channel_manager_delegate.h" -#include "gpu/ipc/service/image_transport_surface.h" #include "ui/base/ui_base_switches.h" -#include "ui/gl/gl_context.h" -#include "ui/gl/init/gl_factory.h" #if BUILDFLAG(IS_WIN) #include "components/viz/service/display_embedder/software_output_device_win.h" #endif -#if BUILDFLAG(IS_ANDROID) -#include "components/viz/service/display_embedder/gl_output_surface_android.h" -#endif - #if BUILDFLAG(IS_APPLE) #include "components/viz/service/display_embedder/software_output_device_mac.h" #include "ui/base/cocoa/remote_layer_api.h" @@ -66,7 +50,6 @@ #endif #if BUILDFLAG(IS_CHROMEOS_ASH) -#include "components/viz/service/display_embedder/gl_output_surface_chromeos.h" #include "components/viz/service/display_embedder/output_surface_unified.h" #endif @@ -74,26 +57,14 @@ namespace viz { OutputSurfaceProviderImpl::OutputSurfaceProviderImpl( GpuServiceImpl* gpu_service_impl, - gpu::CommandBufferTaskExecutor* task_executor, - gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate, - gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, - gpu::ImageFactory* image_factory, bool headless) : gpu_service_impl_(gpu_service_impl), - task_executor_(task_executor), - gpu_channel_manager_delegate_(gpu_channel_manager_delegate), - gpu_memory_buffer_manager_(gpu_memory_buffer_manager), - image_factory_(image_factory), task_runner_(base::ThreadTaskRunnerHandle::Get()), headless_(headless) {} OutputSurfaceProviderImpl::OutputSurfaceProviderImpl(bool headless) : OutputSurfaceProviderImpl( /*gpu_service_impl=*/nullptr, - /*task_executor=*/nullptr, - /*gpu_channel_manager_delegate=*/nullptr, - /*gpu_memory_buffer_manager=*/nullptr, - /*image_factory=*/nullptr, headless) {} OutputSurfaceProviderImpl::~OutputSurfaceProviderImpl() = default; @@ -101,23 +72,15 @@ OutputSurfaceProviderImpl::~OutputSurfaceProviderImpl() = default; std::unique_ptr<DisplayCompositorMemoryAndTaskController> OutputSurfaceProviderImpl::CreateGpuDependency( bool gpu_compositing, - gpu::SurfaceHandle surface_handle, - const RendererSettings& renderer_settings) { + gpu::SurfaceHandle surface_handle) { if (!gpu_compositing) return nullptr; - if (renderer_settings.use_skia_renderer) { - gpu::ScopedAllowScheduleGpuTask allow_schedule_gpu_task; - auto skia_deps = std::make_unique<SkiaOutputSurfaceDependencyImpl>( - gpu_service_impl_, surface_handle); - return std::make_unique<DisplayCompositorMemoryAndTaskController>( - std::move(skia_deps)); - } else { - DCHECK(task_executor_); - gpu::ScopedAllowScheduleGpuTask allow_schedule_gpu_task; - return std::make_unique<DisplayCompositorMemoryAndTaskController>( - task_executor_, image_factory_); - } + gpu::ScopedAllowScheduleGpuTask allow_schedule_gpu_task; + auto skia_deps = std::make_unique<SkiaOutputSurfaceDependencyImpl>( + gpu_service_impl_, surface_handle); + return std::make_unique<DisplayCompositorMemoryAndTaskController>( + std::move(skia_deps)); } std::unique_ptr<OutputSurface> OutputSurfaceProviderImpl::CreateOutputSurface( @@ -139,7 +102,7 @@ std::unique_ptr<OutputSurface> OutputSurfaceProviderImpl::CreateOutputSurface( if (!gpu_compositing) { output_surface = std::make_unique<SoftwareOutputSurface>( CreateSoftwareOutputDeviceForPlatform(surface_handle, display_client)); - } else if (renderer_settings.use_skia_renderer) { + } else { DCHECK(gpu_dependency); { gpu::ScopedAllowScheduleGpuTask allow_schedule_gpu_task; @@ -167,74 +130,6 @@ std::unique_ptr<OutputSurface> OutputSurfaceProviderImpl::CreateOutputSurface( #endif return nullptr; } - } else { - DCHECK(task_executor_); - DCHECK(gpu_dependency); - - scoped_refptr<VizProcessContextProvider> context_provider; - - // Retry creating and binding |context_provider| on transient failures. - gpu::ContextResult context_result = gpu::ContextResult::kTransientFailure; - while (context_result != gpu::ContextResult::kSuccess) { - // We are about to exit the GPU process so don't try to create a context. - // It will be recreated after the GPU process restarts. The same check - // also happens on the GPU thread before the context gets initialized - // there. If GPU process starts to exit after this check but before - // context initialization we'll encounter a transient error, loop and hit - // this check again. - if (gpu_channel_manager_delegate_->IsExiting()) - return nullptr; - - context_provider = base::MakeRefCounted<VizProcessContextProvider>( - task_executor_, surface_handle, gpu_memory_buffer_manager_.get(), - image_factory_, gpu_channel_manager_delegate_, gpu_dependency, - renderer_settings); - context_result = context_provider->BindToCurrentThread(); - -#if BUILDFLAG(IS_ANDROID) - display_client->OnContextCreationResult(context_result); -#endif - - if (IsFatalOrSurfaceFailure(context_result)) { -#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMECAST) - // GL compositing is expected to always work on Chrome OS so we should - // never encounter fatal context error. This could be an unrecoverable - // hardware error or a bug. - LOG(FATAL) << "Unexpected fatal context error"; -#elif !BUILDFLAG(IS_ANDROID) - gpu_service_impl_->DisableGpuCompositing(); -#endif - return nullptr; - } - } - - if (surface_handle == gpu::kNullSurfaceHandle) { - output_surface = std::make_unique<GLOutputSurfaceOffscreen>( - std::move(context_provider)); - } else if (context_provider->ContextCapabilities().surfaceless) { -#if defined(USE_OZONE) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID) - output_surface = std::make_unique<GLOutputSurfaceBufferQueue>( - std::move(context_provider), surface_handle, - std::make_unique<BufferQueue>( - context_provider->SharedImageInterface(), surface_handle)); -#else - NOTREACHED(); -#endif - } else { -#if BUILDFLAG(IS_WIN) - output_surface = std::make_unique<GLOutputSurface>( - std::move(context_provider), surface_handle); -#elif BUILDFLAG(IS_ANDROID) - output_surface = std::make_unique<GLOutputSurfaceAndroid>( - std::move(context_provider), surface_handle); -#elif BUILDFLAG(IS_CHROMEOS_ASH) - output_surface = std::make_unique<GLOutputSurfaceChromeOS>( - std::move(context_provider), surface_handle); -#else - output_surface = std::make_unique<GLOutputSurface>( - std::move(context_provider), surface_handle); -#endif - } } return output_surface; diff --git a/chromium/components/viz/service/display_embedder/output_surface_provider_impl.h b/chromium/components/viz/service/display_embedder/output_surface_provider_impl.h index fa9bc45b5c1..be10797e6f5 100644 --- a/chromium/components/viz/service/display_embedder/output_surface_provider_impl.h +++ b/chromium/components/viz/service/display_embedder/output_surface_provider_impl.h @@ -14,22 +14,12 @@ #include "components/viz/common/surfaces/frame_sink_id.h" #include "components/viz/service/display_embedder/output_surface_provider.h" #include "components/viz/service/viz_service_export.h" -#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h" #include "gpu/ipc/common/surface_handle.h" -#include "gpu/ipc/in_process_command_buffer.h" #if BUILDFLAG(IS_WIN) #include "components/viz/service/display_embedder/output_device_backing.h" #endif -namespace gpu { -class CommandBufferTaskExecutor; -class GpuChannelManagerDelegate; -class GpuMemoryBufferManager; -class ImageFactory; -class SharedContextState; -} // namespace gpu - namespace viz { class GpuServiceImpl; class SoftwareOutputDevice; @@ -38,13 +28,7 @@ class SoftwareOutputDevice; class VIZ_SERVICE_EXPORT OutputSurfaceProviderImpl : public OutputSurfaceProvider { public: - OutputSurfaceProviderImpl( - GpuServiceImpl* gpu_service_impl, - gpu::CommandBufferTaskExecutor* task_executor, - gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate, - gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, - gpu::ImageFactory* image_factory, - bool headless); + OutputSurfaceProviderImpl(GpuServiceImpl* gpu_service_impl, bool headless); // Software compositing only. explicit OutputSurfaceProviderImpl(bool headless); @@ -56,8 +40,7 @@ class VIZ_SERVICE_EXPORT OutputSurfaceProviderImpl std::unique_ptr<DisplayCompositorMemoryAndTaskController> CreateGpuDependency( bool gpu_compositing, - gpu::SurfaceHandle surface_handle, - const RendererSettings& renderer_settings) override; + gpu::SurfaceHandle surface_handle) override; // OutputSurfaceProvider implementation. std::unique_ptr<OutputSurface> CreateOutputSurface( @@ -74,10 +57,6 @@ class VIZ_SERVICE_EXPORT OutputSurfaceProviderImpl mojom::DisplayClient* display_client); const raw_ptr<GpuServiceImpl> gpu_service_impl_; - const raw_ptr<gpu::CommandBufferTaskExecutor> task_executor_; - const raw_ptr<gpu::GpuChannelManagerDelegate> gpu_channel_manager_delegate_; - const raw_ptr<gpu::GpuMemoryBufferManager> gpu_memory_buffer_manager_; - const raw_ptr<gpu::ImageFactory> image_factory_; #if BUILDFLAG(IS_WIN) // Used for software compositing output on Windows. @@ -86,11 +65,6 @@ class VIZ_SERVICE_EXPORT OutputSurfaceProviderImpl scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - // A shared context which will be used on display compositor thread. - scoped_refptr<gpu::SharedContextState> shared_context_state_; - std::unique_ptr<gpu::MailboxManager> mailbox_manager_; - std::unique_ptr<gpu::SyncPointManager> sync_point_manager_; - const bool headless_; }; diff --git a/chromium/components/viz/service/display_embedder/output_surface_unified.cc b/chromium/components/viz/service/display_embedder/output_surface_unified.cc index f01790c3016..a5c52de25f7 100644 --- a/chromium/components/viz/service/display_embedder/output_surface_unified.cc +++ b/chromium/components/viz/service/display_embedder/output_surface_unified.cc @@ -29,22 +29,6 @@ bool OutputSurfaceUnified::IsDisplayedAsOverlayPlane() const { return false; } -unsigned OutputSurfaceUnified::GetOverlayTextureId() const { - return 0; -} - -bool OutputSurfaceUnified::HasExternalStencilTest() const { - return false; -} - -uint32_t OutputSurfaceUnified::GetFramebufferCopyTextureFormat() { - return 0; -} - -unsigned OutputSurfaceUnified::UpdateGpuFence() { - return 0; -} - gfx::OverlayTransform OutputSurfaceUnified::GetDisplayTransform() { return gfx::OVERLAY_TRANSFORM_NONE; } diff --git a/chromium/components/viz/service/display_embedder/output_surface_unified.h b/chromium/components/viz/service/display_embedder/output_surface_unified.h index 62b6ef6a8df..8d9d4deb688 100644 --- a/chromium/components/viz/service/display_embedder/output_surface_unified.h +++ b/chromium/components/viz/service/display_embedder/output_surface_unified.h @@ -34,19 +34,9 @@ class OutputSurfaceUnified : public OutputSurface { void BindToClient(OutputSurfaceClient* client) override {} void EnsureBackbuffer() override {} void DiscardBackbuffer() override {} - void BindFramebuffer() override {} - void Reshape(const gfx::Size& size, - float scale_factor, - const gfx::ColorSpace& color_space, - gfx::BufferFormat format, - bool stencil) override {} + void Reshape(const ReshapeParams& params) override {} void SwapBuffers(OutputSurfaceFrame frame) override; bool IsDisplayedAsOverlayPlane() const override; - unsigned GetOverlayTextureId() const override; - bool HasExternalStencilTest() const override; - void ApplyExternalStencil() override {} - uint32_t GetFramebufferCopyTextureFormat() override; - unsigned UpdateGpuFence() override; void SetUpdateVSyncParametersCallback( UpdateVSyncParametersCallback callback) override {} void SetDisplayTransformHint(gfx::OverlayTransform transform) override {} 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 1c28222954f..bbc98d2edf7 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_device.cc +++ b/chromium/components/viz/service/display_embedder/skia_output_device.cc @@ -9,7 +9,9 @@ #include "base/bind.h" #include "base/check_op.h" +#include "base/feature_list.h" #include "base/notreached.h" +#include "base/task/task_features.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" #include "base/task/thread_pool/thread_pool_instance.h" @@ -27,11 +29,18 @@ namespace viz { namespace { +const base::Feature kAsyncGpuLatencyReporting CONSTINIT{ + "AsyncGpuLatencyReporting", base::FEATURE_ENABLED_BY_DEFAULT}; + using ::perfetto::protos::pbzero::ChromeLatencyInfo; scoped_refptr<base::SequencedTaskRunner> CreateLatencyTracerRunner() { if (!base::ThreadPoolInstance::Get()) return nullptr; + + if (!base::FeatureList::IsEnabled(kAsyncGpuLatencyReporting)) + return nullptr; + return base::ThreadPool::CreateSequencedTaskRunner( {base::TaskPriority::BEST_EFFORT, base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}); 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 e788908ffd5..f5ea153eaa7 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 @@ -394,11 +394,12 @@ void SkiaOutputDeviceBufferQueue::ScheduleOverlays( auto& overlay = overlays[i]; #if defined(USE_OZONE) - if (overlay.solid_color.has_value()) { + if (overlay.is_solid_color) { + DCHECK(overlay.color.has_value()); // TODO(msisov): reconsider this once Linux Wayland compositors also // support that. See https://bit.ly/2ZqUO0w. if (!supports_non_backed_solid_color_images_) { - overlay.mailbox = GetImageMailboxForColor(overlay.solid_color.value()); + overlay.mailbox = GetImageMailboxForColor(overlay.color.value()); } else { accesses[i] = nullptr; continue; @@ -704,12 +705,13 @@ void SkiaOutputDeviceBufferQueue::MaybeScheduleBackgroundImage() { OverlayCandidate candidate; candidate.color_space = color_space_; candidate.display_rect = gfx::RectF(gfx::SizeF(viewport_size_)); - candidate.solid_color = SK_ColorTRANSPARENT; + candidate.color = SK_ColorTRANSPARENT; candidate.plane_z_order = INT32_MIN; + candidate.is_solid_color = supports_non_backed_solid_color_images_; #if defined(USE_OZONE) if (!supports_non_backed_solid_color_images_) { - auto mailbox = GetImageMailboxForColor(candidate.solid_color.value()); + auto mailbox = GetImageMailboxForColor(candidate.color.value()); DCHECK(mailbox.IsSharedImage()); auto* overlay_data = GetOrCreateOverlayData(mailbox); 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 3f1a3b254d0..3f63127fe35 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 @@ -269,7 +269,7 @@ bool SkiaOutputDeviceGL::Reshape( : kBottomLeft_GrSurfaceOrigin; sk_surface_ = SkSurface::MakeFromBackendRenderTarget( context_state_->gr_context(), render_target, origin, color_type, - color_space.ToSkColorSpace(), &surface_props); + characterization.refColorSpace(), &surface_props); if (!sk_surface_) { LOG(ERROR) << "Couldn't create surface:" << "\n abandoned()=" 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 cb385a7d589..8a450b991bb 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 @@ -65,7 +65,7 @@ bool SkiaOutputDeviceWebView::Reshape( } size_ = size; - color_space_ = color_space; + sk_color_space_ = characterization.refColorSpace(); InitSkiaSurface(gl_surface_->GetBackingFramebufferObject()); return !!sk_surface_; } @@ -115,14 +115,13 @@ void SkiaOutputDeviceWebView::InitSkiaSurface(unsigned int fbo) { SkSurfaceProps surface_props{0, kUnknown_SkPixelGeometry}; sk_surface_ = SkSurface::MakeFromBackendRenderTarget( context_state_->gr_context(), render_target, origin, color_type, - color_space_.ToSkColorSpace(), &surface_props); + sk_color_space_, &surface_props); if (!sk_surface_) { LOG(ERROR) << "Couldn't create surface: " << context_state_->gr_context()->abandoned() << " " << color_type << " " << framebuffer_info.fFBOID << " " - << framebuffer_info.fFormat << " " << color_space_.ToString() - << " " << size_.ToString(); + << framebuffer_info.fFormat << " " << size_.ToString(); } } diff --git a/chromium/components/viz/service/display_embedder/skia_output_device_webview.h b/chromium/components/viz/service/display_embedder/skia_output_device_webview.h index 024727a2155..944310366a5 100644 --- a/chromium/components/viz/service/display_embedder/skia_output_device_webview.h +++ b/chromium/components/viz/service/display_embedder/skia_output_device_webview.h @@ -54,7 +54,7 @@ class SkiaOutputDeviceWebView : public SkiaOutputDevice { sk_sp<SkSurface> sk_surface_; gfx::Size size_; - gfx::ColorSpace color_space_; + sk_sp<SkColorSpace> sk_color_space_; unsigned int last_frame_buffer_object_ = -1; base::WeakPtrFactory<SkiaOutputDeviceWebView> weak_ptr_factory_{this}; 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 f53ec05511e..74fd2601377 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 @@ -82,18 +82,16 @@ OutputSurface::Type GetOutputSurfaceType(SkiaOutputSurfaceDependency* deps) { SkiaOutputSurfaceImpl::ScopedPaint::ScopedPaint( SkDeferredDisplayListRecorder* root_recorder) - : recorder_(root_recorder), render_pass_id_(0) {} + : recorder_(root_recorder) {} SkiaOutputSurfaceImpl::ScopedPaint::ScopedPaint( SkSurfaceCharacterization characterization) - : ScopedPaint(characterization, AggregatedRenderPassId(0), gpu::Mailbox()) { -} + : ScopedPaint(characterization, gpu::Mailbox()) {} SkiaOutputSurfaceImpl::ScopedPaint::ScopedPaint( SkSurfaceCharacterization characterization, - AggregatedRenderPassId render_pass_id, gpu::Mailbox mailbox) - : render_pass_id_(render_pass_id), mailbox_(mailbox) { + : mailbox_(mailbox) { recorder_storage_.emplace(characterization); recorder_ = &recorder_storage_.value(); } @@ -236,10 +234,6 @@ void SkiaOutputSurfaceImpl::BindToClient(OutputSurfaceClient* client) { client_ = client; } -void SkiaOutputSurfaceImpl::BindFramebuffer() { - // TODO(penghuang): remove this method when GLRenderer is removed. -} - void SkiaOutputSurfaceImpl::SetDrawRectangle(const gfx::Rect& draw_rectangle) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(capabilities().supports_dc_layers); @@ -290,21 +284,17 @@ void SkiaOutputSurfaceImpl::RecreateRootRecorder() { std::ignore = root_recorder_->getCanvas(); } -void SkiaOutputSurfaceImpl::Reshape(const gfx::Size& size, - float device_scale_factor, - const gfx::ColorSpace& color_space, - gfx::BufferFormat format, - bool use_stencil) { +void SkiaOutputSurfaceImpl::Reshape(const ReshapeParams& params) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DCHECK(!size.IsEmpty()); + DCHECK(!params.size.IsEmpty()); // SetDrawRectangle() will need to be called at the new size. has_set_draw_rectangle_for_frame_ = false; if (use_damage_area_from_skia_output_device_) { - damage_of_current_buffer_ = gfx::Rect(size); + damage_of_current_buffer_ = gfx::Rect(params.size); } else if (frame_buffer_damage_tracker_) { - frame_buffer_damage_tracker_->FrameBuffersChanged(size); + frame_buffer_damage_tracker_->FrameBuffersChanged(params.size); } if (is_using_raw_draw_ && is_raw_draw_using_msaa_) { @@ -312,34 +302,35 @@ void SkiaOutputSurfaceImpl::Reshape(const gfx::Size& size, // On "low-end" devices use 4 samples per pixel to save memory. sample_count_ = 4; } else { - sample_count_ = device_scale_factor >= 2.0f ? 4 : 8; + sample_count_ = params.device_scale_factor >= 2.0f ? 4 : 8; } } else { sample_count_ = 1; } - const auto format_index = static_cast<int>(format); + const auto format_index = static_cast<int>(params.format); const auto& color_type = capabilities_.sk_color_types[format_index]; DCHECK(color_type != kUnknown_SkColorType) << "SkColorType is invalid for buffer format_index: " << format_index; - auto sk_color_space = color_space.ToSkColorSpace(); + auto sk_color_space = + params.color_space.ToSkColorSpace(params.sdr_white_level); characterization_ = CreateSkSurfaceCharacterization( - size, color_type, /*mipmap=*/false, std::move(sk_color_space), + params.size, color_type, /*mipmap=*/false, std::move(sk_color_space), /*is_root_render_pass=*/true, /*is_overlay=*/false); // impl_on_gpu_ is released on the GPU thread by a posted task from // SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained. - auto task = - base::BindOnce(&SkiaOutputSurfaceImplOnGpu::Reshape, - base::Unretained(impl_on_gpu_.get()), characterization_, - color_space, device_scale_factor, GetDisplayTransform()); + auto task = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::Reshape, + base::Unretained(impl_on_gpu_.get()), + characterization_, params.color_space, + params.device_scale_factor, GetDisplayTransform()); EnqueueGpuTask(std::move(task), {}, /*make_current=*/true, /*need_framebuffer=*/!dependency_->IsOffscreen()); FlushGpuTasks(SyncMode::kNoWait); - size_ = size; - format_ = format; + size_ = params.size; + format_ = params.format; RecreateRootRecorder(); } @@ -604,6 +595,7 @@ SkCanvas* SkiaOutputSurfaceImpl::BeginPaintRenderPass( ResourceFormat format, bool mipmap, sk_sp<SkColorSpace> color_space, + bool is_overlay, const gpu::Mailbox& mailbox) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); // Make sure there is no unsubmitted PaintFrame or PaintRenderPass. @@ -611,78 +603,39 @@ SkCanvas* SkiaOutputSurfaceImpl::BeginPaintRenderPass( DCHECK(resource_sync_tokens_.empty()); SkColorType color_type = - ResourceFormatToClosestSkColorType(true /* gpu_compositing */, format); + ResourceFormatToClosestSkColorType(/*gpu_compositing=*/true, format); SkSurfaceCharacterization characterization = CreateSkSurfaceCharacterization( surface_size, color_type, mipmap, std::move(color_space), - /*is_root_render_pass=*/false, /*is_overlay=*/false); + /*is_root_render_pass=*/false, /*is_overlay=*/is_overlay); if (!characterization.isValid()) return nullptr; - current_paint_.emplace(characterization, id, mailbox); - return current_paint_->recorder()->getCanvas(); -} - -#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) -SkCanvas* SkiaOutputSurfaceImpl::BeginPaintRenderPassOverlay( - const gfx::Size& size, - ResourceFormat format, - bool mipmap, - sk_sp<SkColorSpace> color_space) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - // Make sure there is no unsubmitted PaintFrame or PaintRenderPass. - DCHECK(!current_paint_); - SkColorType color_type = - ResourceFormatToClosestSkColorType(true /* gpu_compositing */, format); - SkSurfaceCharacterization characterization = CreateSkSurfaceCharacterization( - size, color_type, mipmap, std::move(color_space), - /*is_root_render_pass=*/false, /*is_overlay=*/true); - if (!characterization.isValid()) - return nullptr; + // We are going to overwrite the render pass when it is not for overlay, so we + // need to reset the image_context and a new promise image will be created + // when MakePromiseSkImageFromRenderPass() is called. + if (!is_overlay) { + auto it = render_pass_image_cache_.find(id); + if (it != render_pass_image_cache_.end()) { + it->second->clear_image(); + } + } - current_paint_.emplace(characterization); + current_paint_.emplace(characterization, mailbox); return current_paint_->recorder()->getCanvas(); } -sk_sp<SkDeferredDisplayList> -SkiaOutputSurfaceImpl::EndPaintRenderPassOverlay() { +void SkiaOutputSurfaceImpl::EndPaint( + base::OnceClosure on_finished, + base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence_cb) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(current_paint_); - auto ddl = current_paint_->recorder()->detach(); - current_paint_.reset(); - return ddl; -} -#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE) - -void SkiaOutputSurfaceImpl::EndPaint(base::OnceClosure on_finished) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DCHECK(current_paint_); - // If current_render_pass_id_ is not null, we are painting a render pass. - // Otherwise we are painting a frame. - - bool painting_render_pass = !current_paint_->render_pass_id().is_null(); - auto ddl = current_paint_->recorder()->detach(); - - // impl_on_gpu_ is released on the GPU thread by a posted task from - // SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained. - if (painting_render_pass) { - auto it = render_pass_image_cache_.find(current_paint_->render_pass_id()); - if (it != render_pass_image_cache_.end()) { - // We are going to overwrite the render pass, so we need reset the - // image_context, so a new promise image will be created when the - // MakePromiseSkImageFromRenderPass() is called. - it->second->clear_image(); - } - - auto task = base::BindOnce( - &SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass, - base::Unretained(impl_on_gpu_.get()), current_paint_->mailbox(), - std::move(ddl), std::move(images_in_current_paint_), - resource_sync_tokens_, std::move(on_finished)); - EnqueueGpuTask(std::move(task), std::move(resource_sync_tokens_), - /*make_current=*/true, /*need_framebuffer=*/false); - } else { + // If the current paint mailbox is empty, we are painting a frame, otherwise + // we are painting a render pass. impl_on_gpu_ is released on the GPU thread + // by a posted task from SkiaOutputSurfaceImpl::dtor, so it is safe to use + // base::Unretained. + if (current_paint_->mailbox().IsZero()) { // Draw on the root render pass. current_buffer_modified_ = true; sk_sp<SkDeferredDisplayList> overdraw_ddl; @@ -698,10 +651,20 @@ void SkiaOutputSurfaceImpl::EndPaint(base::OnceClosure on_finished) { &SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame, base::Unretained(impl_on_gpu_.get()), std::move(ddl), std::move(overdraw_ddl), std::move(images_in_current_paint_), - resource_sync_tokens_, std::move(on_finished), draw_rectangle_); + resource_sync_tokens_, std::move(on_finished), + std::move(return_release_fence_cb), draw_rectangle_); EnqueueGpuTask(std::move(task), std::move(resource_sync_tokens_), /*make_current=*/true, /*need_framebuffer=*/true); draw_rectangle_.reset(); + } else { + auto task = base::BindOnce( + &SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass, + base::Unretained(impl_on_gpu_.get()), current_paint_->mailbox(), + std::move(ddl), std::move(images_in_current_paint_), + resource_sync_tokens_, std::move(on_finished), + std::move(return_release_fence_cb)); + EnqueueGpuTask(std::move(task), std::move(resource_sync_tokens_), + /*make_current=*/true, /*need_framebuffer=*/false); } images_in_current_paint_.clear(); current_paint_.reset(); @@ -802,30 +765,12 @@ void SkiaOutputSurfaceImpl::CopyOutput( void SkiaOutputSurfaceImpl::ScheduleOverlays( OverlayList overlays, - std::vector<gpu::SyncToken> sync_tokens, - base::OnceClosure on_finished) { -#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) - // If there are render pass overlays, then a gl context is needed for drawing - // the overlay render passes to a backing for being scanned out. - bool make_current = - std::find_if(overlays.begin(), overlays.end(), [](const auto& overlay) { - return !!overlay.ddl; - }) != overlays.end(); - // Append |resource_sync_tokens_| which are depended by drawing render passes - // to overlay backings. - std::move(resource_sync_tokens_.begin(), resource_sync_tokens_.end(), - std::back_inserter(sync_tokens)); - resource_sync_tokens_.clear(); -#else - bool make_current = false; -#endif - auto task = base::BindOnce( - &SkiaOutputSurfaceImplOnGpu::ScheduleOverlays, - base::Unretained(impl_on_gpu_.get()), std::move(overlays), - std::move(images_in_current_paint_), std::move(on_finished)); - EnqueueGpuTask(std::move(task), std::move(sync_tokens), make_current, - /*need_framebuffer=*/false); - images_in_current_paint_.clear(); + std::vector<gpu::SyncToken> sync_tokens) { + auto task = + base::BindOnce(&SkiaOutputSurfaceImplOnGpu::ScheduleOverlays, + base::Unretained(impl_on_gpu_.get()), std::move(overlays)); + EnqueueGpuTask(std::move(task), std::move(sync_tokens), + /*make_current=*/false, /*need_framebuffer=*/false); } void SkiaOutputSurfaceImpl::SetFrameRate(float frame_rate) { @@ -1050,9 +995,6 @@ void SkiaOutputSurfaceImpl::DidSwapBuffersComplete( DCHECK(damage_of_current_buffer_); } - // texture_in_use_responses is used for GLRenderer only. - DCHECK(params.texture_in_use_responses.empty()); - if (!params.ca_layer_params.is_empty) client_->DidReceiveCALayerParams(params.ca_layer_params); client_->DidReceiveSwapBuffersAck(params.swap_response.timings, @@ -1231,40 +1173,15 @@ GrBackendFormat SkiaOutputSurfaceImpl::GetGrBackendFormatForTexture( return GrBackendFormat(); } -uint32_t SkiaOutputSurfaceImpl::GetFramebufferCopyTextureFormat() { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - - return GL_RGB; -} - bool SkiaOutputSurfaceImpl::IsDisplayedAsOverlayPlane() const { return is_displayed_as_overlay_; } -unsigned SkiaOutputSurfaceImpl::GetOverlayTextureId() const { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - return 0; -} - gpu::Mailbox SkiaOutputSurfaceImpl::GetOverlayMailbox() const { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); return last_swapped_mailbox_; } -bool SkiaOutputSurfaceImpl::HasExternalStencilTest() const { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - - return false; -} - -void SkiaOutputSurfaceImpl::ApplyExternalStencil() { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); -} - -unsigned SkiaOutputSurfaceImpl::UpdateGpuFence() { - return 0; -} - void SkiaOutputSurfaceImpl::SetNeedsSwapSizeNotifications( bool needs_swap_size_notifications) { needs_swap_size_notifications_ = needs_swap_size_notifications; 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 be279278a47..685a7003fee 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 @@ -38,7 +38,8 @@ class DelegatedInkPointRenderer; namespace gpu { class SharedImageRepresentationFactory; -} +struct SwapBuffersCompleteParams; +} // namespace gpu namespace viz { @@ -76,16 +77,11 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface { // OutputSurface implementation: gpu::SurfaceHandle GetSurfaceHandle() const override; void BindToClient(OutputSurfaceClient* client) override; - void BindFramebuffer() override; void SetDrawRectangle(const gfx::Rect& draw_rectangle) override; void SetEnableDCLayers(bool enable) override; void EnsureBackbuffer() override; void DiscardBackbuffer() override; - void Reshape(const gfx::Size& size, - float device_scale_factor, - const gfx::ColorSpace& color_space, - gfx::BufferFormat format, - bool use_stencil) override; + void Reshape(const ReshapeParams& params) override; void SetUpdateVSyncParametersCallback( UpdateVSyncParametersCallback callback) override; void SetGpuVSyncEnabled(bool enabled) override; @@ -93,13 +89,8 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface { void SetDisplayTransformHint(gfx::OverlayTransform transform) override; gfx::OverlayTransform GetDisplayTransform() override; void SwapBuffers(OutputSurfaceFrame frame) override; - uint32_t GetFramebufferCopyTextureFormat() override; bool IsDisplayedAsOverlayPlane() const override; - unsigned GetOverlayTextureId() const override; gpu::Mailbox GetOverlayMailbox() const override; - bool HasExternalStencilTest() const override; - void ApplyExternalStencil() override; - unsigned UpdateGpuFence() override; void SetNeedsSwapSizeNotifications( bool needs_swap_size_notifications) override; base::ScopedClosureRunner GetCacheBackBufferCb() override; @@ -124,8 +115,11 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface { ResourceFormat format, bool mipmap, sk_sp<SkColorSpace> color_space, + bool is_overlay, const gpu::Mailbox& mailbox) override; - void EndPaint(base::OnceClosure on_finished) override; + void EndPaint(base::OnceClosure on_finished, + base::OnceCallback<void(gfx::GpuFenceHandle)> + return_release_fence_cb) override; void MakePromiseSkImage(ImageContext* image_context) override; sk_sp<SkImage> MakePromiseSkImageFromRenderPass( const AggregatedRenderPassId& id, @@ -138,8 +132,7 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface { void RemoveRenderPassResource( std::vector<AggregatedRenderPassId> ids) override; void ScheduleOverlays(OverlayList overlays, - std::vector<gpu::SyncToken> sync_tokens, - base::OnceClosure on_finished) override; + std::vector<gpu::SyncToken> sync_tokens) override; void CopyOutput(AggregatedRenderPassId id, const copy_output::RenderPassGeometry& geometry, @@ -153,15 +146,6 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface { gpu::SyncToken Flush() override; bool EnsureMinNumberOfBuffers(int n) override; -#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) - SkCanvas* BeginPaintRenderPassOverlay( - const gfx::Size& size, - ResourceFormat format, - bool mipmap, - sk_sp<SkColorSpace> color_space) override; - sk_sp<SkDeferredDisplayList> EndPaintRenderPassOverlay() override; -#endif - // ExternalUseClient implementation: gpu::SyncToken ReleaseImageContexts( std::vector<std::unique_ptr<ImageContext>> image_contexts) override; @@ -255,21 +239,18 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface { explicit ScopedPaint(SkDeferredDisplayListRecorder* root_recorder); explicit ScopedPaint(SkSurfaceCharacterization characterization); ScopedPaint(SkSurfaceCharacterization characterization, - AggregatedRenderPassId render_pass_id, gpu::Mailbox mailbox); ~ScopedPaint(); SkDeferredDisplayListRecorder* recorder() { return recorder_; } - AggregatedRenderPassId render_pass_id() { return render_pass_id_; } gpu::Mailbox mailbox() { return mailbox_; } private: // This is recorder being used for current paint SkDeferredDisplayListRecorder* recorder_; - // If we need new recorder for this Paint (i.e it's not root render pass), + // If we need new recorder for this Paint (i.e. it's not root render pass), // it's stored here absl::optional<SkDeferredDisplayListRecorder> recorder_storage_; - const AggregatedRenderPassId render_pass_id_; const gpu::Mailbox mailbox_; }; 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 620942a9ec8..dcca5fa92f6 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 @@ -12,6 +12,8 @@ #include "base/callback_helpers.h" #include "base/debug/crash_logging.h" #include "base/memory/raw_ptr.h" +#include "base/memory/scoped_refptr.h" +#include "base/notreached.h" #include "base/task/bind_post_task.h" #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/memory_dump_manager.h" @@ -37,7 +39,10 @@ #include "components/viz/service/display_embedder/skia_output_device_webview.h" #include "components/viz/service/display_embedder/skia_output_surface_dependency.h" #include "components/viz/service/display_embedder/skia_render_copy_results.h" +#include "gpu/command_buffer/common/mailbox_holder.h" #include "gpu/command_buffer/common/swap_buffers_complete_params.h" +#include "gpu/command_buffer/common/sync_token.h" +#include "gpu/command_buffer/service/external_semaphore.h" #include "gpu/command_buffer/service/gr_shader_cache.h" #include "gpu/command_buffer/service/memory_tracking.h" #include "gpu/command_buffer/service/scheduler.h" @@ -64,14 +69,19 @@ #include "third_party/skia/include/core/SkSamplingOptions.h" #include "third_party/skia/include/core/SkSize.h" #include "third_party/skia/include/core/SkYUVAInfo.h" +#include "third_party/skia/include/gpu/GrTypes.h" #include "ui/gfx/color_space.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/skia_conversions.h" #include "ui/gfx/gpu_fence_handle.h" +#include "ui/gl/gl_fence.h" #include "ui/gl/gl_surface.h" #if BUILDFLAG(ENABLE_VULKAN) #include "components/viz/service/display_embedder/skia_output_device_vulkan.h" +#include "gpu/vulkan/vulkan_device_queue.h" +#include "gpu/vulkan/vulkan_function_pointers.h" +#include "gpu/vulkan/vulkan_implementation.h" #include "gpu/vulkan/vulkan_util.h" #if BUILDFLAG(IS_ANDROID) #include "components/viz/service/display_embedder/skia_output_device_vulkan_secondary_cb.h" @@ -357,6 +367,11 @@ SkiaOutputSurfaceImplOnGpu::~SkiaOutputSurfaceImplOnGpu() { // before deleting ImplOnGpu's other member variables. shared_image_factory_.reset(); if (has_context) { + absl::optional<gpu::raster::GrShaderCache::ScopedCacheUse> cache_use; + if (dependency_->GetGrShaderCache()) { + cache_use.emplace(dependency_->GetGrShaderCache(), + gpu::kDisplayCompositorClientId); + } // This ensures any outstanding callbacks for promise images are // performed. gr_context()->flushAndSubmit(); @@ -393,6 +408,7 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame( std::vector<ImageContextImpl*> image_contexts, std::vector<gpu::SyncToken> sync_tokens, base::OnceClosure on_finished, + base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence_cb, absl::optional<gfx::Rect> draw_rectangle) { TRACE_EVENT0("viz", "SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame"); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); @@ -445,7 +461,12 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame( // Draw will only fail if the SkSurface and SkDDL are incompatible. bool draw_success = scoped_output_device_paint_->Draw(ddl); +#if defined(USE_OZONE) + if (!draw_success) + DLOG(ERROR) << "output_sk_surface()->draw() failed."; +#else DCHECK(draw_success); +#endif // USE_OZONE destroy_after_swap_.emplace_back(std::move(ddl)); @@ -470,7 +491,24 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame( end_semaphores.insert(end_semaphores.end(), end_paint_semaphores.begin(), end_paint_semaphores.end()); +#if BUILDFLAG(ENABLE_VULKAN) + // Semaphores for release fences for vulkan should be created before flush. + if (!return_release_fence_cb.is_null() && is_using_vulkan()) { + const bool result = CreateAndStoreExternalSemaphoreVulkan(end_semaphores); + // A release fence will be created on submit as some platforms may use + // VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT handle types for their + // external semaphore. That handle type has COPY transference. Vulkan spec + // says that semaphore has to be signaled, or have an associated semaphore + // signal operation pending execution. Thus, delay importing the handle + // and creating the fence until commands are submitted. + pending_release_fence_cbs_.emplace_back( + result ? end_semaphores.back() : GrBackendSemaphore(), + std::move(return_release_fence_cb)); + } +#endif + const bool end_semaphores_empty = end_semaphores.empty(); + auto result = scoped_output_device_paint_->Flush(vulkan_context_provider_, std::move(end_semaphores), std::move(on_finished)); @@ -481,6 +519,22 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame( FailedSkiaFlush("output_sk_surface()->flush() failed."); return; } + + gfx::GpuFenceHandle release_fence; + if (!return_release_fence_cb.is_null() && is_using_gl()) { + DCHECK(release_fence.is_null()); + release_fence = CreateReleaseFenceForGL(); + } + + if (!return_release_fence_cb.is_null() && is_using_dawn()) + NOTIMPLEMENTED() << "Release fences with dawn are not supported."; + + if (!return_release_fence_cb.is_null()) { + // Returning fences for Vulkan is delayed. See the comment above. + DCHECK(!is_using_vulkan()); + PostTaskToClientThread(base::BindOnce(std::move(return_release_fence_cb), + std::move(release_fence))); + } } } @@ -525,7 +579,8 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass( sk_sp<SkDeferredDisplayList> ddl, std::vector<ImageContextImpl*> image_contexts, std::vector<gpu::SyncToken> sync_tokens, - base::OnceClosure on_finished) { + base::OnceClosure on_finished, + base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence_cb) { TRACE_EVENT0("viz", "SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass"); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(ddl); @@ -576,6 +631,22 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass( backing_representation->SetCleared(); destroy_after_swap_.emplace_back(std::move(ddl)); +#if BUILDFLAG(ENABLE_VULKAN) + // Semaphores for release fences for vulkan should be created before flush. + if (!return_release_fence_cb.is_null() && is_using_vulkan()) { + const bool result = CreateAndStoreExternalSemaphoreVulkan(end_semaphores); + // A release fence will be created on submit as some platforms may use + // VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT handle types for their + // external semaphore. That handle type has COPY transference. Vulkan spec + // says that semaphore has to be signaled, or have an associated semaphore + // signal operation pending execution. Thus, delay importing the handle + // and creating the fence until commands are submitted. + pending_release_fence_cbs_.emplace_back( + result ? end_semaphores.back() : GrBackendSemaphore(), + std::move(return_release_fence_cb)); + } +#endif + GrFlushInfo flush_info = { .fNumSemaphores = end_semaphores.size(), .fSignalSemaphores = end_semaphores.data(), @@ -584,6 +655,7 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass( &flush_info); if (on_finished) gpu::AddCleanupTaskForSkiaFlush(std::move(on_finished), &flush_info); + auto result = surface->flush(flush_info); if (result != GrSemaphoresSubmitted::kYes && !(begin_semaphores.empty() && end_semaphores.empty())) { @@ -591,6 +663,24 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass( FailedSkiaFlush("offscreen.surface()->flush() failed."); return; } + + // If GL is used, create the release fence after flush. + gfx::GpuFenceHandle release_fence; + if (!return_release_fence_cb.is_null() && is_using_gl()) { + DCHECK(release_fence.is_null()); + release_fence = CreateReleaseFenceForGL(); + } + + if (!return_release_fence_cb.is_null() && is_using_dawn()) + NOTIMPLEMENTED() << "Release fences with dawn are not supported."; + + if (!return_release_fence_cb.is_null()) { + // Returning fences for Vulkan is delayed. See the comment above. + DCHECK(!is_using_vulkan()); + PostTaskToClientThread(base::BindOnce(std::move(return_release_fence_cb), + std::move(release_fence))); + } + bool sync_cpu = gpu::ShouldVulkanSyncCpuForSkiaSubmit(vulkan_context_provider_); if (sync_cpu) { @@ -701,12 +791,24 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputRGBA( request->scale_from().y()); } - bool success = RenderSurface(surface, src_rect, scaling, - is_downscale_or_identity_in_both_dimensions, - scoped_write->surface(), begin_semaphores, - end_semaphores); - if (!success) { - DLOG(ERROR) << "failed to render surface."; + scoped_write->surface()->wait(begin_semaphores.size(), + begin_semaphores.data()); + + RenderSurface(surface, src_rect, scaling, + is_downscale_or_identity_in_both_dimensions, + scoped_write->surface()); + + bool should_submit = !end_semaphores.empty(); + + if (!FlushSurface(scoped_write->surface(), end_semaphores, + scoped_write->TakeEndState())) { + // TODO(penghuang): handle vulkan device lost. + FailedSkiaFlush("CopyOutputRGBA dest_surface->flush()"); + return; + } + + if (should_submit && !gr_context()->submit()) { + DLOG(ERROR) << "CopyOutputRGBA gr_context->submit() failed"; return; } @@ -730,16 +832,12 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputRGBA( } } -bool SkiaOutputSurfaceImplOnGpu::RenderSurface( +void SkiaOutputSurfaceImplOnGpu::RenderSurface( SkSurface* surface, const SkIRect& source_selection, absl::optional<SkVector> scaling, bool is_downscale_or_identity_in_both_dimensions, - SkSurface* dest_surface, - std::vector<GrBackendSemaphore>& begin_semaphores, - std::vector<GrBackendSemaphore>& end_semaphores) { - dest_surface->wait(begin_semaphores.size(), begin_semaphores.data()); - + SkSurface* dest_surface) { SkCanvas* dest_canvas = dest_surface->getCanvas(); int state_depth = dest_canvas->save(); @@ -765,21 +863,23 @@ bool SkiaOutputSurfaceImplOnGpu::RenderSurface( sampling, /*paint=*/nullptr); dest_canvas->restoreToCount(state_depth); +} +bool SkiaOutputSurfaceImplOnGpu::FlushSurface( + SkSurface* surface, + std::vector<GrBackendSemaphore>& end_semaphores, + std::unique_ptr<GrBackendSurfaceMutableState> end_state, + GrGpuFinishedProc finished_proc, + GrGpuFinishedContext finished_context) { GrFlushInfo flush_info; flush_info.fNumSemaphores = end_semaphores.size(); flush_info.fSignalSemaphores = end_semaphores.data(); + flush_info.fFinishedProc = finished_proc; + flush_info.fFinishedContext = finished_context; gpu::AddVulkanCleanupTaskForSkiaFlush(vulkan_context_provider_, &flush_info); - GrSemaphoresSubmitted flush_result = dest_surface->flush( - SkSurface::BackendSurfaceAccess::kNoAccess, flush_info); - if (flush_result != GrSemaphoresSubmitted::kYes && - !(begin_semaphores.empty() && end_semaphores.empty())) { - // TODO(penghuang): handle vulkan device lost. - FailedSkiaFlush("dest_surface->flush() failed."); - return false; - } - - return true; + GrSemaphoresSubmitted flush_result = + surface->flush(flush_info, end_state.get()); + return flush_result == GrSemaphoresSubmitted::kYes || end_semaphores.empty(); } SkiaOutputSurfaceImplOnGpu::PlaneAccessData::PlaneAccessData() = default; @@ -838,8 +938,8 @@ bool SkiaOutputSurfaceImplOnGpu::ImportSurfacesForNV12Planes( for (size_t i = 0; i < CopyOutputResult::kNV12MaxPlanes; ++i) { const gpu::MailboxHolder& mailbox_holder = blit_request.mailbox(i); - // Should never happen, maiboxes are validated when setting blit request on - // a CopyOutputResult. + // Should never happen, mailboxes are validated when setting blit request on + // a CopyOutputResult and we only access `kNV12MaxPlanes` mailboxes. DCHECK(!mailbox_holder.mailbox.IsZero()); PlaneAccessData& plane_data = plane_access_datas[i]; @@ -901,6 +1001,9 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputNV12( CopyOutputRequest::ResultDestination::kNativeTextures) << "Only CopyOutputRequests that hand out native textures support blit " "requests!"; + DCHECK(!request->has_blit_request() || request->has_result_selection()) + << "Only CopyOutputRequests that specify result selection support blit " + "requests!"; // Overview: // 1. Try to create surfaces for NV12 planes (we know the needed size in @@ -917,22 +1020,39 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputNV12( // - pass ownership of the textures to the caller (native textures result) // - schedule a read-back & expose its results to the caller (system memory // result) + // + // Note: in case the blit request populates the GMBs, the flow stays the same, + // but we need to ensure that the results are only sent out after the + // GpuMemoryBuffer is safe to map into system memory. // The size of the destination is passed in via `geometry.result_selection` - // it already takes into account the rect of the render pass that is being // copied, as well as area, scaling & result_selection of the `request`. // This represents the size of the intermediate texture that will be then // blitted to the destination textures. - const gfx::Size destination_size = geometry.result_selection.size(); + const gfx::Size intermediate_dst_size = geometry.result_selection.size(); std::array<PlaneAccessData, CopyOutputResult::kNV12MaxPlanes> plane_access_datas; SkYUVAInfo yuva_info; - bool destination_surfaces_created = false; + bool destination_surfaces_ready = false; if (request->has_blit_request()) { - destination_surfaces_created = ImportSurfacesForNV12Planes( + if (request->result_selection().size() != intermediate_dst_size) { + DLOG(WARNING) + << __func__ + << ": result selection is different than render pass output, " + "geometry=" + << geometry.ToString() << ", request=" << request->ToString(); + // Send empty result, we have a blit request that asks for a different + // size than what we have available - the behavior in this case is + // currently unspecified as we'd have to leave parts of the caller's + // region unpopulated. + return; + } + + destination_surfaces_ready = ImportSurfacesForNV12Planes( request->blit_request(), plane_access_datas); // The entire destination image size is the same as the size of the luma @@ -943,7 +1063,8 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputNV12( // Check if the destination will fit in the blit target: const gfx::Rect blit_destination_rect( - request->blit_request().destination_region_offset(), destination_size); + request->blit_request().destination_region_offset(), + intermediate_dst_size); const gfx::Rect blit_target_image_rect( gfx::SkISizeToSize(plane_access_datas[0].size)); @@ -953,24 +1074,25 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputNV12( return; } } else { - yuva_info = SkYUVAInfo( - gfx::SizeToSkISize(destination_size), SkYUVAInfo::PlaneConfig::kY_UV, - SkYUVAInfo::Subsampling::k420, kRec709_Limited_SkYUVColorSpace); + yuva_info = SkYUVAInfo(gfx::SizeToSkISize(intermediate_dst_size), + SkYUVAInfo::PlaneConfig::kY_UV, + SkYUVAInfo::Subsampling::k420, + kRec709_Limited_SkYUVColorSpace); - destination_surfaces_created = + destination_surfaces_ready = CreateSurfacesForNV12Planes(yuva_info, color_space, plane_access_datas); } - if (!destination_surfaces_created) { - DVLOG(1) << "failed to create destination surfaces"; + if (!destination_surfaces_ready) { + DVLOG(1) << "failed to create / import destination surfaces"; // Send empty result. return; } // Create a destination for the scaled & clipped result: - auto representation = CreateSharedImageRepresentationSkia( - ResourceFormat::RGBA_8888, destination_size, color_space); - if (!representation) { + auto intermediate_representation = CreateSharedImageRepresentationSkia( + ResourceFormat::RGBA_8888, intermediate_dst_size, color_space); + if (!intermediate_representation) { DVLOG(1) << "failed to create shared image representation for the " "intermediate surface"; // Send empty result. @@ -981,9 +1103,11 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputNV12( std::vector<GrBackendSemaphore> begin_semaphores; std::vector<GrBackendSemaphore> end_semaphores; - auto scoped_write = representation->BeginScopedWriteAccess( - /*final_msaa_count=*/1, surface_props, &begin_semaphores, &end_semaphores, - gpu::SharedImageRepresentation::AllowUnclearedAccess::kYes); + auto intermediate_scoped_write = + intermediate_representation->BeginScopedWriteAccess( + /*final_msaa_count=*/1, surface_props, &begin_semaphores, + &end_semaphores, + gpu::SharedImageRepresentation::AllowUnclearedAccess::kYes); absl::optional<SkVector> scaling; if (request->is_scaled()) { @@ -993,24 +1117,22 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputNV12( request->scale_from().y()); } - bool success = RenderSurface( - surface, src_rect, scaling, is_downscale_or_identity_in_both_dimensions, - scoped_write->surface(), begin_semaphores, end_semaphores); - if (!success) { - DLOG(ERROR) << "failed to render surface."; - return; - } + intermediate_scoped_write->surface()->wait(begin_semaphores.size(), + begin_semaphores.data()); - representation->SetCleared(); + RenderSurface(surface, src_rect, scaling, + is_downscale_or_identity_in_both_dimensions, + intermediate_scoped_write->surface()); if (request->has_blit_request()) { - BlendBitmapOverlays(scoped_write->surface()->getCanvas(), + BlendBitmapOverlays(intermediate_scoped_write->surface()->getCanvas(), request->blit_request()); } - auto source_image = scoped_write->surface()->makeImageSnapshot(); - if (!source_image) { - DLOG(ERROR) << "failed to retrieve source_image."; + auto intermediate_image = + intermediate_scoped_write->surface()->makeImageSnapshot(); + if (!intermediate_image) { + DLOG(ERROR) << "failed to retrieve `intermediate_image`."; return; } @@ -1022,60 +1144,137 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputNV12( plane_access_datas[1].scoped_write->surface(), nullptr, nullptr}; // The region to be populated in caller's textures is derived from blit - // request's |destination_region_offset|, and from COR's |result_selection|. - // If we have a blit request, use it. Otherwise, use an + // request's |destination_region_offset()|, and from COR's + // |result_selection()|. If we have a blit request, use it. Otherwise, use an // empty rect (which means that entire image will be used as the target of the // blit - this will not result in rescaling since w/o blit request present, - // the image size matches the |result_selection|). - SkRect dst_region = + // the destination image size matches the |geometry.result_selection|). + const SkRect dst_region = request->has_blit_request() ? gfx::RectToSkRect( gfx::Rect(request->blit_request().destination_region_offset(), - destination_size)) + intermediate_dst_size)) : SkRect::MakeEmpty(); - skia::BlitRGBAToYUVA(source_image.get(), plane_surfaces.data(), yuva_info, - dst_region); + // We should clear destination if BlitRequest asked to letterbox everything + // outside of intended destination region: + const bool clear_destination = + request->has_blit_request() + ? request->blit_request().letterboxing_behavior() == + LetterboxingBehavior::kLetterbox + : false; + skia::BlitRGBAToYUVA(intermediate_image.get(), plane_surfaces.data(), + yuva_info, dst_region, clear_destination); + + // Collect mailbox holders for the destination textures. They will be needed + // in case the result is kNativeTextures. It happens here in order to simplify + // the code in case we are populating the GpuMemoryBuffer-backed textures. + std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes> + plane_mailbox_holders = { + gpu::MailboxHolder(plane_access_datas[0].mailbox, gpu::SyncToken(), + GL_TEXTURE_2D), + gpu::MailboxHolder(plane_access_datas[1].mailbox, gpu::SyncToken(), + GL_TEXTURE_2D), + gpu::MailboxHolder(), + }; + + // If we are not the ones allocating the textures, they may come from a GMB, + // in which case we need to delay sending the results until we receive a + // callback that the GPU work has completed - otherwise, memory-mapping the + // GMB may not yield the latest version of the contents. + const bool should_wait_for_gpu_work = + request->result_destination() == + CopyOutputRequest::ResultDestination::kNativeTextures && + request->has_blit_request() && + request->blit_request().populates_gpu_memory_buffer(); + + scoped_refptr<NV12PlanesReadyContext> nv12_planes_ready = nullptr; + if (should_wait_for_gpu_work) { + // Prepare a per-CopyOutputRequest context that will be responsible for + // sending the CopyOutputResult: + nv12_planes_ready = base::MakeRefCounted<NV12PlanesReadyContext>( + weak_ptr_, std::move(request), geometry.result_selection, + plane_mailbox_holders, color_space); + } + + bool should_submit = false; for (size_t i = 0; i < CopyOutputResult::kNV12MaxPlanes; ++i) { plane_access_datas[i].representation->SetCleared(); - GrFlushInfo flush_info; - flush_info.fNumSemaphores = plane_access_datas[i].end_semaphores.size(); - flush_info.fSignalSemaphores = plane_access_datas[i].end_semaphores.data(); - - gpu::AddVulkanCleanupTaskForSkiaFlush(vulkan_context_provider_, - &flush_info); + should_submit |= !plane_access_datas[i].end_semaphores.empty(); + + // Prepare a per-plane context that will notify the per-request context that + // GPU work that produces the contents of a plane that the GPU-side of the + // work has completed. + std::unique_ptr<NV12SinglePlaneReadyContext> nv12_plane_ready = + should_wait_for_gpu_work + ? std::make_unique<NV12SinglePlaneReadyContext>(nv12_planes_ready) + : nullptr; + + if (should_wait_for_gpu_work) { + // Treat the fact that we're waiting for GPU work to finish the same way + // as a readback request. This would allow us to nudge Skia to fire the + // callbacks. See `SkiaOutputSurfaceImplOnGpu::CheckReadbackCompletion()`. + ++num_readbacks_pending_; + } - auto flush_result = plane_surfaces[i]->flush( - SkSurface::BackendSurfaceAccess::kNoAccess, flush_info); - if (flush_result != GrSemaphoresSubmitted::kYes && - !(begin_semaphores.empty() && end_semaphores.empty())) { + if (!FlushSurface( + plane_surfaces[i], plane_access_datas[i].end_semaphores, + plane_access_datas[i].scoped_write->TakeEndState(), + should_wait_for_gpu_work + ? &NV12SinglePlaneReadyContext::OnNV12PlaneReady + : nullptr, + should_wait_for_gpu_work ? nv12_plane_ready.release() : nullptr)) { // TODO(penghuang): handle vulkan device lost. - FailedSkiaFlush("plane_surfaces[i]->flush()"); + FailedSkiaFlush("CopyOutputNV12 plane_surfaces[i]->flush()"); return; } } + should_submit |= !end_semaphores.empty(); + + intermediate_representation->SetCleared(); + if (!FlushSurface(intermediate_scoped_write->surface(), end_semaphores, + intermediate_scoped_write->TakeEndState())) { + // TODO(penghuang): handle vulkan device lost. + FailedSkiaFlush("CopyOutputNV12 dest_surface->flush()"); + return; + } + + if (should_submit && !gr_context()->submit()) { + DLOG(ERROR) << "CopyOutputNV12 gr_context->submit() failed"; + return; + } + + if (should_wait_for_gpu_work) { + // Flow will continue after GPU work is done - see + // `NV12PlanesReadyContext::OnNV12PlaneReady()` that eventually gets called. + return; + } + + // We conditionally move from request (if `should_wait_for_gpu_work` is true), + // DCHECK that we don't accidentally enter this codepath after the request was + // moved from. + DCHECK(request); + switch (request->result_destination()) { case CopyOutputRequest::ResultDestination::kNativeTextures: { CopyOutputResult::ReleaseCallbacks release_callbacks; - std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes> planes; - for (size_t i = 0; i < CopyOutputResult::kNV12MaxPlanes; ++i) { - if (!request->has_blit_request()) { - // In blit requests, we are not responsible for releasing the textures - // (the issuer of the request owns them), do not create the callbacks. + if (!request->has_blit_request()) { + // In blit requests, we are not responsible for releasing the textures + // (the issuer of the request owns them), create the callbacks only if + // we don't have blit request: + for (size_t i = 0; i < CopyOutputResult::kNV12MaxPlanes; ++i) { release_callbacks.push_back( CreateDestroyCopyOutputResourcesOnGpuThreadCallback( std::move(plane_access_datas[i].representation))); } - - planes[i].mailbox = plane_access_datas[i].mailbox; } request->SendResult(std::make_unique<CopyOutputTextureResult>( CopyOutputResult::Format::NV12_PLANES, geometry.result_selection, - CopyOutputResult::TextureResult(planes, color_space), + CopyOutputResult::TextureResult(plane_mailbox_holders, color_space), std::move(release_callbacks))); break; @@ -1148,6 +1347,7 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutput( scoped_access; std::vector<GrBackendSemaphore> begin_semaphores; std::vector<GrBackendSemaphore> end_semaphores; + std::unique_ptr<GrBackendSurfaceMutableState> end_state; if (from_framebuffer) { surface = scoped_output_device_paint_->sk_surface(); } else { @@ -1162,6 +1362,7 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutput( &end_semaphores, gpu::SharedImageRepresentation::AllowUnclearedAccess::kNo); surface = scoped_access->surface(); + end_state = scoped_access->TakeEndState(); if (!begin_semaphores.empty()) { auto result = surface->wait(begin_semaphores.size(), begin_semaphores.data(), @@ -1269,20 +1470,10 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutput( } } - if (!end_semaphores.empty()) { - GrFlushInfo flush_info; - flush_info.fNumSemaphores = end_semaphores.size(); - flush_info.fSignalSemaphores = end_semaphores.data(); - gpu::AddVulkanCleanupTaskForSkiaFlush(vulkan_context_provider_, - &flush_info); - auto flush_result = - surface->flush(SkSurface::BackendSurfaceAccess::kNoAccess, flush_info); - if (flush_result != GrSemaphoresSubmitted::kYes && - !(begin_semaphores.empty() && end_semaphores.empty())) { - // TODO(penghuang): handle vulkan device lost. - FailedSkiaFlush("surface->flush() failed."); - return; - } + if (!FlushSurface(surface, end_semaphores, std::move(end_state))) { + // TODO(penghuang): handle vulkan device lost. + FailedSkiaFlush("surface->flush() failed."); + return; } ScheduleCheckReadbackCompletion(); @@ -1326,8 +1517,9 @@ void SkiaOutputSurfaceImplOnGpu::BeginAccessImages( context->BeginAccessIfNecessary( context_state_.get(), shared_image_representation_factory_.get(), dependency_->GetMailboxManager(), begin_semaphores, end_semaphores); - if (context->end_access_state()) - image_contexts_with_end_access_state_.emplace(context); + if (auto end_state = context->TakeAccessEndState()) + image_contexts_with_end_access_state_.emplace(context, + std::move(end_state)); // Texture parameters can be modified by concurrent reads so reset them // before compositing from the texture. See https://crbug.com/1092080. @@ -1342,11 +1534,10 @@ void SkiaOutputSurfaceImplOnGpu::BeginAccessImages( } void SkiaOutputSurfaceImplOnGpu::ResetStateOfImages() { - for (auto* context : image_contexts_with_end_access_state_) { - DCHECK(context->end_access_state()); + for (auto& context : image_contexts_with_end_access_state_) { if (!gr_context()->setBackendTextureState( - context->promise_image_texture()->backendTexture(), - *context->end_access_state())) { + context.first->promise_image_texture()->backendTexture(), + *context.second)) { DLOG(ERROR) << "setBackendTextureState() failed."; } } @@ -1382,73 +1573,21 @@ void SkiaOutputSurfaceImplOnGpu::ReleaseImageContexts( } void SkiaOutputSurfaceImplOnGpu::ScheduleOverlays( - SkiaOutputSurface::OverlayList overlays, - std::vector<ImageContextImpl*> image_contexts, - base::OnceClosure on_finished) { + SkiaOutputSurface::OverlayList overlays) { TRACE_EVENT1("viz", "SkiaOutputSurfaceImplOnGpu::ScheduleOverlays", "num_overlays", overlays.size()); -#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) - if (context_is_lost_) - return; - - bool have_image_contexts = !image_contexts.empty(); - if (have_image_contexts) { - DCHECK(context_state_->GrContextIsGL()); - - // GL doesn't use semaphores. - promise_image_access_helper_.BeginAccess(std::move(image_contexts), - /*begin_semaphores=*/nullptr, - /*end_semaphores=*/nullptr); - } - - using ScopedWriteAccess = - std::unique_ptr<gpu::SharedImageRepresentationSkia::ScopedWriteAccess>; - std::vector<ScopedWriteAccess> scoped_write_accesses; - for (auto& overlay : overlays) { - if (!overlay.ddl) - continue; - const auto& characterization = overlay.ddl->characterization(); - auto backing = GetOrCreateRenderPassOverlayBacking(characterization); - if (!backing) - break; - DCHECK(overlay.mailbox.IsZero()); - overlay.mailbox = backing->mailbox(); - auto scoped_access = backing->BeginScopedWriteAccess( - characterization.sampleCount(), characterization.surfaceProps(), - /*begin_semaphores=*/nullptr, - /*end_semaphores=*/nullptr, - gpu::SharedImageRepresentation::AllowUnclearedAccess::kYes); - bool result = scoped_access->surface()->draw(overlay.ddl); - DCHECK(result); - scoped_write_accesses.push_back(std::move(scoped_access)); - backing->SetCleared(); - in_flight_render_pass_overlay_backings_.insert(std::move(backing)); - } - - if (!scoped_write_accesses.empty()) { - absl::optional<gpu::raster::GrShaderCache::ScopedCacheUse> cache_use; - if (dependency_->GetGrShaderCache()) { - cache_use.emplace(dependency_->GetGrShaderCache(), - gpu::kDisplayCompositorClientId); - } - - GrFlushInfo flush_info = {}; - if (on_finished) - gpu::AddCleanupTaskForSkiaFlush(std::move(on_finished), &flush_info); - context_state_->gr_context()->flush(flush_info); - context_state_->gr_context()->submit(); - scoped_write_accesses.clear(); - } - - if (have_image_contexts) - promise_image_access_helper_.EndAccess(); + constexpr base::TimeDelta kHistogramMinTime = base::Microseconds(5); + constexpr base::TimeDelta kHistogramMaxTime = base::Milliseconds(16); + constexpr int kHistogramTimeBuckets = 50; + base::TimeTicks start_time = base::TimeTicks::Now(); output_device_->ScheduleOverlays(std::move(overlays)); -#else - DCHECK(image_contexts.empty()); - output_device_->ScheduleOverlays(std::move(overlays)); -#endif + + UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( + "Gpu.OutputSurface.ScheduleOverlaysUs", + base::TimeTicks::Now() - start_time, kHistogramMinTime, kHistogramMaxTime, + kHistogramTimeBuckets); } void SkiaOutputSurfaceImplOnGpu::SetEnableDCLayers(bool enable) { @@ -1769,17 +1908,6 @@ void SkiaOutputSurfaceImplOnGpu::SwapBuffersInternal( DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(output_device_); -#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) - // Release any backings which are not reused by the current frame, probably - // because the properties of render passes are changed or render passes are - // removed - if (context_is_lost_) { - for (auto& image : available_render_pass_overlay_backings_) - image->OnContextLost(); - } - available_render_pass_overlay_backings_.clear(); -#endif - if (context_is_lost_) return; @@ -1806,6 +1934,20 @@ void SkiaOutputSurfaceImplOnGpu::PostSubmit( promise_image_access_helper_.EndAccess(); scoped_output_device_paint_.reset(); +#if BUILDFLAG(ENABLE_VULKAN) + while (!pending_release_fence_cbs_.empty()) { + auto& item = pending_release_fence_cbs_.front(); + auto release_fence = CreateReleaseFenceForVulkan(item.first); + if (release_fence.is_null()) + LOG(ERROR) << "Unable to create a release fence for Vulkan."; + PostTaskToClientThread( + base::BindOnce(std::move(item.second), std::move(release_fence))); + pending_release_fence_cbs_.pop_front(); + } +#else + DCHECK(pending_release_fence_cbs_.empty()); +#endif + if (frame) { if (waiting_for_full_damage_) { // If we're using partial swap, we need to check whether the sub-buffer @@ -1901,17 +2043,6 @@ base::TimeDelta SkiaOutputSurfaceImplOnGpu::GetGpuBlockedTimeSinceLastSwap() { return dependency_->GetGpuBlockedTimeSinceLastSwap(); } -void SkiaOutputSurfaceImplOnGpu::DidSwapBuffersComplete( - gpu::SwapBuffersCompleteParams params, - gfx::GpuFenceHandle release_fence) { - // Handled by SkiaOutputDevice already. -} - -void SkiaOutputSurfaceImplOnGpu::BufferPresented( - const gfx::PresentationFeedback& feedback) { - // Handled by SkiaOutputDevice already. -} - void SkiaOutputSurfaceImplOnGpu::DidSwapBuffersCompleteInternal( gpu::SwapBuffersCompleteParams params, const gfx::Size& pixel_size, @@ -1935,36 +2066,6 @@ void SkiaOutputSurfaceImplOnGpu::DidSwapBuffersCompleteInternal( waiting_for_full_damage_ = true; } -#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) - // |available_render_pass_overlay_backings_| are used or released in - // SwapBuffers() for every frames. For Ozone-Wayland - // |available_render_pass_overlay_backings_| is not always empty because the - // buffer management in 'GbmSurfacelessWayland::SwapBuffersAsync' will cause - // an accumulation of unsubmitted frames in |unsubmitted_frames_|. These are - // submitted in 'GbmSurfacelessWayland::MaybeSubmitFrames'. Later - // 'GbmSurfacelessWayland::OnSubmission' will then call the callback of these - // |submitted_frames| more than once. This means that this function - // 'DidSwapBuffersCompleteInternal' will get executed again before the - // corresponding 'SkiaOutputSurfaceImplOnGpu::SwapBuffersInternal' has cleared - // 'available_render_pass_overlay_backings_'. -#if !defined(USE_OZONE) - DCHECK(available_render_pass_overlay_backings_.empty()); -#endif - // Erase mailboxes of render pass overlays from |params.released_overlays| and - // move released backings for those render pass overlays from - // |in_flight_render_pass_overlay_backings_| to - // |available_render_pass_overlay_backings_| for reusing. - base::EraseIf(params.released_overlays, [this](const gpu::Mailbox& mailbox) { - auto it = in_flight_render_pass_overlay_backings_.find(mailbox); - if (it == in_flight_render_pass_overlay_backings_.end()) - return false; - available_render_pass_overlay_backings_.push_back(std::move(*it)); - in_flight_render_pass_overlay_backings_.erase(it); - return true; - }); - -#endif - PostTaskToClientThread(base::BindOnce(did_swap_buffer_complete_callback_, params, pixel_size, std::move(release_fence))); @@ -2024,76 +2125,6 @@ void SkiaOutputSurfaceImplOnGpu::PreserveChildSurfaceControls() { gl_surface_->PreserveChildSurfaceControls(); } -#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) -std::unique_ptr<gpu::SharedImageRepresentationSkia> -SkiaOutputSurfaceImplOnGpu::GetOrCreateRenderPassOverlayBacking( - const SkSurfaceCharacterization& characterization) { - ResourceFormat resource_format; - switch (characterization.colorType()) { - case kRGBA_8888_SkColorType: - resource_format = ResourceFormat::RGBA_8888; - break; - case kBGRA_8888_SkColorType: - resource_format = ResourceFormat::BGRA_8888; - break; - case kRGBA_F16_SkColorType: - resource_format = ResourceFormat::RGBA_F16; - break; - default: - resource_format = ResourceFormat::RGBA_8888; - NOTREACHED(); - } - - gfx::Size size(characterization.width(), characterization.height()); - gfx::ColorSpace color_space; - if (characterization.colorSpace()) - color_space = gfx::ColorSpace(*characterization.colorSpace()); - auto it = std::find_if( - available_render_pass_overlay_backings_.begin(), - available_render_pass_overlay_backings_.end(), - [&characterization, &resource_format, &size, &color_space]( - const std::unique_ptr<gpu::SharedImageRepresentationSkia>& backing) { - return backing->format() == resource_format && - backing->size() == size && - backing->color_space() == color_space && - backing->surface_origin() == characterization.origin() && - backing->alpha_type() == - characterization.imageInfo().alphaType(); - }); - - if (it != available_render_pass_overlay_backings_.end()) { - auto backing = std::move(*it); - available_render_pass_overlay_backings_.erase(it); - return backing; - } - - auto mailbox = gpu::Mailbox::GenerateForSharedImage(); - constexpr auto kOverlayUsage = gpu::SHARED_IMAGE_USAGE_SCANOUT | - gpu::SHARED_IMAGE_USAGE_DISPLAY | - gpu::SHARED_IMAGE_USAGE_RASTER; - - bool result = shared_image_factory_->CreateSharedImage( - mailbox, resource_format, size, color_space, characterization.origin(), - characterization.imageInfo().alphaType(), gpu::kNullSurfaceHandle, - kOverlayUsage); - if (!result) { - LOG(ERROR) << "CreateSharedImage() failed."; - MarkContextLost(CONTEXT_LOST_OUT_OF_MEMORY); - return nullptr; - } - - auto backing = shared_image_representation_factory_->ProduceSkia( - mailbox, context_state_.get()); - DCHECK(backing); - - // The |backing| will keep a ref on the shared image, so the image will not be - // released until |backing| is released. - shared_image_factory_->DestroySharedImage(mailbox); - - return backing; -} -#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE) - void SkiaOutputSurfaceImplOnGpu::InitDelegatedInkPointRendererReceiver( mojo::PendingReceiver<gfx::mojom::DelegatedInkPointRenderer> pending_receiver) { @@ -2138,4 +2169,57 @@ void SkiaOutputSurfaceImplOnGpu::DiscardBackbuffer() { output_device_->DiscardBackbuffer(); } +#if BUILDFLAG(ENABLE_VULKAN) +gfx::GpuFenceHandle SkiaOutputSurfaceImplOnGpu::CreateReleaseFenceForVulkan( + const GrBackendSemaphore& semaphore) { + DCHECK(is_using_vulkan()); + + if (semaphore.vkSemaphore() == VK_NULL_HANDLE) + return {}; + + auto* implementation = vulkan_context_provider_->GetVulkanImplementation(); + VkDevice device = + vulkan_context_provider_->GetDeviceQueue()->GetVulkanDevice(); + + auto handle = + implementation->GetSemaphoreHandle(device, semaphore.vkSemaphore()); + if (!handle.is_valid()) { + vkDestroySemaphore(device, semaphore.vkSemaphore(), + /*pAllocator=*/nullptr); + LOG(ERROR) << "Failed to create a release fence for Vulkan."; + return {}; + } + return std::move(handle).ToGpuFenceHandle(); +} + +bool SkiaOutputSurfaceImplOnGpu::CreateAndStoreExternalSemaphoreVulkan( + std::vector<GrBackendSemaphore>& end_semaphores) { + DCHECK(is_using_vulkan()); + + auto* implementation = vulkan_context_provider_->GetVulkanImplementation(); + VkDevice device = + vulkan_context_provider_->GetDeviceQueue()->GetVulkanDevice(); + + VkSemaphore semaphore = implementation->CreateExternalSemaphore(device); + if (semaphore == VK_NULL_HANDLE) { + LOG(ERROR) + << "Creation of an external semaphore for a release fence failed."; + return false; + } + + end_semaphores.emplace_back(); + end_semaphores.back().initVulkan(semaphore); + return true; +} +#endif + +gfx::GpuFenceHandle SkiaOutputSurfaceImplOnGpu::CreateReleaseFenceForGL() { + if (gl::GLFence::IsGpuFenceSupported()) { + auto fence = gl::GLFence::CreateForGpuFence(); + if (fence) + return fence->GetGpuFence()->GetGpuFenceHandle().Clone(); + } + return {}; +} + } // namespace viz 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 20fd2f418d8..204eebdc080 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 @@ -5,6 +5,7 @@ #ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_SURFACE_IMPL_ON_GPU_H_ #define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_SURFACE_IMPL_ON_GPU_H_ +#include <deque> #include <map> #include <memory> #include <utility> @@ -41,6 +42,8 @@ #include "third_party/skia/include/core/SkPromiseImageTexture.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrBackendSemaphore.h" +#include "third_party/skia/include/gpu/GrTypes.h" +#include "ui/gfx/gpu_fence_handle.h" namespace gfx { namespace mojom { @@ -137,12 +140,14 @@ class SkiaOutputSurfaceImplOnGpu const gfx::ColorSpace& color_space, float device_scale_factor, gfx::OverlayTransform transform); - void FinishPaintCurrentFrame(sk_sp<SkDeferredDisplayList> ddl, - sk_sp<SkDeferredDisplayList> overdraw_ddl, - std::vector<ImageContextImpl*> image_contexts, - std::vector<gpu::SyncToken> sync_tokens, - base::OnceClosure on_finished, - absl::optional<gfx::Rect> draw_rectangle); + void FinishPaintCurrentFrame( + sk_sp<SkDeferredDisplayList> ddl, + sk_sp<SkDeferredDisplayList> overdraw_ddl, + std::vector<ImageContextImpl*> image_contexts, + std::vector<gpu::SyncToken> sync_tokens, + base::OnceClosure on_finished, + base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence_cb, + absl::optional<gfx::Rect> draw_rectangle); void ScheduleOutputSurfaceAsOverlay( const OverlayProcessorInterface::OutputSurfaceOverlayPlane& output_surface_plane); @@ -157,11 +162,13 @@ class SkiaOutputSurfaceImplOnGpu void SwapBuffersSkipped(); void EnsureBackbuffer(); void DiscardBackbuffer(); - void FinishPaintRenderPass(const gpu::Mailbox& mailbox, - sk_sp<SkDeferredDisplayList> ddl, - std::vector<ImageContextImpl*> image_contexts, - std::vector<gpu::SyncToken> sync_tokens, - base::OnceClosure on_finished); + void FinishPaintRenderPass( + const gpu::Mailbox& mailbox, + sk_sp<SkDeferredDisplayList> ddl, + std::vector<ImageContextImpl*> image_contexts, + std::vector<gpu::SyncToken> sync_tokens, + base::OnceClosure on_finished, + base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence_cb); // Deletes resources for RenderPasses in |ids|. Also takes ownership of // |images_contexts| and destroys them on GPU thread. void RemoveRenderPassResource( @@ -184,9 +191,7 @@ class SkiaOutputSurfaceImplOnGpu void ReleaseImageContexts( std::vector<std::unique_ptr<ExternalUseClient::ImageContext>> image_contexts); - void ScheduleOverlays(SkiaOutputSurface::OverlayList overlays, - std::vector<ImageContextImpl*> image_contexts, - base::OnceClosure on_finished); + void ScheduleOverlays(SkiaOutputSurface::OverlayList overlays); void SetEnableDCLayers(bool enable); void SetGpuVSyncEnabled(bool enabled); @@ -210,9 +215,6 @@ class SkiaOutputSurfaceImplOnGpu #endif const gpu::gles2::FeatureInfo* GetFeatureInfo() const override; const gpu::GpuPreferences& GetGpuPreferences() const override; - void DidSwapBuffersComplete(gpu::SwapBuffersCompleteParams params, - gfx::GpuFenceHandle release_fence) override; - void BufferPresented(const gfx::PresentationFeedback& feedback) override; GpuVSyncCallback GetGpuVSyncCallback() override; base::TimeDelta GetGpuBlockedTimeSinceLastSwap() override; @@ -301,6 +303,8 @@ class SkiaOutputSurfaceImplOnGpu gpu_preferences_.gr_context_type == gpu::GrContextType::kDawn; } + bool is_using_gl() const { return !is_using_vulkan() && !is_using_dawn(); } + // Helper for `CopyOutput()` method, handles the RGBA format. void CopyOutputRGBA(SkSurface* surface, copy_output::RenderPassGeometry geometry, @@ -336,17 +340,19 @@ class SkiaOutputSurfaceImplOnGpu // |surface| into |dest_surface|'s canvas, cropping and scaling the results // appropriately. |source_selection| is the area of the |surface| that will be // rendered to the destination. - // |begin_semaphores| will be submitted to the GPU backend prior to issuing - // draw calls to the |dest_surface|. - // |end_semaphores| will be submitted to the GPU backend alongside the draw - // calls to the |dest_surface|. - bool RenderSurface(SkSurface* surface, + void RenderSurface(SkSurface* surface, const SkIRect& source_selection, absl::optional<SkVector> scaling, bool is_downscale_or_identity_in_both_dimensions, - SkSurface* dest_surface, - std::vector<GrBackendSemaphore>& begin_semaphores, - std::vector<GrBackendSemaphore>& end_semaphores); + SkSurface* dest_surface); + + // Helper for `CopyOutputNV12()` & `CopyOutputRGBA()` methods, flushes writes + // to |surface| with |end_semaphores| and |end_state|. + bool FlushSurface(SkSurface* surface, + std::vector<GrBackendSemaphore>& end_semaphores, + std::unique_ptr<GrBackendSurfaceMutableState> end_state, + GrGpuFinishedProc finished_proc = nullptr, + GrGpuFinishedContext finished_context = nullptr); // Creates surfaces needed to store the data in NV12 format. // |plane_access_datas| will be populated with information needed to access @@ -382,11 +388,18 @@ class SkiaOutputSurfaceImplOnGpu void ReleaseAsyncReadResultHelpers(); -#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) - std::unique_ptr<gpu::SharedImageRepresentationSkia> - GetOrCreateRenderPassOverlayBacking( - const SkSurfaceCharacterization& characterization); +#if BUILDFLAG(ENABLE_VULKAN) + // Creates a release fence. The semaphore is an external semaphore created + // by CreateAndStoreExternalSemaphoreVulkan(). May destroy VkSemaphore that + // the |semaphore| stores if creation of a release fence fails. In this case, + // invalid fence handle is returned. + gfx::GpuFenceHandle CreateReleaseFenceForVulkan( + const GrBackendSemaphore& semaphore); + // Returns true if succeess. + bool CreateAndStoreExternalSemaphoreVulkan( + std::vector<GrBackendSemaphore>& end_semaphores); #endif + gfx::GpuFenceHandle CreateReleaseFenceForGL(); class ReleaseCurrent { public: @@ -466,7 +479,9 @@ class SkiaOutputSurfaceImplOnGpu base::flat_set<ImageContextImpl*> image_contexts_; }; PromiseImageAccessHelper promise_image_access_helper_{this}; - base::flat_set<ImageContextImpl*> image_contexts_with_end_access_state_; + base::flat_set<std::pair<ImageContextImpl*, + std::unique_ptr<GrBackendSurfaceMutableState>>> + image_contexts_with_end_access_state_; std::unique_ptr<SkiaOutputDevice> output_device_; std::unique_ptr<SkiaOutputDevice::ScopedPaint> scoped_output_device_paint_; @@ -488,33 +503,12 @@ class SkiaOutputSurfaceImplOnGpu // Tracking for ongoing AsyncReadResults. base::flat_set<AsyncReadResultHelper*> async_read_result_helpers_; -#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE) - using UniqueBackingPtr = std::unique_ptr<gpu::SharedImageRepresentationSkia>; - class BackingComparator { - public: - using is_transparent = void; - bool operator()(const UniqueBackingPtr& lhs, - const UniqueBackingPtr& rhs) const { - return lhs->mailbox() < rhs->mailbox(); - } - bool operator()(const UniqueBackingPtr& lhs, - const gpu::Mailbox& rhs) const { - return lhs->mailbox() < rhs; - } - bool operator()(const gpu::Mailbox& lhs, - const UniqueBackingPtr& rhs) const { - return lhs < rhs->mailbox(); - } - }; - // Render pass overlay backings are in flight. - // The base::flat_set uses backing->mailbox() as the unique key. - base::flat_set<UniqueBackingPtr, BackingComparator> - in_flight_render_pass_overlay_backings_; - - // Render pass overlay backings are available for reusing. - std::vector<std::unique_ptr<gpu::SharedImageRepresentationSkia>> - available_render_pass_overlay_backings_; -#endif + // Pending release fence callbacks. These callbacks can be delayed if Vulkan + // external semaphore type has copy transference, which means importing + // semaphores has to be delayed until submission. + std::deque<std::pair<GrBackendSemaphore, + base::OnceCallback<void(gfx::GpuFenceHandle)>>> + pending_release_fence_cbs_; THREAD_CHECKER(thread_checker_); 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 88dd8eab89f..8d694407870 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 @@ -8,6 +8,7 @@ #include <utility> #include "base/bind.h" +#include "base/callback_forward.h" #include "base/callback_helpers.h" #include "base/run_loop.h" #include "cc/test/fake_output_surface_client.h" @@ -23,6 +24,7 @@ #include "gpu/command_buffer/service/service_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/gpu_fence_handle.h" #include "ui/gl/gl_implementation.h" namespace viz { @@ -45,8 +47,10 @@ class SkiaOutputSurfaceImplTest : public testing::Test { void SetUpSkiaOutputSurfaceImpl(); // Paints and submits root RenderPass with a solid color rect of |size|. - gpu::SyncToken PaintRootRenderPass(const gfx::Rect& output_rect, - base::OnceClosure closure); + gpu::SyncToken PaintRootRenderPass( + const gfx::Rect& output_rect, + base::OnceClosure closure, + base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence); void CheckSyncTokenOnGpuThread(const gpu::SyncToken& sync_token); void CopyRequestCallbackOnGpuThread(const gfx::Rect& output_rect, @@ -75,27 +79,27 @@ SkiaOutputSurfaceImplTest::~SkiaOutputSurfaceImplTest() { } void SkiaOutputSurfaceImplTest::SetUpSkiaOutputSurfaceImpl() { - RendererSettings settings; - settings.use_skia_renderer = true; auto skia_deps = std::make_unique<SkiaOutputSurfaceDependencyImpl>( GetGpuService(), gpu::kNullSurfaceHandle); display_controller_ = std::make_unique<DisplayCompositorMemoryAndTaskController>( std::move(skia_deps)); - output_surface_ = SkiaOutputSurfaceImpl::Create(display_controller_.get(), - settings, &debug_settings_); + output_surface_ = SkiaOutputSurfaceImpl::Create( + display_controller_.get(), RendererSettings(), &debug_settings_); output_surface_->BindToClient(&output_surface_client_); } gpu::SyncToken SkiaOutputSurfaceImplTest::PaintRootRenderPass( const gfx::Rect& rect, - base::OnceClosure closure) { + base::OnceClosure closure, + base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence) { SkPaint paint; paint.setColor(kOutputColor); SkCanvas* root_canvas = output_surface_->BeginPaintCurrentFrame(); root_canvas->drawRect( SkRect::MakeXYWH(rect.x(), rect.y(), rect.height(), rect.width()), paint); - output_surface_->EndPaint(std::move(closure)); + output_surface_->EndPaint(std::move(closure), + std::move(return_release_fence)); return output_surface_->Flush(); } @@ -136,16 +140,22 @@ void SkiaOutputSurfaceImplTest::CopyRequestCallbackOnGpuThread( } TEST_F(SkiaOutputSurfaceImplTest, EndPaint) { - output_surface_->Reshape(kSurfaceRect.size(), 1, gfx::ColorSpace(), - gfx::BufferFormat::RGBX_8888, /*use_stencil=*/false); + OutputSurface::ReshapeParams reshape_params; + reshape_params.size = kSurfaceRect.size(); + output_surface_->Reshape(reshape_params); constexpr gfx::Rect output_rect(0, 0, 10, 10); bool on_finished_called = false; base::OnceClosure on_finished = base::BindOnce([](bool* result) { *result = true; }, &on_finished_called); + bool on_return_release_fence_called = false; + base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence_cb = + base::BindOnce( + [](bool* result, gfx::GpuFenceHandle handle) { *result = true; }, + &on_return_release_fence_called); - gpu::SyncToken sync_token = - PaintRootRenderPass(output_rect, std::move(on_finished)); + gpu::SyncToken sync_token = PaintRootRenderPass( + output_rect, std::move(on_finished), std::move(return_release_fence_cb)); EXPECT_TRUE(sync_token.HasData()); // Copy the output @@ -177,19 +187,25 @@ TEST_F(SkiaOutputSurfaceImplTest, EndPaint) { output_surface_->ScheduleGpuTaskForTesting(std::move(closure), {sync_token}); BlockMainThread(); EXPECT_TRUE(on_finished_called); + + // Let the cb to come back. + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(on_return_release_fence_called); } // Draws two frames and calls Reshape() between the two frames changing the // color space. Verifies draw after color space change is successful. TEST_F(SkiaOutputSurfaceImplTest, SupportsColorSpaceChange) { for (auto& color_space : {gfx::ColorSpace(), gfx::ColorSpace::CreateSRGB()}) { - output_surface_->Reshape(kSurfaceRect.size(), 1, color_space, - gfx::BufferFormat::RGBX_8888, - /*use_stencil=*/false); + OutputSurface::ReshapeParams reshape_params; + reshape_params.size = kSurfaceRect.size(); + reshape_params.color_space = color_space; + output_surface_->Reshape(reshape_params); // Draw something, it's not important what. base::RunLoop run_loop; - PaintRootRenderPass(kSurfaceRect, run_loop.QuitClosure()); + PaintRootRenderPass(kSurfaceRect, run_loop.QuitClosure(), + base::DoNothing()); OutputSurfaceFrame frame; frame.size = kSurfaceRect.size(); @@ -203,8 +219,9 @@ TEST_F(SkiaOutputSurfaceImplTest, SupportsColorSpaceChange) { // Tests that the destination color space is preserved across a CopyOutput for // ColorSpaces supported by SkColorSpace. TEST_F(SkiaOutputSurfaceImplTest, CopyOutputBitmapSupportedColorSpace) { - output_surface_->Reshape(kSurfaceRect.size(), 1, gfx::ColorSpace(), - gfx::BufferFormat::RGBX_8888, /*use_stencil=*/false); + OutputSurface::ReshapeParams reshape_params; + reshape_params.size = kSurfaceRect.size(); + output_surface_->Reshape(reshape_params); constexpr gfx::Rect output_rect(0, 0, 10, 10); const gfx::ColorSpace color_space = gfx::ColorSpace( @@ -230,7 +247,7 @@ TEST_F(SkiaOutputSurfaceImplTest, CopyOutputBitmapSupportedColorSpace) { geometry.sampling_bounds = output_rect; geometry.readback_offset = gfx::Vector2d(0, 0); - PaintRootRenderPass(kSurfaceRect, base::DoNothing()); + PaintRootRenderPass(kSurfaceRect, base::DoNothing(), base::DoNothing()); output_surface_->CopyOutput(AggregatedRenderPassId{0}, geometry, color_space, std::move(request), gpu::Mailbox()); output_surface_->SwapBuffersSkipped(kSurfaceRect); @@ -243,8 +260,9 @@ TEST_F(SkiaOutputSurfaceImplTest, CopyOutputBitmapSupportedColorSpace) { // Tests that copying from a source with a color space that can't be converted // to a SkColorSpace will fallback to a transform to sRGB. TEST_F(SkiaOutputSurfaceImplTest, CopyOutputBitmapUnsupportedColorSpace) { - output_surface_->Reshape(kSurfaceRect.size(), 1, gfx::ColorSpace(), - gfx::BufferFormat::RGBX_8888, /*use_stencil=*/false); + OutputSurface::ReshapeParams reshape_params; + reshape_params.size = kSurfaceRect.size(); + output_surface_->Reshape(reshape_params); constexpr gfx::Rect output_rect(0, 0, 10, 10); const gfx::ColorSpace color_space = gfx::ColorSpace::CreatePiecewiseHDR( @@ -270,7 +288,7 @@ TEST_F(SkiaOutputSurfaceImplTest, CopyOutputBitmapUnsupportedColorSpace) { geometry.sampling_bounds = output_rect; geometry.readback_offset = gfx::Vector2d(0, 0); - PaintRootRenderPass(kSurfaceRect, base::DoNothing()); + PaintRootRenderPass(kSurfaceRect, base::DoNothing(), base::DoNothing()); output_surface_->CopyOutput(AggregatedRenderPassId{0}, geometry, color_space, std::move(request), gpu::Mailbox()); output_surface_->SwapBuffersSkipped(kSurfaceRect); diff --git a/chromium/components/viz/service/display_embedder/skia_render_copy_results.cc b/chromium/components/viz/service/display_embedder/skia_render_copy_results.cc index 7c400b6d1bb..20541a6144a 100644 --- a/chromium/components/viz/service/display_embedder/skia_render_copy_results.cc +++ b/chromium/components/viz/service/display_embedder/skia_render_copy_results.cc @@ -8,6 +8,7 @@ #include <utility> #include "base/memory/ptr_util.h" +#include "components/viz/common/frame_sinks/copy_output_result.h" #include "components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h" #include "third_party/libyuv/include/libyuv/planar_functions.h" #include "third_party/skia/include/core/SkPixelRef.h" @@ -322,4 +323,49 @@ void CopyOutputResultSkiaNV12::OnNV12PlaneReadbackDone( std::move(async_result)); } +NV12PlanesReadyContext::NV12PlanesReadyContext( + base::WeakPtr<SkiaOutputSurfaceImplOnGpu> impl_on_gpu, + std::unique_ptr<CopyOutputRequest> request, + const gfx::Rect& result_rect, + const std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes>& + plane_mailbox_holders, + const gfx::ColorSpace& color_space) + : request_(std::move(request)), + result_rect_(result_rect), + plane_mailbox_holders_(plane_mailbox_holders), + color_space_(color_space) {} + +NV12PlanesReadyContext::~NV12PlanesReadyContext() { + DCHECK_EQ(outstanding_planes_, 0); +} + +void NV12PlanesReadyContext::OnNV12PlaneReady() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + if (impl_on_gpu_) { + impl_on_gpu_->ReadbackDone(); + } + + outstanding_planes_--; + if (outstanding_planes_ == 0) { + request_->SendResult(std::make_unique<CopyOutputTextureResult>( + CopyOutputResult::Format::NV12_PLANES, result_rect_, + CopyOutputResult::TextureResult(plane_mailbox_holders_, color_space_), + CopyOutputResult::ReleaseCallbacks())); + } +} + +NV12SinglePlaneReadyContext::NV12SinglePlaneReadyContext( + scoped_refptr<NV12PlanesReadyContext> nv12_planes_flushed) + : nv12_planes_flushed(nv12_planes_flushed) {} + +NV12SinglePlaneReadyContext::~NV12SinglePlaneReadyContext() = default; + +// static +void NV12SinglePlaneReadyContext::OnNV12PlaneReady(GrGpuFinishedContext c) { + auto context = base::WrapUnique(static_cast<NV12SinglePlaneReadyContext*>(c)); + + context->nv12_planes_flushed->OnNV12PlaneReady(); +} + } // namespace viz diff --git a/chromium/components/viz/service/display_embedder/skia_render_copy_results.h b/chromium/components/viz/service/display_embedder/skia_render_copy_results.h index f305a081177..5c056688d2f 100644 --- a/chromium/components/viz/service/display_embedder/skia_render_copy_results.h +++ b/chromium/components/viz/service/display_embedder/skia_render_copy_results.h @@ -16,6 +16,8 @@ #include "third_party/libyuv/include/libyuv/planar_functions.h" #include "third_party/skia/include/core/SkPixelRef.h" #include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/gpu/GrTypes.h" +#include "ui/gfx/color_space.h" namespace viz { @@ -174,6 +176,70 @@ struct NV12PlanePixelReadContext { int plane_index; }; +// Context that is responsible for sending a CopyOutputResult once the GPU work +// that populates the GpuMemoryBuffer for the NV12 planes has completed. It will +// be notified by multiple `NV12SinglePlaneReadyContext`s that the plane has +// been populated, and once all planes have been completed, it will send the +// CopyOutputResult. +class NV12PlanesReadyContext : public base::RefCounted<NV12PlanesReadyContext> { + public: + NV12PlanesReadyContext( + base::WeakPtr<SkiaOutputSurfaceImplOnGpu> impl_on_gpu, + std::unique_ptr<CopyOutputRequest> request, + const gfx::Rect& result_rect, + const std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes>& + plane_mailbox_holders, + const gfx::ColorSpace& color_space); + + NV12PlanesReadyContext(const NV12PlanesReadyContext& other) = delete; + NV12PlanesReadyContext& operator=(const NV12PlanesReadyContext& other) = + delete; + + void OnNV12PlaneReady(); + + private: + friend class base::RefCounted<NV12PlanesReadyContext>; + ~NV12PlanesReadyContext(); + + // Needed to notify `SkiaOutputSurfaceImplOnGpu` that readback has completed. + // GPU work is not a readback, but we rely on the same mechanism for nudging + // Skia to periodically check for asynchronous event completion. + base::WeakPtr<SkiaOutputSurfaceImplOnGpu> impl_on_gpu_; + // Request that we will send a response to: + std::unique_ptr<CopyOutputRequest> request_; + + // Data needed to create a response to `request_`: + gfx::Rect result_rect_; + std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes> + plane_mailbox_holders_; + gfx::ColorSpace color_space_; + + // Number of planes that still need to report completion: + int outstanding_planes_ = CopyOutputResult::kNV12MaxPlanes; + + THREAD_CHECKER(thread_checker_); +}; + +// Context that is responsible for notifying `NV12PlanesReadyContext` that GPU +// side of populating the GpuMemoryBuffer has completed. +struct NV12SinglePlaneReadyContext { + explicit NV12SinglePlaneReadyContext( + scoped_refptr<NV12PlanesReadyContext> nv12_planes_flushed); + + NV12SinglePlaneReadyContext(const NV12SinglePlaneReadyContext& other) = + delete; + NV12SinglePlaneReadyContext& operator=( + const NV12SinglePlaneReadyContext& other) = delete; + + ~NV12SinglePlaneReadyContext(); + + // Will be called with `NV12PlaneFlushedContext*`: + static void OnNV12PlaneReady(GrGpuFinishedContext context); + + // Context to be notified that a plane has been populated. + scoped_refptr<NV12PlanesReadyContext> nv12_planes_flushed; +}; + class CopyOutputResultSkiaNV12 : public CopyOutputResult { public: CopyOutputResultSkiaNV12( diff --git a/chromium/components/viz/service/display_embedder/software_output_surface.cc b/chromium/components/viz/service/display_embedder/software_output_surface.cc index 35b4b9dd371..0fe3318ef87 100644 --- a/chromium/components/viz/service/display_embedder/software_output_surface.cc +++ b/chromium/components/viz/service/display_embedder/software_output_surface.cc @@ -24,12 +24,12 @@ namespace viz { SoftwareOutputSurface::SoftwareOutputSurface( - std::unique_ptr<SoftwareOutputDevice> software_device) - : OutputSurface(std::move(software_device)) { + std::unique_ptr<SoftwareOutputDevice> device) + : OutputSurface(std::move(device)) { capabilities_.pending_swap_params.max_pending_swaps = - software_device_->MaxFramesPending(); + software_device()->MaxFramesPending(); capabilities_.resize_based_on_root_surface = - software_device_->SupportsOverridePlatformSize(); + software_device()->SupportsOverridePlatformSize(); } SoftwareOutputSurface::~SoftwareOutputSurface() = default; @@ -48,17 +48,8 @@ void SoftwareOutputSurface::DiscardBackbuffer() { software_device()->DiscardBackbuffer(); } -void SoftwareOutputSurface::BindFramebuffer() { - // Not used for software surfaces. - NOTREACHED(); -} - -void SoftwareOutputSurface::Reshape(const gfx::Size& size, - float device_scale_factor, - const gfx::ColorSpace& color_space, - gfx::BufferFormat format, - bool use_stencil) { - software_device()->Resize(size, device_scale_factor); +void SoftwareOutputSurface::Reshape(const ReshapeParams& params) { + software_device()->Resize(params.size, params.device_scale_factor); } void SoftwareOutputSurface::SwapBuffers(OutputSurfaceFrame frame) { @@ -89,22 +80,6 @@ bool SoftwareOutputSurface::IsDisplayedAsOverlayPlane() const { return false; } -unsigned SoftwareOutputSurface::GetOverlayTextureId() const { - return 0; -} - -bool SoftwareOutputSurface::HasExternalStencilTest() const { - return false; -} - -void SoftwareOutputSurface::ApplyExternalStencil() {} - -uint32_t SoftwareOutputSurface::GetFramebufferCopyTextureFormat() { - // Not used for software surfaces. - NOTREACHED(); - return 0; -} - void SoftwareOutputSurface::SwapBuffersCallback(base::TimeTicks swap_time, const gfx::Size& pixel_size) { latency_tracker_.OnGpuSwapBuffersCompleted( @@ -134,10 +109,6 @@ void SoftwareOutputSurface::UpdateVSyncParameters(base::TimeTicks timebase, update_vsync_parameters_callback_.Run(timebase, interval); } -unsigned SoftwareOutputSurface::UpdateGpuFence() { - return 0; -} - void SoftwareOutputSurface::SetUpdateVSyncParametersCallback( UpdateVSyncParametersCallback callback) { update_vsync_parameters_callback_ = std::move(callback); diff --git a/chromium/components/viz/service/display_embedder/software_output_surface.h b/chromium/components/viz/service/display_embedder/software_output_surface.h index 4425e9f28a6..a7127891314 100644 --- a/chromium/components/viz/service/display_embedder/software_output_surface.h +++ b/chromium/components/viz/service/display_embedder/software_output_surface.h @@ -38,19 +38,9 @@ class VIZ_SERVICE_EXPORT SoftwareOutputSurface : public OutputSurface { void BindToClient(OutputSurfaceClient* client) override; void EnsureBackbuffer() override; void DiscardBackbuffer() override; - void BindFramebuffer() override; - void Reshape(const gfx::Size& size, - float device_scale_factor, - const gfx::ColorSpace& color_space, - gfx::BufferFormat format, - bool use_stencil) override; + void Reshape(const ReshapeParams& params) override; void SwapBuffers(OutputSurfaceFrame frame) override; bool IsDisplayedAsOverlayPlane() const override; - unsigned GetOverlayTextureId() const override; - bool HasExternalStencilTest() const override; - void ApplyExternalStencil() override; - uint32_t GetFramebufferCopyTextureFormat() override; - unsigned UpdateGpuFence() override; void SetUpdateVSyncParametersCallback( UpdateVSyncParametersCallback callback) override; void SetDisplayTransformHint(gfx::OverlayTransform transform) override {} diff --git a/chromium/components/viz/service/display_embedder/viz_process_context_provider.cc b/chromium/components/viz/service/display_embedder/viz_process_context_provider.cc deleted file mode 100644 index ed33d5f717f..00000000000 --- a/chromium/components/viz/service/display_embedder/viz_process_context_provider.cc +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright 2016 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/viz_process_context_provider.h" - -#include <stdint.h> - -#include <utility> - -#include "base/bind.h" -#include "base/lazy_instance.h" -#include "base/metrics/histogram_macros.h" -#include "base/observer_list.h" -#include "base/system/sys_info.h" -#include "base/task/single_thread_task_runner.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/trace_event/memory_dump_manager.h" -#include "build/build_config.h" -#include "build/chromeos_buildflags.h" -#include "components/viz/common/display/renderer_settings.h" -#include "components/viz/common/gpu/context_lost_observer.h" -#include "components/viz/common/gpu/context_lost_reason.h" -#include "components/viz/common/resources/platform_color.h" -#include "components/viz/common/viz_utils.h" -#include "components/viz/service/display/display_compositor_memory_and_task_controller.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "gpu/command_buffer/client/gles2_cmd_helper.h" -#include "gpu/command_buffer/client/gles2_implementation.h" -#include "gpu/command_buffer/client/raster_implementation_gles.h" -#include "gpu/command_buffer/client/shared_memory_limits.h" -#include "gpu/command_buffer/client/transfer_buffer.h" -#include "gpu/config/gpu_feature_info.h" -#include "gpu/config/gpu_preferences.h" -#include "gpu/config/skia_limits.h" -#include "gpu/ipc/common/surface_handle.h" -#include "gpu/ipc/in_process_command_buffer.h" -#include "gpu/skia_bindings/gles2_implementation_with_grcontext_support.h" -#include "gpu/skia_bindings/grcontext_for_gles2_interface.h" -#include "third_party/khronos/GLES2/gl2.h" -#include "third_party/khronos/GLES2/gl2ext.h" -#include "third_party/skia/include/gpu/GrDirectContext.h" -#include "third_party/skia/include/gpu/gl/GrGLInterface.h" - -namespace viz { - -namespace { - -gpu::ContextCreationAttribs CreateAttributes( - bool requires_alpha_channel, - const RendererSettings& renderer_settings) { - gpu::ContextCreationAttribs attributes; - attributes.alpha_size = requires_alpha_channel ? 8 : -1; - attributes.depth_size = 0; -#if BUILDFLAG(IS_CHROMEOS_ASH) - // Chrome OS uses surfaceless when running on a real device and stencil - // buffers can then be added dynamically so supporting them does not have an - // impact on normal usage. If we are not running on a real Chrome OS device - // but instead on a workstation for development, then stencil support is - // useful as it allows the overdraw feedback debugging feature to be used. - attributes.stencil_size = 8; -#else - attributes.stencil_size = 0; -#endif - attributes.samples = 0; - attributes.sample_buffers = 0; - attributes.bind_generates_resource = false; - attributes.fail_if_major_perf_caveat = false; - attributes.lose_context_when_out_of_memory = true; - -#if BUILDFLAG(IS_ANDROID) - if (renderer_settings.color_space == gfx::ColorSpace::CreateSRGB()) { - attributes.color_space = gpu::COLOR_SPACE_SRGB; - } else if (renderer_settings.color_space == - gfx::ColorSpace::CreateDisplayP3D65()) { - attributes.color_space = gpu::COLOR_SPACE_DISPLAY_P3; - } else { - // The browser only sends the above two color spaces. - NOTREACHED(); - } - - if (!requires_alpha_channel && PreferRGB565ResourcesForDisplay()) { - // See compositor_impl_android.cc for more information about this. - // It is inside GetCompositorContextAttributes(). - attributes.alpha_size = 0; - attributes.red_size = 5; - attributes.green_size = 6; - attributes.blue_size = 5; - } - - attributes.enable_swap_timestamps_if_supported = true; -#endif // BUILDFLAG(IS_ANDROID) - - return attributes; -} - -void UmaRecordContextLost(ContextLostReason reason) { - UMA_HISTOGRAM_ENUMERATION("GPU.ContextLost.DisplayCompositor", reason); -} - -gpu::SharedMemoryLimits SharedMemoryLimitsForRendererSettings( - const RendererSettings& renderer_settings) { -#if BUILDFLAG(IS_ANDROID) - return gpu::SharedMemoryLimits::ForDisplayCompositor( - renderer_settings.initial_screen_size); -#else - return gpu::SharedMemoryLimits::ForDisplayCompositor(); -#endif -} - -} // namespace - -VizProcessContextProvider::VizProcessContextProvider( - gpu::CommandBufferTaskExecutor* task_executor, - gpu::SurfaceHandle surface_handle, - gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, - gpu::ImageFactory* image_factory, - gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate, - DisplayCompositorMemoryAndTaskController* display_controller, - const RendererSettings& renderer_settings) - : attributes_(CreateAttributes(renderer_settings.requires_alpha_channel, - renderer_settings)) { - InitializeContext(std::move(task_executor), surface_handle, - gpu_memory_buffer_manager, image_factory, - gpu_channel_manager_delegate, display_controller, - SharedMemoryLimitsForRendererSettings(renderer_settings)); - - if (context_result_ == gpu::ContextResult::kSuccess) { - // |gles2_implementation_| is owned here so bind an unretained pointer or - // there will be a circular reference preventing destruction. - gles2_implementation_->SetLostContextCallback(base::BindOnce( - &VizProcessContextProvider::OnContextLost, base::Unretained(this))); - - base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( - this, "VizProcessContextProvider", base::ThreadTaskRunnerHandle::Get()); - } else { - UmaRecordContextLost(CONTEXT_INIT_FAILED); - } -} - -VizProcessContextProvider::VizProcessContextProvider() = default; - -VizProcessContextProvider::~VizProcessContextProvider() { - if (context_result_ == gpu::ContextResult::kSuccess) { - base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( - this); - } - - // cache_controller_ might ne nullptr if we failed to initialize - if (cache_controller_) - cache_controller_->SetGrContext(nullptr); -} - -void VizProcessContextProvider::AddRef() const { - base::RefCountedThreadSafe<VizProcessContextProvider>::AddRef(); -} - -void VizProcessContextProvider::Release() const { - base::RefCountedThreadSafe<VizProcessContextProvider>::Release(); -} - -gpu::ContextResult VizProcessContextProvider::BindToCurrentThread() { - return context_result_; -} - -gpu::gles2::GLES2Interface* VizProcessContextProvider::ContextGL() { - return gles2_implementation_.get(); -} - -gpu::ContextSupport* VizProcessContextProvider::ContextSupport() { - return gles2_implementation_.get(); -} - -class GrDirectContext* VizProcessContextProvider::GrContext() { - if (gr_context_) - return gr_context_->get(); - - size_t max_resource_cache_bytes; - size_t max_glyph_cache_texture_bytes; - gpu::DetermineGrCacheLimitsFromAvailableMemory( - &max_resource_cache_bytes, &max_glyph_cache_texture_bytes); - - gr_context_ = std::make_unique<skia_bindings::GrContextForGLES2Interface>( - ContextGL(), ContextSupport(), ContextCapabilities(), - max_resource_cache_bytes, max_glyph_cache_texture_bytes); - cache_controller_->SetGrContext(gr_context_->get()); - return gr_context_->get(); -} - -gpu::SharedImageInterface* VizProcessContextProvider::SharedImageInterface() { - return command_buffer_->GetSharedImageInterface(); -} - -ContextCacheController* VizProcessContextProvider::CacheController() { - return cache_controller_.get(); -} - -base::Lock* VizProcessContextProvider::GetLock() { - // Locking isn't supported on display compositor contexts. - return nullptr; -} - -const gpu::Capabilities& VizProcessContextProvider::ContextCapabilities() - const { - return command_buffer_->GetCapabilities(); -} - -const gpu::GpuFeatureInfo& VizProcessContextProvider::GetGpuFeatureInfo() - const { - return command_buffer_->GetGpuFeatureInfo(); -} - -void VizProcessContextProvider::AddObserver(ContextLostObserver* obs) { - observers_.AddObserver(obs); -} - -void VizProcessContextProvider::RemoveObserver(ContextLostObserver* obs) { - observers_.RemoveObserver(obs); -} - -void VizProcessContextProvider::SetUpdateVSyncParametersCallback( - UpdateVSyncParametersCallback callback) { - command_buffer_->SetUpdateVSyncParametersCallback(std::move(callback)); -} - -void VizProcessContextProvider::SetGpuVSyncCallback(GpuVSyncCallback callback) { - command_buffer_->SetGpuVSyncCallback(std::move(callback)); -} - -void VizProcessContextProvider::SetGpuVSyncEnabled(bool enabled) { - command_buffer_->SetGpuVSyncEnabled(enabled); -} - -bool VizProcessContextProvider::UseRGB565PixelFormat() const { - return attributes_.alpha_size == 0 && attributes_.red_size == 5 && - attributes_.green_size == 6 && attributes_.blue_size == 5; -} - -uint32_t VizProcessContextProvider::GetCopyTextureInternalFormat() { - return attributes_.alpha_size > 0 ? GL_RGBA : GL_RGB; -} - -void VizProcessContextProvider::InitializeContext( - gpu::CommandBufferTaskExecutor* task_executor, - gpu::SurfaceHandle surface_handle, - gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, - gpu::ImageFactory* image_factory, - gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate, - DisplayCompositorMemoryAndTaskController* display_controller, - const gpu::SharedMemoryLimits& mem_limits) { - const bool is_offscreen = surface_handle == gpu::kNullSurfaceHandle; - DCHECK(display_controller); - gpu_task_scheduler_helper_ = display_controller->gpu_task_scheduler(); - - command_buffer_ = std::make_unique<gpu::InProcessCommandBuffer>( - task_executor, - GURL("chrome://gpu/VizProcessContextProvider::InitializeContext")); - context_result_ = command_buffer_->Initialize( - /*surface=*/nullptr, is_offscreen, surface_handle, attributes_, - gpu_memory_buffer_manager, image_factory, gpu_channel_manager_delegate, - base::ThreadTaskRunnerHandle::Get(), - gpu_task_scheduler_helper_->GetTaskSequence(), - display_controller->controller_on_gpu(), nullptr, nullptr); - if (context_result_ != gpu::ContextResult::kSuccess) { - DLOG(ERROR) << "Failed to initialize InProcessCommmandBuffer"; - return; - } - - // Create the GLES2 helper, which writes the command buffer protocol. - gles2_helper_ = - std::make_unique<gpu::gles2::GLES2CmdHelper>(command_buffer_.get()); - context_result_ = gles2_helper_->Initialize(mem_limits.command_buffer_size); - if (context_result_ != gpu::ContextResult::kSuccess) { - DLOG(ERROR) << "Failed to initialize GLES2CmdHelper"; - return; - } - - if (gpu_task_scheduler_helper_) - gpu_task_scheduler_helper_->Initialize(gles2_helper_.get()); - - transfer_buffer_ = std::make_unique<gpu::TransferBuffer>(gles2_helper_.get()); - - // Create the object exposing the OpenGL API. - gles2_implementation_ = - std::make_unique<skia_bindings::GLES2ImplementationWithGrContextSupport>( - gles2_helper_.get(), /*share_group=*/nullptr, transfer_buffer_.get(), - attributes_.bind_generates_resource, - attributes_.lose_context_when_out_of_memory, - /*support_client_side_arrays=*/false, command_buffer_.get()); - - context_result_ = gles2_implementation_->Initialize(mem_limits); - if (context_result_ != gpu::ContextResult::kSuccess) { - DLOG(ERROR) << "Failed to initialize GLES2Implementation"; - return; - } - - cache_controller_ = std::make_unique<ContextCacheController>( - gles2_implementation_.get(), base::ThreadTaskRunnerHandle::Get()); - - // TraceEndCHROMIUM is implicit when the context is destroyed - gles2_implementation_->TraceBeginCHROMIUM("VizCompositor", - "DisplayCompositor"); -} - -void VizProcessContextProvider::OnContextLost() { - for (auto& observer : observers_) - observer.OnContextLost(); - if (gr_context_) - gr_context_->OnLostContext(); - - gpu::CommandBuffer::State state = command_buffer_->GetLastState(); - UmaRecordContextLost( - GetContextLostReason(state.error, state.context_lost_reason)); -} - -bool VizProcessContextProvider::OnMemoryDump( - const base::trace_event::MemoryDumpArgs& args, - base::trace_event::ProcessMemoryDump* pmd) { - DCHECK_EQ(context_result_, gpu::ContextResult::kSuccess); - if (args.level_of_detail == - base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND) { - if (gr_context_) - gpu::raster::DumpBackgroundGrMemoryStatistics(gr_context_->get(), pmd); - - // Early out, no need for more detail in a BACKGROUND dump. - return true; - } - - gles2_implementation_->OnMemoryDump(args, pmd); - gles2_helper_->OnMemoryDump(args, pmd); - - if (gr_context_) { - gpu::raster::DumpGrMemoryStatistics( - gr_context_->get(), pmd, - gles2_implementation_->ShareGroupTracingGUID()); - } - return true; -} - -base::ScopedClosureRunner VizProcessContextProvider::GetCacheBackBufferCb() { - return command_buffer_->GetCacheBackBufferCb(); -} - -void VizProcessContextProvider::SetNeedsMeasureNextDrawLatency() { - return command_buffer_->SetNeedsMeasureNextDrawLatency(); -} - -} // namespace viz diff --git a/chromium/components/viz/service/display_embedder/viz_process_context_provider.h b/chromium/components/viz/service/display_embedder/viz_process_context_provider.h deleted file mode 100644 index 8ff0ccce732..00000000000 --- a/chromium/components/viz/service/display_embedder/viz_process_context_provider.h +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2016 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_VIZ_PROCESS_CONTEXT_PROVIDER_H_ -#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_VIZ_PROCESS_CONTEXT_PROVIDER_H_ - -#include <stdint.h> - -#include <memory> - -#include "base/callback_helpers.h" -#include "base/memory/raw_ptr.h" -#include "base/observer_list.h" -#include "base/trace_event/memory_dump_provider.h" -#include "components/viz/common/display/update_vsync_parameters_callback.h" -#include "components/viz/common/gpu/context_cache_controller.h" -#include "components/viz/common/gpu/context_provider.h" -#include "components/viz/common/gpu/gpu_vsync_callback.h" -#include "components/viz/service/viz_service_export.h" -#include "gpu/command_buffer/common/context_creation_attribs.h" -#include "gpu/ipc/common/surface_handle.h" -#include "gpu/ipc/gpu_task_scheduler_helper.h" -#include "ui/gfx/native_widget_types.h" - -class GrDirectContext; - -namespace gpu { -namespace gles2 { -class GLES2CmdHelper; -class GLES2Implementation; -} // namespace gles2 -class CommandBufferTaskExecutor; -class GpuChannelManagerDelegate; -class GpuMemoryBufferManager; -class ImageFactory; -class InProcessCommandBuffer; -class TransferBuffer; -struct SharedMemoryLimits; -} // namespace gpu - -namespace skia_bindings { -class GrContextForGLES2Interface; -} - -namespace viz { -class ContextLostObserver; -class DisplayCompositorMemoryAndTaskController; -class GpuTaskSchedulerHelper; -class RendererSettings; - -// A ContextProvider used in the viz process to setup an InProcessCommandBuffer -// for the display compositor. -class VIZ_SERVICE_EXPORT VizProcessContextProvider - : public base::RefCountedThreadSafe<VizProcessContextProvider>, - public ContextProvider, - public base::trace_event::MemoryDumpProvider { - public: - VizProcessContextProvider( - gpu::CommandBufferTaskExecutor* task_executor, - gpu::SurfaceHandle surface_handle, - gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, - gpu::ImageFactory* image_factory, - gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate, - DisplayCompositorMemoryAndTaskController* display_controller, - const RendererSettings& renderer_settings); - - // ContextProvider implementation. - void AddRef() const override; - void Release() const override; - gpu::ContextResult BindToCurrentThread() override; - gpu::gles2::GLES2Interface* ContextGL() override; - gpu::ContextSupport* ContextSupport() override; - class GrDirectContext* GrContext() override; - gpu::SharedImageInterface* SharedImageInterface() override; - ContextCacheController* CacheController() override; - base::Lock* GetLock() override; - const gpu::Capabilities& ContextCapabilities() const override; - const gpu::GpuFeatureInfo& GetGpuFeatureInfo() const override; - void AddObserver(ContextLostObserver* obs) override; - void RemoveObserver(ContextLostObserver* obs) override; - - virtual void SetUpdateVSyncParametersCallback( - UpdateVSyncParametersCallback callback); - virtual void SetGpuVSyncCallback(GpuVSyncCallback callback); - virtual void SetGpuVSyncEnabled(bool enabled); - virtual bool UseRGB565PixelFormat() const; - - // Provides the GL internal format that should be used when calling - // glCopyTexImage2D() on the default framebuffer. - virtual uint32_t GetCopyTextureInternalFormat(); - - virtual base::ScopedClosureRunner GetCacheBackBufferCb(); - - void SetNeedsMeasureNextDrawLatency(); - - protected: - friend class base::RefCountedThreadSafe<VizProcessContextProvider>; - VizProcessContextProvider(); // For testing only. - ~VizProcessContextProvider() override; - - private: - void InitializeContext( - gpu::CommandBufferTaskExecutor* task_executor, - gpu::SurfaceHandle surface_handle, - gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, - gpu::ImageFactory* image_factory, - gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate, - DisplayCompositorMemoryAndTaskController* display_controller, - const gpu::SharedMemoryLimits& mem_limits); - void OnContextLost(); - - // base::trace_event::MemoryDumpProvider implementation. - bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, - base::trace_event::ProcessMemoryDump* pmd) override; - - const gpu::ContextCreationAttribs attributes_; - - // The |gpu_task_scheduler_helper_| has 1:1 relationship with the Display - // compositor. - raw_ptr<gpu::GpuTaskSchedulerHelper> gpu_task_scheduler_helper_; - std::unique_ptr<gpu::InProcessCommandBuffer> command_buffer_; - std::unique_ptr<gpu::gles2::GLES2CmdHelper> gles2_helper_; - std::unique_ptr<gpu::TransferBuffer> transfer_buffer_; - std::unique_ptr<gpu::gles2::GLES2Implementation> gles2_implementation_; - std::unique_ptr<ContextCacheController> cache_controller_; - gpu::ContextResult context_result_ = gpu::ContextResult::kSuccess; - - std::unique_ptr<skia_bindings::GrContextForGLES2Interface> gr_context_; - - base::ObserverList<ContextLostObserver>::Unchecked observers_; -}; - -} // namespace viz - -#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_VIZ_PROCESS_CONTEXT_PROVIDER_H_ 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 51c829c9e0b..4828b3ed23f 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 @@ -192,6 +192,14 @@ void CompositorFrameSinkSupport::ThrottleBeginFrame(base::TimeDelta interval) { begin_frame_interval_ = interval; } +void CompositorFrameSinkSupport::OnSurfaceCommitted(Surface* surface) { + if (surface->HasPendingFrame()) { + // Make sure we periodically check if the frame should activate. + pending_surfaces_.insert(surface); + UpdateNeedsBeginFramesInternal(); + } +} + void CompositorFrameSinkSupport::OnSurfaceActivated(Surface* surface) { DCHECK(surface); DCHECK(surface->HasActiveFrame()); @@ -281,7 +289,7 @@ void CompositorFrameSinkSupport::OnFrameTokenChanged(uint32_t frame_token) { frame_sink_manager_->OnFrameTokenChanged(frame_sink_id_, frame_token); } -void CompositorFrameSinkSupport::OnSurfaceProcessed(Surface* surface) { +void CompositorFrameSinkSupport::SendCompositorFrameAck() { DidReceiveCompositorFrameAck(); } @@ -676,9 +684,7 @@ SubmitResult CompositorFrameSinkSupport::MaybeSubmitCompositorFrame( TRACE_EVENT_SCOPE_THREAD); return SubmitResult::SIZE_MISMATCH; case Surface::QueueFrameResult::ACCEPTED_PENDING: - // Make sure we periodically check if the frame should activate. - pending_surfaces_.insert(current_surface); - UpdateNeedsBeginFramesInternal(); + // Pending frames are processed in OnSurfaceCommitted. break; case Surface::QueueFrameResult::ACCEPTED_ACTIVE: // Nothing to do here. diff --git a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.h b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.h index f3a5548db7a..704adeffbc4 100644 --- a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.h +++ b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.h @@ -141,6 +141,7 @@ class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport void ThrottleBeginFrame(base::TimeDelta interval); // SurfaceClient implementation. + void OnSurfaceCommitted(Surface* surface) override; void OnSurfaceActivated(Surface* surface) override; void OnSurfaceDestroyed(Surface* surface) override; void OnSurfaceWillDraw(Surface* surface) override; @@ -155,7 +156,7 @@ class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport std::vector<PendingCopyOutputRequest> TakeCopyOutputRequests( const LocalSurfaceId& local_surface_id) override; void OnFrameTokenChanged(uint32_t frame_token) override; - void OnSurfaceProcessed(Surface* surface) override; + void SendCompositorFrameAck() override; void OnSurfaceAggregatedDamage( Surface* surface, const LocalSurfaceId& local_surface_id, diff --git a/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.cc index 0a4a21b7915..083f26653b7 100644 --- a/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.cc +++ b/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.cc @@ -70,7 +70,9 @@ FrameSinkManagerImpl::FrameSinkManagerImpl(const InitParams& params) : shared_bitmap_manager_(params.shared_bitmap_manager), output_surface_provider_(params.output_surface_provider), gmb_context_provider_(params.gmb_context_provider), - surface_manager_(this, params.activation_deadline_in_frames), + surface_manager_(this, + params.activation_deadline_in_frames, + params.max_uncommitted_frames), hit_test_manager_(surface_manager()), restart_id_(params.restart_id), run_all_compositor_stages_before_draw_( @@ -414,6 +416,11 @@ void FrameSinkManagerImpl::RegisterCompositorFrameSinkSupport( for (auto& observer : observer_list_) observer.OnCreatedCompositorFrameSink(frame_sink_id, support->is_root()); + + if (global_throttle_interval_) { + UpdateThrottlingRecursively(frame_sink_id, + global_throttle_interval_.value()); + } } void FrameSinkManagerImpl::UnregisterCompositorFrameSinkSupport( @@ -733,16 +740,41 @@ void FrameSinkManagerImpl::Throttle(const std::vector<FrameSinkId>& ids, UpdateThrottling(); } +void FrameSinkManagerImpl::StartThrottlingAllFrameSinks( + base::TimeDelta interval) { + global_throttle_interval_ = interval; + UpdateThrottling(); +} + +void FrameSinkManagerImpl::StopThrottlingAllFrameSinks() { + global_throttle_interval_ = absl::nullopt; + UpdateThrottling(); +} + void FrameSinkManagerImpl::UpdateThrottling() { // Clear previous throttling effect on all frame sinks. for (auto& support_map_item : support_map_) { support_map_item.second->ThrottleBeginFrame(base::TimeDelta()); } - if (throttle_interval_.is_zero()) + if (throttle_interval_.is_zero() && + (!global_throttle_interval_ || + global_throttle_interval_.value().is_zero())) return; - for (const auto& id : frame_sink_ids_to_throttle_) { - UpdateThrottlingRecursively(id, throttle_interval_); + if (global_throttle_interval_) { + for (const auto& support : support_map_) { + support.second->ThrottleBeginFrame(global_throttle_interval_.value()); + } + } + + // If the per-frame sink throttle interval is more aggressive than the global + // throttling interval, apply it to those frame sinks effectively always + // throttling a frame sink as much as possible. + if (!global_throttle_interval_ || + throttle_interval_ > global_throttle_interval_) { + for (const auto& id : frame_sink_ids_to_throttle_) { + UpdateThrottlingRecursively(id, throttle_interval_); + } } // Clear throttling on frame sinks currently being captured. for (const auto& id : captured_frame_sink_ids_) { 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 833d23c839e..7c7c2791d4a 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 @@ -87,6 +87,7 @@ class VIZ_SERVICE_EXPORT FrameSinkManagerImpl DebugRendererSettings debug_renderer_settings; base::ProcessId host_process_id = base::kNullProcessId; raw_ptr<HintSessionFactory> hint_session_factory = nullptr; + size_t max_uncommitted_frames = 0; }; explicit FrameSinkManagerImpl(const InitParams& params); @@ -152,6 +153,8 @@ class VIZ_SERVICE_EXPORT FrameSinkManagerImpl const DebugRendererSettings& debug_settings) override; void Throttle(const std::vector<FrameSinkId>& ids, base::TimeDelta interval) override; + void StartThrottlingAllFrameSinks(base::TimeDelta interval) override; + void StopThrottlingAllFrameSinks() override; void DestroyFrameSinkBundle(const FrameSinkBundleId& id); @@ -394,9 +397,16 @@ class VIZ_SERVICE_EXPORT FrameSinkManagerImpl // Ids of the frame sinks that have been requested to throttle. std::vector<FrameSinkId> frame_sink_ids_to_throttle_; - // The throttling interval which defines how often BeginFrames are sent. + // The throttling interval which defines how often BeginFrames are sent for + // frame sinks in `frame_sink_ids_to_throttle_`, if + // `global_throttle_interval_` is unset or if this interval is longer than + // `global_throttle_interval_`. base::TimeDelta throttle_interval_ = BeginFrameArgs::DefaultInterval(); + // If present, the throttling interval which defines the upper bound of how + // often BeginFrames are sent for all current and future frame sinks. + absl::optional<base::TimeDelta> global_throttle_interval_ = absl::nullopt; + base::flat_map<uint32_t, base::ScopedClosureRunner> cached_back_buffers_; THREAD_CHECKER(thread_checker_); diff --git a/chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc b/chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc index c49494e98fb..8f8ba7ef6c9 100644 --- a/chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc +++ b/chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc @@ -33,6 +33,8 @@ constexpr FrameSinkId kFrameSinkIdA(2, 1); constexpr FrameSinkId kFrameSinkIdB(3, 1); constexpr FrameSinkId kFrameSinkIdC(4, 1); constexpr FrameSinkId kFrameSinkIdD(5, 1); +constexpr FrameSinkId kFrameSinkIdE(6, 1); +constexpr FrameSinkId kFrameSinkIdF(7, 1); // Holds the four interface objects needed to create a RootCompositorFrameSink. struct RootCompositorFrameSinkData { @@ -472,6 +474,86 @@ TEST_F(FrameSinkManagerTest, Throttle) { client_d->frame_sink_id()); } +TEST_F(FrameSinkManagerTest, GlobalThrottle) { + // root -> A -> B + // -> C -> D + auto root = CreateCompositorFrameSinkSupport(kFrameSinkIdRoot); + auto client_a = CreateCompositorFrameSinkSupport(kFrameSinkIdA); + auto client_b = CreateCompositorFrameSinkSupport(kFrameSinkIdB); + auto client_c = CreateCompositorFrameSinkSupport(kFrameSinkIdC); + auto client_d = CreateCompositorFrameSinkSupport(kFrameSinkIdD); + + // Set up the hierarchy. + manager_.RegisterFrameSinkHierarchy(root->frame_sink_id(), + client_a->frame_sink_id()); + manager_.RegisterFrameSinkHierarchy(client_a->frame_sink_id(), + client_b->frame_sink_id()); + manager_.RegisterFrameSinkHierarchy(root->frame_sink_id(), + client_c->frame_sink_id()); + manager_.RegisterFrameSinkHierarchy(client_c->frame_sink_id(), + client_d->frame_sink_id()); + + constexpr base::TimeDelta global_interval = base::Hertz(30); + constexpr base::TimeDelta interval = base::Hertz(20); + + std::vector<FrameSinkId> ids{kFrameSinkIdRoot, kFrameSinkIdA, kFrameSinkIdB, + kFrameSinkIdC, kFrameSinkIdD}; + + // By default, a CompositorFrameSinkSupport shouldn't have its + // |begin_frame_interval| set. + VerifyThrottling(base::TimeDelta(), ids); + + // Starting global throttling should throttle the entire hierarchy. + manager_.StartThrottlingAllFrameSinks(global_interval); + VerifyThrottling(global_interval, ids); + + // Throttling more aggressively on top of global throttling should further + // throttle the specified frame sink hierarchy, but preserve global throttling + // on the unaffected framesinks. + manager_.Throttle({kFrameSinkIdC}, interval); + VerifyThrottling(global_interval, + {kFrameSinkIdRoot, kFrameSinkIdA, kFrameSinkIdB}); + VerifyThrottling(interval, {kFrameSinkIdC, kFrameSinkIdD}); + + // Attempting to per-sink throttle to an interval shorter than the global + // throttling should still throttle all frame sinks to the global interval. + manager_.Throttle({kFrameSinkIdA}, base::Hertz(40)); + VerifyThrottling(global_interval, ids); + + // Add a new branch to the hierarchy. These new frame sinks should be globally + // throttled immediately. root -> A -> B + // -> C -> D + // -> E -> F + auto client_e = CreateCompositorFrameSinkSupport(kFrameSinkIdE); + auto client_f = CreateCompositorFrameSinkSupport(kFrameSinkIdF); + manager_.RegisterFrameSinkHierarchy(root->frame_sink_id(), + client_e->frame_sink_id()); + manager_.RegisterFrameSinkHierarchy(client_e->frame_sink_id(), + client_f->frame_sink_id()); + VerifyThrottling( + global_interval, + {kFrameSinkIdRoot, kFrameSinkIdA, kFrameSinkIdB, kFrameSinkIdC, + kFrameSinkIdD, kFrameSinkIdE, kFrameSinkIdF}); + + // Disabling global throttling should revert back to only the up-to-date + // per-frame sink throttling. + manager_.StopThrottlingAllFrameSinks(); + VerifyThrottling(base::Hertz(40), {kFrameSinkIdA, kFrameSinkIdB}); + + manager_.UnregisterFrameSinkHierarchy(root->frame_sink_id(), + client_a->frame_sink_id()); + manager_.UnregisterFrameSinkHierarchy(client_a->frame_sink_id(), + client_b->frame_sink_id()); + manager_.UnregisterFrameSinkHierarchy(root->frame_sink_id(), + client_c->frame_sink_id()); + manager_.UnregisterFrameSinkHierarchy(client_c->frame_sink_id(), + client_d->frame_sink_id()); + manager_.UnregisterFrameSinkHierarchy(root->frame_sink_id(), + client_e->frame_sink_id()); + manager_.UnregisterFrameSinkHierarchy(client_e->frame_sink_id(), + client_f->frame_sink_id()); +} + // Verifies if a frame sink is being captured, it should not be throttled. TEST_F(FrameSinkManagerTest, NoThrottleOnFrameSinksBeingCaptured) { // root -> A -> B -> C 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 4e38e0f4905..8eeb87e2322 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 @@ -81,7 +81,7 @@ RootCompositorFrameSinkImpl::Create( mojo::Remote<mojom::DisplayClient> display_client( std::move(params->display_client)); auto display_controller = output_surface_provider->CreateGpuDependency( - params->gpu_compositing, params->widget, params->renderer_settings); + params->gpu_compositing, params->widget); auto output_surface = output_surface_provider->CreateOutputSurface( params->widget, params->gpu_compositing, display_client.get(), display_controller.get(), params->renderer_settings, debug_settings); @@ -175,9 +175,7 @@ RootCompositorFrameSinkImpl::Create( auto* output_surface_ptr = output_surface.get(); #endif gpu::SharedImageInterface* sii = nullptr; - if (output_surface->context_provider()) - sii = output_surface->context_provider()->SharedImageInterface(); - else if (display_controller) + if (display_controller) sii = display_controller->shared_image_interface(); auto overlay_processor = OverlayProcessorInterface::CreateOverlayProcessor( 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 87a00f3433e..d4924b12e0e 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 @@ -37,6 +37,8 @@ #include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/gfx/color_space.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" using media::VideoCaptureOracle; @@ -111,6 +113,46 @@ CopyOutputRequest::ResultFormat VideoPixelFormatToCopyOutputRequestFormat( } } +bool IsCompatibleWithFormat(const gfx::Rect& rect, + media::VideoPixelFormat format) { + DCHECK(format == media::PIXEL_FORMAT_I420 || + format == media::PIXEL_FORMAT_NV12 || + format == media::PIXEL_FORMAT_ARGB); + if (format == media::PIXEL_FORMAT_ARGB) { + // No special requirements: + return true; + } + + return rect.origin().x() % 2 == 0 && rect.origin().y() % 2 == 0 && + rect.width() % 2 == 0 && rect.height() % 2 == 0; +} + +// Given a |visible_rect| representing visible rectangle of some video frame, +// calculates a centered rectangle that fits entirely within |visible_rect| and +// has the same aspect ratio as |source_size|, taking into account +// |pixel_format|. +gfx::Rect GetContentRectangle(const gfx::Rect& visible_rect, + const gfx::Size& source_size, + media::VideoPixelFormat pixel_format) { + DCHECK(pixel_format == media::PIXEL_FORMAT_I420 || + pixel_format == media::PIXEL_FORMAT_NV12 || + pixel_format == media::PIXEL_FORMAT_ARGB); + + if (pixel_format == media::PIXEL_FORMAT_I420 || + pixel_format == media::PIXEL_FORMAT_NV12) { + return media::ComputeLetterboxRegionForI420(visible_rect, source_size); + } else { + DCHECK_EQ(media::PIXEL_FORMAT_ARGB, pixel_format); + const gfx::Rect content_rect = + media::ComputeLetterboxRegion(visible_rect, source_size); + + // The media letterboxing computation explicitly allows for off-by-one + // errors due to computation, so we address those here. + return content_rect.ApproximatelyEqual(visible_rect, 1) ? visible_rect + : content_rect; + } +} + } // namespace // static @@ -208,6 +250,7 @@ void FrameSinkVideoCapturerImpl::SetResolvedTarget( resolved_target_->AttachCaptureClient(this); RefreshEntireSourceNow(); } else { + MaybeInformConsumerOfEmptyRegion(); // The capturer will remain idle until either: 1) the requested target is // re-resolved by the |frame_sink_manager_|, or 2) a new target is set via a // call to ChangeTarget(). @@ -308,6 +351,10 @@ void FrameSinkVideoCapturerImpl::SetResolutionConstraints( bool use_fixed_aspect_ratio) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DVLOG(2) << __func__ << ": min_size=" << min_size.ToString() + << ", max_size=" << max_size.ToString() + << ", use_fixed_aspect_ratio=" << use_fixed_aspect_ratio; + TRACE_EVENT_INSTANT2("gpu.capture", "SetResolutionConstraints", TRACE_EVENT_SCOPE_THREAD, "min_size.width", min_size.width(), "min_size.height", min_size.height()); @@ -534,7 +581,7 @@ void FrameSinkVideoCapturerImpl::RefreshInternal( // If the capture region is empty, it means one of two things: the first // frame has not been composited yet or the current region selected for // capture has a current size of zero. We schedule a frame refresh here, - // although its not useful in all circumstances. + // although it's not useful in all circumstances. MaybeScheduleRefreshFrame(); return; } @@ -695,6 +742,7 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame( // TODO(https://crbug.com/1300943): we should likely just get the frame // region from the last aggregated surface. if (!compositor_frame_region.Contains(capture_region)) { + DVLOG(3) << __func__ << ": skipping capture!"; MaybeScheduleRefreshFrame(); return; } @@ -707,9 +755,24 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame( // Reserve a buffer from the pool for the next frame. const OracleFrameNumber oracle_frame_number = oracle_->next_frame_number(); + + // Size of the video frames that we are supposed to produce. Depends on the + // pixel format and the capture size as determined by the oracle (which in + // turn depends on the capture constraints). const gfx::Size capture_size = AdjustSizeForPixelFormat(oracle_->capture_size()); + // Size of the source that we are capturing: + const gfx::Size source_size = oracle_->source_size(); + DCHECK_EQ(capture_region.size(), source_size); + DCHECK(!source_size.IsEmpty()); + + DVLOG(3) << __func__ + << ": compositor_frame_region=" << compositor_frame_region.ToString() + << ", capture_region=" << capture_region.ToString() + << ", capture_size=" << capture_size.ToString() + << ", event=" << event; + const bool can_resurrect_content = CanResurrectFrame(capture_size); scoped_refptr<VideoFrame> frame; if (can_resurrect_content) { @@ -757,6 +820,13 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame( return; } + // If frame was resurrected / allocated from the pool, its visible rectangle + // should match what we requested: + DCHECK_EQ(frame->visible_rect().size(), capture_size); + // The pool should return a frame with visible rectangle that is compatible + // with the capture format. + DCHECK(IsCompatibleWithFormat(frame->visible_rect(), pixel_format_)); + // Record a trace event if the capture pipeline is redlining, but capture will // still proceed. if (utilization >= 1.0) { @@ -791,28 +861,33 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame( metadata.top_controls_visible_height = last_top_controls_visible_height_; oracle_->RecordCapture(utilization); - TRACE_EVENT_NESTABLE_ASYNC_BEGIN2("gpu.capture", "Capture", - oracle_frame_number, "frame_number", - capture_frame_number, "trigger", - VideoCaptureOracle::EventAsString(event)); - - const gfx::Size& source_size = oracle_->source_size(); - DCHECK(!source_size.IsEmpty()); - gfx::Rect content_rect; - if (pixel_format_ == media::PIXEL_FORMAT_I420 || - pixel_format_ == media::PIXEL_FORMAT_NV12) { - content_rect = media::ComputeLetterboxRegionForI420(frame->visible_rect(), - source_size); - } else { - DCHECK_EQ(media::PIXEL_FORMAT_ARGB, pixel_format_); - content_rect = - media::ComputeLetterboxRegion(frame->visible_rect(), source_size); - // The media letterboxing computation explicitly allows for off-by-one - // errors due to computation, so we address those here. - if (content_rect.ApproximatelyEqual(frame->visible_rect(), 1)) { - content_rect = frame->visible_rect(); - } - } + // Note: The following is used by + // chrome/browser/media/cast_mirroring_performance_browsertest.cc, in + // addition to the usual runtime tracing + // TODO(https://crbug.com/1322573): change to _NESTABLE_ variant of the macro + // once the bug is fixed. + TRACE_EVENT_ASYNC_BEGIN2("gpu.capture", "Capture", oracle_frame_number, + "frame_number", capture_frame_number, "trigger", + VideoCaptureOracle::EventAsString(event)); + + // `content_rect` is the region of the `frame` that we would like to populate. + // We know our source is of size `source_size`, and we have + // `frame->visible_rect()` to fill out - find the largest centered rectangle + // that will fit within the frame and maintains the aspect ratio of the + // source. + // TODO(https://crbug.com/1323342): currently, both the frame's visible + // rectangle and source size are controlled by oracle + // (`frame->visible_rect().size() == `capture_size`). Oracle also knows if we + // need to maintain fixed aspect ratio, so it should compute both the + // `capture_size` and `content_rect` for us, thus ensuring that letterboxing + // happens only when it needs to (i.e. when we allocate a frame and know that + // aspect ratio does not have to be maintained, we should use a size that we + // know would not require letterboxing). + const gfx::Rect content_rect = + GetContentRectangle(frame->visible_rect(), source_size, pixel_format_); + DVLOG(3) << __func__ << ": content_rect=" << content_rect.ToString() + << ", source_size=" << source_size.ToString() + << ", frame=" << frame->AsHumanReadableString(); // Determine what rectangular region has changed since the last captured // frame. @@ -896,8 +971,17 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame( return; } - DCHECK(capture_region.size() == source_size); - if (absl::holds_alternative<RegionCaptureCropId>(target_->sub_target)) { + // If the target is in a different renderer than the root renderer (indicated + // by having a different frame sink ID), we currently cannot provide + // reasonable metadata about the region capture rect. For more context, see + // https://crbug.com/1327560. + // + // TODO(https://crbug.com/1335175): Provide accurate bounds for elements + // embedded in different renderers. + const bool is_same_frame_sink_as_requested = + resolved_target_->GetFrameSinkId() == target_->frame_sink_id; + if (absl::holds_alternative<RegionCaptureCropId>(target_->sub_target) && + is_same_frame_sink_as_requested) { const float scale_factor = frame_metadata.device_scale_factor; metadata.region_capture_rect = scale_factor ? ScaleToEnclosingRect(capture_region, 1.0f / scale_factor) @@ -923,7 +1007,12 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame( std::array<gpu::MailboxHolder, 3> mailbox_holders = { request_properties.frame->mailbox_holder(0), request_properties.frame->mailbox_holder(1), gpu::MailboxHolder{}}; - blit_request = BlitRequest(content_rect.origin(), mailbox_holders); + + // TODO(https://crbug.com/775740): change the capturer to only request the + // parts of the frame that have changed whenever possible. + blit_request = + BlitRequest(content_rect.origin(), LetterboxingBehavior::kLetterbox, + mailbox_holders, true); // We haven't captured the frame yet, but let's pretend that we did for the // sake of blend information computation. We will be asking for an entire @@ -1017,6 +1106,7 @@ void FrameSinkVideoCapturerImpl::DidCopyFrame( scoped_refptr<media::VideoFrame>& frame = properties.frame; const gfx::Rect& content_rect = properties.content_rect; + if (log_to_webrtc_ && consumer_) { std::string format = ""; std::string strides = ""; @@ -1106,8 +1196,6 @@ void FrameSinkVideoCapturerImpl::DidCopyFrame( } else { frame = nullptr; } - - UMA_HISTOGRAM_CAPTURE_SUCCEEDED("RGBA", success); } else { DCHECK_EQ(pixel_format_, media::PIXEL_FORMAT_NV12); // NV12 is only supported for GMBs for now, in which case there is nothing @@ -1127,6 +1215,13 @@ void FrameSinkVideoCapturerImpl::DidCopyFrame( } if (frame) { + // The result may be smaller than what was requested, if unforeseen + // clamping to the source boundaries occurred by the executor of the + // CopyOutputRequest. However, the result should never contain more than + // what was requested. + DCHECK_LE(result->size().width(), content_rect.width()); + DCHECK_LE(result->size().height(), content_rect.height()); + if (!frame->HasGpuMemoryBuffer()) { // For GMB-backed video frames, overlays were already applied by // CopyOutputRequest API. For in-memory frames, apply overlays here: @@ -1141,15 +1236,21 @@ void FrameSinkVideoCapturerImpl::DidCopyFrame( } } - // The result may be smaller than what was requested, if unforeseen - // clamping to the source boundaries occurred by the executor of the - // CopyOutputRequest. However, the result should never contain more than - // what was requested. - DCHECK_LE(result->size().width(), content_rect.width()); - DCHECK_LE(result->size().height(), content_rect.height()); - media::LetterboxVideoFrame( - frame.get(), gfx::Rect(content_rect.origin(), - AdjustSizeForPixelFormat(result->size()))); + const gfx::Rect result_rect = + gfx::Rect(content_rect.origin(), result->size()); + DCHECK(IsCompatibleWithFormat(result_rect, pixel_format_)); + + DVLOG(3) << __func__ << ": result->size()=" << result->size().ToString() + << ", content_rect=" << content_rect.ToString() + << ", result_rect=" << result_rect.ToString() + << ", frame=" << frame->AsHumanReadableString(); + + if (frame->visible_rect() != result_rect && !frame->HasGpuMemoryBuffer()) { + // If there are parts of the frame that are visible but we have not wrote + // into them, letterbox them. This is not needed for GMB-backed frames as + // the letterboxing happens on GPU. + media::LetterboxVideoFrame(frame.get(), result_rect); + } if (ShouldMark(*frame, properties.content_version)) { MarkFrame(frame, properties.content_version); @@ -1220,11 +1321,13 @@ void FrameSinkVideoCapturerImpl::MaybeDeliverFrame( // original source content. base::TimeTicks media_ticks; if (!oracle_->CompleteCapture(oracle_frame_number, !!frame, &media_ticks)) { - // The following is used by + // Note: The following is used by // chrome/browser/media/cast_mirroring_performance_browsertest.cc, in - // addition to the usual runtime tracing. - TRACE_EVENT_NESTABLE_ASYNC_END1("gpu.capture", "Capture", - oracle_frame_number, "success", false); + // addition to the usual runtime tracing + // TODO(https://crbug.com/1322573): change to _NESTABLE_ variant of the + // macro once the bug is fixed. + TRACE_EVENT_ASYNC_END1("gpu.capture", "Capture", oracle_frame_number, + "success", false); MaybeScheduleRefreshFrame(); return; @@ -1236,12 +1339,14 @@ void FrameSinkVideoCapturerImpl::MaybeDeliverFrame( } frame->set_timestamp(media_ticks - *first_frame_media_ticks_); - // The following is used by + // Note: The following is used by // chrome/browser/media/cast_mirroring_performance_browsertest.cc, in - // addition to the usual runtime tracing. - TRACE_EVENT_NESTABLE_ASYNC_END2("gpu.capture", "Capture", oracle_frame_number, - "success", true, "time_delta", - frame->timestamp().InMicroseconds()); + // addition to the usual runtime tracing + // TODO(https://crbug.com/1322573): change to _NESTABLE_ variant of the macro + // once the bug is fixed. + TRACE_EVENT_ASYNC_END2("gpu.capture", "Capture", oracle_frame_number, + "success", true, "time_delta", + frame->timestamp().InMicroseconds()); // Clone a handle to the shared memory backing the populated video frame, to // send to the consumer. 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 3e4f833cfdd..20565f12fb2 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 @@ -442,8 +442,9 @@ MATCHER_P2(IsLetterboxedFrame, color, content_rect, "") { const VideoFrame& frame = *arg; const gfx::Rect kContentRect = content_rect; - const auto IsLetterboxedPlane = [&frame, kContentRect](int plane, - uint8_t component) { + + const auto IsLetterboxedPlane = [&frame, kContentRect, result_listener]( + int plane, uint8_t component) { gfx::Rect content_rect_copy = kContentRect; if (plane != VideoFrame::kYPlane) { content_rect_copy = gfx::Rect( @@ -455,10 +456,19 @@ MATCHER_P2(IsLetterboxedFrame, color, content_rect, "") { for (int col = 0; col < frame.row_bytes(plane); ++col) { if (content_rect_copy.Contains(gfx::Point(col, row))) { if (p[col] != component) { + *result_listener << " where pixel at (" << col << ", " << row + << ") should be inside content rectangle and the " + "component should match 0x" + << std::hex << component << " but is 0x" + << std::hex << static_cast<unsigned int>(p[col]); return false; } } else { // Letterbox border around content. if (plane == VideoFrame::kYPlane && p[col] != 0x00) { + *result_listener << " where pixel at (" << col << ", " << row + << ") should be outside content rectangle and the " + "component should match 0x00 but is 0x" + << std::hex << static_cast<unsigned int>(p[col]); return false; } } @@ -1138,6 +1148,45 @@ TEST_F(FrameSinkVideoCapturerTest, RefreshDemandsAreProperlyHandled) { StopCapture(); } +// Tests that the capturer honors requested refresh frames (see +// crbug.com/1320798) +TEST_F(FrameSinkVideoCapturerTest, HonorsRequestRefreshFrame) { + frame_sink_.SetCopyOutputColor(YUVColor{0x80, 0x80, 0x80}); + ON_CALL(frame_sink_manager_, FindCapturableFrameSink(kVideoCaptureTarget)) + .WillByDefault(Return(&frame_sink_)); + + capturer_->ChangeTarget(kVideoCaptureTarget, /*crop_version=*/0); + + // Start off and consume the immediate refresh and copy result. + MockConsumer consumer; + StartCapture(&consumer); + frame_sink_.SendCopyOutputResult(0); + ASSERT_EQ(1, consumer.num_frames_received()); + consumer.SendDoneNotification(0); + + // Advance time to avoid being frame rate limited by the oracle. + // Demand a refresh frame. We should be past the minimum time to add one, so + // it should be done immediately. + AdvanceClockToNextVsync(); + capturer_->RefreshNow(); + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(2, consumer.num_frames_received()); + + // Advance time to avoid being frame rate limited by the oracle. + // Request a refresh frame. The request should be serviced immediately. + AdvanceClockToNextVsync(); + capturer_->RequestRefreshFrame(); + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(3, consumer.num_frames_received()); + + // Advance time to avoid being frame rate limited by the oracle. + // Request again and expect service. + AdvanceClockToNextVsync(); + capturer_->RequestRefreshFrame(); + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(4, consumer.num_frames_received()); +} + // Tests that full capture happens on capture resolution change due to oracle, // but only once and resurrected frames are used after that. TEST_F(FrameSinkVideoCapturerTest, diff --git a/chromium/components/viz/service/gl/DEPS b/chromium/components/viz/service/gl/DEPS index 1510a5d8abd..f5fbbbfbc39 100644 --- a/chromium/components/viz/service/gl/DEPS +++ b/chromium/components/viz/service/gl/DEPS @@ -15,6 +15,7 @@ include_rules = [ "+ipc", "+media/base/android/media_codec_util.h", "+media/base/media_log.h", + "+media/base/win/mf_feature_checks.h", "+media/gpu", "+media/mojo", "+mojo/public/cpp", diff --git a/chromium/components/viz/service/gl/gpu_service_impl.cc b/chromium/components/viz/service/gl/gpu_service_impl.cc index a965233a61b..0f9b617fd03 100644 --- a/chromium/components/viz/service/gl/gpu_service_impl.cc +++ b/chromium/components/viz/service/gl/gpu_service_impl.cc @@ -38,7 +38,6 @@ #include "gpu/ipc/common/gpu_memory_buffer_support.h" #include "gpu/ipc/common/gpu_peak_memory.h" #include "gpu/ipc/common/memory_stats.h" -#include "gpu/ipc/in_process_command_buffer.h" #include "gpu/ipc/service/gpu_channel.h" #include "gpu/ipc/service/gpu_channel_manager.h" #include "gpu/ipc/service/gpu_memory_buffer_factory.h" @@ -99,6 +98,8 @@ #endif // BUILDFLAG(IS_CHROMEOS_ASH) #if BUILDFLAG(IS_WIN) +#include "components/viz/common/overlay_state/win/overlay_state_service.h" +#include "media/base/win/mf_feature_checks.h" #include "mojo/public/cpp/system/platform_handle.h" #include "ui/gl/dcomp_surface_registry.h" #include "ui/gl/direct_composition_surface_win.h" @@ -297,10 +298,8 @@ void GetVideoCapabilities(const gpu::GpuPreferences& gpu_preferences, } #if BUILDFLAG(USE_PROPRIETARY_CODECS) - if (media::MediaCodecUtil::IsH264EncoderAvailable(/*use_codec_list*/ false)) { - vea_profile.profile = gpu::H264PROFILE_BASELINE; - encoding_profiles.push_back(vea_profile); - } + vea_profile.profile = gpu::H264PROFILE_BASELINE; + encoding_profiles.push_back(vea_profile); #endif // BUILDFLAG(USE_PROPRIETARY_CODECS) // Note: Since Android doesn't have to support PPAPI/Flash, we have not @@ -431,10 +430,18 @@ GpuServiceImpl::GpuServiceImpl( #endif #if BUILDFLAG(IS_WIN) - auto info_callback = base::BindRepeating( - &GpuServiceImpl::UpdateOverlayAndHDRInfo, weak_ptr_factory_.GetWeakPtr()); + auto info_callback = + base::BindRepeating(&GpuServiceImpl::UpdateOverlayAndDXGIInfo, + weak_ptr_factory_.GetWeakPtr()); gl::DirectCompositionSurfaceWin::SetOverlayHDRGpuInfoUpdateCallback( info_callback); + + if (media::SupportMediaFoundationClearPlayback()) { + // Initialize the OverlayStateService using the GPUServiceImpl task + // sequence. + auto* overlay_state_service = OverlayStateService::GetInstance(); + overlay_state_service->Initialize(base::SequencedTaskRunnerHandle::Get()); + } #endif gpu_memory_buffer_factory_ = @@ -806,7 +813,7 @@ void GpuServiceImpl::CreateJpegEncodeAccelerator( void GpuServiceImpl::RegisterDCOMPSurfaceHandle( mojo::PlatformHandle surface_handle, RegisterDCOMPSurfaceHandleCallback callback) { - auto token = + base::UnguessableToken token = gl::DCOMPSurfaceRegistry::GetInstance()->RegisterDCOMPSurfaceHandle( surface_handle.TakeHandle()); std::move(callback).Run(token); @@ -894,23 +901,22 @@ void GpuServiceImpl::GetPeakMemoryUsage(uint32_t sequence_num, weak_ptr_, sequence_num, std::move(callback))); } -void GpuServiceImpl::RequestHDRStatus(RequestHDRStatusCallback callback) { +#if BUILDFLAG(IS_WIN) +void GpuServiceImpl::RequestDXGIInfo(RequestDXGIInfoCallback callback) { DCHECK(io_runner_->BelongsToCurrentThread()); main_runner_->PostTask( - FROM_HERE, base::BindOnce(&GpuServiceImpl::RequestHDRStatusOnMainThread, + FROM_HERE, base::BindOnce(&GpuServiceImpl::RequestDXGIInfoOnMainThread, weak_ptr_, std::move(callback))); } -void GpuServiceImpl::RequestHDRStatusOnMainThread( - RequestHDRStatusCallback callback) { +void GpuServiceImpl::RequestDXGIInfoOnMainThread( + RequestDXGIInfoCallback callback) { DCHECK(main_runner_->BelongsToCurrentThread()); - -#if BUILDFLAG(IS_WIN) - hdr_enabled_ = gl::DirectCompositionSurfaceWin::IsHDRSupported(); -#endif + dxgi_info_ = gl::DirectCompositionSurfaceWin::GetDXGIInfo(); io_runner_->PostTask(FROM_HERE, - base::BindOnce(std::move(callback), hdr_enabled_)); + base::BindOnce(std::move(callback), dxgi_info_.Clone())); } +#endif void GpuServiceImpl::RegisterDisplayContext( gpu::DisplayContext* display_context) { @@ -960,17 +966,6 @@ void GpuServiceImpl::DidLoseContext(bool offscreen, gpu_host_->DidLoseContext(offscreen, reason, active_url); } -#if BUILDFLAG(IS_WIN) -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, const std::string& key, const std::string& shader) { @@ -1260,7 +1255,7 @@ void GpuServiceImpl::OnForegroundedOnMainThread() { #if !BUILDFLAG(IS_ANDROID) void GpuServiceImpl::OnMemoryPressure( - ::base::MemoryPressureListener::MemoryPressureLevel level) { + base::MemoryPressureListener::MemoryPressureLevel level) { // Forward the notification to the registry of MemoryPressureListeners. base::MemoryPressureListener::NotifyMemoryPressure(level); } @@ -1327,20 +1322,20 @@ gpu::Scheduler* GpuServiceImpl::GetGpuScheduler() { } #if BUILDFLAG(IS_WIN) -void GpuServiceImpl::UpdateOverlayAndHDRInfo() { +void GpuServiceImpl::UpdateOverlayAndDXGIInfo() { 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); + gpu_host_->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_); + // Update DXGI adapter info in the GPU process through the GPU host mojom. + auto old_dxgi_info = std::move(dxgi_info_); + dxgi_info_ = gl::DirectCompositionSurfaceWin::GetDXGIInfo(); + if (!mojo::Equals(dxgi_info_, old_dxgi_info)) + gpu_host_->DidUpdateDXGIInfo(dxgi_info_.Clone()); } #endif diff --git a/chromium/components/viz/service/gl/gpu_service_impl.h b/chromium/components/viz/service/gl/gpu_service_impl.h index 450ebc7730b..194edf37ff7 100644 --- a/chromium/components/viz/service/gl/gpu_service_impl.h +++ b/chromium/components/viz/service/gl/gpu_service_impl.h @@ -59,18 +59,15 @@ class GpuMemoryBufferFactory; class GpuWatchdogThread; class ImageDecodeAcceleratorWorker; class Scheduler; -class SyncPointManager; +class SharedContextState; class SharedImageManager; +class SyncPointManager; class VulkanImplementation; } // namespace gpu namespace media { class MediaGpuChannelManager; -} - -namespace gpu { -class SharedContextState; -} // namespace gpu +} // namespace media namespace viz { @@ -194,8 +191,9 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate, void StartPeakMemoryMonitor(uint32_t sequence_num) override; void GetPeakMemoryUsage(uint32_t sequence_num, GetPeakMemoryUsageCallback callback) override; - - void RequestHDRStatus(RequestHDRStatusCallback callback) override; +#if BUILDFLAG(IS_WIN) + void RequestDXGIInfo(RequestDXGIInfoCallback callback) override; +#endif void LoadedShader(int32_t client_id, const std::string& key, const std::string& data) override; @@ -236,10 +234,6 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate, void DidLoseContext(bool offscreen, gpu::error::ContextLostReason reason, const GURL& active_url) override; -#if BUILDFLAG(IS_WIN) - void DidUpdateOverlayInfo(const gpu::OverlayInfo& overlay_info) override; - void DidUpdateHDRStatus(bool hdr_enabled) override; -#endif void GetDawnInfo(GetDawnInfoCallback callback) override; void StoreShaderToDisk(int client_id, @@ -386,7 +380,9 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate, #endif // BUILDFLAG(IS_CHROMEOS_ASH) && // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION) - void RequestHDRStatusOnMainThread(RequestHDRStatusCallback callback); +#if BUILDFLAG(IS_WIN) + void RequestDXGIInfoOnMainThread(RequestDXGIInfoCallback callback); +#endif void OnBackgroundedOnMainThread(); void OnForegroundedOnMainThread(); @@ -403,7 +399,7 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate, // 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 BUILDFLAG(IS_WIN) - void UpdateOverlayAndHDRInfo(); + void UpdateOverlayAndDXGIInfo(); #endif void GetDawnInfoOnMain(GetDawnInfoCallback callback); @@ -425,7 +421,9 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate, const gpu::GpuDriverBugWorkarounds gpu_driver_bug_workarounds_; - bool hdr_enabled_ = false; +#if BUILDFLAG(IS_WIN) + gfx::mojom::DXGIInfoPtr dxgi_info_; +#endif // What we would have gotten if we haven't fallen back to SwiftShader or // pure software (in the viz case). diff --git a/chromium/components/viz/service/main/viz_compositor_thread_runner.h b/chromium/components/viz/service/main/viz_compositor_thread_runner.h index 4d69c164c50..69798c9c4ff 100644 --- a/chromium/components/viz/service/main/viz_compositor_thread_runner.h +++ b/chromium/components/viz/service/main/viz_compositor_thread_runner.h @@ -14,10 +14,6 @@ namespace base { class SingleThreadTaskRunner; } -namespace gpu { -class CommandBufferTaskExecutor; -} // namespace gpu - namespace viz { class GpuServiceImpl; @@ -36,16 +32,12 @@ class VizCompositorThreadRunner { base::flat_set<base::PlatformThreadId> thread_ids, base::RepeatingClosure* wake_up_closure) = 0; - // Creates FrameSinkManager from |params|. The version with |gpu_service| and - // |task_executor| supports both GPU and software compositing, while the - // version without supports only software compositing. Should be called from - // the thread that owns |this| to initialize state on VizCompositorThread. - virtual void CreateFrameSinkManager( - mojom::FrameSinkManagerParamsPtr params) = 0; - virtual void CreateFrameSinkManager( - mojom::FrameSinkManagerParamsPtr params, - gpu::CommandBufferTaskExecutor* task_executor, - GpuServiceImpl* gpu_service) = 0; + // Creates FrameSinkManager from |params|. If |gpu_service| is null the + // display compositor will only support software compositing. Should be called + // from the thread that owns |this| to initialize state on + // VizCompositorThread. + virtual void CreateFrameSinkManager(mojom::FrameSinkManagerParamsPtr params, + GpuServiceImpl* gpu_service) = 0; }; } // namespace viz diff --git a/chromium/components/viz/service/main/viz_compositor_thread_runner_impl.cc b/chromium/components/viz/service/main/viz_compositor_thread_runner_impl.cc index 798434cf6df..6b7d8fcc565 100644 --- a/chromium/components/viz/service/main/viz_compositor_thread_runner_impl.cc +++ b/chromium/components/viz/service/main/viz_compositor_thread_runner_impl.cc @@ -28,7 +28,6 @@ #include "components/viz/service/performance_hint/hint_session.h" #include "gpu/config/gpu_finch_features.h" #include "gpu/config/gpu_switches.h" -#include "gpu/ipc/command_buffer_task_executor.h" #include "gpu/ipc/scheduler_sequence.h" #include "gpu/ipc/service/gpu_memory_buffer_factory.h" #include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h" @@ -151,17 +150,7 @@ base::SingleThreadTaskRunner* VizCompositorThreadRunnerImpl::task_runner() { } void VizCompositorThreadRunnerImpl::CreateFrameSinkManager( - mojom::FrameSinkManagerParamsPtr params) { - task_runner_->PostTask( - FROM_HERE, base::BindOnce(&VizCompositorThreadRunnerImpl:: - CreateFrameSinkManagerOnCompositorThread, - base::Unretained(this), std::move(params), - nullptr, nullptr)); -} - -void VizCompositorThreadRunnerImpl::CreateFrameSinkManager( mojom::FrameSinkManagerParamsPtr params, - gpu::CommandBufferTaskExecutor* task_executor, GpuServiceImpl* gpu_service) { // All of the unretained objects are owned on the GPU thread and destroyed // after VizCompositorThread has been shutdown. @@ -169,18 +158,15 @@ void VizCompositorThreadRunnerImpl::CreateFrameSinkManager( FROM_HERE, base::BindOnce(&VizCompositorThreadRunnerImpl:: CreateFrameSinkManagerOnCompositorThread, base::Unretained(this), std::move(params), - base::Unretained(task_executor), base::Unretained(gpu_service))); } void VizCompositorThreadRunnerImpl::CreateFrameSinkManagerOnCompositorThread( mojom::FrameSinkManagerParamsPtr params, - gpu::CommandBufferTaskExecutor* task_executor, GpuServiceImpl* gpu_service) { DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(!frame_sink_manager_); - if (features::IsUsingSkiaRenderer()) - gpu::SchedulerSequence::DefaultDisallowScheduleTaskOnCurrentThread(); + gpu::SchedulerSequence::DefaultDisallowScheduleTaskOnCurrentThread(); server_shared_bitmap_manager_ = std::make_unique<ServerSharedBitmapManager>(); base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( @@ -192,17 +178,14 @@ void VizCompositorThreadRunnerImpl::CreateFrameSinkManagerOnCompositorThread( const bool run_all_compositor_stages_before_draw = command_line->HasSwitch(switches::kRunAllCompositorStagesBeforeDraw); - if (task_executor) { - DCHECK(gpu_service); + if (gpu_service) { // Create OutputSurfaceProvider usable for GPU + software compositing. gpu_memory_buffer_manager_ = std::make_unique<InProcessGpuMemoryBufferManager>( gpu_service->gpu_memory_buffer_factory(), gpu_service->sync_point_manager()); - auto* image_factory = gpu_service->gpu_image_factory(); - output_surface_provider_ = std::make_unique<OutputSurfaceProviderImpl>( - gpu_service, task_executor, gpu_service, - gpu_memory_buffer_manager_.get(), image_factory, headless); + output_surface_provider_ = + std::make_unique<OutputSurfaceProviderImpl>(gpu_service, headless); // Create video frame pool context provider that will enable the frame sink // manager to create GMB-backed video frames. diff --git a/chromium/components/viz/service/main/viz_compositor_thread_runner_impl.h b/chromium/components/viz/service/main/viz_compositor_thread_runner_impl.h index d0ea9a64371..c03ed0ef016 100644 --- a/chromium/components/viz/service/main/viz_compositor_thread_runner_impl.h +++ b/chromium/components/viz/service/main/viz_compositor_thread_runner_impl.h @@ -52,9 +52,7 @@ class VizCompositorThreadRunnerImpl : public VizCompositorThreadRunner { bool CreateHintSessionFactory( base::flat_set<base::PlatformThreadId> thread_ids, base::RepeatingClosure* wake_up_closure) override; - void CreateFrameSinkManager(mojom::FrameSinkManagerParamsPtr params) override; void CreateFrameSinkManager(mojom::FrameSinkManagerParamsPtr params, - gpu::CommandBufferTaskExecutor* task_executor, GpuServiceImpl* gpu_service) override; private: @@ -65,7 +63,6 @@ class VizCompositorThreadRunnerImpl : public VizCompositorThreadRunner { void WakeUpOnCompositorThread(); void CreateFrameSinkManagerOnCompositorThread( mojom::FrameSinkManagerParamsPtr params, - gpu::CommandBufferTaskExecutor* task_executor, GpuServiceImpl* gpu_service); void TearDownOnCompositorThread(); diff --git a/chromium/components/viz/service/main/viz_main_impl.cc b/chromium/components/viz/service/main/viz_main_impl.cc index f682cf1fced..8275099bb63 100644 --- a/chromium/components/viz/service/main/viz_main_impl.cc +++ b/chromium/components/viz/service/main/viz_main_impl.cc @@ -172,7 +172,7 @@ void VizMainImpl::CreateGpuService( mojo::PendingRemote< discardable_memory::mojom::DiscardableSharedMemoryManager> discardable_memory_manager, - mojo::ScopedSharedBufferHandle activity_flags, + base::UnsafeSharedMemoryRegion activity_flags_region, gfx::FontRenderParams::SubpixelRendering subpixel_rendering) { DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread()); @@ -208,7 +208,7 @@ void VizMainImpl::CreateGpuService( gpu_service_->InitializeWithHost( gpu_host.Unbind(), - gpu::GpuProcessActivityFlags(std::move(activity_flags)), + gpu::GpuProcessActivityFlags(std::move(activity_flags_region)), gpu_init_->TakeDefaultOffscreenSurface(), dependencies_.sync_point_manager, dependencies_.shared_image_manager, dependencies_.shutdown_event); @@ -261,16 +261,6 @@ void VizMainImpl::CreateFrameSinkManagerInternal( DCHECK(gpu_service_); DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread()); - gl::GLSurfaceFormat format; - // If we are running a SW Viz process, we may not have a default offscreen - // surface. - if (auto* offscreen_surface = - gpu_service_->gpu_channel_manager()->default_offscreen_surface()) { - format = offscreen_surface->GetFormat(); - } else { - DCHECK_EQ(gl::GetGLImplementation(), gl::kGLImplementationDisabled); - } - // When the host loses its connection to the viz process, it assumes the // process has crashed and tries to reinitialize it. However, it is possible // to have lost the connection for other reasons (e.g. deserialization @@ -278,18 +268,11 @@ void VizMainImpl::CreateFrameSinkManagerInternal( // FrameSinkManagerImpl, so just do a hard CHECK rather than crashing down the // road so that all crash reports caused by this issue look the same and have // the same signature. https://crbug.com/928845 - CHECK(!task_executor_); - - task_executor_ = std::make_unique<gpu::GpuInProcessThreadService>( - this, gpu_thread_task_runner_, gpu_service_->GetGpuScheduler(), - gpu_service_->sync_point_manager(), gpu_service_->mailbox_manager(), - format, gpu_service_->gpu_feature_info(), - gpu_service_->gpu_channel_manager()->gpu_preferences(), - gpu_service_->shared_image_manager(), - gpu_service_->gpu_channel_manager()->program_cache()); - - viz_compositor_thread_runner_->CreateFrameSinkManager( - std::move(params), task_executor_.get(), gpu_service_.get()); + CHECK(!has_created_frame_sink_manager_); + has_created_frame_sink_manager_ = true; + + viz_compositor_thread_runner_->CreateFrameSinkManager(std::move(params), + gpu_service_.get()); } #if BUILDFLAG(USE_VIZ_DEBUGGER) @@ -307,20 +290,6 @@ void VizMainImpl::StopDebugStream() { } #endif -scoped_refptr<gpu::SharedContextState> VizMainImpl::GetSharedContextState() { - // This method should be only called for GLRenderer and not for SkiaRenderer. - // Hence adding DCHECK since DrDc only works with SkiaRenderer. - DCHECK(!features::IsDrDcEnabled()); - return gpu_service_->GetContextState(); -} - -scoped_refptr<gl::GLShareGroup> VizMainImpl::GetShareGroup() { - // This method should be only called for GLRenderer and not for SkiaRenderer. - // Hence adding DCHECK since DrDc only works with SkiaRenderer. - DCHECK(!features::IsDrDcEnabled()); - return gpu_service_->share_group(); -} - void VizMainImpl::ExitProcess(ExitCode immediate_exit_code) { DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread()); diff --git a/chromium/components/viz/service/main/viz_main_impl.h b/chromium/components/viz/service/main/viz_main_impl.h index b2965ba9abf..74024c98bef 100644 --- a/chromium/components/viz/service/main/viz_main_impl.h +++ b/chromium/components/viz/service/main/viz_main_impl.h @@ -8,6 +8,7 @@ #include <memory> #include "base/memory/raw_ptr.h" +#include "base/memory/unsafe_shared_memory_region.h" #include "base/process/process_handle.h" #include "base/task/single_thread_task_runner.h" #include "base/threading/thread.h" @@ -16,8 +17,6 @@ #include "components/viz/common/buildflags.h" #include "components/viz/service/gl/gpu_service_impl.h" #include "components/viz/service/main/viz_compositor_thread_runner_impl.h" -#include "gpu/ipc/gpu_in_process_thread_service.h" -#include "gpu/ipc/in_process_command_buffer.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" @@ -50,8 +49,7 @@ namespace viz { class InfoCollectionGpuServiceImpl; #endif -class VizMainImpl : public mojom::VizMain, - public gpu::GpuInProcessThreadServiceDelegate { +class VizMainImpl : public mojom::VizMain { public: class Delegate { public: @@ -120,7 +118,7 @@ class VizMainImpl : public mojom::VizMain, mojo::PendingRemote< discardable_memory::mojom::DiscardableSharedMemoryManager> discardable_memory_manager, - mojo::ScopedSharedBufferHandle activity_flags, + base::UnsafeSharedMemoryRegion activity_flags_region, gfx::FontRenderParams::SubpixelRendering subpixel_rendering) override; #if BUILDFLAG(IS_WIN) void CreateInfoCollectionGpuService( @@ -138,10 +136,6 @@ class VizMainImpl : public mojom::VizMain, void StopDebugStream() override; #endif - // gpu::GpuInProcessThreadServiceDelegate implementation: - scoped_refptr<gpu::SharedContextState> GetSharedContextState() override; - scoped_refptr<gl::GLShareGroup> GetShareGroup() override; - GpuServiceImpl* gpu_service() { return gpu_service_.get(); } const GpuServiceImpl* gpu_service() const { return gpu_service_.get(); } @@ -179,22 +173,19 @@ class VizMainImpl : public mojom::VizMain, std::unique_ptr<InfoCollectionGpuServiceImpl> info_collection_gpu_service_; #endif - // Allows the display compositor to use InProcessCommandBuffer to send GPU - // commands to the GPU thread from the compositor thread. This must outlive - // |viz_compositor_thread_runner_|. - std::unique_ptr<gpu::CommandBufferTaskExecutor> task_executor_; - // If the gpu service is not yet ready then we stash pending // FrameSinkManagerParams. mojom::FrameSinkManagerParamsPtr pending_frame_sink_manager_params_; + bool has_created_frame_sink_manager_ = false; + // Runs the VizCompositorThread for the display compositor. std::unique_ptr<VizCompositorThreadRunnerImpl> viz_compositor_thread_runner_impl_; // Note under Android WebView where VizCompositorThreadRunner is not created // and owned by this, Viz does not interact with other objects in this class, - // such as GpuServiceImpl or CommandBufferTaskExecutor. Code should take care - // to avoid introducing such assumptions. + // such as GpuServiceImpl. Code should take care to avoid introducing such + // assumptions. raw_ptr<VizCompositorThreadRunner> viz_compositor_thread_runner_ = nullptr; const scoped_refptr<base::SingleThreadTaskRunner> gpu_thread_task_runner_; diff --git a/chromium/components/viz/service/main/viz_main_impl_unittest.cc b/chromium/components/viz/service/main/viz_main_impl_unittest.cc index abc59d12216..f5c74005135 100644 --- a/chromium/components/viz/service/main/viz_main_impl_unittest.cc +++ b/chromium/components/viz/service/main/viz_main_impl_unittest.cc @@ -57,11 +57,8 @@ class MockVizCompositorThreadRunner : public VizCompositorThreadRunner { base::RepeatingClosure* wake_up_closure) override { return false; } - MOCK_METHOD1(CreateFrameSinkManager, void(mojom::FrameSinkManagerParamsPtr)); - MOCK_METHOD3(CreateFrameSinkManager, - void(mojom::FrameSinkManagerParamsPtr, - gpu::CommandBufferTaskExecutor*, - GpuServiceImpl*)); + MOCK_METHOD2(CreateFrameSinkManager, + void(mojom::FrameSinkManagerParamsPtr, GpuServiceImpl*)); private: const raw_ptr<base::SingleThreadTaskRunner> task_runner_; diff --git a/chromium/components/viz/service/performance_hint/hint_session.cc b/chromium/components/viz/service/performance_hint/hint_session.cc index ecb5a5f2281..1731aef08dc 100644 --- a/chromium/components/viz/service/performance_hint/hint_session.cc +++ b/chromium/components/viz/service/performance_hint/hint_session.cc @@ -35,6 +35,8 @@ using pAPerformanceHint_createSession = const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos); +using pAPerformanceHint_updateTargetWorkDuration = + int (*)(APerformanceHintSession* session, int64_t targetDurationNanos); using pAPerformanceHint_reportActualWorkDuration = int (*)(APerformanceHintSession* session, int64_t actualDurationNanos); using pAPerformanceHint_closeSession = @@ -74,6 +76,7 @@ struct AdpfMethods { LOAD_FUNCTION(main_dl_handle, APerformanceHint_getManager); LOAD_FUNCTION(main_dl_handle, APerformanceHint_createSession); + LOAD_FUNCTION(main_dl_handle, APerformanceHint_updateTargetWorkDuration); LOAD_FUNCTION(main_dl_handle, APerformanceHint_reportActualWorkDuration); LOAD_FUNCTION(main_dl_handle, APerformanceHint_closeSession); } @@ -83,6 +86,8 @@ struct AdpfMethods { bool supported = true; pAPerformanceHint_getManager APerformanceHint_getManagerFn; pAPerformanceHint_createSession APerformanceHint_createSessionFn; + pAPerformanceHint_updateTargetWorkDuration + APerformanceHint_updateTargetWorkDurationFn; pAPerformanceHint_reportActualWorkDuration APerformanceHint_reportActualWorkDurationFn; pAPerformanceHint_closeSession APerformanceHint_closeSessionFn; @@ -95,6 +100,7 @@ class AdpfHintSession : public HintSession { base::TimeDelta target_duration); ~AdpfHintSession() override; + void UpdateTargetDuration(base::TimeDelta target_duration) override; void ReportCpuCompletionTime(base::TimeDelta actual_duration) override; void WakeUp(); @@ -102,7 +108,7 @@ class AdpfHintSession : public HintSession { private: APerformanceHintSession* const hint_session_; HintSessionFactoryImpl* const factory_; - const base::TimeDelta target_duration_; + base::TimeDelta target_duration_; }; class HintSessionFactoryImpl : public HintSessionFactory { @@ -143,6 +149,15 @@ AdpfHintSession::~AdpfHintSession() { AdpfMethods::Get().APerformanceHint_closeSessionFn(hint_session_); } +void AdpfHintSession::UpdateTargetDuration(base::TimeDelta target_duration) { + DCHECK_CALLED_ON_VALID_THREAD(factory_->thread_checker_); + if (target_duration_ == target_duration) + return; + target_duration_ = target_duration; + AdpfMethods::Get().APerformanceHint_updateTargetWorkDurationFn( + hint_session_, target_duration.InNanoseconds()); +} + void AdpfHintSession::ReportCpuCompletionTime(base::TimeDelta actual_duration) { DCHECK_CALLED_ON_VALID_THREAD(factory_->thread_checker_); AdpfMethods::Get().APerformanceHint_reportActualWorkDurationFn( diff --git a/chromium/components/viz/service/performance_hint/hint_session.h b/chromium/components/viz/service/performance_hint/hint_session.h index a961ea93522..91b601465ff 100644 --- a/chromium/components/viz/service/performance_hint/hint_session.h +++ b/chromium/components/viz/service/performance_hint/hint_session.h @@ -22,6 +22,8 @@ class VIZ_SERVICE_EXPORT HintSession { public: virtual ~HintSession() = default; + virtual void UpdateTargetDuration(base::TimeDelta target_duration) = 0; + // `actual_duration` is compared to `target_duration` in `CreateSession` to // determine the performance of a frame. virtual void ReportCpuCompletionTime(base::TimeDelta actual_duration) = 0; diff --git a/chromium/components/viz/service/surfaces/surface.cc b/chromium/components/viz/service/surfaces/surface.cc index 900270f8b6d..9a68ef9a234 100644 --- a/chromium/components/viz/service/surfaces/surface.cc +++ b/chromium/components/viz/service/surfaces/surface.cc @@ -80,11 +80,13 @@ void Surface::PresentationHelper::DidPresent( Surface::Surface(const SurfaceInfo& surface_info, SurfaceManager* surface_manager, SurfaceAllocationGroup* allocation_group, - base::WeakPtr<SurfaceClient> surface_client) + base::WeakPtr<SurfaceClient> surface_client, + size_t max_uncommitted_frames) : surface_info_(surface_info), surface_manager_(surface_manager), surface_client_(std::move(surface_client)), - allocation_group_(allocation_group) { + allocation_group_(allocation_group), + max_uncommitted_frames_(max_uncommitted_frames) { TRACE_EVENT_ASYNC_BEGIN1(TRACE_DISABLED_BY_DEFAULT("viz.surface_lifetime"), "Surface", this, "surface_info", surface_info.ToString()); @@ -189,30 +191,70 @@ Surface::QueueFrameResult Surface::QueueFrame( return QueueFrameResult::REJECTED; } - QueueFrameResult result = QueueFrameResult::ACCEPTED_ACTIVE; + // Receive and track the resources referenced from the CompositorFrame + // regardless of whether it's pending or active. + surface_client_->ReceiveFromChild(frame.resource_list); + + QueueFrameResult result = QueueFrameResult::ACCEPTED_PENDING; + + if (!max_uncommitted_frames_) { + result = CommitFrame(FrameData(std::move(frame), frame_index)); + } else { + // Return oldest frame if uncommitted queue is full. + DCHECK_LE(uncommitted_frames_.size(), max_uncommitted_frames_); + if (uncommitted_frames_.size() == max_uncommitted_frames_) { + TRACE_EVENT_INSTANT1("viz", "DropUncommitedFrame", + TRACE_EVENT_SCOPE_THREAD, "queue_length", + uncommitted_frames_.size()); + + UnrefFrameResourcesAndRunCallbacks( + std::move(uncommitted_frames_.front())); + uncommitted_frames_.pop_front(); + } + + uncommitted_frames_.push_back(FrameData(std::move(frame), frame_index)); + + // If we still have space in queue we should send ack the client because we + // can receive another frame without dropping it. + if (uncommitted_frames_.size() < max_uncommitted_frames_) { + TRACE_EVENT_INSTANT1("viz", "AckingUncommitedFrame", + TRACE_EVENT_SCOPE_THREAD, "queue_length", + uncommitted_frames_.size()); + uncommitted_frames_.back().SendAckIfNeeded(surface_client_.get()); + } + + surface_manager_->OnSurfaceHasNewUncommittedFrame(this); + } + // The frame should not fail to display beyond this point. Release the + // callback so it is not called. + std::ignore = frame_rejected_callback.Release(); + + return result; +} + +Surface::QueueFrameResult Surface::CommitFrame(FrameData frame) { + TRACE_EVENT1("viz", "Surface::CommitFrame", "SurfaceId", + surface_id().ToString()); is_latency_info_taken_ = false; if (active_frame_data_ || pending_frame_data_) previous_frame_surface_id_ = surface_id(); - TakePendingLatencyInfo(&frame.metadata.latency_info); + TakePendingLatencyInfo(&frame.frame.metadata.latency_info); absl::optional<FrameData> previous_pending_frame_data = std::move(pending_frame_data_); pending_frame_data_.reset(); - UpdateActivationDependencies(frame); - - // Receive and track the resources referenced from the CompositorFrame - // regardless of whether it's pending or active. - surface_client_->ReceiveFromChild(frame.resource_list); + UpdateActivationDependencies(frame.frame); + QueueFrameResult result = QueueFrameResult::ACCEPTED_ACTIVE; if (activation_dependencies_.empty()) { // If there are no blockers, then immediately activate the frame. - ActivateFrame(FrameData(std::move(frame), frame_index)); + ActivateFrame(std::move(frame)); } else { - pending_frame_data_ = FrameData(std::move(frame), frame_index); + pending_frame_data_ = std::move(frame); auto traced_value = std::make_unique<base::trace_event::TracedValue>(); traced_value->BeginArray("Pending"); @@ -240,9 +282,8 @@ Surface::QueueFrameResult Surface::QueueFrame( // Returns resources for the previous pending frame. UnrefFrameResourcesAndRunCallbacks(std::move(previous_pending_frame_data)); - // The frame should not fail to display beyond this point. Release the - // callback so it is not called. - std::ignore = frame_rejected_callback.Release(); + if (surface_client_) + surface_client_->OnSurfaceCommitted(this); return result; } @@ -340,6 +381,14 @@ Surface::FrameData& Surface::FrameData::operator=(FrameData&& other) = default; Surface::FrameData::~FrameData() = default; +void Surface::FrameData::SendAckIfNeeded(SurfaceClient* client) { + if (!frame_acked) { + frame_acked = true; + if (client) + client->SendCompositorFrameAck(); + } +} + void Surface::ActivatePendingFrame() { DCHECK(pending_frame_data_); FrameData frame_data = std::move(*pending_frame_data_); @@ -356,6 +405,67 @@ void Surface::ActivatePendingFrame() { ActivateFrame(std::move(frame_data)); } +void Surface::CommitFramesRecursively(const CommitPredicate& predicate) { + TRACE_EVENT1("viz", "Surface::CommitFramesRecursively", "SurfaceId", + surface_id().ToString()); + + // This should only be called if we use uncommitted frames queue. + DCHECK_GT(max_uncommitted_frames_, 0u); + + while (!uncommitted_frames_.empty()) { + const auto& ack = + uncommitted_frames_.front().frame.metadata.begin_frame_ack; + + if (!predicate.Run(surface_id(), ack.frame_id)) + break; + + CommitFrame(std::move(uncommitted_frames_.front())); + uncommitted_frames_.pop_front(); + } + + if (HasPendingFrame()) { + for (auto& range : pending_frame_data_->frame.metadata.referenced_surfaces) + surface_manager_->CommitFramesInRangeRecursively(range, predicate); + } + + if (HasActiveFrame()) { + for (auto& range : active_frame_data_->frame.metadata.referenced_surfaces) + surface_manager_->CommitFramesInRangeRecursively(range, predicate); + } + + // If we freed up some space in queue send ack for the last frame if it's + // still unacked, so client can continue producing frames. + if (uncommitted_frames_.size() < max_uncommitted_frames_) { + if (!uncommitted_frames_.empty()) + uncommitted_frames_.back().SendAckIfNeeded(surface_client_.get()); + + // Only last frame can be unacked because we ack frames as we put them in + // queue if queue isn't full. If we acked frame above, now verify that + // they all are acked, to ensure we ack frame in order. +#if DCHECK_IS_ON() + for (auto& frames : uncommitted_frames_) { + DCHECK(frames.frame_acked); + } +#endif + } +} + +absl::optional<BeginFrameId> Surface::GetFirstUncommitedFrameId() { + if (uncommitted_frames_.empty()) + return absl::nullopt; + return uncommitted_frames_.front().frame.metadata.begin_frame_ack.frame_id; +} + +absl::optional<BeginFrameId> Surface::GetUncommitedFrameIdNewerThan( + const BeginFrameId& frame_id) { + for (auto& frame : uncommitted_frames_) { + if (frame.frame.metadata.begin_frame_ack.frame_id.IsNextInSequenceTo( + frame_id)) + return frame.frame.metadata.begin_frame_ack.frame_id; + } + return absl::nullopt; +} + void Surface::UpdateReferencedAllocationGroups( std::vector<SurfaceAllocationGroup*> new_referenced_allocation_groups) { base::flat_set<SurfaceAllocationGroup*> new_set( @@ -668,11 +778,8 @@ Surface::TakePresentationHelperForPresentNotification() { } void Surface::SendAckToClient() { - if (!active_frame_data_ || active_frame_data_->frame_acked) - return; - active_frame_data_->frame_acked = true; - if (surface_client_) - surface_client_->OnSurfaceProcessed(this); + if (active_frame_data_) + active_frame_data_->SendAckIfNeeded(surface_client_.get()); } void Surface::MarkAsDrawn() { @@ -718,8 +825,7 @@ void Surface::UnrefFrameResourcesAndRunCallbacks( resource.sync_token.Clear(); surface_client_->UnrefResources(std::move(resources)); - if (!frame_data->frame_acked) - surface_client_->OnSurfaceProcessed(this); + frame_data->SendAckIfNeeded(surface_client_.get()); // If we won't be getting a presented notification, we'll notify the client // when the frame is unref'd. @@ -790,7 +896,6 @@ void Surface::OnWillBeDrawn() { } void Surface::ActivatePendingFrameForInheritedDeadline() { - DCHECK(HasPendingFrame()); // Deadline inheritance implies that this surface was blocking the embedder, // so there shouldn't be an active frame. DCHECK(!HasActiveFrame()); diff --git a/chromium/components/viz/service/surfaces/surface.h b/chromium/components/viz/service/surfaces/surface.h index 30c4619c576..565c043f33e 100644 --- a/chromium/components/viz/service/surfaces/surface.h +++ b/chromium/components/viz/service/surfaces/surface.h @@ -17,6 +17,7 @@ #include "base/callback.h" #include "base/callback_helpers.h" +#include "base/containers/circular_deque.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "base/threading/platform_thread.h" @@ -53,24 +54,32 @@ class SurfaceManager; // A Surface is a representation of a sequence of CompositorFrames with a // common set of properties uniquely identified by a SurfaceId. In particular, // all CompositorFrames submitted to a single Surface share properties described -// in SurfaceInfo: device scale factor and size. A Surface can hold up to two +// in SurfaceInfo: device scale factor and size. A Surface can hold up few // CompositorFrames at a given time: // -// Active frame: An active frame is a candidate for display. A -// CompositorFrame is active if it has been explicitly marked -// as active after a deadline has passed or all its -// dependencies are active. +// Uncommitted frames: It's frame that has been received, but hasn't been +// processed yet. There can be up to +// `max_uncommitted_frames_` in this state. If +// `max_uncommitted_frames_` is zero all frames are +// committed as soon as they are received. // -// Pending frame: A pending CompositorFrame cannot be displayed on screen. A -// CompositorFrame is pending if it has unresolved -// dependencies: surface Ids to which there are no active -// CompositorFrames. +// Pending frame: A pending CompositorFrame cannot be displayed on +// screen. A CompositorFrame is pending when it has been +// committed but has unresolved dependencies: surface Ids +// to which there are no active CompositorFrames. There +// can be only one pending frame. // -// This two stage mechanism for managing CompositorFrames from a client exists -// to enable best-effort synchronization across clients. A surface subtree will -// remain pending until all dependencies are resolved: all clients have -// submitted CompositorFrames corresponding to a new property of the subtree -// (e.g. a new size). +// Active frame: An active frame is a candidate for display. A +// CompositorFrame is active if it has been explicitly +// marked as active after a deadline has passed or all +// its dependencies are active. There can be only one +// active frame. +// +// This pending+active frame mechanism for managing CompositorFrames from a +// client exists to enable best-effort synchronization across clients. A surface +// subtree will remain pending until all dependencies are resolved: all clients +// have submitted CompositorFrames corresponding to a new property of the +// subtree (e.g. a new size). // // Clients are assumed to be untrusted and so a client may not submit a // CompositorFrame to satisfy the dependency of the parent. Thus, by default, a @@ -78,6 +87,13 @@ class SurfaceManager; // deadline passes, then the CompositorFrame will activate despite missing // dependencies. The activated CompositorFrame can specify fallback behavior in // the event of missing dependencies at display time. +// +// On WebView display compositor runs asynchronously in regards of BeginFrames +// and CompositorFrame submissions, to avoid frame drops due to racyness +// uncommitted queue mechanism is used. When clients submits frame it goes to +// the queue and when the display compositor draws frames are committed from +// the queue to the pending or active frame. + class VIZ_SERVICE_EXPORT Surface final { public: class PresentationHelper { @@ -103,10 +119,14 @@ class VIZ_SERVICE_EXPORT Surface final { base::OnceCallback<void(const gfx::PresentationFeedback&)>; enum QueueFrameResult { REJECTED, ACCEPTED_ACTIVE, ACCEPTED_PENDING }; + using CommitPredicate = + base::RepeatingCallback<bool(const SurfaceId&, const BeginFrameId&)>; + Surface(const SurfaceInfo& surface_info, SurfaceManager* surface_manager, SurfaceAllocationGroup* allocation_group, - base::WeakPtr<SurfaceClient> surface_client); + base::WeakPtr<SurfaceClient> surface_client, + size_t max_uncommitted_frames); Surface(const Surface&) = delete; Surface& operator=(const Surface&) = delete; @@ -137,14 +157,19 @@ class VIZ_SERVICE_EXPORT Surface final { void SetPreviousFrameSurface(Surface* surface); - // Returns false if |frame| is invalid. - // |frame_rejected_callback| will be called once if the frame will not be - // displayed. + // Returns false if |frame| is invalid. |frame_rejected_callback| will be + // called once if the frame will not be displayed. QueueFrameResult QueueFrame( CompositorFrame frame, uint64_t frame_index, base::ScopedClosureRunner frame_rejected_callback); + // Commits frame(s) in this Surface and its dependencies. For each affected + // surface, the predicate will be called for each uncommitted frame in each + // surface from the oldest to the newest and will abort at first case of + // returning false. + void CommitFramesRecursively(const CommitPredicate& predicate); + // Notifies the Surface that a blocking SurfaceId now has an active // frame. void NotifySurfaceIdAvailable(const SurfaceId& surface_id); @@ -286,6 +311,14 @@ class VIZ_SERVICE_EXPORT Surface final { void DidAggregate(); + // Returns frame id of the oldest uncommitted frame if any, + absl::optional<BeginFrameId> GetFirstUncommitedFrameId(); + + // Returns frame id of the oldest uncommitted frame that is newer than + // provided `frame_id`. + absl::optional<BeginFrameId> GetUncommitedFrameIdNewerThan( + const BeginFrameId& frame_id); + private: struct FrameData { FrameData(CompositorFrame&& frame, uint64_t frame_index); @@ -299,6 +332,8 @@ class VIZ_SERVICE_EXPORT Surface final { return std::move(frame.metadata.delegated_ink_metadata); } + void SendAckIfNeeded(SurfaceClient* client); + CompositorFrame frame; uint64_t frame_index; // Whether the frame has been displayed or not. @@ -337,6 +372,10 @@ class VIZ_SERVICE_EXPORT Surface final { // Called when all of the surface's dependencies have been resolved. void ActivateFrame(FrameData frame_data); + // Called when display compositor is ready for this frame to be processed and + // it can become pending or active. + QueueFrameResult CommitFrame(FrameData frame); + // Resolve the activation deadline specified by |current_frame| into a wall // time to be used by SurfaceDependencyDeadline. FrameDeadline ResolveFrameDeadline(const CompositorFrame& current_frame); @@ -362,6 +401,10 @@ class VIZ_SERVICE_EXPORT Surface final { absl::optional<FrameData> pending_frame_data_; absl::optional<FrameData> active_frame_data_; + + // Queue of uncommitted frames, oldest first. + base::circular_deque<FrameData> uncommitted_frames_; + absl::optional<CompositorFrame> interpolated_frame_; bool seen_first_frame_activation_ = false; bool seen_first_surface_embedding_ = false; @@ -397,6 +440,8 @@ class VIZ_SERVICE_EXPORT Surface final { bool has_damage_from_interpolated_frame_ = false; + const size_t max_uncommitted_frames_; + base::WeakPtrFactory<Surface> weak_factory_{this}; }; diff --git a/chromium/components/viz/service/surfaces/surface_allocation_group.h b/chromium/components/viz/service/surfaces/surface_allocation_group.h index 29cc1418134..8a89cccff64 100644 --- a/chromium/components/viz/service/surfaces/surface_allocation_group.h +++ b/chromium/components/viz/service/surfaces/surface_allocation_group.h @@ -127,6 +127,8 @@ class VIZ_SERVICE_EXPORT SurfaceAllocationGroup { return surfaces_.empty() ? nullptr : surfaces_.back(); } + const std::vector<Surface*>& surfaces() const { return surfaces_; } + private: // Returns an iterator to the latest surface in |surfaces_| whose SurfaceId is // older than or equal to |surface_id|. The returned surface may not be active diff --git a/chromium/components/viz/service/surfaces/surface_client.h b/chromium/components/viz/service/surfaces/surface_client.h index 19e303ca26f..37482e1a41d 100644 --- a/chromium/components/viz/service/surfaces/surface_client.h +++ b/chromium/components/viz/service/surfaces/surface_client.h @@ -40,6 +40,10 @@ class VIZ_SERVICE_EXPORT SurfaceClient { virtual ~SurfaceClient() = default; + // Called when |surface| has committed a new CompositorFrame that become + // pending or active. + virtual void OnSurfaceCommitted(Surface* surface) = 0; + // Called when |surface| has a new CompositorFrame available for display. virtual void OnSurfaceActivated(Surface* surface) = 0; @@ -73,9 +77,9 @@ class VIZ_SERVICE_EXPORT SurfaceClient { // Notifies the client that a frame with |token| has been activated. virtual void OnFrameTokenChanged(uint32_t frame_token) = 0; - // Notifies the client that the submitted CompositorFrame has been processed - // (where processed may mean the frame has been displayed, or discarded). - virtual void OnSurfaceProcessed(Surface* surface) = 0; + // Sends a compositor frame ack to the client. Usually happens when viz is + // ready to receive another frame without dropping previous one. + virtual void SendCompositorFrameAck() = 0; // Notifies the client that a frame with |token| has been presented. virtual void OnSurfacePresented( diff --git a/chromium/components/viz/service/surfaces/surface_manager.cc b/chromium/components/viz/service/surfaces/surface_manager.cc index 43a0037f5de..924fe2d7b13 100644 --- a/chromium/components/viz/service/surfaces/surface_manager.cc +++ b/chromium/components/viz/service/surfaces/surface_manager.cc @@ -38,12 +38,14 @@ constexpr base::TimeDelta kExpireInterval = base::Seconds(10); SurfaceManager::SurfaceManager( SurfaceManagerDelegate* delegate, - absl::optional<uint32_t> activation_deadline_in_frames) + absl::optional<uint32_t> activation_deadline_in_frames, + size_t max_uncommitted_frames) : delegate_(delegate), activation_deadline_in_frames_(activation_deadline_in_frames), root_surface_id_(FrameSinkId(0u, 0u), LocalSurfaceId(1u, base::UnguessableToken::Create())), - tick_clock_(base::DefaultTickClock::GetInstance()) { + tick_clock_(base::DefaultTickClock::GetInstance()), + max_uncommitted_frames_(max_uncommitted_frames) { thread_checker_.DetachFromThread(); // Android WebView doesn't have a task runner and doesn't need the timer. @@ -113,8 +115,9 @@ Surface* SurfaceManager::CreateSurface( if (!allocation_group) return nullptr; - std::unique_ptr<Surface> surface = std::make_unique<Surface>( - surface_info, this, allocation_group, surface_client); + std::unique_ptr<Surface> surface = + std::make_unique<Surface>(surface_info, this, allocation_group, + surface_client, max_uncommitted_frames_); surface->SetDependencyDeadline( std::make_unique<SurfaceDependencyDeadline>(tick_clock_)); surface_map_[surface_info.id()] = std::move(surface); @@ -450,6 +453,11 @@ void SurfaceManager::FirstSurfaceActivation(const SurfaceInfo& surface_info) { observer.OnFirstSurfaceActivation(surface_info); } +void SurfaceManager::OnSurfaceHasNewUncommittedFrame(Surface* surface) { + for (auto& observer : observer_list_) + observer.OnSurfaceHasNewUncommittedFrame(surface->surface_id()); +} + void SurfaceManager::SurfaceActivated(Surface* surface) { // Trigger a display frame if necessary. const CompositorFrameMetadata& metadata = surface->GetActiveFrameMetadata(); @@ -629,4 +637,33 @@ void SurfaceManager::AggregatedFrameSinksChanged() { delegate_->AggregatedFrameSinksChanged(); } +void SurfaceManager::CommitFramesInRangeRecursively( + const SurfaceRange& range, + const CommitPredicate& predicate) { + // Technically we need only latest active surface, but because activation will + // happen during commit, it's impossible to predict which one will be active, + // so we're committing all surfaces in range. + + // If start of the range is in a different allocation group, process it first + // to keep activation in order. + if (range.start() && range.start()->local_surface_id().embed_token() != + range.end().local_surface_id().embed_token()) { + if (auto* allocation_group = + GetAllocationGroupForSurfaceId(*range.start())) { + for (auto* surface : allocation_group->surfaces()) { + if (range.IsInRangeInclusive(surface->surface_id())) + surface->CommitFramesRecursively(predicate); + } + } + } + + // Process the allocation group of the end of the range. + if (auto* allocation_group = GetAllocationGroupForSurfaceId(range.end())) { + for (auto* surface : allocation_group->surfaces()) { + if (range.IsInRangeInclusive(surface->surface_id())) + surface->CommitFramesRecursively(predicate); + } + } +} + } // namespace viz diff --git a/chromium/components/viz/service/surfaces/surface_manager.h b/chromium/components/viz/service/surfaces/surface_manager.h index b20b20bb0a1..36ca45d07e9 100644 --- a/chromium/components/viz/service/surfaces/surface_manager.h +++ b/chromium/components/viz/service/surfaces/surface_manager.h @@ -44,11 +44,13 @@ class SurfaceManagerDelegate; class SurfaceRange; struct BeginFrameAck; struct BeginFrameArgs; +struct BeginFrameId; class VIZ_SERVICE_EXPORT SurfaceManager { public: SurfaceManager(SurfaceManagerDelegate* delegate, - absl::optional<uint32_t> activation_deadline_in_frames); + absl::optional<uint32_t> activation_deadline_in_frames, + size_t max_uncommitted_frames); SurfaceManager(const SurfaceManager&) = delete; SurfaceManager& operator=(const SurfaceManager&) = delete; @@ -108,6 +110,9 @@ class VIZ_SERVICE_EXPORT SurfaceManager { // Called when a surface has an active frame for the first time. void FirstSurfaceActivation(const SurfaceInfo& surface_info); + // Called when there is new frame in uncommitted queue of the surface. + void OnSurfaceHasNewUncommittedFrame(Surface* surface); + // Called when a CompositorFrame within |surface| has activated. void SurfaceActivated(Surface* surface); @@ -200,6 +205,15 @@ class VIZ_SERVICE_EXPORT SurfaceManager { // changed since the previous aggregation. void AggregatedFrameSinksChanged(); + using CommitPredicate = + base::RepeatingCallback<bool(const SurfaceId&, const BeginFrameId&)>; + // Commits all surfaces in range and their referenced surfaces. For each + // surface processed calls `predicate` for each uncommitted frame from oldest + // to newest. If predicate returns true, surface is committed. If not the + // surface processing stops and we go to the next surface. + void CommitFramesInRangeRecursively(const SurfaceRange& range, + const CommitPredicate& predicate); + private: friend class CompositorFrameSinkSupportTest; friend class FrameSinkManagerTest; @@ -326,6 +340,10 @@ class VIZ_SERVICE_EXPORT SurfaceManager { bool allocation_groups_need_garbage_collection_ = false; + // Maximum length of uncommitted queue, zero means all frames are committed + // automatically. + const size_t max_uncommitted_frames_; + base::WeakPtrFactory<SurfaceManager> weak_factory_{this}; }; diff --git a/chromium/components/viz/service/surfaces/surface_observer.h b/chromium/components/viz/service/surfaces/surface_observer.h index 7bbb7b9c499..6bb23de3ad0 100644 --- a/chromium/components/viz/service/surfaces/surface_observer.h +++ b/chromium/components/viz/service/surfaces/surface_observer.h @@ -23,6 +23,9 @@ class VIZ_SERVICE_EXPORT SurfaceObserver { // time. virtual void OnFirstSurfaceActivation(const SurfaceInfo& surface_info) {} + // Called when there is new frame in uncommitted queue of the surface. + virtual void OnSurfaceHasNewUncommittedFrame(const SurfaceId& surface_id) {} + // Called when a CompositorFrame within a surface corresponding to // |surface_id| activates. virtual void OnSurfaceActivated(const SurfaceId& surface_id) {} diff --git a/chromium/components/viz/service/transitions/surface_animation_manager.cc b/chromium/components/viz/service/transitions/surface_animation_manager.cc index 6877eacd5e4..53696dc22a2 100644 --- a/chromium/components/viz/service/transitions/surface_animation_manager.cc +++ b/chromium/components/viz/service/transitions/surface_animation_manager.cc @@ -182,8 +182,9 @@ void ReplaceSharedElementWithRenderPass( auto* render_pass_quad = target_render_pass ->CreateAndAppendDrawQuad<CompositorRenderPassDrawQuad>(); - gfx::RectF tex_coord_rect( - gfx::SizeF(shared_element_content_pass->output_rect.size())); + gfx::RectF tex_coord_rect(gfx::SizeF(shared_element_quad.rect.size())); + tex_coord_rect.Offset(-shared_pass_output_rect.x(), + -shared_pass_output_rect.y()); render_pass_quad->SetNew( /*shared_quad_state=*/copied_quad_state, /*rect=*/shared_element_quad.rect, @@ -322,9 +323,7 @@ class SurfaceAnimationManager::StorageWithSurface { SurfaceAnimationManager::SurfaceAnimationManager( SharedBitmapManager* shared_bitmap_manager) - : animation_slowdown_factor_( - switches::GetDocumentTransitionSlowDownFactor()), - transferable_resource_tracker_(shared_bitmap_manager) {} + : transferable_resource_tracker_(shared_bitmap_manager) {} SurfaceAnimationManager::~SurfaceAnimationManager() = default; @@ -1069,10 +1068,8 @@ void SurfaceAnimationManager::CreateRootAnimationCurves( : gfx::CubicBezierTimingFunction::EaseType::EASE_OUT); // Create the transform curve. - base::TimeDelta total_duration = - ApplySlowdownFactor(save_directive_->root_config().duration); - base::TimeDelta total_delay = - ApplySlowdownFactor(save_directive_->root_config().delay); + base::TimeDelta total_duration = save_directive_->root_config().duration; + base::TimeDelta total_delay = save_directive_->root_config().delay; // The transform animation runs for the entire duration of the root // transition. @@ -1131,14 +1128,14 @@ void SurfaceAnimationManager::CreateSharedElementCurves() { const bool has_src_element = shared.has_value(); const auto& config = save_directive_->shared_elements()[i].config; - const auto total_duration = ApplySlowdownFactor(config.duration); - const auto total_delay = ApplySlowdownFactor(config.delay); + const auto total_duration = config.duration; + const auto total_delay = config.delay; - const auto opacity_duration = ApplySlowdownFactor( - total_duration * kSharedOpacityTransitionDurationScaleFactor); - const auto opacity_delay = ApplySlowdownFactor( + const auto opacity_duration = + total_duration * kSharedOpacityTransitionDurationScaleFactor; + const auto opacity_delay = total_delay + - (total_duration * kSharedOpacityTransitionDelayScaleFactor)); + (total_duration * kSharedOpacityTransitionDelayScaleFactor); // The kSrcOpacity curve animates the screen space opacity applied to the // blended content from src and dest elements. The value goes from the @@ -1320,11 +1317,6 @@ SurfaceAnimationManager::GetSurfaceSavedFrameStorageForTesting() { return &surface_saved_frame_storage_; } -base::TimeDelta SurfaceAnimationManager::ApplySlowdownFactor( - base::TimeDelta original) const { - return original * animation_slowdown_factor_; -} - // RootAnimationState SurfaceAnimationManager::RootAnimationState::RootAnimationState() = default; SurfaceAnimationManager::RootAnimationState::RootAnimationState( diff --git a/chromium/components/viz/service/transitions/surface_animation_manager.h b/chromium/components/viz/service/transitions/surface_animation_manager.h index 03efd964403..03a96239c3c 100644 --- a/chromium/components/viz/service/transitions/surface_animation_manager.h +++ b/chromium/components/viz/service/transitions/surface_animation_manager.h @@ -159,8 +159,6 @@ class VIZ_SERVICE_EXPORT SurfaceAnimationManager { // Returns true if we have a running animation for root or shared elements. bool HasRunningAnimations() const; - base::TimeDelta ApplySlowdownFactor(base::TimeDelta original) const; - // The state machine can take the following paths : // 1) Viz driven animation : kIdle -> kAnimating -> kLastFrame -> kIdle // 2) Renderer driven animation : kIdle -> kAnimatingRenderer -> kIdle @@ -170,7 +168,6 @@ class VIZ_SERVICE_EXPORT SurfaceAnimationManager { uint32_t last_processed_sequence_id_ = 0; - const int animation_slowdown_factor_ = 1; TransferableResourceTracker transferable_resource_tracker_; absl::optional<TransferableResourceTracker::ResourceFrame> saved_textures_; diff --git a/chromium/components/viz/test/BUILD.gn b/chromium/components/viz/test/BUILD.gn index 147c6dfd364..ba5d6888d89 100644 --- a/chromium/components/viz/test/BUILD.gn +++ b/chromium/components/viz/test/BUILD.gn @@ -11,7 +11,6 @@ buildflag_header("buildflags") { header = "buildflags.h" flags = [ - "ENABLE_GL_RENDERER_TESTS=$enable_gl_renderer_tests", "ENABLE_GL_BACKEND_TESTS=$enable_gl_backend_tests", "ENABLE_VULKAN_BACKEND_TESTS=$enable_vulkan_backend_tests", "ENABLE_DAWN_BACKEND_TESTS=$enable_dawn_backend_tests", diff --git a/chromium/components/viz/viz.gni b/chromium/components/viz/viz.gni index 8cf273608e3..2a5d9d72722 100644 --- a/chromium/components/viz/viz.gni +++ b/chromium/components/viz/viz.gni @@ -10,10 +10,6 @@ import("//testing/test.gni") # that code path. enable_gl_backend_tests = !is_fuchsia -# Controls if GLRenderer related tests should be built and run. -# TODO(crbug.com/1247756): Delete this flag along with GLRenderer. -enable_gl_renderer_tests = false - # TODO(samans): Support more configurations. # CFI issue: https://crbug.com/967819 # Fuchsia ARM64 https://crbug.com/1058247 @@ -72,8 +68,5 @@ template("viz_test") { } configs -= viz_remove_configs configs += viz_add_configs - - # TODO(crbug.com/1292951): Remove this line. - configs -= [ "//build/config/compiler:prevent_unsafe_narrowing" ] } } |