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