| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/viz/common/viz_utils.h" |
| |
| #include <algorithm> |
| #include <vector> |
| |
| #include "base/command_line.h" |
| #include "base/system/sys_info.h" |
| #include "build/build_config.h" |
| #include "cc/base/features.h" |
| #include "cc/base/math_util.h" |
| #include "components/viz/common/frame_sinks/copy_output_request.h" |
| #include "components/viz/common/quads/render_pass_draw_quad_internal.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/rect_conversions.h" |
| #include "ui/gfx/geometry/rrect_f.h" |
| |
| #if BUILDFLAG(IS_ANDROID) |
| #include <array> |
| #include <string> |
| |
| #include "base/android/android_info.h" |
| #endif |
| |
| #if BUILDFLAG(IS_POSIX) |
| #include <poll.h> |
| #include <sys/resource.h> |
| #endif |
| |
| namespace viz { |
| |
| #if BUILDFLAG(IS_ANDROID) |
| bool AlwaysUseWideColorGamut() { |
| // Full stack integration tests draw in sRGB and expect to read back in sRGB. |
| // WideColorGamut causes pixels to be drawn in P3, but read back doesn't tell |
| // us the color space. So disable WCG for tests. |
| const base::CommandLine& command_line = |
| *base::CommandLine::ForCurrentProcess(); |
| static const char kDisableWCGForTest[] = "disable-wcg-for-test"; |
| if (command_line.HasSwitch(kDisableWCGForTest)) |
| return false; |
| |
| // As it takes some work to compute this, cache the result. |
| static bool is_always_use_wide_color_gamut_enabled = [] { |
| const std::string& current_model = base::android::android_info::model(); |
| const std::array<std::string, 2> enabled_models = { |
| std::string{"Pixel 4"}, std::string{"Pixel 4 XL"}}; |
| for (const std::string& model : enabled_models) { |
| if (model == current_model) { |
| return true; |
| } |
| } |
| |
| return false; |
| }(); |
| |
| return is_always_use_wide_color_gamut_enabled; |
| } |
| #endif |
| |
| bool GatherFDStats(base::TimeDelta* delta_time_taken, |
| int* fd_max, |
| int* active_fd_count, |
| int* rlim_cur) { |
| #if !BUILDFLAG(IS_POSIX) |
| return false; |
| #else // BUILDFLAG(IS_POSIX) |
| // https://stackoverflow.com/questions/7976769/ |
| // getting-count-of-current-used-file-descriptors-from-c-code |
| base::ElapsedTimer timer; |
| rlimit limit_data; |
| getrlimit(RLIMIT_NOFILE, &limit_data); |
| std::vector<pollfd> poll_data; |
| constexpr int kMaxNumFDTested = 1 << 16; |
| // |rlim_cur| is the soft max but is likely the value we can rely on instead |
| // of the real max. |
| *rlim_cur = static_cast<int>(limit_data.rlim_cur); |
| *fd_max = std::max(1, std::min(*rlim_cur, kMaxNumFDTested)); |
| poll_data.resize(*fd_max); |
| for (size_t i = 0; i < poll_data.size(); i++) { |
| auto& each = poll_data[i]; |
| each.fd = static_cast<int>(i); |
| each.events = 0; |
| each.revents = 0; |
| } |
| |
| poll(poll_data.data(), poll_data.size(), 0); |
| *active_fd_count = 0; |
| for (auto&& each : poll_data) { |
| if (each.revents != POLLNVAL) |
| (*active_fd_count)++; |
| } |
| *delta_time_taken = timer.Elapsed(); |
| return true; |
| #endif // BUILDFLAG(IS_POSIX) |
| } |
| gfx::RectF ClippedQuadRectangleF(const DrawQuad* quad) { |
| gfx::RectF quad_rect = cc::MathUtil::MapClippedRect( |
| quad->shared_quad_state->quad_to_target_transform, |
| gfx::RectF(quad->rect)); |
| if (quad->shared_quad_state->clip_rect) |
| quad_rect.Intersect(gfx::RectF(*quad->shared_quad_state->clip_rect)); |
| return quad_rect; |
| } |
| |
| gfx::Rect ClippedQuadRectangle(const DrawQuad* quad) { |
| return gfx::ToEnclosingRect(ClippedQuadRectangleF(quad)); |
| } |
| |
| gfx::Rect GetTargetExpandedRectForPixelMovingFilters( |
| const RenderPassDrawQuadInternal& rpdq, |
| const cc::FilterOperations& filters) { |
| const SharedQuadState* shared_quad_state = rpdq.shared_quad_state; |
| gfx::Rect expanded_rect = GetExpandedRectForPixelMovingFilters(rpdq, filters); |
| return cc::MathUtil::MapEnclosingClippedRect( |
| shared_quad_state->quad_to_target_transform, expanded_rect); |
| } |
| |
| gfx::Rect GetExpandedRectForPixelMovingFilters( |
| const RenderPassDrawQuadInternal& rpdq, |
| const cc::FilterOperations& filters) { |
| SkMatrix local_matrix = |
| SkMatrix::Translate(rpdq.filters_origin.x(), rpdq.filters_origin.y()); |
| local_matrix.postScale(rpdq.filters_scale.x(), rpdq.filters_scale.y()); |
| |
| return filters.MapRect(rpdq.visible_rect, local_matrix); |
| } |
| |
| gfx::Transform GetViewTransitionTransform( |
| gfx::Rect shared_element_quad, |
| gfx::Rect view_transition_content_output) { |
| gfx::Transform view_transition_transform; |
| |
| view_transition_transform.Translate(shared_element_quad.x(), |
| shared_element_quad.y()); |
| |
| view_transition_transform.Scale( |
| shared_element_quad.width() / |
| static_cast<SkScalar>(view_transition_content_output.width()), |
| shared_element_quad.height() / |
| static_cast<SkScalar>(view_transition_content_output.height())); |
| |
| view_transition_transform.Translate(-view_transition_content_output.x(), |
| -view_transition_content_output.y()); |
| |
| return view_transition_transform; |
| } |
| |
| bool QuadRoundedCornersBoundsIntersects(const DrawQuad* quad, |
| const gfx::RectF& target_quad) { |
| const SharedQuadState* sqs = quad->shared_quad_state; |
| const gfx::MaskFilterInfo& mask_filter_info = sqs->mask_filter_info; |
| |
| // There is no rounded corner set. |
| if (!mask_filter_info.HasRoundedCorners()) { |
| return false; |
| } |
| |
| const gfx::RRectF& rounded_corner_bounds = |
| mask_filter_info.rounded_corner_bounds(); |
| |
| const gfx::RRectF::Corner corners[] = { |
| gfx::RRectF::Corner::kUpperLeft, gfx::RRectF::Corner::kUpperRight, |
| gfx::RRectF::Corner::kLowerRight, gfx::RRectF::Corner::kLowerLeft}; |
| for (auto c : corners) { |
| if (rounded_corner_bounds.CornerBoundingRect(c).Intersects(target_quad)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void SetCopyOutputRequestResultSize(CopyOutputRequest* request, |
| const gfx::Rect& src_rect, |
| const gfx::Size& output_size, |
| const gfx::Size& surface_size_in_pixels) { |
| CHECK(request); |
| if (!src_rect.IsEmpty()) { |
| request->set_area(src_rect); |
| } |
| if (output_size.IsEmpty()) { |
| return; |
| } |
| // The CopyOutputRequest API does not allow fixing the output size. Instead |
| // we have the set area and scale in such a way that it would result in the |
| // desired output size. |
| if (!request->has_area()) { |
| request->set_area(gfx::Rect(surface_size_in_pixels)); |
| } |
| request->set_result_selection(gfx::Rect(output_size)); |
| const gfx::Rect& area = request->area(); |
| // Viz would normally return an empty result for an empty area. |
| // However, this guard here is still necessary to protect against setting |
| // an illegal scaling ratio. |
| if (area.IsEmpty()) { |
| return; |
| } |
| request->SetScaleRatio( |
| gfx::Vector2d(area.width(), area.height()), |
| gfx::Vector2d(output_size.width(), output_size.height())); |
| } |
| |
| } // namespace viz |