1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/viz/common/quads/render_pass_io.h"
6 
7 #include <memory>
8 #include <string>
9 
10 #include "base/files/file_util.h"
11 #include "base/json/json_reader.h"
12 #include "base/path_service.h"
13 #include "base/values.h"
14 #include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
15 #include "components/viz/common/quads/solid_color_draw_quad.h"
16 #include "components/viz/common/quads/stream_video_draw_quad.h"
17 #include "components/viz/common/quads/tile_draw_quad.h"
18 #include "components/viz/common/quads/video_hole_draw_quad.h"
19 #include "components/viz/common/quads/yuv_video_draw_quad.h"
20 #include "components/viz/test/paths.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 
23 namespace gl {
24 struct HDRMetadata;
25 }
26 
27 namespace viz {
28 namespace {
29 
TEST(RenderPassIOTest,Default)30 TEST(RenderPassIOTest, Default) {
31   auto render_pass0 = CompositorRenderPass::Create();
32   base::Value dict0 = CompositorRenderPassToDict(*render_pass0);
33   auto render_pass1 = CompositorRenderPassFromDict(dict0);
34   EXPECT_TRUE(render_pass1);
35   base::Value dict1 = CompositorRenderPassToDict(*render_pass1);
36   EXPECT_EQ(dict0, dict1);
37 }
38 
TEST(RenderPassIOTest,FilterOperations)39 TEST(RenderPassIOTest, FilterOperations) {
40   auto render_pass0 = CompositorRenderPass::Create();
41   {
42     // Add two filters.
43     cc::FilterOperation grayscale =
44         cc::FilterOperation::CreateGrayscaleFilter(0.25f);
45     render_pass0->filters.Append(grayscale);
46     cc::FilterOperation opacity =
47         cc::FilterOperation::CreateOpacityFilter(0.8f);
48     render_pass0->filters.Append(opacity);
49   }
50   {
51     // Add three backdrop filters.
52     cc::FilterOperation drop_shadow =
53         cc::FilterOperation::CreateDropShadowFilter(gfx::Point(1.0f, 2.0f),
54                                                     0.8f, SK_ColorYELLOW);
55     render_pass0->backdrop_filters.Append(drop_shadow);
56     cc::FilterOperation invert = cc::FilterOperation::CreateInvertFilter(0.64f);
57     render_pass0->backdrop_filters.Append(invert);
58     cc::FilterOperation zoom = cc::FilterOperation::CreateZoomFilter(2.1f, 10);
59     render_pass0->backdrop_filters.Append(zoom);
60   }
61   {
62     // Set backdrop filter bounds.
63     gfx::RRectF rrect(gfx::RectF(2.f, 3.f, 4.f, 5.f), 1.5f);
64     ASSERT_EQ(gfx::RRectF::Type::kSingle, rrect.GetType());
65     render_pass0->backdrop_filter_bounds = rrect;
66   }
67   base::Value dict0 = CompositorRenderPassToDict(*render_pass0);
68   auto render_pass1 = CompositorRenderPassFromDict(dict0);
69   EXPECT_TRUE(render_pass1);
70   {
71     // Verify two filters are as expected.
72     EXPECT_EQ(render_pass0->filters, render_pass1->filters);
73     EXPECT_EQ(2u, render_pass1->filters.size());
74     EXPECT_EQ(cc::FilterOperation::GRAYSCALE,
75               render_pass1->filters.at(0).type());
76     EXPECT_EQ(0.25f, render_pass1->filters.at(0).amount());
77     EXPECT_EQ(cc::FilterOperation::OPACITY, render_pass1->filters.at(1).type());
78     EXPECT_EQ(0.8f, render_pass1->filters.at(1).amount());
79   }
80   {
81     // Verify three backdrop filters are as expected.
82     EXPECT_EQ(render_pass0->backdrop_filters, render_pass1->backdrop_filters);
83     EXPECT_EQ(3u, render_pass1->backdrop_filters.size());
84     EXPECT_EQ(cc::FilterOperation::DROP_SHADOW,
85               render_pass1->backdrop_filters.at(0).type());
86     EXPECT_EQ(0.8f, render_pass1->backdrop_filters.at(0).amount());
87     EXPECT_EQ(SK_ColorYELLOW,
88               render_pass1->backdrop_filters.at(0).drop_shadow_color());
89     EXPECT_EQ(gfx::Point(1.0f, 2.0f),
90               render_pass1->backdrop_filters.at(0).drop_shadow_offset());
91     EXPECT_EQ(cc::FilterOperation::INVERT,
92               render_pass1->backdrop_filters.at(1).type());
93     EXPECT_EQ(0.64f, render_pass1->backdrop_filters.at(1).amount());
94     EXPECT_EQ(cc::FilterOperation::ZOOM,
95               render_pass1->backdrop_filters.at(2).type());
96     EXPECT_EQ(2.1f, render_pass1->backdrop_filters.at(2).amount());
97     EXPECT_EQ(10, render_pass1->backdrop_filters.at(2).zoom_inset());
98   }
99   {
100     // Verify backdrop filter bounds are as expected.
101     EXPECT_TRUE(render_pass1->backdrop_filter_bounds.has_value());
102     EXPECT_TRUE(render_pass0->backdrop_filter_bounds->Equals(
103         render_pass1->backdrop_filter_bounds.value()));
104     EXPECT_EQ(gfx::RRectF::Type::kSingle,
105               render_pass1->backdrop_filter_bounds->GetType());
106     EXPECT_EQ(1.5f, render_pass1->backdrop_filter_bounds->GetSimpleRadius());
107     EXPECT_EQ(gfx::RectF(2.f, 3.f, 4.f, 5.f),
108               render_pass1->backdrop_filter_bounds->rect());
109   }
110   base::Value dict1 = CompositorRenderPassToDict(*render_pass1);
111   EXPECT_EQ(dict0, dict1);
112 }
113 
TEST(RenderPassIOTest,SharedQuadStateList)114 TEST(RenderPassIOTest, SharedQuadStateList) {
115   auto render_pass0 = CompositorRenderPass::Create();
116   {
117     // Add two SQS.
118     SharedQuadState* sqs0 = render_pass0->CreateAndAppendSharedQuadState();
119     ASSERT_TRUE(sqs0);
120     SharedQuadState* sqs1 = render_pass0->CreateAndAppendSharedQuadState();
121     ASSERT_TRUE(sqs1);
122     gfx::Transform transform;
123     transform.MakeIdentity();
124     sqs1->SetAll(
125         transform, gfx::Rect(0, 0, 640, 480), gfx::Rect(10, 10, 600, 400),
126         gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(2.f, 3.f, 4.f, 5.f), 1.5f)),
127         gfx::Rect(5, 20, 1000, 200), true, false, 0.5f, SkBlendMode::kDstOver,
128         101);
129     sqs1->is_fast_rounded_corner = true;
130     sqs1->de_jelly_delta_y = 0.7f;
131   }
132   base::Value dict0 = CompositorRenderPassToDict(*render_pass0);
133   auto render_pass1 = CompositorRenderPassFromDict(dict0);
134   EXPECT_TRUE(render_pass1);
135   {
136     // Verify two SQS.
137     EXPECT_EQ(2u, render_pass1->shared_quad_state_list.size());
138     const SharedQuadState* sqs0 =
139         render_pass1->shared_quad_state_list.ElementAt(0);
140     EXPECT_TRUE(sqs0);
141     EXPECT_TRUE(sqs0->quad_to_target_transform.IsIdentity());
142     EXPECT_EQ(gfx::Rect(), sqs0->quad_layer_rect);
143     EXPECT_EQ(gfx::Rect(), sqs0->visible_quad_layer_rect);
144     EXPECT_FALSE(sqs0->mask_filter_info.HasRoundedCorners());
145     EXPECT_EQ(gfx::Rect(), sqs0->clip_rect);
146     EXPECT_FALSE(sqs0->is_clipped);
147     EXPECT_TRUE(sqs0->are_contents_opaque);
148     EXPECT_EQ(1.0f, sqs0->opacity);
149     EXPECT_EQ(SkBlendMode::kSrcOver, sqs0->blend_mode);
150     EXPECT_EQ(0, sqs0->sorting_context_id);
151     EXPECT_FALSE(sqs0->is_fast_rounded_corner);
152     EXPECT_EQ(0.0f, sqs0->de_jelly_delta_y);
153 
154     const SharedQuadState* sqs1 =
155         render_pass1->shared_quad_state_list.ElementAt(1);
156     EXPECT_TRUE(sqs1);
157     EXPECT_TRUE(sqs1->quad_to_target_transform.IsIdentity());
158     EXPECT_EQ(gfx::Rect(0, 0, 640, 480), sqs1->quad_layer_rect);
159     EXPECT_EQ(gfx::Rect(10, 10, 600, 400), sqs1->visible_quad_layer_rect);
160     EXPECT_EQ(gfx::RRectF::Type::kSingle,
161               sqs1->mask_filter_info.rounded_corner_bounds().GetType());
162     EXPECT_EQ(1.5f,
163               sqs1->mask_filter_info.rounded_corner_bounds().GetSimpleRadius());
164     EXPECT_EQ(gfx::RectF(2.f, 3.f, 4.f, 5.f), sqs1->mask_filter_info.bounds());
165     EXPECT_EQ(gfx::Rect(5, 20, 1000, 200), sqs1->clip_rect);
166     EXPECT_TRUE(sqs1->is_clipped);
167     EXPECT_FALSE(sqs1->are_contents_opaque);
168     EXPECT_EQ(0.5f, sqs1->opacity);
169     EXPECT_EQ(SkBlendMode::kDstOver, sqs1->blend_mode);
170     EXPECT_EQ(101, sqs1->sorting_context_id);
171     EXPECT_TRUE(sqs1->is_fast_rounded_corner);
172     EXPECT_EQ(0.7f, sqs1->de_jelly_delta_y);
173   }
174   base::Value dict1 = CompositorRenderPassToDict(*render_pass1);
175   EXPECT_EQ(dict0, dict1);
176 }
177 
TEST(RenderPassIOTest,QuadList)178 TEST(RenderPassIOTest, QuadList) {
179   const size_t kSharedQuadStateCount = 5;
180   size_t quad_count = 0;
181   const DrawQuad::Material kQuadMaterials[] = {
182       DrawQuad::Material::kSolidColor,
183       DrawQuad::Material::kStreamVideoContent,
184       DrawQuad::Material::kVideoHole,
185       DrawQuad::Material::kYuvVideoContent,
186       DrawQuad::Material::kTextureContent,
187       DrawQuad::Material::kCompositorRenderPass,
188       DrawQuad::Material::kTiledContent,
189   };
190   auto render_pass0 = CompositorRenderPass::Create();
191   {
192     // Add to shared_quad_state_list.
193     for (size_t ii = 0; ii < kSharedQuadStateCount; ++ii) {
194       SharedQuadState* sqs = render_pass0->CreateAndAppendSharedQuadState();
195       ASSERT_TRUE(sqs);
196     }
197 
198     size_t sqs_index = 0;
199 
200     // Add to quad_list.
201     {
202       // 1. SolidColorDrawQuad
203       SolidColorDrawQuad* quad =
204           render_pass0->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
205       quad->SetAll(render_pass0->shared_quad_state_list.ElementAt(sqs_index),
206                    gfx::Rect(0, 0, 30, 40), gfx::Rect(1, 2, 20, 30), true,
207                    SK_ColorRED, false);
208       ++quad_count;
209     }
210     {
211       // 2. StreamVideoDrawQuad
212       StreamVideoDrawQuad* quad =
213           render_pass0->CreateAndAppendDrawQuad<StreamVideoDrawQuad>();
214       quad->SetAll(render_pass0->shared_quad_state_list.ElementAt(sqs_index),
215                    gfx::Rect(10, 10, 300, 400), gfx::Rect(10, 10, 200, 400),
216                    false, 100, gfx::Size(600, 800), gfx::PointF(0.f, 0.f),
217                    gfx::PointF(1.f, 1.f));
218       ++sqs_index;
219       ++quad_count;
220     }
221     {
222       // 3. VideoHoleDrawQuad
223       VideoHoleDrawQuad* quad =
224           render_pass0->CreateAndAppendDrawQuad<VideoHoleDrawQuad>();
225       quad->SetAll(render_pass0->shared_quad_state_list.ElementAt(sqs_index),
226                    gfx::Rect(5, 5, 305, 405), gfx::Rect(15, 15, 205, 305),
227                    false, base::UnguessableToken::Deserialize(2001, 2019));
228       ++quad_count;
229     }
230     {
231       // 4. YUVVideoDrawQuad
232       YUVVideoDrawQuad* quad =
233           render_pass0->CreateAndAppendDrawQuad<YUVVideoDrawQuad>();
234       skcms_Matrix3x3 primary_matrix = {{{0.6587f, 0.3206f, 0.1508f},
235                                          {0.3332f, 0.6135f, 0.0527f},
236                                          {0.0081f, 0.0659f, 0.7965f}}};
237       skcms_TransferFunction transfer_func = {
238           0.9495f, 0.0495f, 0.6587f, 0.3206f, 0.0003f, 0.f, 2.3955f};
239       quad->SetAll(
240           render_pass0->shared_quad_state_list.ElementAt(sqs_index),
241           gfx::Rect(0, 0, 800, 600), gfx::Rect(10, 15, 780, 570), false,
242           gfx::RectF(0.f, 0.f, 0.5f, 0.6f), gfx::RectF(0.1f, 0.2f, 0.7f, 0.8f),
243           gfx::Size(400, 200), gfx::Size(800, 400), 1u, 2u, 3u, 4u,
244           gfx::ColorSpace::CreateCustom(primary_matrix, transfer_func), 3.f,
245           1.1f, 12u, gfx::ProtectedVideoType::kClear, gfx::HDRMetadata());
246       ++sqs_index;
247       ++quad_count;
248     }
249     {
250       // 5. TextureDrawQuad
251       TextureDrawQuad* quad =
252           render_pass0->CreateAndAppendDrawQuad<TextureDrawQuad>();
253       float vertex_opacity[4] = {1.f, 0.5f, 0.6f, 1.f};
254       quad->SetAll(render_pass0->shared_quad_state_list.ElementAt(sqs_index),
255                    gfx::Rect(0, 0, 100, 50), gfx::Rect(0, 0, 100, 50), false,
256                    9u, gfx::Size(100, 50), false, gfx::PointF(0.f, 0.f),
257                    gfx::PointF(1.f, 1.f), SK_ColorBLUE, vertex_opacity, false,
258                    true, false, gfx::ProtectedVideoType::kHardwareProtected);
259       ++sqs_index;
260       ++quad_count;
261     }
262     {
263       // 6. CompositorRenderPassDrawQuad
264       CompositorRenderPassDrawQuad* quad =
265           render_pass0->CreateAndAppendDrawQuad<CompositorRenderPassDrawQuad>();
266       quad->SetAll(render_pass0->shared_quad_state_list.ElementAt(sqs_index),
267                    gfx::Rect(2, 3, 100, 50), gfx::Rect(2, 3, 100, 50), true,
268                    CompositorRenderPassId{198u}, 81u,
269                    gfx::RectF(0.1f, 0.2f, 0.5f, 0.6f), gfx::Size(800, 600),
270                    gfx::Vector2dF(1.1f, 0.9f), gfx::PointF(0.01f, 0.02f),
271                    gfx::RectF(0.2f, 0.3f, 0.3f, 0.4f), true, 0.88f, true);
272       ++sqs_index;
273       ++quad_count;
274     }
275     {
276       // 7. TileDrawQuad
277       TileDrawQuad* quad =
278           render_pass0->CreateAndAppendDrawQuad<TileDrawQuad>();
279       quad->SetAll(render_pass0->shared_quad_state_list.ElementAt(sqs_index),
280                    gfx::Rect(0, 0, 256, 512), gfx::Rect(2, 2, 250, 500), true,
281                    512u, gfx::RectF(0.0f, 0.0f, 0.9f, 0.8f),
282                    gfx::Size(256, 512), true, true, true);
283       ++quad_count;
284     }
285     DCHECK_EQ(kSharedQuadStateCount, sqs_index + 1);
286   }
287   base::Value dict0 = CompositorRenderPassToDict(*render_pass0);
288   auto render_pass1 = CompositorRenderPassFromDict(dict0);
289   EXPECT_TRUE(render_pass1);
290   EXPECT_EQ(kSharedQuadStateCount, render_pass1->shared_quad_state_list.size());
291   EXPECT_EQ(quad_count, render_pass1->quad_list.size());
292   for (size_t ii = 0; ii < quad_count; ++ii) {
293     EXPECT_EQ(kQuadMaterials[ii],
294               render_pass1->quad_list.ElementAt(ii)->material);
295   }
296   base::Value dict1 = CompositorRenderPassToDict(*render_pass1);
297   EXPECT_EQ(dict0, dict1);
298 }
299 
TEST(RenderPassIOTest,CompositorRenderPassList)300 TEST(RenderPassIOTest, CompositorRenderPassList) {
301   // Validate recorded render pass list data from https://www.espn.com/.
302   base::FilePath test_data_dir;
303   ASSERT_TRUE(base::PathService::Get(Paths::DIR_TEST_DATA, &test_data_dir));
304   base::FilePath json_path =
305       test_data_dir.Append(FILE_PATH_LITERAL("render_pass_data"))
306           .Append(FILE_PATH_LITERAL("top_real_world_desktop"))
307           .Append(FILE_PATH_LITERAL("espn_2018"))
308           .Append(FILE_PATH_LITERAL("0463.json"));
309   ASSERT_TRUE(base::PathExists(json_path));
310   std::string json_text;
311   ASSERT_TRUE(base::ReadFileToString(json_path, &json_text));
312 
313   base::Optional<base::Value> dict0 = base::JSONReader::Read(json_text);
314   EXPECT_TRUE(dict0.has_value());
315   CompositorRenderPassList render_pass_list;
316   EXPECT_TRUE(
317       CompositorRenderPassListFromDict(dict0.value(), &render_pass_list));
318   base::Value dict1 = CompositorRenderPassListToDict(render_pass_list);
319   // Since the test file doesn't contain the field
320   // 'can_use_backdrop_filter_cache' in its CompositorRenderPassDrawQuad, I'm
321   // removing the field on dict1 for the exact comparison to work.
322   base::Value* list = dict1.FindListKey("render_pass_list");
323   for (size_t i = 0; i < list->GetList().size(); ++i) {
324     base::Value* quad_list = list->GetList()[i].FindListKey("quad_list");
325 
326     for (size_t ii = 0; ii < quad_list->GetList().size(); ++ii) {
327       if (const base::Value* extra_value = quad_list->GetList()[ii].FindKey(
328               "can_use_backdrop_filter_cache")) {
329         EXPECT_FALSE(extra_value->GetBool());
330         ASSERT_TRUE(quad_list->GetList()[ii].RemoveKey(
331             "can_use_backdrop_filter_cache"));
332       }
333     }
334   }
335 
336   EXPECT_EQ(dict0, dict1);
337 }
338 
339 }  // namespace
340 }  // namespace viz
341