Implement RenderPassDrawQuad fuzzing.

This CL introduces RenderPassDrawQuads to the fuzzable quad types. It
does not yet fuzz masks/filters.

R=kylechar@chromium.org, riajiang@chromium.org

Bug: 923088
Change-Id: Ia3c8331c3c8093aff5842d4d23ad6338c1a8897c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1566363
Commit-Queue: Céline O'Neil <celineo@google.com>
Reviewed-by: kylechar <kylechar@chromium.org>
Reviewed-by: Ria Jiang <riajiang@chromium.org>
Auto-Submit: Céline O'Neil <celineo@google.com>
Cr-Commit-Position: refs/heads/master@{#654133}
diff --git a/components/viz/service/compositor_frame_fuzzer/BUILD.gn b/components/viz/service/compositor_frame_fuzzer/BUILD.gn
index 4530798..12ea598 100644
--- a/components/viz/service/compositor_frame_fuzzer/BUILD.gn
+++ b/components/viz/service/compositor_frame_fuzzer/BUILD.gn
@@ -29,6 +29,8 @@
   sources = [
     "$input_dir/1_quad_renderpass.asciipb",
     "$input_dir/2_quad_renderpass.asciipb",
+    "$input_dir/nested_render_pass_draw_quads.asciipb",
+    "$input_dir/overlapping_quads_in_render_pass_draw_quad.asciipb",
     "$input_dir/solid_color_tiled_background_with_2_quads_on_top.asciipb",
   ]
   outputs = [
@@ -68,6 +70,7 @@
 
   deps = [
     ":compositor_frame_fuzzer_proto",
+    ":generate_seed_corpus",
     "//components/viz/service",
     "//components/viz/test:test_support",
     "//mojo/core/embedder",
diff --git a/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer.proto b/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer.proto
index b0153d5..9b9c1f9 100644
--- a/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer.proto
+++ b/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer.proto
@@ -15,7 +15,8 @@
 
 message DrawQuad {
   // If not present, defaults to same dimensions as quad, with identity
-  // transform
+  // transform (or with |transform_to_root_target| if quad is a
+  // RenderPassDrawQuad).
   optional SharedQuadState sqs = 1;
 
   required Rect rect = 2;
@@ -26,6 +27,7 @@
   oneof quad {
     SolidColorDrawQuad solid_color_quad = 4;
     TileDrawQuad tile_quad = 5;
+    RenderPassDrawQuad render_pass_quad = 6;
   }
 }
 
@@ -70,6 +72,13 @@
   required bool force_anti_aliasing_off = 8;
 }
 
+message RenderPassDrawQuad {
+  required RenderPass render_pass = 1;
+
+  // If not present, defaults to DrawQuad's |rect|
+  optional Rect tex_coord_rect = 2;
+}
+
 // Spec to initialize a gfx::Rect.
 // Defaults to the size of the renderer frame as defined in
 // fuzzer_browser_process.cc, in the hopes that this generally yields more
diff --git a/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc b/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc
index 6f3587a..e991f18 100644
--- a/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc
+++ b/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc
@@ -8,6 +8,8 @@
 #include <utility>
 
 #include "base/logging.h"
+#include "cc/base/math_util.h"
+#include "components/viz/common/quads/render_pass_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/resource_sizes.h"
@@ -121,7 +123,7 @@
   return std::move(data_);
 }
 
-void FuzzedCompositorFrameBuilder::AddRenderPass(
+RenderPassId FuzzedCompositorFrameBuilder::AddRenderPass(
     const content::fuzzing::proto::RenderPass& render_pass_spec) {
   std::unique_ptr<RenderPass> pass = RenderPass::Create();
   gfx::Rect rp_output_rect =
@@ -141,7 +143,8 @@
           ? GetTransformFromProtobuf(
                 render_pass_spec.transform_to_root_target())
           : gfx::Transform();
-  pass->SetNew(1, rp_output_rect, rp_damage_rect, transform_to_root_target);
+  pass->SetNew(next_pass_id_++, rp_output_rect, rp_damage_rect,
+               transform_to_root_target);
 
   for (const content::fuzzing::proto::DrawQuad& quad_spec :
        render_pass_spec.quad_list()) {
@@ -169,12 +172,18 @@
         TryAddTileDrawQuad(pass.get(), quad_rect, quad_visible_rect, quad_spec);
         break;
       }
+      case content::fuzzing::proto::DrawQuad::kRenderPassQuad: {
+        TryAddRenderPassDrawQuad(pass.get(), quad_rect, quad_visible_rect,
+                                 quad_spec);
+        break;
+      }
       case content::fuzzing::proto::DrawQuad::QUAD_NOT_SET: {
         NOTREACHED();
       }
     }
   }
   data_.frame.render_pass_list.push_back(std::move(pass));
+  return data_.frame.render_pass_list.back()->id;
 }
 
 void FuzzedCompositorFrameBuilder::AddSolidColorDrawQuad(
@@ -228,6 +237,64 @@
   data_.frame.resource_list.push_back(transferable_resource);
 }
 
