| // Copyright 2021 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/transition_utils.h" |
| |
| #include <memory> |
| #include <sstream> |
| #include <string> |
| #include <unordered_set> |
| #include <vector> |
| |
| #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/render_pass_io.h" |
| #include "components/viz/common/quads/shared_element_draw_quad.h" |
| #include "components/viz/common/quads/solid_color_draw_quad.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| |
| namespace viz { |
| |
| namespace { |
| |
| constexpr int kMaxListToProcess = 32; |
| constexpr int kMaxQuadsPerFrame = 8; |
| |
| struct StackFrame { |
| StackFrame(int list_index, |
| SharedQuadStateList::ConstIterator sqs_iter, |
| QuadList::ConstIterator quad_iter, |
| int indent) |
| : list_index(list_index), |
| sqs_iter(sqs_iter), |
| quad_iter(quad_iter), |
| indent(indent) {} |
| |
| int list_index; |
| SharedQuadStateList::ConstIterator sqs_iter; |
| QuadList::ConstIterator quad_iter; |
| int indent; |
| bool first_pass_visit = true; |
| }; |
| |
| std::string SkColorToRGBAString(SkColor color) { |
| std::ostringstream str; |
| str << "rgba(" << SkColorGetR(color) << ", " << SkColorGetG(color) << ", " |
| << SkColorGetB(color) << ", " << SkColorGetA(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, |
| const CompositorRenderPassList& list) { |
| auto write_indent = [&str](int indent) { |
| for (int i = 0; i < indent; ++i) |
| str << " "; |
| }; |
| auto write_render_pass = [&str](const CompositorRenderPass* pass) { |
| str << "(" << pass << ") render pass id=" << pass->id.GetUnsafeValue() |
| << " output_rect=" << pass->output_rect.ToString(); |
| if (pass->view_transition_element_resource_id.IsValid()) |
| str << " " << pass->view_transition_element_resource_id.ToString(); |
| str << "\n"; |
| }; |
| auto write_sqs = [&str](const SharedQuadState* sqs) { |
| str << "(" << sqs << ") switched to sqs with opacity=" << sqs->opacity |
| << ", blend_mode=" << BlendModeToString(sqs->blend_mode) |
| << " quad_layer_rect=" << sqs->quad_layer_rect.ToString() << "\n"; |
| }; |
| auto write_shared_element_quad = [&str](const SharedElementDrawQuad* quad) { |
| str << "(" << quad << ") SharedElementDrawQuad " |
| << quad->resource_id.ToString() << "\n"; |
| }; |
| auto write_solid_color_quad = [&str](const SolidColorDrawQuad* quad) { |
| str << "(" << quad |
| << ") SolidColorDrawQuad color=" << SkColorToRGBAString(quad->color) |
| << "\n"; |
| }; |
| auto write_compositor_render_pass_quad = |
| [&str](const CompositorRenderPassDrawQuad* quad) { |
| 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]; |
| |
| if (frame.first_pass_visit) { |
| frame.first_pass_visit = false; |
| write_indent(frame.indent); |
| write_render_pass(pass.get()); |
| seen_render_pass_ids.insert(pass->id.GetUnsafeValue()); |
| |
| if (const auto* sqs = *frame.sqs_iter) { |
| frame.indent += 2; |
| write_indent(frame.indent); |
| write_sqs(sqs); |
| } else { |
| stack.pop_back(); |
| continue; |
| } |
| |
| 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; |
| } |
| frame.indent -= 2; |
| while ((*frame.quad_iter)->shared_quad_state != *frame.sqs_iter) { |
| ++frame.sqs_iter; |
| DCHECK(frame.sqs_iter != pass->shared_quad_state_list.end()); |
| write_indent(frame.indent); |
| write_sqs(*frame.sqs_iter); |
| } |
| frame.indent += 2; |
| } |
| |
| write_indent(frame.indent); |
| switch ((*frame.quad_iter)->material) { |
| case DrawQuad::Material::kCompositorRenderPass: { |
| auto* quad = |
| CompositorRenderPassDrawQuad::MaterialCast((*frame.quad_iter)); |
| write_compositor_render_pass_quad(quad); |
| |
| bool found = false; |
| for (auto i = frame.list_index - 1; i >= 0; --i) { |
| if (list[i]->id == quad->render_pass_id) { |
| found = true; |
| stack.emplace_back(i, list[i]->shared_quad_state_list.begin(), |
| list[i]->quad_list.begin(), frame.indent + 2); |
| break; |
| } |
| } |
| CHECK(found) << "Couldn't find referenced render pass id"; |
| break; |
| } |
| case DrawQuad::Material::kSharedElement: { |
| auto* quad = SharedElementDrawQuad::MaterialCast((*frame.quad_iter)); |
| write_shared_element_quad(quad); |
| break; |
| } |
| case DrawQuad::Material::kSolidColor: { |
| auto* quad = SolidColorDrawQuad::MaterialCast((*frame.quad_iter)); |
| write_solid_color_quad(quad); |
| break; |
| } |
| default: |
| str << "DrawQuad, material: " |
| << DrawQuadMaterialToString((*frame.quad_iter)->material) << "\n"; |
| break; |
| } |
| } |
| return seen_render_pass_ids; |
| } |
| |
| } // namespace |
| |
| 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(), |
| list.back()->quad_list.begin(), 0); |
| str << "render pass ids in order:\n"; |
| for (const auto& pass : list) |
| str << " " << pass->id.GetUnsafeValue(); |
| str << "\n"; |
| |
| str << "rooted render pass tree:\n"; |
| std::unordered_set<uint64_t> seen_render_pass_ids = |
| ProcessStack(str, stack, list); |
| if (list.size() != seen_render_pass_ids.size()) |
| str << "orphaned render pass tree(s):\n"; |
| while (true) { |
| int i; |
| for (i = list.size() - 1; i >= 0; --i) { |
| if (seen_render_pass_ids.count(list[i]->id.GetUnsafeValue()) == 0) |
| break; |
| } |
| if (i < 0) |
| break; |
| |
| DCHECK(stack.empty()); |
| stack.emplace_back(i, list[i]->shared_quad_state_list.begin(), |
| list[i]->quad_list.begin(), 0); |
| auto new_seen_pass_ids = ProcessStack(str, stack, list); |
| seen_render_pass_ids.insert(new_seen_pass_ids.begin(), |
| new_seen_pass_ids.end()); |
| } |
| return str.str(); |
| } |
| |
| // static |
| float TransitionUtils::ComputeAccumulatedOpacity( |
| const CompositorRenderPassList& render_passes, |
| CompositorRenderPassId target_id) { |
| float opacity = 1.f; |
| bool found_render_pass = false; |
| for (auto& render_pass : render_passes) { |
| // If we haven't even reached the needed render pass, then we don't need to |
| // iterate the quads. Note that we also don't iterate the quads of the |
| // target render pass itself, since it can't draw itself. |
| if (!found_render_pass) { |
| found_render_pass = render_pass->id == target_id; |
| continue; |
| } |
| |
| for (auto* quad : render_pass->quad_list) { |
| if (quad->material != DrawQuad::Material::kCompositorRenderPass) |
| continue; |
| |
| const auto* pass_quad = CompositorRenderPassDrawQuad::MaterialCast(quad); |
| if (pass_quad->render_pass_id != target_id) |
| continue; |
| |
| // TODO(vmpstr): We need to consider different blend modes as well, |
| // although it's difficult in general. For the simple case of common |
| // SrcOver blend modes however, we can just multiply the opacity. |
| opacity *= pass_quad->shared_quad_state->opacity; |
| target_id = render_pass->id; |
| break; |
| } |
| } |
| return opacity; |
| } |
| |
| // static |
| std::unique_ptr<CompositorRenderPass> |
| TransitionUtils::CopyPassWithQuadFiltering( |
| const CompositorRenderPass& source_pass, |
| FilterCallback filter_callback) { |
| // This code is similar to CompositorRenderPass::DeepCopy, but does special |
| // logic when copying compositor render pass draw quads. |
| auto copy_pass = CompositorRenderPass::Create( |
| source_pass.shared_quad_state_list.size(), source_pass.quad_list.size()); |
| |
| copy_pass->SetAll( |
| source_pass.id, source_pass.output_rect, source_pass.damage_rect, |
| source_pass.transform_to_root_target, source_pass.filters, |
| source_pass.backdrop_filters, source_pass.backdrop_filter_bounds, |
| source_pass.subtree_capture_id, source_pass.subtree_size, |
| source_pass.view_transition_element_resource_id, |
| source_pass.has_transparent_background, source_pass.cache_render_pass, |
| source_pass.has_damage_from_contributing_content, |
| source_pass.generate_mipmap, source_pass.has_per_quad_damage); |
| |
| if (source_pass.shared_quad_state_list.empty()) |
| return copy_pass; |
| |
| SharedQuadStateList::ConstIterator sqs_iter = |
| source_pass.shared_quad_state_list.begin(); |
| SharedQuadState* copy_shared_quad_state = |
| copy_pass->CreateAndAppendSharedQuadState(); |
| *copy_shared_quad_state = **sqs_iter; |
| |
| for (auto* quad : source_pass.quad_list) { |
| while (quad->shared_quad_state != *sqs_iter) { |
| ++sqs_iter; |
| DCHECK(sqs_iter != source_pass.shared_quad_state_list.end()); |
| copy_shared_quad_state = copy_pass->CreateAndAppendSharedQuadState(); |
| *copy_shared_quad_state = **sqs_iter; |
| } |
| DCHECK(quad->shared_quad_state == *sqs_iter); |
| |
| if (filter_callback.Run(*quad, *copy_pass.get())) |
| continue; |
| |
| if (quad->material == DrawQuad::Material::kCompositorRenderPass) { |
| const auto* pass_quad = CompositorRenderPassDrawQuad::MaterialCast(quad); |
| copy_pass->CopyFromAndAppendRenderPassDrawQuad(pass_quad, |
| pass_quad->render_pass_id); |
| } else { |
| copy_pass->CopyFromAndAppendDrawQuad(quad); |
| } |
| } |
| |
| return copy_pass; |
| } |
| |
| } // namespace viz |