1 // Copyright 2017 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 <limits>
6 #include <vector>
7 
8 #include "base/bind.h"
9 #include "base/test/null_task_runner.h"
10 #include "base/time/time.h"
11 #include "base/timer/lap_timer.h"
12 #include "components/viz/common/display/renderer_settings.h"
13 #include "components/viz/common/quads/compositor_frame.h"
14 #include "components/viz/common/quads/compositor_render_pass.h"
15 #include "components/viz/common/quads/draw_quad.h"
16 #include "components/viz/common/quads/texture_draw_quad.h"
17 #include "components/viz/common/surfaces/frame_sink_id.h"
18 #include "components/viz/service/display/aggregated_frame.h"
19 #include "components/viz/service/display/display.h"
20 #include "components/viz/service/display/display_scheduler.h"
21 #include "components/viz/service/display/output_surface.h"
22 #include "components/viz/service/display/overlay_processor_stub.h"
23 #include "components/viz/service/display/shared_bitmap_manager.h"
24 #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
25 #include "components/viz/test/fake_output_surface.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27 #include "testing/perf/perf_result_reporter.h"
28 
29 namespace viz {
30 namespace {
31 
32 static const int kTimeLimitMillis = 3000;
33 static const int kWarmupRuns = 5;
34 static const int kTimeCheckInterval = 10;
35 static const int kHeight = 1000;
36 static const int kWidth = 1000;
37 
38 constexpr char kMetricPrefixRemoveOverdrawQuad[] = "RemoveOverdrawQuad.";
39 constexpr char kMetricOverlapThroughputRunsPerS[] = "overlap_throughput";
40 constexpr char kMetricIsolatedThroughputRunsPerS[] = "isolated_throughput";
41 constexpr char kMetricPartialOverlapThroughputRunsPerS[] =
42     "partial_overlap_throughput";
43 constexpr char kMetricAdjacentThroughputRunsPerS[] = "adjacent_throughput";
44 
SetUpRemoveOverdrawQuadReporter(const std::string & story)45 perf_test::PerfResultReporter SetUpRemoveOverdrawQuadReporter(
46     const std::string& story) {
47   perf_test::PerfResultReporter reporter(kMetricPrefixRemoveOverdrawQuad,
48                                          story);
49   reporter.RegisterImportantMetric(kMetricOverlapThroughputRunsPerS, "runs/s");
50   reporter.RegisterImportantMetric(kMetricIsolatedThroughputRunsPerS, "runs/s");
51   reporter.RegisterImportantMetric(kMetricPartialOverlapThroughputRunsPerS,
52                                    "runs/s");
53   reporter.RegisterImportantMetric(kMetricAdjacentThroughputRunsPerS, "runs/s");
54   return reporter;
55 }
56 
57 class RemoveOverdrawQuadPerfTest : public testing::Test {
58  public:
RemoveOverdrawQuadPerfTest()59   RemoveOverdrawQuadPerfTest()
60       : timer_(kWarmupRuns,
61                base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
62                kTimeCheckInterval),
63         task_runner_(base::MakeRefCounted<base::NullTaskRunner>()) {}
64 
CreateDisplay()65   std::unique_ptr<Display> CreateDisplay() {
66     FrameSinkId frame_sink_id(3, 3);
67 
68     auto scheduler = std::make_unique<DisplayScheduler>(&begin_frame_source_,
69                                                         task_runner_.get(), 1);
70 
71     std::unique_ptr<FakeOutputSurface> output_surface =
72         FakeOutputSurface::Create3d();
73 
74     auto overlay_processor = std::make_unique<OverlayProcessorStub>();
75     // Normally display will need to take ownership of a
76     // gpu::GpuTaskschedulerhelper in order to keep it alive to share between
77     // the output surface and the overlay processor. In this case the overlay
78     // processor is a stub and the output surface is test only as well, so there
79     // is no need to pass in a real gpu::GpuTaskSchedulerHelper.
80     // TODO(weiliangc): Figure out a better way to set up test without passing
81     // in nullptr.
82     auto display = std::make_unique<Display>(
83         &bitmap_manager_, RendererSettings(), &debug_settings_, frame_sink_id,
84         nullptr /* gpu::GpuTaskSchedulerHelper */, std::move(output_surface),
85         std::move(overlay_processor), std::move(scheduler), task_runner_.get());
86     return display;
87   }
88 
89   // Create an arbitrary SharedQuadState for the given |render_pass|.
CreateSharedQuadState(AggregatedRenderPass * render_pass,gfx::Rect rect)90   SharedQuadState* CreateSharedQuadState(AggregatedRenderPass* render_pass,
91                                          gfx::Rect rect) {
92     gfx::Transform quad_transform = gfx::Transform();
93     bool is_clipped = false;
94     bool are_contents_opaque = true;
95     float opacity = 1.f;
96     int sorting_context_id = 65536;
97     SkBlendMode blend_mode = SkBlendMode::kSrcOver;
98 
99     SharedQuadState* state = render_pass->CreateAndAppendSharedQuadState();
100     state->SetAll(quad_transform, rect, rect,
101                   /*mask_filter_info=*/gfx::MaskFilterInfo(), rect, is_clipped,
102                   are_contents_opaque, opacity, blend_mode, sorting_context_id);
103     return state;
104   }
105 
106   // Append draw quads to a given |shared_quad_state|.
AppendQuads(SharedQuadState * shared_quad_state,int quad_height,int quad_width)107   void AppendQuads(SharedQuadState* shared_quad_state,
108                    int quad_height,
109                    int quad_width) {
110     bool needs_blending = false;
111     ResourceId resource_id = 1;
112     bool premultiplied_alpha = true;
113     gfx::PointF uv_top_left(0, 0);
114     gfx::PointF uv_bottom_right(1, 1);
115     SkColor background_color = SK_ColorRED;
116     float vertex_opacity[4] = {1.f, 1.f, 1.f, 1.f};
117     bool y_flipped = false;
118     bool nearest_neighbor = true;
119 
120     int x_left = shared_quad_state->visible_quad_layer_rect.x();
121     int x_right = x_left + shared_quad_state->visible_quad_layer_rect.width();
122     int y_top = shared_quad_state->visible_quad_layer_rect.y();
123     int y_bottom = y_top + shared_quad_state->visible_quad_layer_rect.height();
124     int i = x_left;
125     int j = y_top;
126     while (i + quad_width <= x_right) {
127       while (j + quad_height <= y_bottom) {
128         auto* quad = frame_.render_pass_list.front()
129                          ->CreateAndAppendDrawQuad<TextureDrawQuad>();
130         gfx::Rect rect(i, j, quad_width, quad_height);
131         quad->SetNew(shared_quad_state, rect, rect, needs_blending, resource_id,
132                      premultiplied_alpha, uv_top_left, uv_bottom_right,
133                      background_color, vertex_opacity, y_flipped,
134                      nearest_neighbor, /*secure_output_only=*/false,
135                      gfx::ProtectedVideoType::kClear);
136         j += quad_height;
137       }
138       j = y_top;
139       i += quad_width;
140     }
141   }
142 
143   // All SharedQuadState are overlapping the same region.
144   //  +--------+
145   //  | s1/2/3 |
146   //  +--------+
IterateOverlapShareQuadStates(const std::string & story,int shared_quad_state_count,int quad_count)147   void IterateOverlapShareQuadStates(const std::string& story,
148                                      int shared_quad_state_count,
149                                      int quad_count) {
150     frame_.render_pass_list.push_back(std::make_unique<AggregatedRenderPass>());
151     CreateOverlapShareQuadStates(shared_quad_state_count, quad_count);
152     std::unique_ptr<Display> display = CreateDisplay();
153 
154     timer_.Reset();
155     do {
156       display->RemoveOverdrawQuads(&frame_);
157       timer_.NextLap();
158     } while (!timer_.HasTimeLimitExpired());
159 
160     auto reporter = SetUpRemoveOverdrawQuadReporter(story);
161     reporter.AddResult(kMetricOverlapThroughputRunsPerS,
162                        timer_.LapsPerSecond());
163     frame_ = AggregatedFrame{};
164   }
165 
CreateOverlapShareQuadStates(int shared_quad_state_count,int quad_count)166   void CreateOverlapShareQuadStates(int shared_quad_state_count,
167                                     int quad_count) {
168     int quad_height = kHeight / quad_count;
169     int quad_width = kWidth / quad_count;
170     int total_shared_quad_state =
171         shared_quad_state_count * shared_quad_state_count;
172     for (int i = 0; i < total_shared_quad_state; i++) {
173       gfx::Rect rect(0, 0, kHeight, kWidth);
174       SharedQuadState* new_shared_state(
175           CreateSharedQuadState(frame_.render_pass_list.front().get(), rect));
176       AppendQuads(new_shared_state, quad_height, quad_width);
177     }
178   }
179 
180   // SharedQuadState are non-overlapped as shown in the figure below.
181   // +---+
182   // |s1 |
183   // +---+---+
184   //     |s2 |
185   //     +---+---+
186   //         |s3 |
187   //         +---+
IterateIsolatedSharedQuadStates(const std::string & story,int shared_quad_state_count,int quad_count)188   void IterateIsolatedSharedQuadStates(const std::string& story,
189                                        int shared_quad_state_count,
190                                        int quad_count) {
191     frame_.render_pass_list.push_back(std::make_unique<AggregatedRenderPass>());
192     CreateIsolatedSharedQuadStates(shared_quad_state_count, quad_count);
193     std::unique_ptr<Display> display = CreateDisplay();
194     timer_.Reset();
195     do {
196       display->RemoveOverdrawQuads(&frame_);
197       timer_.NextLap();
198     } while (!timer_.HasTimeLimitExpired());
199 
200     auto reporter = SetUpRemoveOverdrawQuadReporter(story);
201     reporter.AddResult(kMetricIsolatedThroughputRunsPerS,
202                        timer_.LapsPerSecond());
203     frame_ = AggregatedFrame{};
204   }
205 
CreateIsolatedSharedQuadStates(int shared_quad_state_count,int quad_count)206   void CreateIsolatedSharedQuadStates(int shared_quad_state_count,
207                                       int quad_count) {
208     int shared_quad_state_height =
209         kHeight / (shared_quad_state_count * shared_quad_state_count);
210     int shared_quad_state_width =
211         kWidth / (shared_quad_state_count * shared_quad_state_count);
212     int quad_height = shared_quad_state_height / quad_count;
213     int quad_width = shared_quad_state_width / quad_count;
214     int i = 0;
215     int j = 0;
216     while (i + shared_quad_state_height <= kWidth ||
217            j + shared_quad_state_height <= kHeight) {
218       gfx::Rect rect(i, j, shared_quad_state_height, shared_quad_state_width);
219       SharedQuadState* new_shared_state(
220           CreateSharedQuadState(frame_.render_pass_list.front().get(), rect));
221       AppendQuads(new_shared_state, quad_height, quad_width);
222       j += shared_quad_state_height;
223       i += shared_quad_state_width;
224     }
225   }
226 
227   // SharedQuadState are overlapped as shown in the figure below.
228   //  +----+
229   //  | +----+
230   //  | |  +----+
231   //  +-|  |  +----+
232   //    +--|  |    |
233   //       +--|    |
234   //          +----+
IteratePartiallyOverlapSharedQuadStates(const std::string & story,int shared_quad_state_count,float percentage_overlap,int quad_count)235   void IteratePartiallyOverlapSharedQuadStates(const std::string& story,
236                                                int shared_quad_state_count,
237                                                float percentage_overlap,
238                                                int quad_count) {
239     frame_.render_pass_list.push_back(std::make_unique<AggregatedRenderPass>());
240     CreatePartiallyOverlapSharedQuadStates(shared_quad_state_count,
241                                            percentage_overlap, quad_count);
242     std::unique_ptr<Display> display = CreateDisplay();
243     timer_.Reset();
244     do {
245       display->RemoveOverdrawQuads(&frame_);
246       timer_.NextLap();
247     } while (!timer_.HasTimeLimitExpired());
248 
249     auto reporter = SetUpRemoveOverdrawQuadReporter(story);
250     reporter.AddResult(kMetricPartialOverlapThroughputRunsPerS,
251                        timer_.LapsPerSecond());
252     frame_ = AggregatedFrame{};
253   }
254 
CreatePartiallyOverlapSharedQuadStates(int shared_quad_state_count,float percentage_overlap,int quad_count)255   void CreatePartiallyOverlapSharedQuadStates(int shared_quad_state_count,
256                                               float percentage_overlap,
257                                               int quad_count) {
258     int shared_quad_state_height =
259         kHeight / (shared_quad_state_count * shared_quad_state_count);
260     int shared_quad_state_width =
261         kWidth / (shared_quad_state_count * shared_quad_state_count);
262     int quad_height = shared_quad_state_height / quad_count;
263     int quad_width = shared_quad_state_width / quad_count;
264     int i = 0;
265     int j = 0;
266     for (int count = 0; count < shared_quad_state_count; count++) {
267       gfx::Rect rect(i, j, shared_quad_state_height, shared_quad_state_width);
268       SharedQuadState* new_shared_state(
269           CreateSharedQuadState(frame_.render_pass_list.front().get(), rect));
270       AppendQuads(new_shared_state, quad_height, quad_width);
271       i += shared_quad_state_width * percentage_overlap;
272       j += shared_quad_state_height * percentage_overlap;
273     }
274   }
275 
276   // SharedQuadState are all adjacent to each other and added as the order shown
277   // in the figure below.
278   //  +----+----+
279   //  | s1 | s3 |
280   //  +----+----+
281   //  | s2 | s4 |
282   //  +----+----+
IterateAdjacentSharedQuadStates(const std::string & story,int shared_quad_state_count,int quad_count)283   void IterateAdjacentSharedQuadStates(const std::string& story,
284                                        int shared_quad_state_count,
285                                        int quad_count) {
286     frame_.render_pass_list.push_back(std::make_unique<AggregatedRenderPass>());
287     CreateAdjacentSharedQuadStates(shared_quad_state_count, quad_count);
288     std::unique_ptr<Display> display = CreateDisplay();
289 
290     timer_.Reset();
291     do {
292       display->RemoveOverdrawQuads(&frame_);
293       timer_.NextLap();
294     } while (!timer_.HasTimeLimitExpired());
295 
296     auto reporter = SetUpRemoveOverdrawQuadReporter(story);
297     reporter.AddResult(kMetricAdjacentThroughputRunsPerS,
298                        timer_.LapsPerSecond());
299     frame_ = AggregatedFrame{};
300   }
301 
CreateAdjacentSharedQuadStates(int shared_quad_state_count,int quad_count)302   void CreateAdjacentSharedQuadStates(int shared_quad_state_count,
303                                       int quad_count) {
304     int shared_quad_state_height = kHeight / shared_quad_state_count;
305     int shared_quad_state_width = kWidth / shared_quad_state_count;
306     int quad_height = shared_quad_state_height / quad_count;
307     int quad_width = shared_quad_state_width / quad_count;
308     int i = 0;
309     int j = 0;
310     while (i + shared_quad_state_height <= kWidth) {
311       while (j + shared_quad_state_width <= kHeight) {
312         gfx::Rect rect(i, j, shared_quad_state_height, shared_quad_state_width);
313         SharedQuadState* new_shared_state =
314             CreateSharedQuadState(frame_.render_pass_list.front().get(), rect);
315         AppendQuads(new_shared_state, quad_height, quad_width);
316         j += shared_quad_state_height;
317       }
318       j = 0;
319       i += shared_quad_state_width;
320     }
321   }
322 
323  private:
324   DebugRendererSettings debug_settings_;
325   AggregatedFrame frame_;
326   base::LapTimer timer_;
327   StubBeginFrameSource begin_frame_source_;
328   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
329   ServerSharedBitmapManager bitmap_manager_;
330 };
331 
TEST_F(RemoveOverdrawQuadPerfTest,IterateOverlapShareQuadStates)332 TEST_F(RemoveOverdrawQuadPerfTest, IterateOverlapShareQuadStates) {
333   IterateOverlapShareQuadStates("4_sqs_with_4_quads", 2, 2);
334   IterateOverlapShareQuadStates("4_sqs_with_100_quads", 2, 10);
335   IterateOverlapShareQuadStates("100_sqs_with_4_quads", 10, 2);
336   IterateOverlapShareQuadStates("100_sqs_with_100_quads", 10, 10);
337 }
338 
TEST_F(RemoveOverdrawQuadPerfTest,IterateIsolatedSharedQuadStates)339 TEST_F(RemoveOverdrawQuadPerfTest, IterateIsolatedSharedQuadStates) {
340   IterateIsolatedSharedQuadStates("2_sqs_with_4_quads", 2, 2);
341   IterateIsolatedSharedQuadStates("2_sqs_with_100_quads", 2, 10);
342   IterateIsolatedSharedQuadStates("10_sqs_with_4_quads", 10, 2);
343   IterateIsolatedSharedQuadStates("10_sqs_with_100_quads", 10, 10);
344 }
345 
TEST_F(RemoveOverdrawQuadPerfTest,IteratePartiallyOverlapSharedQuadStates)346 TEST_F(RemoveOverdrawQuadPerfTest, IteratePartiallyOverlapSharedQuadStates) {
347   IteratePartiallyOverlapSharedQuadStates("2_sqs_with_4_quads", 2, 0.5, 2);
348   IteratePartiallyOverlapSharedQuadStates("2_sqs_with_100_quads", 2, 0.5, 10);
349   IteratePartiallyOverlapSharedQuadStates("10_sqs_with_4_quads", 10, 0.5, 2);
350   IteratePartiallyOverlapSharedQuadStates("10_sqs_with_100_quads", 10, 0.5, 10);
351 }
352 
TEST_F(RemoveOverdrawQuadPerfTest,IterateAdjacentSharedQuadStates)353 TEST_F(RemoveOverdrawQuadPerfTest, IterateAdjacentSharedQuadStates) {
354   IterateAdjacentSharedQuadStates("4_sqs_with_4_quads", 2, 2);
355   IterateAdjacentSharedQuadStates("4_sqs_with_100_quads", 2, 10);
356   IterateAdjacentSharedQuadStates("100_sqs_with_4_quads", 10, 2);
357   IterateAdjacentSharedQuadStates("100_sqs_with_100_quads", 10, 10);
358 }
359 
360 }  // namespace
361 }  // namespace viz
362