+void FuzzedCompositorFrameBuilder::TryAddRenderPassDrawQuad(
+    RenderPass* pass,
+    const gfx::Rect& rect,
+    const gfx::Rect& visible_rect,
+    const content::fuzzing::proto::DrawQuad& quad_spec) {
+  // Since child RenderPasses are allocated as textures, skip
+  // RenderPasses that would overflow our limit on allocated memory for
+  // this CompositorFrame.
+  gfx::Size render_pass_size =
+      GetRectFromProtobuf(
+          quad_spec.render_pass_quad().render_pass().output_rect())
+          .size();
+
+  // RenderPass texture dimensions are rounded up to a
+  // multiple of 64 pixels to reduce fragmentation.
+  constexpr int multiple = 64;
+  if (!cc::MathUtil::VerifyRoundup(render_pass_size.width(), multiple) ||
+      !cc::MathUtil::VerifyRoundup(render_pass_size.height(), multiple)) {
+    VLOG(1) << "Skipping RenderPassDrawQuad: bitmap of size "
+            << render_pass_size.ToString() << " can't be allocated.";
+    return;
+  }
+  render_pass_size = gfx::Size(
+      cc::MathUtil::UncheckedRoundUp(render_pass_size.width(), multiple),
+      cc::MathUtil::UncheckedRoundUp(render_pass_size.height(), multiple));
+
+  if (!TryReserveBitmapBytes(render_pass_size)) {
+    VLOG(1) << "Skipping RenderPassDrawQuad: bitmap of size "
+            << render_pass_size.ToString() << " can't be allocated.";
+    return;
+  }
+
+  // Build the child RenderPass and add it to the frame's
+  // RenderPassList.
+  RenderPassId child_pass_id =
+      AddRenderPass(quad_spec.render_pass_quad().render_pass());
+
+  // Unless a tex_coord_rect is defined in the protobuf specification,
+  // a good default is a rectangle covering the entire quad.
+  gfx::RectF tex_coord_rect = gfx::RectF(
+      quad_spec.render_pass_quad().has_tex_coord_rect()
+          ? GetRectFromProtobuf(quad_spec.render_pass_quad().tex_coord_rect())
+          : rect);
+
+  auto* shared_quad_state = pass->CreateAndAppendSharedQuadState();
+  ConfigureSharedQuadState(shared_quad_state, quad_spec);
+  auto* quad = pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
+  quad->SetNew(shared_quad_state, rect, visible_rect, child_pass_id,
+               /*mask_resource_id=*/ResourceId(),
+               /*mask_uv_rect=*/gfx::RectF(),
+               /*mask_texture_size=*/gfx::Size(),
+               /*filters_scale=*/gfx::Vector2dF(),
+               /*filters_origin=*/gfx::PointF(),
+               /*tex_coord_rect=*/tex_coord_rect,
+               /*force_anti_aliasing_off=*/false,
+               /*backdrop_filter_quality=*/1.0);
+}
+
 void FuzzedCompositorFrameBuilder::ConfigureSharedQuadState(
     SharedQuadState* shared_quad_state,
     const content::fuzzing::proto::DrawQuad& quad_spec) {
@@ -241,12 +308,24 @@
         Normalize(quad_spec.sqs().opacity()), SkBlendMode::kSrcOver,
         quad_spec.sqs().sorting_context_id());
   } else {
-    shared_quad_state->SetAll(
-        gfx::Transform(), GetRectFromProtobuf(quad_spec.rect()),
-        GetRectFromProtobuf(quad_spec.visible_rect()), gfx::RRectF(),
-        gfx::Rect(), /*is_clipped=*/false,
-        /*are_contents_opaque=*/true, /*opacity=*/1.0, SkBlendMode::kSrcOver,
-        /*sorting_context_id=*/0);
+    gfx::Transform transform;
+
+    if (quad_spec.quad_case() ==
+            content::fuzzing::proto::DrawQuad::kRenderPassQuad &&
+        quad_spec.render_pass_quad()
+            .render_pass()
+            .has_transform_to_root_target()) {
+      transform = GetTransformFromProtobuf(quad_spec.render_pass_quad()
+                                               .render_pass()
+                                               .transform_to_root_target());
+    }
+
+    shared_quad_state->SetAll(transform, GetRectFromProtobuf(quad_spec.rect()),
+                              GetRectFromProtobuf(quad_spec.visible_rect()),
+                              gfx::RRectF(), gfx::Rect(), /*is_clipped=*/false,
+                              /*are_contents_opaque=*/true, /*opacity=*/1.0,
+                              SkBlendMode::kSrcOver,
+                              /*sorting_context_id=*/0);
   }
 }
 
diff --git a/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.h b/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.h
index da318c9..89a16ec 100644
--- a/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.h
+++ b/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.h
@@ -67,7 +67,7 @@
   // reduce this if bots are running out of memory.
   static constexpr uint64_t kMaxTextureMemory = 1 << 29;
 
-  void AddRenderPass(
+  RenderPassId AddRenderPass(
       const content::fuzzing::proto::RenderPass& render_pass_spec);
 
   // Helper methods for AddRenderPass. Try* methods may return before
@@ -81,6 +81,11 @@
                           const gfx::Rect& rect,
                           const gfx::Rect& visible_rect,
                           const content::fuzzing::proto::DrawQuad& quad_spec);
+  void TryAddRenderPassDrawQuad(
+      RenderPass* pass,
+      const gfx::Rect& rect,
+      const gfx::Rect& visible_rect,
+      const content::fuzzing::proto::DrawQuad& quad_spec);
 
   // Configure the SharedQuadState to match the specifications in the
   // protobuf, if they are defined for this quad. Otherwise, use sensible
@@ -106,6 +111,8 @@
   // specific bitmaps/textures.
   uint64_t reserved_bytes_ = 0;
 
+  RenderPassId next_pass_id_ = 1;
+
   // Frame and data being built.
   FuzzedData data_;
 
diff --git a/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/nested_render_pass_draw_quads.asciipb b/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/nested_render_pass_draw_quads.asciipb
new file mode 100644
index 0000000..e028dda
--- /dev/null
+++ b/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/nested_render_pass_draw_quads.asciipb
@@ -0,0 +1,171 @@
+output_rect: {
+  x: 0
+  y: 0
+  width: 620
+  height: 400
+}
+damage_rect: {
+  x: 0
+  y: 0
+  width: 620
+  height: 400
+}
+quad_list: [
+  {
+    rect: {
+      x: 0
+      y: 0
+      width: 200
+      height: 200
+    }
+    visible_rect: {
+      x: 0
+      y: 0
+      width: 200
+      height: 200
+    }
+    render_pass_quad: {
+      render_pass: {
+        output_rect: {
+          x: 0
+          y: 0
+          width: 200
+          height: 200
+        }
+        damage_rect: {
+          x: 0
+          y: 0
+          width: 200
+          height: 200
+        }
+        quad_list: [
+          {
+            rect: {
+              x: 0
+              y: 0
+              width: 100
+              height: 100
+            }
+            visible_rect: {
+              x: 0
+              y: 0
+              width: 100
+              height: 100
+            }
+            render_pass_quad: {
+              render_pass: {
+                output_rect: {
+                  x: 0
+                  y: 0
+                  width: 100
+                  height: 100
+                }
+                damage_rect: {
+                  x: 0
+                  y: 0
+                  width: 100
+                  height: 100
+                }
+                quad_list: [
+                  {
+                    rect: {
+                      x: 0
+                      y: 0
+                      width: 300
+                      height: 300
+                    }
+                    visible_rect: {
+                      x: 0
+                      y: 0
+                      width: 300
+                      height: 300
+                    }
+                    solid_color_quad: {
+                      color: 0xFF0070FF
+                      force_anti_aliasing_off: false
+                    }
+                  }
+                ]
+              }
+            }
+          },
+          {
+            rect: {
+              x: 0
+              y: 0
+              width: 100
+              height: 100
+            }
+            visible_rect: {
+              x: 0
+              y: 0
+              width: 100
+              height: 100
+            }
+            render_pass_quad: {
+              render_pass: {
+                transform_to_root_target : {
+                  rotate: 0
+                  scale_x: 1
+                  scale_y: 1
+                  translate_x: 100
+                  translate_y: 100
+                }
+                output_rect: {
+                  x: 0
+                  y: 0
+                  width: 100
+                  height: 100
+                }
+                damage_rect: {
+                  x: 0
+                  y: 0
+                  width: 100
+                  height: 100
+                }
+                quad_list: [
+                  {
+                    rect: {
+                      x: 0
+                      y: 0
+                      width: 300
+                      height: 300
+                    }
+                    visible_rect: {
+                      x: 0
+                      y: 0
+                      width: 300
+                      height: 300
+                    }
+                    solid_color_quad: {
+                      color: 0xFF9970FF
+                      force_anti_aliasing_off: false
+                    }
+                  }
+                ]
+              }
+            }
+          },
+          {
+            rect: {
+              x: 0
+              y: 0
+              width: 300
+              height: 300
+            }
+            visible_rect: {
+              x: 0
+              y: 0
+              width: 300
+              height: 300
+            }
+            solid_color_quad: {
+              color: 0xFFFF0070
+              force_anti_aliasing_off: false
+            }
+          }
+        ]
+      }
+    }
+  }
+]
diff --git a/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/overlapping_quads_in_render_pass_draw_quad.asciipb b/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/overlapping_quads_in_render_pass_draw_quad.asciipb
new file mode 100644
index 0000000..96dc650
--- /dev/null
+++ b/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/overlapping_quads_in_render_pass_draw_quad.asciipb
@@ -0,0 +1,249 @@
+output_rect: {
+  x: 0
+  y: 0
+  width: 620
+  height: 400
+}
+damage_rect: {
+  x: 0
+  y: 0
+  width: 620
+  height: 400
+}
+quad_list: [
+  {
+    rect: {
+      x: 0
+      y: 0
+      width: 100
+      height: 100
+    }
+    visible_rect: {
+      x: 0
+      y: 0
+      width: 100
+      height: 100
+    }
+    solid_color_quad: {
+      color: 0xFFFF0070
+      force_anti_aliasing_off: false
+    }
+    sqs: {
+      transform: {
+        rotate: -10
+        scale_x: 1
+        scale_y: 1
+        translate_x: 50
+        translate_y: 50
+      }
+      layer_rect: {
+        x: 0
+        y: 0
+        width: 100
+        height: 100
+      }
+      visible_rect: {
+        x: 0
+        y: 0
+        width: 100
+        height: 100
+      }
+      clip_rect: {
+        x: 0
+        y: 0
+        width: 0
+        height: 0
+      }
+      is_clipped: false
+      are_contents_opaque: false
+      opacity: 0xFFFFFFFF
+      sorting_context_id: 0
+    }
+  },
+  {
+    rect: {
+      x: 0
+      y: 0
+      width: 100
+      height: 100
+    }
+    visible_rect: {
+      x: 0
+      y: 0
+      width: 100
+      height: 100
+    }
+    solid_color_quad: {
+      color: 0xFF70FF00
+      force_anti_aliasing_off: false
+    }
+    sqs: {
+      transform: {
+        rotate: 0
+        scale_x: 1
+        scale_y: 1
+        translate_x: 100
+        translate_y: 50
+      }
+      layer_rect: {
+        x: 0
+        y: 0
+        width: 100
+        height: 100
+      }
+      visible_rect: {
+        x: 0
+        y: 0
+        width: 100
+        height: 100
+      }
+      clip_rect: {
+        x: 0
+        y: 0
+        width: 0
+        height: 0
+      }
+      is_clipped: false
+      are_contents_opaque: true
+      opacity: 0xFFFFFFFF
+      sorting_context_id: 0
+    }
+  },
+  {
+    rect: {
+      x: 0
+      y: 0
+      width: 200
+      height: 200
+    }
+    visible_rect: {
+      x: 0
+      y: 0
+      width: 200
+      height: 200
+    }
+    render_pass_quad: {
+      render_pass: {
+        transform_to_root_target : {
+          rotate: 15
+          scale_x: 1
+          scale_y: 1
+          translate_x: 300
+          translate_y: 0
+        }
+        output_rect: {
+          x: 0
+          y: 0
+          width: 200
+          height: 200
+        }
+        damage_rect: {
+          x: 0
+          y: 0
+          width: 200
+          height: 200
+        }
+        quad_list: [
+          {
+            rect: {
+              x: 0
+              y: 0
+              width: 100
+              height: 100
+            }
+            visible_rect: {
+              x: 0
+              y: 0
+              width: 100
+              height: 100
+            }
+            solid_color_quad: {
+              color: 0xFF0070FF
+              force_anti_aliasing_off: false
+            }
+            sqs: {
+              transform: {
+                rotate: -10
+                scale_x: 1
+                scale_y: 1
+                translate_x: 50
+                translate_y: 50
+              }
+              layer_rect: {
+                x: 0
+                y: 0
+                width: 100
+                height: 100
+              }
+              visible_rect: {
+                x: 0
+                y: 0
+                width: 100
+                height: 100
+              }
+              clip_rect: {
+                x: 0
+                y: 0
+                width: 0
+                height: 0
+              }
+              is_clipped: false
+              are_contents_opaque: true
+              opacity: 0xFFFFFFFF
+              sorting_context_id: 0
+            }
+          },
+          {
+            rect: {
+              x: 0
+              y: 0
+              width: 100
+              height: 100
+            }
+            visible_rect: {
+              x: 0
+              y: 0
+              width: 100
+              height: 100
+            }
+            solid_color_quad: {
+              color: 0xFF9970FF
+              force_anti_aliasing_off: false
+            }
+            sqs: {
+              transform: {
+                rotate: 0
+                scale_x: 1
+                scale_y: 1
+                translate_x: 100
+                translate_y: 50
+              }
+              layer_rect: {
+                x: 0
+                y: 0
+                width: 100
+                height: 100
+              }
+              visible_rect: {
+                x: 0
+                y: 0
+                width: 100
+                height: 100
+              }
+              clip_rect: {
+                x: 0
+                y: 0
+                width: 0
+                height: 0
+              }
+              is_clipped: false
+              are_contents_opaque: true
+              opacity: 0xFFFFFFFF
+              sorting_context_id: 0
+            }
+          }
+        ]
+      }
+    }
+  }
+]